1 /*
  2     Copyright 2008-2011
  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 The geometry object Point is defined in this file. Point stores all
 28  * style and functional properties that are required to draw and move a point on
 29  * a board.
 30  */
 31 
 32 
 33 /**
 34  * A point is the basic geometric element. Based on points lines and circles can be constructed which can be intersected
 35  * which in turn are points again which can be used to construct new lines, circles, polygons, etc. This class holds methods for
 36  * all kind of points like free points, gliders, and intersection points.
 37  * @class Creates a new point object. Do not use this constructor to create a point. Use {@link JXG.Board#create} with
 38  * type {@link Point}, {@link Glider}, or {@link Intersection} instead.  
 39  * @augments JXG.GeometryElement
 40  * @param {string|JXG.Board} board The board the new point is drawn on.
 41  * @param {Array} coordinates An array with the affine user coordinates of the point.
 42  * @param {Object} attributes An object containing visual properties like in {@link JXG.Options#point} and
 43  * {@link JXG.Options#elements}, and optional a name and a id.
 44  * @see JXG.Board#generateName
 45  * @see JXG.Board#addPoint
 46  */
 47 JXG.Point = function (board, coordinates, attributes) {
 48     this.constructor(board, attributes, JXG.OBJECT_TYPE_POINT, JXG.OBJECT_CLASS_POINT);
 49     
 50     if (coordinates==null) {
 51         coordinates=[0,0];
 52     }
 53     /**
 54      * Coordinates of the point.
 55      * @type JXG.Coords
 56      * @private
 57      */
 58     this.coords = new JXG.Coords(JXG.COORDS_BY_USER, coordinates, this.board);
 59     this.initialCoords = new JXG.Coords(JXG.COORDS_BY_USER, coordinates, this.board);
 60         
 61     /**
 62      * Relative position on a line if point is a glider on a line.
 63      * @type Number
 64      * @private
 65      */
 66     this.position = null;
 67 
 68     /**
 69      * Determines whether the point slides on a polygon if point is a glider.
 70      * @type boolean
 71      * @default false
 72      * @private
 73      */
 74     this.onPolygon = false;
 75         
 76     /**
 77      * When used as a glider this member stores the object, where to glide on. To set the object to glide on use the method
 78      * {@link JXG.Point#makeGlider} and DO NOT set this property directly as it will break the dependency tree.
 79      * TODO: Requires renaming to glideObject
 80      * @type JXG.GeometryElement
 81      * @name Glider#slideObject
 82      */
 83     this.slideObject = null;
 84 
 85     this.Xjc = null;
 86     this.Yjc = null;
 87 
 88     // documented in GeometryElement
 89     this.methodMap = JXG.deepCopy(this.methodMap, {
 90         move: 'moveTo',
 91         glide: 'makeGlider',
 92         X: 'X',
 93         Y: 'Y',
 94         free: 'free',
 95         setPosition: 'setGliderPosition'
 96     });
 97 
 98     /**
 99      * Stores the groups of this point in an array of Group.
100      * @type array
101      * @see JXG.Group
102      * @private
103      */
104     this.group = [];
105 
106     this.elType = 'point';
107 
108     /* Register point at board. */
109     this.id = this.board.setId(this, 'P');
110     this.board.renderer.drawPoint(this);
111     this.board.finalizeAdding(this);
112 
113     this.createLabel();
114 };
115 
116 /**
117  * Inherits here from {@link JXG.GeometryElement}.
118  */
119 JXG.Point.prototype = new JXG.GeometryElement();
120 
121 
122 JXG.extend(JXG.Point.prototype, /** @lends JXG.Point.prototype */ {
123     /**
124      * Checks whether (x,y) is near the point.
125      * @param {int} x Coordinate in x direction, screen coordinates.
126      * @param {int} y Coordinate in y direction, screen coordinates.
127      * @type boolean
128      * @return True if (x,y) is near the point, False otherwise.
129      * @private
130      */
131     hasPoint: function (x,y) {
132         var coordsScr = this.coords.scrCoords, r;
133         r = parseFloat(this.visProp.size) + parseFloat(this.visProp.strokewidth)*0.5;
134         if(r < this.board.options.precision.hasPoint) {
135             r = this.board.options.precision.hasPoint;
136         }
137 
138         return ((Math.abs(coordsScr[1]-x) < r+2) && (Math.abs(coordsScr[2]-y)) < r+2);
139     },
140 
141     /**
142     * Dummy function for unconstrained points or gliders.
143     * @private
144     */
145     updateConstraint: function() { return this; },
146 
147     /**
148      * Updates the position of the point.
149      */
150     update: function (fromParent) {
151         if (!this.needsUpdate) { return this; }
152 
153         if(typeof fromParent == 'undefined') {
154             fromParent = false;
155         }
156             
157         /*
158          * We need to calculate the new coordinates no matter of the points visibility because
159          * a child could be visible and depend on the coordinates of the point (e.g. perpendicular).
160          * 
161          * Check if point is a glider and calculate new coords in dependency of this.slideObject.
162          * This function is called with fromParent==true for example if
163          * the defining elements of the line or circle have been changed.
164          */
165         if(this.type == JXG.OBJECT_TYPE_GLIDER) {
166             if (fromParent) {
167                 this.updateGliderFromParent();
168             } else {
169                 this.updateGlider();
170             }
171         }
172                 
173         /**
174         * If point is a calculated point, call updateConstraint() to calculate new coords. 
175         * The second test is for dynamic axes.
176         */
177         if (this.type == JXG.OBJECT_TYPE_CAS || this.type == JXG.OBJECT_TYPE_AXISPOINT) {
178             this.updateConstraint();
179         }
180 
181         this.updateTransform();
182                 
183         if(this.visProp.trace) {
184             this.cloneToBackground(true);
185         }
186 
187         return this;
188     },
189 
190     /**
191     * Update of glider in case of dragging the glider or setting the postion of the glider.
192     * The relative position of the glider has to be updated.
193     * If the second point is an ideal point, then -1 < this.position < 1,
194     * this.position==+/-1 equals point2, this.position==0 equals point1
195     * 
196     * If the first point is an ideal point, then 0 < this.position < 2
197     * this.position==0  or 2 equals point1, this.position==1 equals point2
198     * 
199     * @private
200     */
201     updateGlider: function() {
202         var i, p1c, p2c, d, v, poly, cc, pos, sgn,
203             slide = this.slideObject, alpha, beta, angle,
204             cp, c, invMat;
205 
206         if (slide.elementClass == JXG.OBJECT_CLASS_CIRCLE) {
207             this.coords  = JXG.Math.Geometry.projectPointToCircle(this, slide, this.board);
208             this.position = JXG.Math.Geometry.rad([slide.center.X()+1.0,slide.center.Y()],slide.center,this);
209         } else if (slide.elementClass == JXG.OBJECT_CLASS_LINE) {
210 
211             /** 
212              * onPolygon==true: the point is a slider on a seg,ent and this segment is one of the 
213              * "borders" of a polygon.
214              * This is a GEONExT feature.
215              **/
216             if (this.onPolygon) {
217                 p1c = slide.point1.coords.usrCoords;
218                 p2c = slide.point2.coords.usrCoords;
219                 i = 1;
220                 d = p2c[i] - p1c[i];
221                 if (Math.abs(d)<JXG.Math.eps) { 
222                     i = 2; 
223                     d = p2c[i] - p1c[i];
224                 }
225                 cc = JXG.Math.Geometry.projectPointToLine(this, slide, this.board);
226                 pos = (cc.usrCoords[i] - p1c[i]) / d;
227                 poly = slide.parentPolygon;
228 
229                 if (pos<0.0) {
230                     for (i=0; i<poly.borders.length; i++) {
231                         if (slide == poly.borders[i]) {
232                             slide = poly.borders[(i - 1 + poly.borders.length) % poly.borders.length];
233                             break;
234                         }
235                     }
236                 } else if (pos>1.0) {
237                     for (i=0; i<poly.borders.length; i++) {
238                         if(slide == poly.borders[i]) {
239                             slide = poly.borders[(i + 1 + poly.borders.length) % poly.borders.length];
240                             break;                        
241                         }
242                     }
243                 }
244             }
245 
246             p1c = slide.point1.coords;
247             p2c = slide.point2.coords;
248             // Distance between the two defining points
249             d = p1c.distance(JXG.COORDS_BY_USER, p2c);
250             p1c = p1c.usrCoords.slice(0);
251             p2c = p2c.usrCoords.slice(0);
252                         
253             if (d<JXG.Math.eps) {                                        // The defining points are identical
254                 this.coords.setCoordinates(JXG.COORDS_BY_USER, p1c);
255                 this.position = 0.0;
256             } else {
257                 this.coords = JXG.Math.Geometry.projectPointToLine(this, slide, this.board);
258                 if (Math.abs(p2c[0])<JXG.Math.eps) {                 // The second point is an ideal point
259                     i = 1;
260                     d = p2c[i];
261                     if (Math.abs(d)<JXG.Math.eps) { 
262                         i = 2; 
263                         d = p2c[i];
264                     }
265                     d = (this.coords.usrCoords[i] - p1c[i]) / d;
266                     sgn = (d>=0) ? 1 : -1;
267                     d = Math.abs(d);
268                     this.position = sgn * d/(d+1);
269                 } else if (Math.abs(p1c[0])<JXG.Math.eps) {          // The first point is an ideal point
270                     i = 1;
271                     d = p1c[i];
272                     if (Math.abs(d)<JXG.Math.eps) { 
273                         i = 2; 
274                         d = p1c[i];
275                     }
276                     d = (this.coords.usrCoords[i] - p2c[i]) / d;
277                     if (d<0.0) {
278                         this.position = (1 - 2.0*d) / (1.0 - d); // 1.0 - d/(1-d);
279                     } else {
280                         this.position = 1/(d+1);
281                     }
282                 } else {
283                     i = 1;
284                     d = p2c[i] - p1c[i];
285                     if (Math.abs(d)<JXG.Math.eps) { 
286                         i = 2; 
287                         d = p2c[i] - p1c[i];
288                     }
289                     this.position = (this.coords.usrCoords[i] - p1c[i]) / d;
290                 }
291             }        
292                                 
293             // Snap the glider point of the slider into its appropiate position
294             // First, recalculate the new value of this.position
295             // Second, call update(fromParent==true) to make the positioning snappier.
296             if (this.visProp.snapwidth>0.0 && Math.abs(this._smax-this._smin)>=JXG.Math.eps) {
297                 if (this.position<0.0) this.position = 0.0;
298                 if (this.position>1.0) this.position = 1.0;
299                                         
300                 v = this.position*(this._smax-this._smin)+this._smin;
301                 v = Math.round(v/this.visProp.snapwidth)*this.visProp.snapwidth;
302                 this.position = (v-this._smin)/(this._smax-this._smin);
303                 this.update(true);
304             }
305 
306             p1c = slide.point1.coords.usrCoords;
307             if (!slide.visProp.straightfirst && Math.abs(p1c[0])>JXG.Math.eps && this.position<0) {
308                 this.coords.setCoordinates(JXG.COORDS_BY_USER, p1c);
309                 this.position = 0;
310             }
311             p2c = slide.point2.coords.usrCoords;
312             if (!slide.visProp.straightlast && Math.abs(p2c[0])>JXG.Math.eps && this.position>1) {
313                 this.coords.setCoordinates(JXG.COORDS_BY_USER, p2c);
314                 this.position = 1;
315             }
316         
317 
318         } else if (slide.type == JXG.OBJECT_TYPE_TURTLE) {
319             this.updateConstraint(); // In case, the point is a constrained glider.
320             this.coords  = JXG.Math.Geometry.projectPointToTurtle(this, slide, this.board);  // side-effect: this.position is overwritten
321         } else if(slide.elementClass == JXG.OBJECT_CLASS_CURVE) {
322                         
323             if ((slide.type == JXG.OBJECT_TYPE_ARC 
324                 || slide.type == JXG.OBJECT_TYPE_SECTOR)) {
325                                 
326                 this.coords  = JXG.Math.Geometry.projectPointToCircle(this, slide, this.board);
327 
328                 angle = JXG.Math.Geometry.rad(slide.radiuspoint, slide.center, this);
329                 alpha = 0.0;
330                 beta = JXG.Math.Geometry.rad(slide.radiuspoint, slide.center, slide.anglepoint);
331                 this.position = angle;
332                                 
333 
334                 if ((slide.visProp.type=='minor' && beta>Math.PI)
335                     || (slide.visProp.type=='major' && beta<Math.PI)) { 
336                     alpha = beta; 
337                     beta = 2*Math.PI;
338                 }      
339                                                 
340                 if (angle<alpha || angle>beta) {         // Correct the position if we are outside of the sector/arc
341                     this.position = beta;
342                     if ((angle<alpha && angle>alpha*0.5) || (angle>beta && angle>beta*0.5 + Math.PI)) {
343                         this.position = alpha;
344                     }
345                     this.updateGliderFromParent();
346                 } 
347 
348             } else {
349                 this.updateConstraint(); // In case, the point is a constrained glider.
350 
351                 if (slide.transformations.length>0) {
352                     slide.updateTransformMatrix();
353                     invMat = JXG.Math.inverse(slide.transformMat);
354                     c = JXG.Math.matVecMult(invMat, this.coords.usrCoords);
355                                         
356                     cp = (new JXG.Coords(JXG.COORDS_BY_USER, c, this.board)).usrCoords;
357                     c = JXG.Math.Geometry.projectCoordsToCurve(cp[1], cp[2], this.position||0.0, slide, this.board);
358                     this.position = c[1];      // side effect !
359                     this.coords = c[0];
360                 } else {
361                     this.coords  = JXG.Math.Geometry.projectPointToCurve(this, slide, this.board);  
362                     // side-effect: this.position is overwritten
363                 }
364             }
365                         
366         } else if(slide.elementClass == JXG.OBJECT_CLASS_POINT) {
367             this.coords  = JXG.Math.Geometry.projectPointToPoint(this, slide, this.board);
368         }
369     },
370 
371     /**
372     * Update of a glider in case a parent element has been updated. That means the 
373     * relative position of the glider stays the same.
374     * @private
375     */
376     updateGliderFromParent: function() {
377         var p1c, p2c, r, lbda, c,
378             slide = this.slideObject, alpha;
379 
380         if(slide.elementClass == JXG.OBJECT_CLASS_CIRCLE) {
381             r = slide.Radius();
382             this.coords.setCoordinates(JXG.COORDS_BY_USER, [
383                     slide.center.X() + r*Math.cos(this.position),
384                     slide.center.Y() + r*Math.sin(this.position)
385                 ]);
386         } else if(slide.elementClass == JXG.OBJECT_CLASS_LINE) {
387             p1c = slide.point1.coords.usrCoords;
388             p2c = slide.point2.coords.usrCoords;
389             if (Math.abs(p2c[0])<JXG.Math.eps) {                        // The second point is an ideal point
390                 lbda = Math.min(Math.abs(this.position), 1.0-JXG.Math.eps);
391                 lbda /= (1.0-lbda);
392                 if (this.position < 0) {
393                     lbda *= -1.0;
394                 }
395                 this.coords.setCoordinates(JXG.COORDS_BY_USER, [
396                     p1c[0] + lbda*p2c[0],
397                     p1c[1] + lbda*p2c[1],
398                     p1c[2] + lbda*p2c[2]
399                 ]);
400             } else if (Math.abs(p1c[0])<JXG.Math.eps) {                 // The first point is an ideal point
401                 lbda = Math.max(this.position, JXG.Math.eps);
402                 lbda = Math.min(lbda, 2.0-JXG.Math.eps);
403                 if (lbda > 1.0) {
404                     lbda = (lbda-1)/(lbda-2);
405                 } else {
406                     lbda = (1.0-lbda)/lbda;
407                 }
408                 this.coords.setCoordinates(JXG.COORDS_BY_USER, [
409                     p2c[0] + lbda*p1c[0],
410                     p2c[1] + lbda*p1c[1],
411                     p2c[2] + lbda*p1c[2]
412                 ]);
413             } else {
414                 lbda = this.position;
415                 this.coords.setCoordinates(JXG.COORDS_BY_USER, [
416                     p1c[0] + lbda*(p2c[0]-p1c[0]),
417                     p1c[1] + lbda*(p2c[1]-p1c[1]),
418                     p1c[2] + lbda*(p2c[2]-p1c[2])
419                 ]);
420             }
421         } else if(slide.type == JXG.OBJECT_TYPE_TURTLE) {
422             this.coords.setCoordinates(JXG.COORDS_BY_USER, [slide.Z(this.position), slide.X(this.position), slide.Y(this.position)]);
423             this.updateConstraint(); // In case, the point is a constrained glider.
424             this.coords  = JXG.Math.Geometry.projectPointToTurtle(this, slide, this.board);  // side-effect: this.position is overwritten
425         } else if(slide.elementClass == JXG.OBJECT_CLASS_CURVE) {
426             this.coords.setCoordinates(JXG.COORDS_BY_USER, [slide.Z(this.position), slide.X(this.position), slide.Y(this.position)]);
427                         
428             if (slide.type == JXG.OBJECT_TYPE_ARC || slide.type == JXG.OBJECT_TYPE_SECTOR) {
429                 alpha = JXG.Math.Geometry.rad([slide.center.X()+1, slide.center.Y()], slide.center, slide.radiuspoint);
430                 r = slide.Radius();
431                 this.coords.setCoordinates(JXG.COORDS_BY_USER, [
432                         slide.center.X() + r*Math.cos(this.position+alpha),
433                         slide.center.Y() + r*Math.sin(this.position+alpha)
434                     ]);
435             } else {
436                 this.updateConstraint(); // In case, the point is a constrained glider.
437                 this.coords  = JXG.Math.Geometry.projectPointToCurve(this, slide, this.board);  
438                 // side-effect: this.position is overwritten
439             }
440                         
441         } else if(slide.elementClass == JXG.OBJECT_CLASS_POINT) {
442             this.coords  = JXG.Math.Geometry.projectPointToPoint(this, slide, this.board);
443         }
444     },
445 
446     /**
447      * Calls the renderer to update the drawing.
448      * @private
449      */
450     updateRenderer: function () {
451         if (!this.needsUpdate) { return this; }
452 
453         /* Call the renderer only if point is visible. */
454         if(this.visProp.visible && this.visProp.size > 0) {
455             var wasReal = this.isReal;
456             this.isReal = (!isNaN(this.coords.usrCoords[1] + this.coords.usrCoords[2]));
457             this.isReal = (Math.abs(this.coords.usrCoords[0])>JXG.Math.eps)?this.isReal:false;  //Homogeneous coords: ideal point
458             if (this.isReal) {
459                 if (wasReal!=this.isReal) { 
460                     this.board.renderer.show(this); 
461                     if(this.hasLabel && this.label.content.visProp.visible) this.board.renderer.show(this.label.content);
462                 }
463                 this.board.renderer.updatePoint(this);
464             } else {
465                 if (wasReal!=this.isReal) { 
466                     this.board.renderer.hide(this); 
467                     if(this.hasLabel && this.label.content.visProp.visible) this.board.renderer.hide(this.label.content);
468                 }
469             }
470         } 
471 
472         /* Update the label if visible. */
473         if(this.hasLabel && this.visProp.visible && this.label.content && this.label.content.visProp.visible && this.isReal) {
474             this.label.content.update();
475             this.board.renderer.updateText(this.label.content);
476         }
477                 
478         this.needsUpdate = false; 
479         return this;
480     },
481 
482     /**
483      * Getter method for x, this is used by for CAS-points to access point coordinates.
484      * @return User coordinate of point in x direction.
485      * @type Number
486      */
487     X: function () {
488         return this.coords.usrCoords[1];
489     },
490 
491     /**
492      * Getter method for y, this is used by CAS-points to access point coordinates.
493      * @return User coordinate of point in y direction.
494      * @type Number
495      */
496     Y: function () {
497         return this.coords.usrCoords[2];
498     },
499 
500     /**
501      * Getter method for z, this is used by CAS-points to access point coordinates.
502      * @return User coordinate of point in z direction.
503      * @type Number
504      */
505     Z: function () {
506         return this.coords.usrCoords[0];
507     },
508 
509     /**
510      * New evaluation of the function term. 
511      * This is required for CAS-points: Their XTerm() method is overwritten in {@link #addConstraint}
512      * @return User coordinate of point in x direction.
513      * @type Number
514      * @private
515      */
516     XEval: function () {
517         return this.coords.usrCoords[1];
518     },
519 
520     /**
521      * New evaluation of the function term. 
522      * This is required for CAS-points: Their YTerm() method is overwritten in {@link #addConstraint}
523      * @return User coordinate of point in y direction.
524      * @type Number
525      * @private
526      */
527     YEval: function () {
528         return this.coords.usrCoords[2];
529     },
530 
531     /**
532      * New evaluation of the function term. 
533      * This is required for CAS-points: Their ZTerm() method is overwritten in {@link #addConstraint}
534      * @return User coordinate of point in z direction.
535      * @type Number
536      * @private
537      */
538     ZEval: function () {
539         return this.coords.usrCoords[0];
540     },
541 
542     // documented in JXG.GeometryElement
543     bounds: function () {
544         return this.coords.usrCoords.slice(1).concat(this.coords.usrCoords.slice(1));
545     },
546 
547     /**
548      * Getter method for the distance to a second point, this is required for CAS-elements.
549      * Here, function inlining seems to be worthwile  (for plotting).
550      * @param {JXG.Point} point2 The point to which the distance shall be calculated.
551      * @returns {Number} Distance in user coordinate to the given point
552      */
553     Dist: function(point2) {
554         var sum,
555             c = point2.coords.usrCoords,
556             ucr = this.coords.usrCoords,
557             f;
558                         
559         f = ucr[0]-c[0];
560         sum = f*f;
561         f = ucr[1]-c[1];
562         sum += f*f;
563         f = ucr[2]-c[2];
564         sum += f*f;
565         return Math.sqrt(sum);
566     },
567 
568     snapToGrid: function () {
569         return this.handleSnapToGrid();
570     },
571 
572     /**
573      * Move a point to its nearest grid point.
574      * The function uses the coords object of the point as
575      * its actual position.
576      **/
577     handleSnapToGrid: function() {
578         var x, y, sX = this.visProp.snapsizex, sY = this.visProp.snapsizey;
579                 
580         if (this.visProp.snaptogrid) {
581             x = this.coords.usrCoords[1];
582             y = this.coords.usrCoords[2];
583                         
584             if (sX <= 0 && this.board.defaultAxes && this.board.defaultAxes.x.defaultTicks) {
585                 sX = this.board.defaultAxes.x.defaultTicks.ticksDelta*(this.board.defaultAxes.x.defaultTicks.visProp.minorticks+1);
586             }
587 
588             if (sY <= 0 && this.board.defaultAxes && this.board.defaultAxes.y.defaultTicks) {
589                 sY = this.board.defaultAxes.y.defaultTicks.ticksDelta*(this.board.defaultAxes.y.defaultTicks.visProp.minorticks+1);
590             }
591 
592             // if no valid snap sizes are available, don't change the coords.
593             if (sX > 0 && sY > 0) {
594                 this.coords.setCoordinates(JXG.COORDS_BY_USER, [Math.round(x/sX)*sX, Math.round(y/sY)*sY]);
595             }
596         }
597         return this;
598     },
599   
600     /**
601      * Let a point snap to the nearest point in distance of 
602      * {@link JXG.Point#attractorDistance}. 
603      * The function uses the coords object of the point as
604      * its actual position.
605      **/
606     handleSnapToPoints: function() {
607         var el, pEl, pCoords, d=0.0, dMax=Infinity, c=null;
608                 
609         if (this.visProp.snaptopoints) {
610             for (el in this.board.objects) {
611                 pEl = this.board.objects[el];
612                 if (pEl.elementClass==JXG.OBJECT_CLASS_POINT && pEl!==this && pEl.visProp.visible) {
613                     pCoords = JXG.Math.Geometry.projectPointToPoint(this, pEl, this.board); 
614                     d = pCoords.distance(JXG.COORDS_BY_USER, this.coords);
615                     if (d<this.visProp.attractordistance && d<dMax) {
616                         dMax = d;
617                         c = pCoords;
618                     } 
619                 }
620             }
621             if (c!=null) {
622                 this.coords.setCoordinates(JXG.COORDS_BY_USER, c.usrCoords);
623             }
624         }
625 
626         return this;
627     },
628           
629     /**
630      * A point can change its type from free point to glider
631      * and vice versa. If it is given an array of attractor elements 
632      * (attribute attractors) and the attribute attractorDistance
633      * then the pint will be made a glider if it less than attractorDistance
634      * apart from one of its attractor elements.
635      * If attractorDistance is equal to zero, the point stays in its
636      * current form.
637      **/
638     handleAttractors: function() {
639         var len = this.visProp.attractors.length,
640             i, el, projCoords, d = 0.0;
641                         
642         if (this.visProp.attractordistance==0.0) {
643             return;
644         }
645 
646         for (i=0; i<len; i++) {
647             el = JXG.getRef(this.board, this.visProp.attractors[i]);
648             if (!JXG.exists(el) || el===this) {
649                 continue;
650             }
651             if (el.elementClass==JXG.OBJECT_CLASS_POINT) {
652                 projCoords = JXG.Math.Geometry.projectPointToPoint(this, el, this.board);
653             } else if (el.elementClass==JXG.OBJECT_CLASS_LINE) {
654                 projCoords = JXG.Math.Geometry.projectPointToLine(this, el, this.board);
655             } else if (el.elementClass==JXG.OBJECT_CLASS_CIRCLE) {
656                 projCoords = JXG.Math.Geometry.projectPointToCircle(this, el, this.board);
657             } else if (el.elementClass==JXG.OBJECT_CLASS_CURVE) {
658                 projCoords = JXG.Math.Geometry.projectPointToCurve(this, el, this.board);
659             } else if (el.type == JXG.OBJECT_TYPE_TURTLE) {
660                 projCoords = JXG.Math.Geometry.projectPointToTurtle(this, el, this.board);
661             }
662             d = projCoords.distance(JXG.COORDS_BY_USER, this.coords);
663             if (d<this.visProp.attractordistance) {
664                 found = true;
665                 if (!(this.type==JXG.OBJECT_TYPE_GLIDER && this.slideObject==el)) {
666                     this.makeGlider(el);
667                 }
668                 break;
669             } else {
670                 if (el==this.slideObject && d>=this.visProp.snatchdistance) {
671                     this.type = JXG.OBJECT_TYPE_POINT;
672                 }
673             }
674         }
675 
676         return this;
677     },
678         
679     /**
680      * Sets coordinates and calls the point's update() method.
681      * @param {Number} method The type of coordinates used here. Possible values are {@link JXG.COORDS_BY_USER} and {@link JXG.COORDS_BY_SCREEN}.
682      * @param {Array} coords coordinates <tt>(z, x, y)</tt> in screen/user units
683      * @returns {JXG.Point}
684      */
685     setPositionDirectly: function (method, coords) {
686         var i, dx, dy, dz, el, p,
687             oldCoords = this.coords,
688             newCoords;
689 
690         this.coords.setCoordinates(method, coords);
691         this.handleSnapToGrid();
692         this.handleSnapToPoints();
693         this.handleAttractors();
694                 
695         if(this.group.length > 0) {
696             // Here used to be the udpate of the groups. I'm not sure why we don't need to execute
697             // the else branch if there are groups defined on this point, hence I'll let the if live.
698         } else {
699             // Update the initial coordinates. This is needed for free points
700             // that have a transformation bound to it.
701             for (i = this.transformations.length - 1; i >= 0; i--) {
702                 if (method === JXG.COORDS_BY_SCREEN) {
703                     newCoords = (new JXG.Coords(method, coords, this.board)).usrCoords;
704                 } else {
705                     if (coords.length === 2) {
706                         coords = [1].concat(coords);
707                     }
708                     newCoords = coords;
709                 }
710                 this.initialCoords.setCoordinates(JXG.COORDS_BY_USER, JXG.Math.matVecMult(JXG.Math.inverse(this.transformations[i].matrix), newCoords));
711             }
712             this.update();
713         }
714 
715         return this;
716     },
717 
718     /**
719      * Translates the point by <tt>tv = (x, y)</tt>.
720      * @param {Number} method The type of coordinates used here. Possible values are {@link JXG.COORDS_BY_USER} and {@link JXG.COORDS_BY_SCREEN}.
721      * @param {Number} tv (x, y)
722      * @returns {JXG.Point}
723      */
724     setPositionByTransform: function (method, tv) {
725         var t;
726 
727         tv = new JXG.Coords(method, tv, this.board);
728         t = this.board.create('transform', tv.usrCoords.slice(1), {type:'translate'});
729 
730         if (this.transformations.length > 0 && this.transformations[this.transformations.length - 1].isNumericMatrix) {
731             this.transformations[this.transformations.length - 1].melt(t);
732         } else {
733             this.addTransform(this, t);
734         }
735 
736         //if (this.group.length == 0) {
737         this.update();
738         //}
739         return this;
740     },
741 
742     /**
743      * Sets coordinates and calls the point's update() method.
744      * @param {Number} method The type of coordinates used here. Possible values are {@link JXG.COORDS_BY_USER} and {@link JXG.COORDS_BY_SCREEN}.
745      * @param {Array} coords coordinates in screen/user units
746      * @returns {JXG.Point}
747      */
748     setPosition: function (method, coords) {
749         return this.setPositionDirectly(method, coords);
750     },
751 
752     /**
753      * Sets the position of a glider relative to the defining elements of the {@link JXG.Point#slideObject}.
754      * @param {Number} x
755      * @returns {JXG.Point} Reference to the point element.
756      */
757     setGliderPosition: function (x) {
758         if (this.type = JXG.OBJECT_TYPE_GLIDER) {
759             this.position = x;
760             this.board.update();
761         }
762                 
763         return this;
764     },
765 
766     /**
767      * Convert the point to glider and update the construction.
768      * @param {String|Object} glideObject The Object the point will be bound to.
769      */
770     makeGlider: function (glideObject) {
771         //var c = this.coords.usrCoords.slice(1);
772         this.slideObject = JXG.getRef(this.board, glideObject);
773         this.type = JXG.OBJECT_TYPE_GLIDER;
774         this.elType = 'glider';
775         this.visProp.snapwidth = -1;          // By default, deactivate snapWidth
776         this.slideObject.addChild(this);
777         this.isDraggable = true;
778 
779         this.generatePolynomial = function() {
780             return this.slideObject.generatePolynomial(this);
781         };
782 
783         this.updateGlider(); // Determine the initial value of this.position
784         //this.moveTo(c);
785         //this.prepareUpdate().update().updateRenderer();
786         return this;
787     },
788 
789     /**
790      * Converts a glider into a free point.
791      */
792     free: function () {
793         var anc, child;
794 
795         if (this.type !== JXG.OBJECT_TYPE_GLIDER) {
796             if (!this.isDraggable) {
797                 this.isDraggable = true;
798                 this.type = JXG.OBJECT_TYPE_POINT;
799 
800                 this.XEval = function () {
801                     return this.coords.usrCoords[1];
802                 };
803 
804                 this.YEval = function () {
805                     return this.coords.usrCoords[2];
806                 };
807 
808                 this.ZEval = function () {
809                     return this.coords.usrCoords[0];
810                 };
811 
812                 this.Xjc = null;
813                 this.Yjc = null;
814             } else {
815                 return;
816             }
817         }
818 
819         for (anc in this.ancestors) {
820             if (this.ancestors[anc].descendants[this.id])
821                 delete this.ancestors[anc].descendants[this.id];
822           
823             if (this.ancestors[anc].childElements[this.id])
824                 delete this.ancestors[anc].childElements[this.id];
825                         
826             for (child in this.descendants) {
827                 if (this.ancestors[anc].descendants[child])
828                     delete this.ancestors[anc].descendants[child];
829               
830                 if (this.ancestors[anc].childElements[child])
831                     delete this.ancestors[anc].childElements[child];
832             }
833         }
834 
835         this.ancestors = []; // only remove the reference
836 
837         this.slideObject = null;
838         this.elType = 'point';
839         this.type = JXG.OBJECT_TYPE_POINT;
840     },
841 
842     /**
843      * Convert the point to CAS point and call update().
844      * @param {Array} terms [[zterm], xterm, yterm] defining terms for the z, x and y coordinate.
845      * The z-coordinate is optional and it is used for homogeneous coordinates.
846      * The coordinates may be either <ul>
847      *   <li>a JavaScript function,</li>
848      *   <li>a string containing GEONExT syntax. This string will be converted into a JavaScript 
849      *     function here,</li>
850      *   <li>a Number</li>
851      *   <li>a pointer to a slider object. This will be converted into a call of the Value()-method 
852      *     of this slider.</li>
853      *   </ul>
854      * @see JXG.GeonextParser#geonext2JS
855      */
856     addConstraint: function (terms) {
857         this.type = JXG.OBJECT_TYPE_CAS;
858         var newfuncs = [],
859             fs, i, v, t,
860             what = ['X', 'Y'];
861                 
862         this.isDraggable = false;
863         for (i=0;i<terms.length;i++) {
864             v = terms[i];
865             if (typeof v=='string') {
866                 // Convert GEONExT syntax into  JavaScript syntax
867                 //t  = JXG.GeonextParser.geonext2JS(v, this.board);
868                 //newfuncs[i] = new Function('','return ' + t + ';');
869                 newfuncs[i] = this.board.jc.snippet(v, true, null, true);
870 
871                 if (terms.length === 2) {
872                     this[what[i] + 'jc'] = terms[i];
873                 }
874             } else if (typeof v=='function') {
875                 newfuncs[i] = v;
876             } else if (typeof v=='number') {
877                 newfuncs[i] = function(z){ return function() { return z; }; }(v);
878             } else if (typeof v == 'object' && typeof v.Value == 'function') {    // Slider
879                 newfuncs[i] = (function(a) { return function() { return a.Value(); };})(v);
880             }
881 
882             newfuncs[i].origin = v;
883         }
884         if (terms.length==1) { // Intersection function
885             this.updateConstraint = function() { 
886                     var c = newfuncs[0](); 
887                     if (JXG.isArray(c)) {      // Array
888                         this.coords.setCoordinates(JXG.COORDS_BY_USER,c);
889                     } else {                   // Coords object
890                         this.coords = c;
891                     }
892                 };
893         } else if (terms.length==2) { // Euclidean coordinates
894             this.XEval = newfuncs[0];
895             this.YEval = newfuncs[1];
896             fs = 'this.coords.setCoordinates(' + JXG.COORDS_BY_USER + ',[this.XEval(),this.YEval()]);';
897             this.updateConstraint = new Function('',fs);
898         } else { // Homogeneous coordinates
899             this.ZEval = newfuncs[0];
900             this.XEval = newfuncs[1];
901             this.YEval = newfuncs[2];
902             fs = 'this.coords.setCoordinates(' + JXG.COORDS_BY_USER + ',[this.ZEval(),this.XEval(),this.YEval()]);';
903             this.updateConstraint = new Function('',fs);
904         }
905 
906         if (!this.board.isSuspendedUpdate) { this.prepareUpdate().update().updateRenderer(); }
907         return this;
908     },
909 
910     /**
911      * Applies the transformations of the curve to {@link JXG.Point#baseElement}.
912      * @returns {JXG.Point} Reference to this point object.
913      */
914     updateTransform: function () {
915         if (this.transformations.length==0 || this.baseElement==null) {
916             return this;
917         }
918         var c, i;
919 
920         if (this===this.baseElement) {      // case of bindTo
921             c = this.transformations[0].apply(this.baseElement, 'self');
922         } else {                           // case of board.create('point',[baseElement,transform]);
923             c = this.transformations[0].apply(this.baseElement);
924         }
925         this.coords.setCoordinates(JXG.COORDS_BY_USER,c);
926         for (i = 1; i < this.transformations.length; i++) {
927             this.coords.setCoordinates(JXG.COORDS_BY_USER, this.transformations[i].apply(this));
928         }
929         return this;
930     },
931 
932     /**
933      * Add transformations to this point.
934      * @param {JXG.GeometryElement} el TODO base element
935      * @param {JXG.Transform|Array} transform Either one {@link JXG.Transform} or an array of {@link JXG.Transform}s.
936      * @returns {JXG.Point} Reference to this point object.
937      */
938     addTransform: function (el, transform) {
939         var i,
940             list = JXG.isArray(transform) ? transform : [transform],
941             len = list.length;
942 
943         if (this.transformations.length === 0) { // There is only one baseElement possible
944             this.baseElement = el;
945         }
946 
947         for (i = 0; i < len; i++) {
948             this.transformations.push(list[i]);
949         }
950         return this;
951     },
952 
953     /**
954      * Animate the point. 
955      * @param {Number} direction The direction the glider is animated. Can be +1 or -1.
956      * @param {Number} stepCount The number of steps.
957      * @name Glider#startAnimation
958      * @see Glider#stopAnimation
959      * @function
960      */
961     startAnimation: function(direction, stepCount) {
962         if((this.type == JXG.OBJECT_TYPE_GLIDER) && (typeof this.intervalCode == 'undefined')) {
963             this.intervalCode = window.setInterval('JXG.JSXGraph.boards[\'' + this.board.id + '\'].objects[\'' + this.id + '\']._anim(' 
964                                                     + direction + ', ' + stepCount + ')', 250);
965             if(typeof this.intervalCount == 'undefined')
966                 this.intervalCount = 0;
967         }
968         return this;
969     },
970 
971     /**
972      * Stop animation.
973      * @name Glider#stopAnimation
974      * @see Glider#startAnimation
975      * @function
976      */
977     stopAnimation: function() {
978         if(typeof this.intervalCode != 'undefined') {
979             window.clearInterval(this.intervalCode);
980             delete(this.intervalCode);
981         }
982         return this;
983     },
984 
985     /**
986      * Starts an animation which moves the point along a given path in given time.
987      * @param {Array,function} path The path the point is moved on. This can be either an array of arrays containing x and y values of the points of
988      * the path, or  function taking the amount of elapsed time since the animation has started and returns an array containing a x and a y value or NaN.
989      * In case of NaN the animation stops.
990      * @param {Number} time The time in milliseconds in which to finish the animation
991      * @param {Object} [options] Optional settings for the animation:<ul><li>callback: A function that is called as soon as the animation is finished.</li></ul>
992      * @returns {JXG.Point} Reference to the point.
993      */
994     moveAlong: function(path, time, options) {
995         options = options || {};
996         var interpath = [],
997             delay = this.board.options.animationDelay,
998             makeFakeFunction = function (i, j) {
999                 return function() {
1000                     return path[i][j];
1001                 };
1002             },
1003             p = [], i, neville,
1004             steps = time/delay;
1005 
1006         if (JXG.isArray(path)) {
1007             for (i = 0; i < path.length; i++) {
1008                 if (JXG.isPoint(path[i])) {
1009                     p[i] = path[i];
1010                 } else {
1011                     p[i] = {
1012                         elementClass: JXG.OBJECT_CLASS_POINT,
1013                         X: makeFakeFunction(i, 0),
1014                         Y: makeFakeFunction(i, 1)
1015                     };
1016                 }
1017             }
1018 
1019             time = time || 0;
1020             if (time === 0) {
1021                 this.setPosition(JXG.COORDS_BY_USER, [p[p.length - 1].X(), p[p.length - 1].Y()]);
1022                 return this.board.update(this);
1023             }
1024 
1025             neville = JXG.Math.Numerics.Neville(p);
1026             for (i = 0; i < steps; i++) {
1027                 interpath[i] = [];
1028                 interpath[i][0] = neville[0]((steps - i) / steps * neville[3]());
1029                 interpath[i][1] = neville[1]((steps - i) / steps * neville[3]());
1030             }
1031 
1032             this.animationPath = interpath;
1033         } else if (JXG.isFunction(path)) {
1034             this.animationPath = path;
1035             this.animationStart = new Date().getTime();
1036         }
1037         this.animationCallback = options.callback;
1038 
1039         this.board.addAnimation(this);
1040         return this;
1041     },
1042 
1043     /**
1044      * Starts an animated point movement towards the given coordinates <tt>where</tt>. The animation is done after <tt>time</tt> milliseconds.
1045      * If the second parameter is not given or is equal to 0, setPosition() is called, see #setPosition.
1046      * @param {Array} where Array containing the x and y coordinate of the target location.
1047      * @param {Number} [time] Number of milliseconds the animation should last.
1048      * @param {Object} [options] Optional settings for the animation:<ul><li>callback: A function that is called as soon as the animation is finished.</li>
1049      * <li>effect: animation effects like speed fade in and out. possible values are '<>' for speed increase on start and slow down at the end (default)
1050      * and '--' for constant speed during the whole animation.</li></ul>
1051      * @returns {JXG.Point} Reference to itself.
1052      * @see #animate
1053      */
1054     moveTo: function(where, time, options) {
1055         where = new JXG.Coords(JXG.COORDS_BY_USER, where, this.board);
1056 
1057         if (typeof time == 'undefined' || time == 0 || (Math.abs(where.usrCoords[0] - this.coords.usrCoords[0]) > JXG.Math.eps)) {
1058             this.setPosition(JXG.COORDS_BY_USER, where.usrCoords);
1059             return this.board.update(this);
1060         }
1061 
1062         options = options || {};
1063                 
1064         var delay = this.board.options.animationDelay,
1065             steps = Math.ceil(time/(delay * 1.0)),
1066             coords = new Array(steps+1),
1067             X = this.coords.usrCoords[1],
1068             Y = this.coords.usrCoords[2],
1069             dX = (where.usrCoords[1] - X),
1070             dY = (where.usrCoords[2] - Y),
1071             i,
1072             stepFun = function (i) {
1073                 if (options.effect && options.effect == '<>') {
1074                     return Math.pow(Math.sin((i/(steps*1.0))*Math.PI/2.), 2);
1075                 }
1076                 return i/steps;
1077             };
1078 
1079         if(Math.abs(dX) < JXG.Math.eps && Math.abs(dY) < JXG.Math.eps) {
1080             return this;
1081         }
1082 
1083         for(i=steps; i>=0; i--) {
1084             coords[steps-i] = [where.usrCoords[0], X + dX * stepFun(i), Y+ dY * stepFun(i)];
1085         }
1086 
1087         this.animationPath = coords;
1088         this.animationCallback = options.callback;
1089         this.board.addAnimation(this);
1090         return this;
1091     },
1092 
1093     /**
1094      * Starts an animated point movement towards the given coordinates <tt>where</tt>. After arriving at <tt>where</tt> the point moves back to where it started.
1095      * The animation is done after <tt>time</tt> milliseconds.
1096      * @param {Array} where Array containing the x and y coordinate of the target location.
1097      * @param {Number} time Number of milliseconds the animation should last.
1098      * @param {Object} [options] Optional settings for the animation:<ul><li>callback: A function that is called as soon as the animation is finished.</li>
1099      * <li>effect: animation effects like speed fade in and out. possible values are '<>' for speed increase on start and slow down at the end (default)
1100      * and '--' for constant speed during the whole animation.</li><li>repeat: How often this animation should be repeated (default: 1)</li></ul>
1101      * @returns {JXG.Point} Reference to itself.
1102      * @see #animate
1103      */
1104     visit: function(where, time, options) {
1105         // support legacy interface where the third parameter was the number of repeats
1106         if (typeof options == 'number') {
1107             options = {repeat: options};
1108         } else {
1109             options = options || {};
1110             if(typeof options.repeat == 'undefined')
1111                 options.repeat = 1;
1112         }
1113 
1114         var delay = this.board.options.animationDelay,
1115             steps = Math.ceil(time/(delay*options.repeat)),
1116             coords = new Array(options.repeat*(steps+1)),
1117             X = this.coords.usrCoords[1],
1118             Y = this.coords.usrCoords[2],
1119             dX = (where[0] - X),
1120             dY = (where[1] - Y),
1121             i, j,
1122             stepFun = function (i) {
1123                 var x = (i < steps/2 ? 2*i/steps : 2*(steps-i)/steps);
1124                 if (options.effect && options.effect == '<>') {
1125                     return Math.pow(Math.sin((x)*Math.PI/2.), 2);
1126                 }
1127                 return x;
1128             };
1129 
1130         for (j = 0; j < options.repeat; j++) {
1131             for(i = steps; i >= 0; i--) {
1132                 coords[j*(steps+1) + steps-i] = [where[0], X + dX * stepFun(i), Y+ dY * stepFun(i)];
1133             }
1134         }
1135         this.animationPath = coords;
1136         this.animationCallback = options.callback;
1137         this.board.addAnimation(this);
1138         return this;
1139     },
1140 
1141     /**
1142      * Animates a glider. Is called by the browser after startAnimation is called.
1143      * @param {Number} direction The direction the glider is animated.
1144      * @param {Number} stepCount The number of steps.
1145      * @see #startAnimation
1146      * @see #stopAnimation
1147      * @private
1148      */
1149     _anim: function(direction, stepCount) {
1150         var distance, slope, dX, dY, alpha, startPoint,
1151             factor = 1, newX, radius;
1152                 
1153         this.intervalCount++;
1154         if(this.intervalCount > stepCount)
1155             this.intervalCount = 0;
1156                 
1157         if(this.slideObject.elementClass == JXG.OBJECT_CLASS_LINE) {
1158             distance = this.slideObject.point1.coords.distance(JXG.COORDS_BY_SCREEN, this.slideObject.point2.coords);
1159             slope = this.slideObject.getSlope();
1160             if(slope != 'INF') {
1161                 alpha = Math.atan(slope);
1162                 dX = Math.round((this.intervalCount/stepCount) * distance*Math.cos(alpha));
1163                 dY = Math.round((this.intervalCount/stepCount) * distance*Math.sin(alpha));
1164             } else {
1165                 dX = 0;
1166                 dY = Math.round((this.intervalCount/stepCount) * distance);
1167             }
1168                         
1169             if(direction < 0) {
1170                 startPoint = this.slideObject.point2;
1171                 if(this.slideObject.point2.coords.scrCoords[1] - this.slideObject.point1.coords.scrCoords[1] > 0)
1172                     factor = -1;
1173                 else if(this.slideObject.point2.coords.scrCoords[1] - this.slideObject.point1.coords.scrCoords[1] == 0) {
1174                     if(this.slideObject.point2.coords.scrCoords[2] - this.slideObject.point1.coords.scrCoords[2] > 0)
1175                         factor = -1;
1176                 }
1177             } else {
1178                 startPoint = this.slideObject.point1;
1179                 if(this.slideObject.point1.coords.scrCoords[1] - this.slideObject.point2.coords.scrCoords[1] > 0)
1180                     factor = -1;
1181                 else if(this.slideObject.point1.coords.scrCoords[1] - this.slideObject.point2.coords.scrCoords[1] == 0) {
1182                     if(this.slideObject.point1.coords.scrCoords[2] - this.slideObject.point2.coords.scrCoords[2] > 0)
1183                         factor = -1;
1184                 }
1185             }
1186                         
1187             this.coords.setCoordinates(JXG.COORDS_BY_SCREEN, [startPoint.coords.scrCoords[1] + factor*dX, 
1188                                                               startPoint.coords.scrCoords[2] + factor*dY]);
1189         } else if(this.slideObject.elementClass == JXG.OBJECT_CLASS_CURVE) {
1190             if(direction > 0) {
1191                 newX = Math.round(this.intervalCount/stepCount * this.board.canvasWidth);
1192             } else {
1193                 newX = Math.round((stepCount - this.intervalCount)/stepCount * this.board.canvasWidth);
1194             }
1195             
1196             this.coords.setCoordinates(JXG.COORDS_BY_SCREEN, [newX, 0]);
1197             this.coords = JXG.Math.Geometry.projectPointToCurve(this, this.slideObject, this.board);
1198         } else if(this.slideObject.elementClass == JXG.OBJECT_CLASS_CIRCLE) {
1199             if(direction < 0) {
1200                 alpha = this.intervalCount/stepCount * 2*Math.PI;
1201             } else {
1202                 alpha = (stepCount - this.intervalCount)/stepCount * 2*Math.PI;
1203             }
1204 
1205             radius = this.slideObject.Radius();
1206 
1207             this.coords.setCoordinates(JXG.COORDS_BY_USER, [this.slideObject.center.coords.usrCoords[1] + radius*Math.cos(alpha),
1208                                                             this.slideObject.center.coords.usrCoords[2] + radius*Math.sin(alpha)]);
1209         }
1210                 
1211         this.board.update(this);
1212         return this;
1213     },
1214 
1215     /**
1216      * Set the style of a point. Used for GEONExT import and should not be used to set the point's face and size.
1217      * @param {Number} i Integer to determine the style.
1218      * @private
1219      */
1220     setStyle: function(i) {
1221         var facemap = [
1222                 // 0-2
1223                 'cross', 'cross', 'cross',
1224                 // 3-6
1225                 'circle', 'circle', 'circle', 'circle',
1226                 // 7-9
1227                 'square', 'square', 'square',
1228                 // 10-12
1229                 'plus', 'plus', 'plus'
1230             ], sizemap = [
1231                 // 0-2
1232                 2, 3, 4,
1233                 // 3-6
1234                 1, 2, 3, 4,
1235                 // 7-9
1236                 2, 3, 4,
1237                 // 10-12
1238                 2, 3, 4
1239             ];
1240 
1241         this.visProp.face = facemap[i];
1242         this.visProp.size = sizemap[i];
1243 
1244         this.board.renderer.changePointStyle(this);
1245         return this;
1246     },
1247 
1248     /**
1249      * All point faces can be defined with more than one name, e.g. a cross faced point can be given
1250      * by face equal to 'cross' or equal to 'x'. This method maps all possible values to fixed ones to
1251      * simplify if- and switch-clauses regarding point faces. The translation table is as follows:
1252      * <table>
1253      * <tr><th>Input</th><th>Output</th></tr>
1254      * <tr><td>cross, x</td><td>x</td></tr>
1255      * <tr><td>circle, o</td><td>o</td></tr>
1256      * <tr><td>square, []</td><td>[]</td></tr>
1257      * <tr><td>plus, +</td><td>+</td></tr>
1258      * <tr><td>diamond, <></td><td><></td></tr>
1259      * <tr><td>triangleup, a, ^</td><td>A</td></tr>
1260      * <tr><td>triangledown, v</td><td>v</td></tr>
1261      * <tr><td>triangleleft, <</td><td><</td></tr>
1262      * <tr><td>triangleright, ></td><td>></td></tr>
1263      * </table>
1264      * @param {String} s A string which should determine a valid point face.
1265      * @returns {String} Returns a normalized string or undefined if the given string is not a valid
1266      * point face.
1267      */
1268     normalizeFace: function(s) {
1269         var map = {
1270                 cross: 'x',
1271                 x: 'x',
1272                 circle: 'o',
1273                 o: 'o',
1274                 square: '[]',
1275                 '[]': '[]',
1276                 plus: '+',
1277                 '+': '+',
1278                 diamond: '<>',
1279                 '<>': '<>',
1280                 triangleup: '^',
1281                 a: '^',
1282                 '^': '^',
1283                 triangledown: 'v',
1284                 v: 'v',
1285                 triangleleft: '<',
1286                 '<': '<',
1287                 triangleright: '>',
1288                 '>': '>'
1289             };
1290 
1291         return map[s];
1292     },
1293 
1294     /**
1295      * Remove the point from the drawing. This only removes the SVG or VML node of the point and its label from the renderer, to remove
1296      * the object completely you should use {@link JXG.Board#removeObject}.
1297      */
1298     remove: function() {    
1299         if (this.hasLabel) {
1300             this.board.renderer.remove(this.board.renderer.getElementById(this.label.content.id));
1301         }
1302         this.board.renderer.remove(this.board.renderer.getElementById(this.id));
1303     },
1304 
1305     // documented in GeometryElement
1306     getTextAnchor: function() {
1307         return this.coords;
1308     },
1309 
1310     // documented in GeometryElement
1311     getLabelAnchor: function() {
1312         return this.coords;
1313     },
1314 
1315     /**
1316      * Set the face of a point element.
1317      * @param {string} f String which determines the face of the point. See {@link JXG.GeometryElement#face} for a list of available faces.
1318      * @see JXG.GeometryElement#face
1319      */
1320     face: function(f) {
1321         this.setProperty({face:f});
1322     },
1323 
1324     /**
1325      * Set the size of a point element
1326      * @param {int} s Integer which determines the size of the point.
1327      * @see JXG.GeometryElement#size
1328      */
1329     size: function(s) {
1330         this.setProperty({size:s});
1331     },
1332 
1333     // already documented in GeometryElement
1334     cloneToBackground: function() {
1335         var copy = {};
1336 
1337         copy.id = this.id + 'T' + this.numTraces;
1338         this.numTraces++;
1339 
1340         copy.coords = this.coords;
1341         copy.visProp = JXG.deepCopy(this.visProp, this.visProp.traceattributes, true);
1342         copy.visProp.layer = this.board.options.layer.trace;
1343         copy.elementClass = JXG.OBJECT_CLASS_POINT;
1344         copy.board = this.board;
1345         JXG.clearVisPropOld(copy);
1346                 
1347         this.board.renderer.drawPoint(copy);
1348         this.traces[copy.id] = copy.rendNode;
1349                 
1350         return this;
1351     },
1352 
1353     getParents: function () {
1354         var p = [this.Z(), this.X(), this.Y()];
1355 
1356         if (this.parents) {
1357             p = this.parents;
1358         }
1359 
1360         if (this.type == JXG.OBJECT_TYPE_GLIDER) {
1361             p = [this.X(), this.Y(), this.slideObject.id];
1362 
1363         }
1364 
1365         return p;
1366     }
1367 });
1368 
1369 
1370 /**
1371  * @class This element is used to provide a constructor for a general point. A free point is created if the given parent elements are all numbers
1372  * and the property fixed is not set or set to false. If one or more parent elements is not a number but a string containing a GEONE<sub>x</sub>T
1373  * constraint or a function the point will be considered as constrained). That means that the user won't be able to change the point's
1374  * position directly.
1375  * @pseudo
1376  * @description
1377  * @name Point
1378  * @augments JXG.Point
1379  * @constructor
1380  * @type JXG.Point
1381  * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
1382  * @param {Number,string,function_Number,string,function_Number,string,function} z_,x,y Parent elements can be two or three elements of type number, a string containing a GEONE<sub>x</sub>T
1383  * constraint, or a function which takes no parameter and returns a number. Every parent element determines one coordinate. If a coordinate is
1384  * given by a number, the number determines the initial position of a free point. If given by a string or a function that coordinate will be constrained
1385  * that means the user won't be able to change the point's position directly by mouse because it will be calculated automatically depending on the string
1386  * or the function's return value. If two parent elements are given the coordinates will be interpreted as 2D affine euclidean coordinates, if three such
1387  * parent elements are given they will be interpreted as homogeneous coordinates.
1388  * @param {JXG.Point_JXG.Transformation} Point,Transformation A point can also be created providing a transformation. The resulting point is a clone of the base
1389  * point transformed by the given Transformation. {@see JXG.Transformation}.
1390  * @example
1391  * // Create a free point using affine euclidean coordinates 
1392  * var p1 = board.create('point', [3.5, 2.0]);
1393  * </pre><div id="672f1764-7dfa-4abc-a2c6-81fbbf83e44b" style="width: 200px; height: 200px;"></div>
1394  * <script type="text/javascript">
1395  *   var board = JXG.JSXGraph.initBoard('672f1764-7dfa-4abc-a2c6-81fbbf83e44b', {boundingbox: [-1, 5, 5, -1], axis: true, showcopyright: false, shownavigation: false});
1396  *   var p1 = board.create('point', [3.5, 2.0]);
1397  * </script><pre>
1398  * @example
1399  * // Create a constrained point using anonymous function 
1400  * var p2 = board.create('point', [3.5, function () { return p1.X(); }]);
1401  * </pre><div id="4fd4410c-3383-4e80-b1bb-961f5eeef224" style="width: 200px; height: 200px;"></div>
1402  * <script type="text/javascript">
1403  *   var fpex1_board = JXG.JSXGraph.initBoard('4fd4410c-3383-4e80-b1bb-961f5eeef224', {boundingbox: [-1, 5, 5, -1], axis: true, showcopyright: false, shownavigation: false});
1404  *   var fpex1_p1 = fpex1_board.create('point', [3.5, 2.0]);
1405  *   var fpex1_p2 = fpex1_board.create('point', [3.5, function () { return fpex1_p1.X(); }]);
1406  * </script><pre>
1407  * @example
1408  * // Create a point using transformations 
1409  * var trans = board.create('transform', [2, 0.5], {type:'scale'});
1410  * var p3 = board.create('point', [p2, trans]);
1411  * </pre><div id="630afdf3-0a64-46e0-8a44-f51bd197bb8d" style="width: 400px; height: 400px;"></div>
1412  * <script type="text/javascript">
1413  *   var fpex2_board = JXG.JSXGraph.initBoard('630afdf3-0a64-46e0-8a44-f51bd197bb8d', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false});
1414  *   var fpex2_trans = fpex2_board.create('transform', [2, 0.5], {type:'scale'});
1415  *   var fpex2_p2 = fpex2_board.create('point', [3.5, 2.0]);
1416  *   var fpex2_p3 = fpex2_board.create('point', [fpex2_p2, fpex2_trans]);
1417  * </script><pre>
1418  */
1419 JXG.createPoint = function(board, parents, attributes) {
1420     var el, isConstrained = false, i, attr;
1421 
1422     attr = JXG.copyAttributes(attributes, board.options, 'point');
1423 
1424     for (i=0;i<parents.length;i++) {
1425         if (typeof parents[i]=='function' || typeof parents[i]=='string') {
1426             isConstrained = true;
1427         }
1428     }
1429     if (!isConstrained) {
1430         if ( (JXG.isNumber(parents[0])) && (JXG.isNumber(parents[1])) ) {
1431             el = new JXG.Point(board, parents, attr);
1432             if ( JXG.exists(attr["slideobject"]) ) {
1433                 el.makeGlider(attr["slideobject"]);
1434             } else {
1435                 el.baseElement = el; // Free point
1436             }
1437             el.isDraggable = true;
1438         } else if ( (typeof parents[0]=='object') && (typeof parents[1]=='object') ) { // Transformation
1439             el = new JXG.Point(board, [0,0], attr);
1440             el.addTransform(parents[0], parents[1]);
1441             el.isDraggable = false;
1442 
1443             //el.parents = [parents[0].id, parents[1].id];
1444             el.parents = [parents[0].id];
1445         }
1446         else {// Failure
1447             throw new Error("JSXGraph: Can't create point with parent types '" + 
1448                             (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." +
1449                             "\nPossible parent types: [x,y], [z,x,y], [point,transformation]");
1450         }
1451 
1452     } else {
1453         el = new JXG.Point(board, [NaN, NaN], attr);
1454         el.addConstraint(parents);
1455     }
1456 
1457     return el;
1458 };
1459 
1460 /**
1461  * @class This element is used to provide a constructor for a glider point. 
1462  * @pseudo
1463  * @description A glider is a point which lives on another geometric element like a line, circle, curve, turtle.
1464  * @name Glider
1465  * @augments JXG.Point
1466  * @constructor
1467  * @type JXG.Point
1468  * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
1469  * @param {Number_Number_Number_JXG.GeometryElement} z_,x_,y_,GlideObject Parent elements can be two or three elements of type number and the object the glider lives on.
1470  * The coordinates are completely optional. If not given the origin is used. If you provide two numbers for coordinates they will be interpreted as affine euclidean
1471  * coordinates, otherwise they will be interpreted as homogeneous coordinates. In any case the point will be projected on the glide object.
1472  * @example
1473  * // Create a glider with user defined coordinates. If the coordinates are not on
1474  * // the circle (like in this case) the point will be projected onto the circle.
1475  * var p1 = board.create('point', [2.0, 2.0]);
1476  * var c1 = board.create('circle', [p1, 2.0]);
1477  * var p2 = board.create('glider', [2.0, 1.5, c1]);
1478  * </pre><div id="4f65f32f-e50a-4b50-9b7c-f6ec41652930" style="width: 300px; height: 300px;"></div>
1479  * <script type="text/javascript">
1480  *   var gpex1_board = JXG.JSXGraph.initBoard('4f65f32f-e50a-4b50-9b7c-f6ec41652930', {boundingbox: [-1, 5, 5, -1], axis: true, showcopyright: false, shownavigation: false});
1481  *   var gpex1_p1 = gpex1_board.create('point', [2.0, 2.0]);
1482  *   var gpex1_c1 = gpex1_board.create('circle', [gpex1_p1, 2.0]);
1483  *   var gpex1_p2 = gpex1_board.create('glider', [2.0, 1.5, gpex1_c1]);
1484  * </script><pre>
1485  * @example
1486  * // Create a glider with default coordinates (1,0,0). Same premises as above.
1487  * var p1 = board.create('point', [2.0, 2.0]);
1488  * var c1 = board.create('circle', [p1, 2.0]);
1489  * var p2 = board.create('glider', [c1]);
1490  * </pre><div id="4de7f181-631a-44b1-a12f-bc4d995609e8" style="width: 200px; height: 200px;"></div>
1491  * <script type="text/javascript">
1492  *   var gpex2_board = JXG.JSXGraph.initBoard('4de7f181-631a-44b1-a12f-bc4d995609e8', {boundingbox: [-1, 5, 5, -1], axis: true, showcopyright: false, shownavigation: false});
1493  *   var gpex2_p1 = gpex2_board.create('point', [2.0, 2.0]);
1494  *   var gpex2_c1 = gpex2_board.create('circle', [gpex2_p1, 2.0]);
1495  *   var gpex2_p2 = gpex2_board.create('glider', [gpex2_c1]);
1496  * </script><pre>
1497  */
1498 JXG.createGlider = function(board, parents, attributes) {
1499     var el, 
1500         attr = JXG.copyAttributes(attributes, board.options, 'glider');
1501                 
1502     if (parents.length === 1) {
1503         el = board.create('point', [0, 0], attr);
1504     } else {
1505         el = board.create('point', parents.slice(0, 2), attr);
1506     }
1507 
1508     // eltype is set in here
1509     el.makeGlider(parents[parents.length-1]);
1510 
1511     return el;
1512 };
1513 
1514 /**
1515  * @class This element is used to provide a constructor for an intersection point. 
1516  * @pseudo
1517  * @description An intersection point is a point which lives on two Lines or Circles or one Line and one Circle at the same time, i.e.
1518  * an intersection point of the two elements.
1519  * @name Intersection
1520  * @augments JXG.Point
1521  * @constructor
1522  * @type JXG.Point
1523  * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
1524  * @param {JXG.Line,JXG.Circle_JXG.Line,JXG.Circle_Number} el1,el2,i The result will be a intersection point on el1 and el2. i determines the
1525  * intersection point if two points are available: <ul>
1526  *   <li>i==0: use the positive square root,</li> 
1527  *   <li>i==1: use the negative square root.</li></ul>
1528  * @example
1529  * // Create an intersection point of circle and line
1530  * var p1 = board.create('point', [2.0, 2.0]);
1531  * var c1 = board.create('circle', [p1, 2.0]);
1532  * 
1533  * var p2 = board.create('point', [2.0, 2.0]);
1534  * var p3 = board.create('point', [2.0, 2.0]);
1535  * var l1 = board.create('line', [p2, p3]);
1536  * 
1537  * var i = board.create('intersection', [c1, l1, 0]);
1538  * </pre><div id="e5b0e190-5200-4bc3-b995-b6cc53dc5dc0" style="width: 300px; height: 300px;"></div>
1539  * <script type="text/javascript">
1540  *   var ipex1_board = JXG.JSXGraph.initBoard('e5b0e190-5200-4bc3-b995-b6cc53dc5dc0', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false});
1541  *   var ipex1_p1 = ipex1_board.create('point', [4.0, 4.0]);
1542  *   var ipex1_c1 = ipex1_board.create('circle', [ipex1_p1, 2.0]);
1543  *   var ipex1_p2 = ipex1_board.create('point', [1.0, 1.0]);
1544  *   var ipex1_p3 = ipex1_board.create('point', [5.0, 3.0]);
1545  *   var ipex1_l1 = ipex1_board.create('line', [ipex1_p2, ipex1_p3]);
1546  *   var ipex1_i = ipex1_board.create('intersection', [ipex1_c1, ipex1_l1, 0]);
1547  * </script><pre>
1548  */
1549 JXG.createIntersectionPoint = function(board, parents, attributes) {
1550     var el,
1551         attr = JXG.copyAttributes(attributes, board.options, 'intersection'),
1552         func;
1553 
1554 
1555     // make sure we definitely have the indices
1556     parents.push(0, 0);
1557         
1558     el = board.create('point', [0,0,0], attr);
1559     func = new board.intersection(parents[0], parents[1], parents[2], parents[3], {point:el});
1560     el.addConstraint([func]);
1561         
1562     try {
1563         parents[0].addChild(el);
1564         parents[1].addChild(el);
1565     } catch (e) {
1566         throw new Error("JSXGraph: Can't create 'intersection' with parent types '" +
1567                                 (typeof parents[0]) + "' and '" + (typeof parents[1])+ "'.");
1568     }
1569 
1570     el.elType = 'intersection';
1571     el.parents = [parents[0].id, parents[1].id, parents[2]];
1572 
1573     if (parents[3] != null) {
1574         el.parents.push(parents[3]);
1575     }
1576 
1577     el.generatePolynomial = function () {
1578         var poly1 = parents[0].generatePolynomial(el);
1579         var poly2 = parents[1].generatePolynomial(el);
1580 
1581         if((poly1.length == 0) || (poly2.length == 0))
1582             return [];
1583         else
1584             return [poly1[0], poly2[0]];
1585     };
1586         
1587     return el;
1588 };
1589 
1590 /**
1591  * @class This element is used to provide a constructor for the "other" intersection point.
1592  * @pseudo
1593  * @description An intersection point is a point which lives on two Lines or Circles or one Line and one Circle at the same time, i.e.
1594  * an intersection point of the two elements. Additionally, one intersection point is provided. The function returns the other intersection point.
1595  * @name OtherIntersection
1596  * @augments JXG.Point
1597  * @constructor
1598  * @type JXG.Point
1599  * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
1600  * @param {JXG.Line,JXG.Circle_JXG.Line,JXG.Circle_JXG.Point} el1,el2,p The result will be a intersection point on el1 and el2. i determines the
1601  * intersection point different from p: 
1602  * @example
1603  * // Create an intersection point of circle and line
1604  * var p1 = board.create('point', [2.0, 2.0]);
1605  * var c1 = board.create('circle', [p1, 2.0]);
1606  * 
1607  * var p2 = board.create('point', [2.0, 2.0]);
1608  * var p3 = board.create('point', [2.0, 2.0]);
1609  * var l1 = board.create('line', [p2, p3]);
1610  * 
1611  * var i = board.create('intersection', [c1, l1, 0]);
1612  * var j = board.create('otherintersection', [c1, l1, i]);
1613  * </pre><div id="45e25f12-a1de-4257-a466-27a2ae73614c" style="width: 300px; height: 300px;"></div>
1614  * <script type="text/javascript">
1615  *   var ipex2_board = JXG.JSXGraph.initBoard('45e25f12-a1de-4257-a466-27a2ae73614c', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false});
1616  *   var ipex2_p1 = ipex2_board.create('point', [4.0, 4.0]);
1617  *   var ipex2_c1 = ipex2_board.create('circle', [ipex2_p1, 2.0]);
1618  *   var ipex2_p2 = ipex2_board.create('point', [1.0, 1.0]);
1619  *   var ipex2_p3 = ipex2_board.create('point', [5.0, 3.0]);
1620  *   var ipex2_l1 = ipex2_board.create('line', [ipex2_p2, ipex2_p3]);
1621  *   var ipex2_i = ipex2_board.create('intersection', [ipex2_c1, ipex2_l1, 0], {name:'D'});
1622  *   var ipex2_j = ipex2_board.create('otherintersection', [ipex2_c1, ipex2_l1, ipex2_i], {name:'E'});
1623  * </script><pre>
1624  */
1625 JXG.createOtherIntersectionPoint = function(board, parents, attributes) {
1626     var el;
1627     if (parents.length!=3 
1628         || !JXG.isPoint(parents[2]) 
1629         || (parents[0].elementClass != JXG.OBJECT_CLASS_LINE && parents[0].elementClass != JXG.OBJECT_CLASS_CIRCLE)
1630         || (parents[1].elementClass != JXG.OBJECT_CLASS_LINE && parents[1].elementClass != JXG.OBJECT_CLASS_CIRCLE) ) {
1631         // Failure
1632         throw new Error("JSXGraph: Can't create 'other intersection point' with parent types '" + 
1633                         (typeof parents[0]) + "',  '" + (typeof parents[1])+ "'and  '" + (typeof parents[2]) + "'." +
1634                         "\nPossible parent types: [circle|line,circle|line,point]");
1635     }
1636     else {
1637         el = board.create('point', [board.otherIntersection(parents[0], parents[1], parents[2])], attributes);
1638     }
1639     el.elType = 'otherintersection';
1640     el.parents = [parents[0].id, parents[1].id, parents[2]];
1641         
1642     parents[0].addChild(el);
1643     parents[1].addChild(el);
1644 
1645     el.generatePolynomial = function () {
1646         var poly1 = parents[0].generatePolynomial(el);
1647         var poly2 = parents[1].generatePolynomial(el);
1648 
1649         if((poly1.length == 0) || (poly2.length == 0))
1650             return [];
1651         else
1652             return [poly1[0], poly2[0]];
1653     };
1654         
1655     return el;
1656 };
1657 
1658 
1659 JXG.JSXGraph.registerElement('point',JXG.createPoint);
1660 JXG.JSXGraph.registerElement('glider', JXG.createGlider);
1661 JXG.JSXGraph.registerElement('intersection', JXG.createIntersectionPoint);
1662 JXG.JSXGraph.registerElement('otherintersection', JXG.createOtherIntersectionPoint);
1663