      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             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          * Triggered whenever the coordinates change.
    256          * @name JXG.Coords#update
    257          * @param {Array} ou Old user coordinates
    258          * @param {Array} os Old screen coordinates
    259          * @event
    260          */
    261         __evt__update: function (ou, os) { },
    262
    263         /**
    264          * @ignore
    265          */
    266         __evt: function () {}
    267     });
    268
    269     return JXG.Coords;
    270 });
    271