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