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 /** 82 * Stores coordinates for screen view as homogeneous coordinates. 83 * @type Array 84 */ 85 this.scrCoords = []; 86 87 /** 88 * If true, this coordinates object will emit update events every time 89 * the coordinates are set. 90 * @type {boolean} 91 * @default true 92 */ 93 this.emitter = !Type.exists(emitter) || emitter; 94 95 if (this.emitter) { 96 EventEmitter.eventify(this); 97 } 98 this.setCoordinates(method, coordinates, true, true); 99 }; 100 101 JXG.extend(JXG.Coords.prototype, /** @lends JXG.Coords.prototype */ { 102 /** 103 * Normalize homogeneous coordinates 104 * @private 105 */ 106 normalizeUsrCoords: function () { 107 var eps = Mat.eps; 108 if (Math.abs(this.usrCoords[0]) > eps) { 109 this.usrCoords[1] /= this.usrCoords[0]; 110 this.usrCoords[2] /= this.usrCoords[0]; 111 this.usrCoords[0] = 1.0; 112 } 113 }, 114 115 /** 116 * Compute screen coordinates out of given user coordinates. 117 * @private 118 */ 119 usr2screen: function (doRound) { 120 var mround = Math.round, // Is faster on IE, maybe slower with JIT compilers 121 b = this.board, 122 uc = this.usrCoords, 123 oc = b.origin.scrCoords; 124 125 if (doRound === null || doRound) { 126 this.scrCoords[0] = mround(uc[0]); 127 this.scrCoords[1] = mround(uc[0] * oc[1] + uc[1] * b.unitX); 128 this.scrCoords[2] = mround(uc[0] * oc[2] - uc[2] * b.unitY); 129 } else { 130 this.scrCoords[0] = uc[0]; 131 this.scrCoords[1] = uc[0] * oc[1] + uc[1] * b.unitX; 132 this.scrCoords[2] = uc[0] * oc[2] - uc[2] * b.unitY; 133 } 134 }, 135 136 /** 137 * Compute user coordinates out of given screen coordinates. 138 * @private 139 */ 140 screen2usr: function () { 141 var o = this.board.origin.scrCoords, 142 sc = this.scrCoords, 143 b = this.board; 144 145 this.usrCoords[0] = 1.0; 146 this.usrCoords[1] = (sc[1] - o[1]) / b.unitX; 147 this.usrCoords[2] = (o[2] - sc[2]) / b.unitY; 148 }, 149 150 /** 151 * Calculate distance of one point to another. 152 * @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>. 153 * @param {JXG.Coords} coordinates The Coords object to which the distance is calculated. 154 * @returns {Number} The distance 155 */ 156 distance: function (coord_type, coordinates) { 157 var sum = 0, 158 c, 159 ucr = this.usrCoords, 160 scr = this.scrCoords, 161 f; 162 163 if (coord_type === Const.COORDS_BY_USER) { 164 c = coordinates.usrCoords; 165 f = ucr[0] - c[0]; 166 sum = f * f; 167 168 if (sum > Mat.eps) { 169 return Number.POSITIVE_INFINITY; 170 } 171 f = ucr[1] - c[1]; 172 sum += f * f; 173 f = ucr[2] - c[2]; 174 sum += f * f; 175 } else { 176 c = coordinates.scrCoords; 177 //f = scr[0]-c[0]; 178 //sum = f*f; 179 f = scr[1] - c[1]; 180 sum += f * f; 181 f = scr[2] - c[2]; 182 sum += f * f; 183 } 184 185 return Math.sqrt(sum); 186 }, 187 188 /** 189 * Set coordinates by either user coordinates or screen coordinates and recalculate the other one. 190 * @param {Number} coord_type The type of coordinates used here. Possible values are <b>COORDS_BY_USER</b> and <b>COORDS_BY_SCREEN</b>. 191 * @param {Array} coordinates An array of affine coordinates the Coords object is set to. 192 * @param {Boolean} [doRound=true] flag If true or null round the coordinates in usr2screen. This is used in smooth curve plotting. 193 * The IE needs rounded coordinates. Id doRound==false we have to round in updatePathString. 194 * @param {Boolean} [noevent=false] 195 * @returns {JXG.Coords} Reference to the coords object. 196 */ 197 setCoordinates: function (coord_type, coordinates, doRound, noevent) { 198 var uc = this.usrCoords, 199 sc = this.scrCoords, 200 ou = [uc[0], uc[1], uc[2]], 201 os = [sc[0], sc[1], sc[2]]; 202 203 if (coord_type === Const.COORDS_BY_USER) { 204 if (coordinates.length === 2) { // Euclidean coordinates 205 uc[0] = 1.0; 206 uc[1] = coordinates[0]; 207 uc[2] = coordinates[1]; 208 } else { // Homogeneous coordinates (normalized) 209 uc[0] = coordinates[0]; 210 uc[1] = coordinates[1]; 211 uc[2] = coordinates[2]; 212 this.normalizeUsrCoords(); 213 } 214 this.usr2screen(doRound); 215 } else { 216 sc[1] = coordinates[0]; 217 sc[2] = coordinates[1]; 218 this.screen2usr(); 219 } 220 221 if (this.emitter && !noevent && (os[1] !== sc[1] || os[2] !== sc[2])) { 222 this.triggerEventHandlers(['update'], [ou, os]); 223 } 224 225 return this; 226 }, 227 228 /** 229 * Triggered whenever the coordinates change. 230 * @name JXG.Coords#update 231 * @param {Array} ou Old user coordinates 232 * @param {Array} os Old screen coordinates 233 * @event 234 */ 235 __evt__update: function (ou, os) { }, 236 237 /** 238 * @ignore 239 */ 240 __evt: function () {} 241 }); 242 243 return JXG.Coords; 244 }); 245