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