1 /* 2 Copyright 2008-2021 3 Matthias Ehmann, 4 Michael Gerhaeuser, 5 Carsten Miller, 6 Bianca Valentin, 7 Alfred Wassermann, 8 Peter Wilfahrt 9 10 This file is part of JSXGraph. 11 12 JSXGraph is free software dual licensed under the GNU LGPL or MIT License. 13 14 You can redistribute it and/or modify it under the terms of the 15 16 * GNU Lesser General Public License as published by 17 the Free Software Foundation, either version 3 of the License, or 18 (at your option) any later version 19 OR 20 * MIT License: https://github.com/jsxgraph/jsxgraph/blob/master/LICENSE.MIT 21 22 JSXGraph is distributed in the hope that it will be useful, 23 but WITHOUT ANY WARRANTY; without even the implied warranty of 24 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 25 GNU Lesser General Public License for more details. 26 27 You should have received a copy of the GNU Lesser General Public License and 28 the MIT License along with JSXGraph. If not, see <http://www.gnu.org/licenses/> 29 and <http://opensource.org/licenses/MIT/>. 30 */ 31 32 33 /*global JXG: true, define: true, AMprocessNode: true, MathJax: true, document: true */ 34 /*jslint nomen: true, plusplus: true*/ 35 36 /* depends: 37 jxg 38 base/constants 39 utils/event 40 math/math 41 */ 42 43 define([ 44 'jxg', 'base/constants', 'utils/event', 'utils/type', 'math/math' 45 ], function (JXG, Const, EventEmitter, Type, Mat) { 46 47 "use strict"; 48 49 /** 50 * @fileoverview In this file the Coords object is defined, a class to manage all 51 * properties and methods coordinates usually have. 52 */ 53 54 /** 55 * Constructs a new Coordinates object. 56 * @class This is the Coordinates class. 57 * All members a coordinate has to provide 58 * are defined here. 59 * @param {Number} method The type of coordinates given by the user. Accepted values are <b>COORDS_BY_SCREEN</b> and <b>COORDS_BY_USER</b>. 60 * @param {Array} coordinates An array of affine coordinates. 61 * @param {JXG.Board} board A reference to a board. 62 * @oaram {Boolean} [emitter=true] 63 * @borrows JXG.EventEmitter#on as this.on 64 * @borrows JXG.EventEmitter#off as this.off 65 * @borrows JXG.EventEmitter#triggerEventHandlers as this.triggerEventHandlers 66 * @borrows JXG.EventEmitter#eventHandlers as this.eventHandlers 67 * @constructor 68 */ 69 JXG.Coords = function (method, coordinates, board, emitter) { 70 /** 71 * Stores the board the object is used on. 72 * @type JXG.Board 73 */ 74 this.board = board; 75 76 /** 77 * Stores coordinates for user view as homogeneous coordinates. 78 * @type Array 79 */ 80 this.usrCoords = []; 81 //this.usrCoords = new Float64Array(3); 82 83 /** 84 * Stores coordinates for screen view as homogeneous coordinates. 85 * @type Array 86 */ 87 this.scrCoords = []; 88 //this.scrCoords = new Float64Array(3); 89 90 /** 91 * If true, this coordinates object will emit update events every time 92 * the coordinates are set. 93 * @type boolean 94 * @default true 95 */ 96 this.emitter = !Type.exists(emitter) || emitter; 97 98 if (this.emitter) { 99 EventEmitter.eventify(this); 100 } 101 this.setCoordinates(method, coordinates, false, true); 102 }; 103 104 JXG.extend(JXG.Coords.prototype, /** @lends JXG.Coords.prototype */ { 105 /** 106 * Normalize homogeneous coordinates 107 * @private 108 */ 109 normalizeUsrCoords: function () { 110 if (Math.abs(this.usrCoords[0]) > Mat.eps) { 111 this.usrCoords[1] /= this.usrCoords[0]; 112 this.usrCoords[2] /= this.usrCoords[0]; 113 this.usrCoords[0] = 1.0; 114 } 115 }, 116 117 /** 118 * Compute screen coordinates out of given user coordinates. 119 * @private 120 */ 121 usr2screen: function (doRound) { 122 var mround = Math.round, // Is faster on IE, maybe slower with JIT compilers 123 b = this.board, 124 uc = this.usrCoords, 125 oc = b.origin.scrCoords; 126 127 if (doRound === true) { 128 this.scrCoords[0] = mround(uc[0]); 129 this.scrCoords[1] = mround(uc[0] * oc[1] + uc[1] * b.unitX); 130 this.scrCoords[2] = mround(uc[0] * oc[2] - uc[2] * b.unitY); 131 } else { 132 this.scrCoords[0] = uc[0]; 133 this.scrCoords[1] = uc[0] * oc[1] + uc[1] * b.unitX; 134 this.scrCoords[2] = uc[0] * oc[2] - uc[2] * b.unitY; 135 } 136 }, 137 138 /** 139 * Compute user coordinates out of given screen coordinates. 140 * @private 141 */ 142 screen2usr: function () { 143 var o = this.board.origin.scrCoords, 144 sc = this.scrCoords, 145 b = this.board; 146 147 this.usrCoords[0] = 1.0; 148 this.usrCoords[1] = (sc[1] - o[1]) / b.unitX; 149 this.usrCoords[2] = (o[2] - sc[2]) / b.unitY; 150 }, 151 152 /** 153 * Calculate distance of one point to another. 154 * @param {Number} coord_type The type of coordinates used here. Possible values are <b>JXG.COORDS_BY_USER</b> and <b>JXG.COORDS_BY_SCREEN</b>. 155 * @param {JXG.Coords} coordinates The Coords object to which the distance is calculated. 156 * @returns {Number} The distance 157 */ 158 distance: function (coord_type, coordinates) { 159 var sum = 0, 160 c, 161 ucr = this.usrCoords, 162 scr = this.scrCoords, 163 f; 164 165 if (coord_type === Const.COORDS_BY_USER) { 166 c = coordinates.usrCoords; 167 f = ucr[0] - c[0]; 168 sum = f * f; 169 170 if (sum > Mat.eps * Mat.eps) { 171 return Number.POSITIVE_INFINITY; 172 } 173 f = ucr[1] - c[1]; 174 sum += f * f; 175 f = ucr[2] - c[2]; 176 sum += f * f; 177 } else { 178 c = coordinates.scrCoords; 179 //f = scr[0]-c[0]; 180 //sum = f*f; 181 f = scr[1] - c[1]; 182 sum += f * f; 183 f = scr[2] - c[2]; 184 sum += f * f; 185 } 186 187 return Math.sqrt(sum); 188 }, 189 190 /** 191 * Set coordinates by either user coordinates or screen coordinates and recalculate the other one. 192 * @param {Number} coord_type The type of coordinates used here. Possible values are <b>COORDS_BY_USER</b> and <b>COORDS_BY_SCREEN</b>. 193 * @param {Array} coordinates An array of affine coordinates the Coords object is set to. 194 * @param {Boolean} [doRound=true] flag If true or null round the coordinates in usr2screen. This is used in smooth curve plotting. 195 * The IE needs rounded coordinates. Id doRound==false we have to round in updatePathString. 196 * @param {Boolean} [noevent=false] 197 * @returns {JXG.Coords} Reference to the coords object. 198 */ 199 setCoordinates: function (coord_type, coordinates, doRound, noevent) { 200 var uc = this.usrCoords, 201 sc = this.scrCoords, 202 // Original values 203 ou = [uc[0], uc[1], uc[2]], 204 os = [sc[0], sc[1], sc[2]]; 205 206 if (coord_type === Const.COORDS_BY_USER) { 207 if (coordinates.length === 2) { // Euclidean coordinates 208 uc[0] = 1.0; 209 uc[1] = coordinates[0]; 210 uc[2] = coordinates[1]; 211 } else { // Homogeneous coordinates (normalized) 212 uc[0] = coordinates[0]; 213 uc[1] = coordinates[1]; 214 uc[2] = coordinates[2]; 215 this.normalizeUsrCoords(); 216 } 217 this.usr2screen(doRound); 218 } else { 219 if (coordinates.length === 2) { // Euclidean coordinates 220 sc[1] = coordinates[0]; 221 sc[2] = coordinates[1]; 222 } else { // Homogeneous coordinates (normalized) 223 sc[1] = coordinates[1]; 224 sc[2] = coordinates[2]; 225 } 226 this.screen2usr(); 227 } 228 229 if (this.emitter && !noevent && (os[1] !== sc[1] || os[2] !== sc[2])) { 230 this.triggerEventHandlers(['update'], [ou, os]); 231 } 232 233 return this; 234 }, 235 236 /** 237 * Copy array, either srcCoords or usrCoords 238 * Uses slice() in case of standard arrays and set() in case of 239 * typed arrays. 240 * @private 241 * @param {String} obj Either 'srcCoords' or 'usrCoords' 242 * @param {Number} offset Offset, defaults to 0 if not given 243 * @returns {Array} Returns copy of the coords array either as standard array or as 244 * typed array. 245 */ 246 copy: function (obj, offset) { 247 if (offset === undefined) { 248 offset = 0; 249 } 250 251 return this[obj].slice(offset); 252 }, 253 254 /** 255 * Test if one of the usrCoords is NaN or the coordinates are infinite. 256 * @returns {Boolean} true if the coordinates are finite, false otherwise. 257 */ 258 isReal: function() { 259 return (!isNaN(this.usrCoords[1] + this.usrCoords[2])) && (Math.abs(this.usrCoords[0]) > Mat.eps); 260 }, 261 262 /** 263 * Triggered whenever the coordinates change. 264 * @name JXG.Coords#update 265 * @param {Array} ou Old user coordinates 266 * @param {Array} os Old screen coordinates 267 * @event 268 */ 269 __evt__update: function (ou, os) { }, 270 271 /** 272 * @ignore 273 */ 274 __evt: function () {} 275 }); 276 277 return JXG.Coords; 278 }); 279