1 /* 
  2     Copyright 2008,2009
  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: you can redistribute it and/or modify
 13     it under the terms of the GNU Lesser General Public License as published by
 14     the Free Software Foundation, either version 3 of the License, or
 15     (at your option) any later version.
 16 
 17     JSXGraph is distributed in the hope that it will be useful,
 18     but WITHOUT ANY WARRANTY; without even the implied warranty of
 19     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 20     GNU Lesser General Public License for more details.
 21 
 22     You should have received a copy of the GNU Lesser General Public License
 23     along with JSXGraph.  If not, see <http://www.gnu.org/licenses/>.
 24 */
 25 
 26 /** 
 27  * @fileoverview In this file the Coords object is defined, a class to manage all
 28  * properties and methods coordinates usually have.
 29  * @author graphjs
 30  * @version 0.1
 31  */
 32 
 33 JXG.COORDS_BY_USER = 0x0001;
 34 JXG.COORDS_BY_SCREEN = 0x0002;
 35 
 36 /**
 37  * Constructs a new Coordinates object.
 38  * @class This is the Coordinates class.  
 39  * All members a coordinate has to provide
 40  * are defined here.
 41  * @param {int} method The type of coordinates given by the user. Accepted values are <b>COORDS_BY_SCREEN</b> and <b>COORDS_BY_USER</b>.
 42  * @param {Array} coordinates An array of affine coordinates.
 43  * @param {JXG.AbstractRenderer} renderer A reference to a Renderer.
 44  * @borrows JXG.EventEmitter#on as this.on
 45  * @borrows JXG.EventEmitter#off as this.off
 46  * @borrows JXG.EventEmitter#triggerEventHandlers as this.triggerEventHandlers
 47  * @borrows JXG.EventEmitter#eventHandlers as this.eventHandlers
 48  * @constructor
 49  */
 50 JXG.Coords = function (method, coordinates, board) {
 51     /**
 52      * Stores the board the object is used on.
 53      * @type JXG.Board
 54      */
 55     this.board = board;
 56     
 57     /**
 58      * Stores coordinates for user view as homogeneous coordinates.
 59      * @type Array
 60      */
 61     this.usrCoords = [];
 62     /**
 63      * Stores coordinates for screen view as homogeneous coordinates.
 64      * @type Array
 65      */
 66     this.scrCoords = [];
 67     
 68     JXG.EventEmitter.eventify(this);
 69     this.setCoordinates(method, coordinates);
 70 };
 71 
 72 JXG.extend(JXG.Coords.prototype, /** @lends JXG.Coords.prototype */ {
 73     /**
 74      * Normalize homogeneous coordinates
 75      * @private
 76      */
 77     normalizeUsrCoords: function () {
 78         var eps = JXG.Math.eps;
 79         if (Math.abs(this.usrCoords[0]) > eps) {
 80             this.usrCoords[1] /= this.usrCoords[0];
 81             this.usrCoords[2] /= this.usrCoords[0];
 82             this.usrCoords[0] = 1.0;
 83         }
 84     },
 85 
 86     /**
 87      * Compute screen coordinates out of given user coordinates.
 88      * @private
 89      */
 90     usr2screen: function (doRound) {
 91         var mround = Math.round,  // Is faster on IE, maybe slower with JIT compilers
 92             b = this.board,
 93             uc = this.usrCoords,
 94             oc = b.origin.scrCoords;
 95 
 96         if (doRound === null || doRound) {
 97             this.scrCoords[0] = mround(uc[0]);
 98             this.scrCoords[1] = mround(uc[0]*oc[1] + uc[1]*b.unitX);
 99             this.scrCoords[2] = mround(uc[0]*oc[2] - uc[2]*b.unitY);
100         } else {
101             this.scrCoords[0] = uc[0];
102             this.scrCoords[1] = uc[0]*oc[1] + uc[1]*b.unitX;
103             this.scrCoords[2] = uc[0]*oc[2] - uc[2]*b.unitY;
104         }
105     },
106 
107     /**
108      * Compute user coordinates out of given screen coordinates.
109      * @private
110      */
111     screen2usr: function () {
112         var o = this.board.origin.scrCoords,
113             sc = this.scrCoords,
114             b = this.board;
115         this.usrCoords[0] =  1.0;
116         this.usrCoords[1] = (sc[1] - o[1])/b.unitX;
117         this.usrCoords[2] = (o[2] - sc[2])/b.unitY;
118     },
119 
120     /**
121      * Calculate distance of one point to another.
122      * @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>.
123      * @param {JXG.Coords} coordinates The Coords object to which the distance is calculated.
124      * @returns {Number} The distance
125      */
126     distance: function (coord_type, coordinates) {
127         var sum = 0,
128             c,
129             ucr = this.usrCoords,
130             scr = this.scrCoords,
131             f;
132 
133         if (coord_type === JXG.COORDS_BY_USER) {
134             c = coordinates.usrCoords;
135             f = ucr[0]-c[0];
136             sum = f*f;
137             if (sum>JXG.Math.eps) {
138                 return Number.POSITIVE_INFINITY;
139             }
140             f = ucr[1]-c[1];
141             sum += f*f;
142             f = ucr[2]-c[2];
143             sum += f*f;
144         } else {
145             c = coordinates.scrCoords;
146             //f = scr[0]-c[0];
147             //sum = f*f;
148             f = scr[1]-c[1];
149             sum += f*f;
150             f = scr[2]-c[2];
151             sum += f*f;
152         }
153 
154         return Math.sqrt(sum);
155     },
156 
157     /**
158      * Set coordinates by either user coordinates or screen coordinates and recalculate the other one.
159      * @param {Number} coord_type The type of coordinates used here. Possible values are <b>COORDS_BY_USER</b> and <b>COORDS_BY_SCREEN</b>.
160      * @param {Array} coordinates An array of affine coordinates the Coords object is set to.
161      * @param {Boolean} [doRound=true] flag If true or null round the coordinates in usr2screen. This is used in smooth curve plotting.
162      * The IE needs rounded coordinates. Id doRound==false we have to round in updatePathString.
163      * @returns {JXG.Coords} Reference to the coords object.
164      */
165     setCoordinates: function (coord_type, coordinates, doRound) {
166         var uc = this.usrCoords,
167             sc = this.scrCoords,
168             ou = this.usrCoords.slice(0), os = this.scrCoords.slice(0);
169 
170         if (coord_type === JXG.COORDS_BY_USER) {
171             if (coordinates.length === 2) { // Euclidean coordinates
172                 uc[0] = 1.0;
173                 uc[1] = coordinates[0];
174                 uc[2] = coordinates[1];
175             } else { // Homogeneous coordinates (normalized)
176                 uc[0] = coordinates[0];
177                 uc[1] = coordinates[1];
178                 uc[2] = coordinates[2];
179                 this.normalizeUsrCoords();
180             }
181             this.usr2screen(doRound);
182         } else {
183             sc[1] = coordinates[0];
184             sc[2] = coordinates[1];
185             this.screen2usr();
186         }
187         
188         this.triggerEventHandlers('update', ou, os);
189 
190         return this;
191     },
192 
193     /**
194      * Triggered whenever the coordinates change.
195      * @name JXG.Coords#update
196      * @param {Array} ou Old user coordinates
197      * @param {Array} os Old screen coordinates
198      * @event
199      */
200     __evt__: function (ou, os) { },
201 
202     /**
203      * @ignore
204      */
205     __evt__: function () {}
206 });
207