1 /* 2 Copyright 2008-2013 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, true, 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 var eps = Mat.eps; 111 if (Math.abs(this.usrCoords[0]) > eps) { 112 this.usrCoords[1] /= this.usrCoords[0]; 113 this.usrCoords[2] /= this.usrCoords[0]; 114 this.usrCoords[0] = 1.0; 115 } 116 }, 117 118 /** 119 * Compute screen coordinates out of given user coordinates. 120 * @private 121 */ 122 usr2screen: function (doRound) { 123 var mround = Math.round, // Is faster on IE, maybe slower with JIT compilers 124 b = this.board, 125 uc = this.usrCoords, 126 oc = b.origin.scrCoords; 127 128 if (doRound === false) { 129 this.scrCoords[0] = uc[0]; 130 this.scrCoords[1] = uc[0] * oc[1] + uc[1] * b.unitX; 131 this.scrCoords[2] = uc[0] * oc[2] - uc[2] * b.unitY; 132 } else { 133 this.scrCoords[0] = mround(uc[0]); 134 this.scrCoords[1] = mround(uc[0] * oc[1] + uc[1] * b.unitX); 135 this.scrCoords[2] = mround(uc[0] * oc[2] - uc[2] * b.unitY); 136 } 137 }, 138 139 /** 140 * Compute user coordinates out of given screen coordinates. 141 * @private 142 */ 143 screen2usr: function () { 144 var o = this.board.origin.scrCoords, 145 sc = this.scrCoords, 146 b = this.board; 147 148 this.usrCoords[0] = 1.0; 149 this.usrCoords[1] = (sc[1] - o[1]) / b.unitX; 150 this.usrCoords[2] = (o[2] - sc[2]) / b.unitY; 151 }, 152 153 /** 154 * Calculate distance of one point to another. 155 * @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>. 156 * @param {JXG.Coords} coordinates The Coords object to which the distance is calculated. 157 * @returns {Number} The distance 158 */ 159 distance: function (coord_type, coordinates) { 160 var sum = 0, 161 c, 162 ucr = this.usrCoords, 163 scr = this.scrCoords, 164 f; 165 166 if (coord_type === Const.COORDS_BY_USER) { 167 c = coordinates.usrCoords; 168 f = ucr[0] - c[0]; 169 sum = f * f; 170 171 if (sum > Mat.eps) { 172 return Number.POSITIVE_INFINITY; 173 } 174 f = ucr[1] - c[1]; 175 sum += f * f; 176 f = ucr[2] - c[2]; 177 sum += f * f; 178 } else { 179 c = coordinates.scrCoords; 180 //f = scr[0]-c[0]; 181 //sum = f*f; 182 f = scr[1] - c[1]; 183 sum += f * f; 184 f = scr[2] - c[2]; 185 sum += f * f; 186 } 187 188 return Math.sqrt(sum); 189 }, 190 191 /** 192 * Set coordinates by either user coordinates or screen coordinates and recalculate the other one. 193 * @param {Number} coord_type The type of coordinates used here. Possible values are <b>COORDS_BY_USER</b> and <b>COORDS_BY_SCREEN</b>. 194 * @param {Array} coordinates An array of affine coordinates the Coords object is set to. 195 * @param {Boolean} [doRound=true] flag If true or null round the coordinates in usr2screen. This is used in smooth curve plotting. 196 * The IE needs rounded coordinates. Id doRound==false we have to round in updatePathString. 197 * @param {Boolean} [noevent=false] 198 * @returns {JXG.Coords} Reference to the coords object. 199 */ 200 setCoordinates: function (coord_type, coordinates, doRound, noevent) { 201 var uc = this.usrCoords, 202 sc = this.scrCoords, 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 sc[1] = coordinates[0]; 220 sc[2] = coordinates[1]; 221 this.screen2usr(); 222 } 223 224 if (this.emitter && !noevent && (os[1] !== sc[1] || os[2] !== sc[2])) { 225 this.triggerEventHandlers(['update'], [ou, os]); 226 } 227 228 return this; 229 }, 230 231 /** 232 * Copy array, either srcCoords or usrCoords 233 * Uses slice() in case of standard arrays and set() in case of 234 * typed arrays. 235 * @private 236 * @param {String) obj Either 'srcCoords' or 'usrCoords' 237 * @param {Number} offset Offset, defaults to 0 if not given 238 * @returns {Array} Returns copy of the coords array either as standard array or as 239 * typed array. 240 */ 241 copy: function (obj, offset) { 242 if (typeof offset === 'undefined') { 243 offset = 0; 244 } 245 246 return this[obj].slice(offset); 247 }, 248 249 /** 250 * Triggered whenever the coordinates change. 251 * @name JXG.Coords#update 252 * @param {Array} ou Old user coordinates 253 * @param {Array} os Old screen coordinates 254 * @event 255 */ 256 __evt__update: function (ou, os) { }, 257 258 /** 259 * @ignore 260 */ 261 __evt: function () {} 262 }); 263 264 return JXG.Coords; 265 }); 266