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