1 /*
  2     Copyright 2008-2010
  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  * @author graphjs
 31  * @version 0.1
 32  */
 33 
 34 
 35 JXG.POINT_STYLE_X_SMALL      = 0;  // a small sized x
 36 JXG.POINT_STYLE_X            = 1;  // a medium sized x
 37 JXG.POINT_STYLE_X_BIG        = 2;  // a big sized x
 38 JXG.POINT_STYLE_CIRCLE_TINY  = 3;  // a tiny circle
 39 JXG.POINT_STYLE_CIRCLE_SMALL = 4;  // a small circle
 40 JXG.POINT_STYLE_CIRCLE       = 5;  // a medium circle
 41 JXG.POINT_STYLE_CIRCLE_BIG   = 6;  // a big circle
 42 JXG.POINT_STYLE_SQUARE_SMALL = 7;  // a small rectangle
 43 JXG.POINT_STYLE_SQUARE       = 8;  // a medium rectangle
 44 JXG.POINT_STYLE_SQUARE_BIG   = 9;  // a big rectangle
 45 JXG.POINT_STYLE_PLUS_SMALL   = 10; // a small +
 46 JXG.POINT_STYLE_PLUS         = 11; // a medium +
 47 JXG.POINT_STYLE_PLUS_BIG     = 12; // a big +
 48 
 49 /**
 50  * A point is the basic geometric element. Based on points lines and circles can be constructed which can be intersected
 51  * which in turn are points again which can be used to construct new lines, circles, polygons, etc. This class holds methods for
 52  * all kind of points like free points, gliders, and intersection points.
 53  * @class Creates a new point object. Do not use this constructor to create a point. Use {@link JXG.Board#create} with
 54  * type {@link Point}, {@link Glider}, or {@link Intersection} instead.  
 55  * @augments JXG.GeometryElement
 56  * @param {string,JXG.Board} board The board the new point is drawn on.
 57  * @param {Array} coordinates An array with the affine user coordinates of the point.
 58  * @param {String} id Unique identifier for the point. If null or an empty string is given,
 59  *  an unique id will be generated by Board
 60  * @param {String} name Not necessarily unique name for the point. If null or an
 61  *  empty string is given, an unique name will be generated
 62  * @param {boolean} show False if the point is invisible, True otherwise
 63  * @see JXG.Board#generateName
 64  * @see JXG.Board#addPoint
 65  */
 66 JXG.Point = function (board, coordinates, id, name, show, withLabel, layer) {
 67     this.constructor();
 68     
 69     /**
 70      * Type of point; Possible values are {@link JXG.OBJECT_TYPE_POINT}, {@link JXG.OBJECT_TYPE_GLIDER}, {@link JXG.OBJECT_TYPE_CAS}.
 71      * @default {@link JXG.OBJECT_TYPE_POINT}
 72      * @type number
 73      * @private
 74      */
 75     this.type = JXG.OBJECT_TYPE_POINT;
 76     
 77     /**
 78      * Class of this point element; Values is OBJECT_CLASS_POINT.
 79      * @constant
 80      * @type number
 81      * @private
 82      */
 83     this.elementClass = JXG.OBJECT_CLASS_POINT;
 84 
 85     this.init(board, id, name);
 86 
 87     if (coordinates==null) {
 88         coordinates=[0,0];
 89     }
 90     /**
 91      * Coordinates of the point.
 92      * @type JXG.Coords
 93      * @private
 94      */
 95     this.coords = new JXG.Coords(JXG.COORDS_BY_USER, coordinates, this.board);
 96     this.initialCoords = new JXG.Coords(JXG.COORDS_BY_USER, coordinates, this.board);
 97 
 98     /**
 99      * Set the display layer.
100      */
101     if (layer == null) layer = board.options.layer['point'];
102     this.layer = layer;
103 
104     /**
105      * If true, the infobox is shown on mouse over, else not.
106      * @type boolean
107      * @default true
108      */
109     this.showInfobox = JXG.Options.point.showInfobox;
110     
111     /**
112      * Descriptive character, displayed next to the point
113      * @type JXG.Label
114      * @private
115      */
116     this.label = {};
117     this.label.relativeCoords = [10,-10];
118     this.nameHTML = JXG.GeonextParser.replaceSup(JXG.GeonextParser.replaceSub(this.name)); //?
119     if (typeof withLabel=='undefined' || withLabel==true) {
120         this.board.objects[this.id] = this;
121         this.label.content = new JXG.Text(this.board, this.nameHTML, this.id, 
122             this.label.relativeCoords, this.id+"Label", '', null, true, this.board.options.text.defaultDisplay);
123         delete(this.board.objects[this.id]);
124 
125         this.label.color = '#000000';
126         if(!show) {
127             this.label.hiddenByParent = true;
128             this.label.content.visProp['visible'] = false;
129         }
130         this.hasLabel = true;
131     } else {
132         this.showInfobox = false;
133     }
134     
135     /**
136      * False: Point can be moved, True: Point can't be moved with the mouse.
137      * @type boolean
138      * @default false
139      */
140     this.fixed = false;
141     
142     /**
143      * Relative position on a line if point is a glider on a line.
144      * @type number
145      * @private
146      */
147     this.position = null;
148 
149     /**
150      * Determines whether the point slides on a polygon if point is a glider.
151      * @type boolean
152      * @default false
153      * @private
154      */
155     this.onPolygon = false;
156     
157     /**
158      * There are different point styles which differ in appearance and size.
159      * Possible values are
160      * <table><tr><th>Constant name</th><th>Value</th><th>Meaning</th><th>face</th><th>size</th></tr>
161      * <tr><td>JXG.POINT_STYLE_X_SMALL</td><td>0</td><td>small sized x</td><td>cross or x</td><td>2</td></tr>
162      * <tr><td>JXG.POINT_STYLE_X</td><td>1</td><td>medium sized x</td><td>cross or x</td><td>3</td></tr>
163      * <tr><td>JXG.POINT_STYLE_X_BIG</td><td>2</td><td>big sized x</td><td>cross or x</td><td>4</td></tr>
164      * <tr><td>JXG.POINT_STYLE_CIRCLE_TINY</td><td>3</td><td>tiny circle</td><td>circle or o</td><td>1</td></tr>
165      * <tr><td>JXG.POINT_STYLE_CIRCLE_SMALL</td><td>4</td><td>small circle</td><td>circle or o</td><td>2</td></tr>
166      * <tr><td>JXG.POINT_STYLE_CIRCLE</td><td>5</td><td>medium circle</td><td>circle or o</td><td>3</td></tr>
167      * <tr><td>JXG.POINT_STYLE_CIRCLE_BIG</td><td>6</td><td>big circle</td><td>circle or o</td><td>4</td></tr>
168      * <tr><td>JXG.POINT_STYLE_SQUARE_SMALL</td><td>7</td><td>small rectangle</td><td>square or []</td><td>2</td></tr>
169      * <tr><td>JXG.POINT_STYLE_SQUARE</td><td>8</td><td>medium rectangle</td><td>square or []</td><td>3</td></tr>
170      * <tr><td>JXG.POINT_STYLE_SQUARE_BIG</td><td>9</td><td>big rectangle</td><td>square or []</td><td>4</td></tr>
171      * <tr><td>JXG.POINT_STYLE_PLUS_SMALL</td><td>10</td><td>small +</td><td>plus or +</td><td>2</td></tr>
172      * <tr><td>JXG.POINT_STYLE_PLUS</td><td>11</td><td>medium +</td><td>plus or +</td><td>3</td></tr>
173      * <tr><td>JXG.POINT_STYLE_PLUS_BIG</td><td>12</td><td>big +</td><td>plus or +</td><td>4</td></tr></table>
174      * <b>Hint:</b> This attribute is internally replaced by face and size, whose opportunities are wider, , as given in the table above.
175      * @see JXG.Point#face
176      * @see JXG.Point#size
177      * @type number
178      * @see #setStyle
179      * @default JXG.Options.point#style
180      * @name JXG.Point#style
181      * @deprecated
182      */
183     this.visProp['style'] = this.board.options.point.style;
184     
185     /**
186      * There are different point styles which differ in appearance.
187      * Posssible values are
188      * <table><tr><th>Value</th></tr>
189      * <tr><td>cross</td></tr>
190      * <tr><td>circle</td></tr>
191      * <tr><td>square</td></tr>
192      * <tr><td>plus</td></tr>
193      * <tr><td>diamond</td></tr>
194      * <tr><td>triangleUp</td></tr>
195      * <tr><td>triangleDown</td></tr>
196      * <tr><td>triangleLeft</td></tr>
197      * <tr><td>triangleRight</td></tr>
198      * </table>
199      * @type string
200      * @see #setStyle
201      * @default circle
202      * @name JXG.Point#face
203      */
204     this.visProp['face'] = this.board.options.point.face;
205     /**
206     * Determines the size of a point.
207     * Means radius resp. half the width of a point (depending on the face).
208      * @see JXG.Point#face    
209     * @type number
210      * @see #setStyle
211      * @default 3     
212      * @name JXG.Point#size
213      */    
214     this.visProp['size'] = this.board.options.point.size;
215 
216     /**
217      * Size of the point. This is just for the renderer and the hasPoint() method
218      * to draw the point as a circle.
219      * @type number
220      * @private
221      */
222     //this.r = this.board.options.precision.hasPoint;
223     
224     /*
225      * The visprop properties are documented in JXG.GeometryElement
226      */
227     this.visProp['fillColor'] = this.board.options.point.fillColor;
228     this.visProp['highlightFillColor'] = this.board.options.point.highlightFillColor;  
229     this.visProp['strokeColor'] = this.board.options.point.strokeColor;
230     this.visProp['highlightStrokeColor'] = this.board.options.point.highlightStrokeColor; 
231     this.visProp['strokeWidth'] = this.board.options.point.strokeWidth;    
232     this.visProp['visible'] = show; 
233 
234     /**
235      * When used as a glider this member stores the object, where to glide on. To set the object to glide on use the method
236      * {@link JXG.Point#makeGlider} and DO NOT set this property directly as it will break the dependency tree.
237      * TODO: Requires renaming to glideObject
238      * @type JXG.GeometryElement
239      * @name Glider#slideObject
240      */
241     this.slideObject = null;
242     
243     /**
244      * Stores the groups of this point in an array of Group.
245      * @type array
246      * @see JXG.Group
247      * @private
248      */
249     this.group = [];
250     
251     /* Register point at board. */
252     this.id = this.board.setId(this, 'P');
253     this.board.renderer.drawPoint(this);
254     this.board.finalizeAdding(this);
255 };
256 
257 /**
258  * Inherits here from {@link JXG.GeometryElement}.
259  */
260 JXG.Point.prototype = new JXG.GeometryElement();
261 
262 /**
263  * Checks whether (x,y) is near the point.
264  * @param {int} x Coordinate in x direction, screen coordinates.
265  * @param {int} y Coordinate in y direction, screen coordinates.
266  * @type boolean
267  * @return True if (x,y) is near the point, False otherwise.
268  * @private
269  */
270 JXG.Point.prototype.hasPoint = function (x,y) {
271     var coordsScr = this.coords.scrCoords, r;
272     r = this.visProp['size'];
273     if(r < this.board.options.precision.hasPoint) {
274         r = this.board.options.precision.hasPoint;
275     }
276     return ((Math.abs(coordsScr[1]-x) < r+2) && (Math.abs(coordsScr[2]-y)) < r+2);
277 };
278 
279 /**
280 * Dummy function for unconstrained points or gliders.
281 * @private
282 */
283 JXG.Point.prototype.updateConstraint = function() { return this; };
284 
285 /**
286  * Updates the position of the point.
287  */
288 JXG.Point.prototype.update = function (fromParent) {
289     if (!this.needsUpdate) { return; }
290 
291     if(typeof fromParent == 'undefined') {
292         fromParent = false;
293     }
294   
295     if(this.traced) {
296         this.cloneToBackground(true);
297     }
298  /*
299      * We need to calculate the new coordinates no matter of the points visibility because
300      * a child could be visible and depend on the coordinates of the point (e.g. perpendicular).
301      * 
302      * Check if point is a glider and calculate new coords in dependency of this.slideObject.
303      * This function is called with fromParent==true for example if
304      * the defining elements of the line or circle have been changed.
305      */
306     if(this.type == JXG.OBJECT_TYPE_GLIDER) {
307         if(this.slideObject.type == JXG.OBJECT_TYPE_CIRCLE) {
308 //fromParent = false;        
309             if (fromParent) {
310                 this.coords.setCoordinates(JXG.COORDS_BY_USER, [this.slideObject.midpoint.X()+Math.cos(this.position),this.slideObject.midpoint.Y()+Math.sin(this.position)]);
311                 this.coords  = JXG.Math.Geometry.projectPointToCircle(this, this.slideObject, this.board);
312             } else {
313                 this.coords  = JXG.Math.Geometry.projectPointToCircle(this, this.slideObject, this.board);
314                 this.position = JXG.Math.Geometry.rad([this.slideObject.midpoint.X()+1.0,this.slideObject.midpoint.Y()],this.slideObject.midpoint,this);
315             }
316         } else if(this.slideObject.type == JXG.OBJECT_TYPE_LINE) {
317             this.coords  = JXG.Math.Geometry.projectPointToLine(this, this.slideObject, this.board);
318             
319             var p1coords = this.slideObject.point1.coords;
320             var p2coords = this.slideObject.point2.coords;
321             if (fromParent) {
322                 if (Math.abs(p1coords.usrCoords[0])>=JXG.Math.eps && Math.abs(p2coords.usrCoords[0])>=JXG.Math.eps) {
323                     this.coords.setCoordinates(JXG.COORDS_BY_USER, 
324                                            [p1coords.usrCoords[1] + this.position*(p2coords.usrCoords[1] - p1coords.usrCoords[1]),
325                                             p1coords.usrCoords[2] + this.position*(p2coords.usrCoords[2] - p1coords.usrCoords[2])]);
326                 }
327             } else {
328                 var factor = 1;
329                 var distP1S = p1coords.distance(JXG.COORDS_BY_USER, this.coords);
330                 var distP1P2 = p1coords.distance(JXG.COORDS_BY_USER, p2coords);
331                 var distP2S = p2coords.distance(JXG.COORDS_BY_USER, this.coords);
332                 
333                 if( ((distP1S > distP1P2) || (distP2S > distP1P2)) && (distP1S < distP2S)) { // Glider not between P1 & P2 and beyond P1
334                     factor = -1;
335                 }
336                 this.position = factor*distP1S/distP1P2;
337 
338                 // Snap the glider point of the slider into its appropiate position
339                 // First, recalculate the new value of this.position
340                 // Second, call update(fromParent==true) to make the positioning snappier.
341                 if (this.snapWidth!=null && Math.abs(this._smax-this._smin)>=JXG.Math.eps) {
342                     if (this.position<0.0) this.position = 0.0;
343                     if (this.position>1.0) this.position = 1.0;
344                     
345                     var v = this.position*(this._smax-this._smin)+this._smin;
346                         v = Math.round(v/this.snapWidth)*this.snapWidth;
347                     this.position = (v-this._smin)/(this._smax-this._smin);
348                     this.update(true);
349                 }
350             }
351             var p1Scr = this.slideObject.point1.coords.scrCoords;
352             var p2Scr = this.slideObject.point2.coords.scrCoords;
353 
354             var i;
355             if(this.slideObject.getSlope() == 0) {
356                 i = 1;
357             } else {
358                 i = 2;
359             }
360 
361             var y = this.coords.scrCoords[i];
362             if(!this.slideObject.visProp['straightFirst']) {
363                 if(p1Scr[i] < p2Scr[i]) {
364                     if(y < p1Scr[i]) {
365                        this.coords = this.slideObject.point1.coords;
366                        this.position = 0;
367                     }
368                 }
369                 else if(p1Scr[i] > p2Scr[i]) {
370                     if(y > p1Scr[i]) {
371                        this.coords = this.slideObject.point1.coords;
372                        this.position = 0;
373                     }
374                 }
375             }
376             if(!this.slideObject.visProp['straightLast']) {
377                 if(p1Scr[i] < p2Scr[i]) {
378                     if(y > p2Scr[i]) {
379                        this.coords = this.slideObject.point2.coords;
380                        this.position = 1;
381                     }
382                 }
383                 else if(p1Scr[i] > p2Scr[i]) {
384                     if(y < p2Scr[i]) {
385                        this.coords = this.slideObject.point2.coords;
386                        this.position = 1;
387                     }
388                 }
389             }  
390 
391             if(this.onPolygon) {
392                 var p1 = this.slideObject.point1.coords;
393                 var p2 = this.slideObject.point2.coords;
394                 if(Math.abs(this.coords.scrCoords[1]-p1.scrCoords[1])<this.board.options.precision.hasPoint && Math.abs(this.coords.scrCoords[2]-p1.scrCoords[2])<this.board.options.precision.hasPoint) {
395                     var poly = this.slideObject.parentPolygon;
396                     for(var i=0; i<poly.borders.length; i++) {
397                         if(this.slideObject == poly.borders[i]) {
398                             this.slideObject = poly.borders[(i - 1 + poly.borders.length) % poly.borders.length];
399                             break;
400                         }
401                     }
402                 }
403                 else if(Math.abs(this.coords.scrCoords[1]-p2.scrCoords[1])<this.board.options.precision.hasPoint && Math.abs(this.coords.scrCoords[2]-p2.scrCoords[2])<this.board.options.precision.hasPoint) {
404                     var poly = this.slideObject.parentPolygon;
405                     for(var i=0; i<poly.borders.length; i++) {
406                         if(this.slideObject == poly.borders[i]) {
407                             this.slideObject = poly.borders[(i + 1 + poly.borders.length) % poly.borders.length];
408                             break;                        
409                         }
410                     }
411                 }
412             }
413         } else if(this.slideObject.type == JXG.OBJECT_TYPE_TURTLE) {
414             this.updateConstraint(); // In case, the point is a constrained glider.
415             this.coords  = JXG.Math.Geometry.projectPointToTurtle(this, this.slideObject, this.board);
416         } else if(this.slideObject.elementClass == JXG.OBJECT_CLASS_CURVE) {
417             this.updateConstraint(); // In case, the point is a constrained glider.
418             this.coords  = JXG.Math.Geometry.projectPointToCurve(this, this.slideObject, this.board);
419         }
420     }
421     
422     /* If point is a calculated point, call updateConstraint() to calculate new coords. */
423     if (this.type == JXG.OBJECT_TYPE_CAS) {
424         this.updateConstraint();
425     }
426 
427     this.updateTransform();
428     
429     //this.updateRenderer();
430     this.needsUpdate = false;
431     return this;
432 };
433 
434 /**
435  * Calls the renderer to update the drawing.
436  * @private
437  */
438 JXG.Point.prototype.updateRenderer = function () {
439     /* Call the renderer only if point is visible. */
440     if(this.visProp['visible']) {
441         var wasReal = this.isReal;
442         this.isReal = (isNaN(this.coords.usrCoords[1]+this.coords.usrCoords[2]))?false:true;
443         this.isReal = (Math.abs(this.coords.usrCoords[0])>JXG.Math.eps)?this.isReal:false;  //Homogeneous coords: ideal point
444         if (this.isReal) {
445             if (wasReal!=this.isReal) { 
446                 this.board.renderer.show(this); 
447                 if(this.hasLabel && this.label.content.visProp['visible']) this.board.renderer.show(this.label.content); 
448             }
449             this.board.renderer.updatePoint(this);
450         } else {
451             if (wasReal!=this.isReal) { 
452                 this.board.renderer.hide(this); 
453                 if(this.hasLabel && this.label.content.visProp['visible']) this.board.renderer.hide(this.label.content); 
454             }
455         }
456     } 
457 
458     /* Update the label if visible. */
459     if(this.hasLabel && this.label.content.visProp['visible'] && this.isReal) {
460         //this.label.setCoordinates(this.coords);
461         this.label.content.update();
462         //this.board.renderer.updateLabel(this.label);
463         this.board.renderer.updateText(this.label.content);
464     }
465     return this;
466 };
467 
468 /**
469  * Getter method for x, this is used by for CAS-points to access point coordinates.
470  * @return User coordinate of point in x direction.
471  * @type number
472  */
473 JXG.Point.prototype.X = function () {
474     return this.coords.usrCoords[1];
475 };
476 
477 /**
478  * Getter method for y, this is used by CAS-points to access point coordinates.
479  * @return User coordinate of point in y direction.
480  * @type number
481  */
482 JXG.Point.prototype.Y = function () {
483     return this.coords.usrCoords[2];
484 };
485 
486 /**
487  * Getter method for z, this is used by CAS-points to access point coordinates.
488  * @return User coordinate of point in z direction.
489  * @type number
490  */
491 JXG.Point.prototype.Z = function () {
492     return this.coords.usrCoords[0];
493 };
494 
495 /**
496  * New evaluation of the function term. 
497  * This is required for CAS-points: Their XTerm() method is overwritten in {@link #addConstraint}
498  * @return User coordinate of point in x direction.
499  * @type number
500  * @private
501  */
502 JXG.Point.prototype.XEval = function () {
503     return this.coords.usrCoords[1];
504 };
505 
506 /**
507  * New evaluation of the function term. 
508  * This is required for CAS-points: Their YTerm() method is overwritten in {@link #addConstraint}
509  * @return User coordinate of point in y direction.
510  * @type number
511  * @private
512  */
513 JXG.Point.prototype.YEval = function () {
514     return this.coords.usrCoords[2];
515 };
516 
517 /**
518  * New evaluation of the function term. 
519  * This is required for CAS-points: Their ZTerm() method is overwritten in {@link #addConstraint}
520  * @return User coordinate of point in z direction.
521  * @type number
522  * @private
523  */
524 JXG.Point.prototype.ZEval = function () {
525     return this.coords.usrCoords[0];
526 };
527 
528 /**
529  * Getter method for the distance to a second point, this is required for CAS-elements.
530  * Here, function inlining seems to be worthwile  (for plotting).
531  * @param {JXG.Point} point2 The point to which the distance shall be calculated.
532  * @return Distance in user coordinate to the given point
533  * @type number
534  */
535 JXG.Point.prototype.Dist = function(point2) {
536     var sum,
537         c = point2.coords.usrCoords,
538         ucr = this.coords.usrCoords,
539         f;
540         
541     f = ucr[0]-c[0];
542     sum = f*f;
543     f = ucr[1]-c[1];
544     sum += f*f;
545     f = ucr[2]-c[2];
546     sum += f*f;
547     return Math.sqrt(sum);
548     //return this.coords.distance(JXG.COORDS_BY_USER, point2.coords);
549 };
550 
551 /**
552  * Sets x and y coordinate and calls the point's update() method.
553  * @param {number} method The type of coordinates used here. Possible values are {@link JXG.COORDS_BY_USER} and {@link JXG.COORDS_BY_SCREEN}.
554  * @param {number} x x coordinate in screen/user units
555  * @param {number} y y coordinate in screen/user units
556  */
557 JXG.Point.prototype.setPositionDirectly = function (method, x, y) {
558     var i, dx, dy, el, p,
559         oldCoords = this.coords;
560         
561     this.coords = new JXG.Coords(method, [x,y], this.board);
562 
563     if(this.group.length != 0) {
564         // Update the initial coordinates. This is needed for free points
565         // that have a transformation bound to it.
566         dx = this.coords.usrCoords[1]-oldCoords.usrCoords[1];
567         dy = this.coords.usrCoords[2]-oldCoords.usrCoords[2];
568         for (i=0;i<this.group.length;i++) {
569             for (el in this.group[i].objects) {
570                 p = this.group[i].objects[el];
571                 p.initialCoords = new JXG.Coords(JXG.COORDS_BY_USER, 
572                     [p.initialCoords.usrCoords[1]+dx,p.initialCoords.usrCoords[2]+dy], 
573                     this.board);
574             }
575         }
576 
577         this.group[this.group.length-1].dX = this.coords.scrCoords[1] - oldCoords.scrCoords[1];
578         this.group[this.group.length-1].dY = this.coords.scrCoords[2] - oldCoords.scrCoords[2];
579         this.group[this.group.length-1].update(this);
580     } else {
581         // Update the initial coordinates. This is needed for free points
582         // that have a transformation bound to it.
583         for (i=this.transformations.length-1;i>=0;i--) {
584             this.initialCoords = new JXG.Coords(method, 
585                 JXG.Math.matVecMult(JXG.Math.inverse(this.transformations[i].matrix),[1,x,y]), 
586                 this.board);      
587         }
588         this.update();
589     }
590     return this;
591 };
592 
593 /**
594  * TODO
595  * @param {number} method The type of coordinates used here. Possible values are {@link JXG.COORDS_BY_USER} and {@link JXG.COORDS_BY_SCREEN}.
596  * @param {number} x x coordinate in screen/user units
597  * @param {number} y y coordinate in screen/user units
598  */
599 JXG.Point.prototype.setPositionByTransform = function (method, x, y) {
600     var oldCoords = this.coords;
601     var t = this.board.create('transform',[x,y],{type:'translate'});
602     if (this.transformations.length>0 && this.transformations[this.transformations.length-1].isNumericMatrix) {
603         this.transformations[this.transformations.length-1].melt(t);
604     } else {
605         this.addTransform(this,t);
606     }
607 
608     if (this.group.length != 0) {
609 /*
610         var dCoords = new JXG.Coords(method, [x,y], this.board);
611         this.group[this.group.length-1].dX = dCoords.scrCoords[1]-this.board.origin.scrCoords[1]; 
612         this.group[this.group.length-1].dY = dCoords.scrCoords[2]-this.board.origin.scrCoords[2]; 
613         this.group[this.group.length-1].update(this);
614 */
615     } else {
616         this.update();
617     }
618     return this;
619 };
620 
621 /**
622  * Sets x and y coordinate and calls the point's update() method.
623  * @param {number} method The type of coordinates used here. Possible values are {@link JXG.COORDS_BY_USER} and {@link JXG.COORDS_BY_SCREEN}.
624  * @param {number} x x coordinate in screen/user units
625  * @param {number} y y coordinate in screen/user units
626  */
627 JXG.Point.prototype.setPosition = function (method, x, y) { 
628     //this.setPositionByTransform(method, x, y);
629     this.setPositionDirectly(method, x, y);
630     return this;
631 };
632 
633 /**
634  * Convert the point to glider and update the construction.
635  * @param {String,Object} glideObject The Object the point will be bound to.
636  */
637 JXG.Point.prototype.makeGlider = function (glideObject) {
638     this.slideObject = JXG.getReference(this.board, glideObject);
639     this.type = JXG.OBJECT_TYPE_GLIDER;
640     this.snapWidth = null;
641     
642     this.slideObject.addChild(this);
643 
644     this.generatePolynomial = function() {
645         return this.slideObject.generatePolynomial(this);
646     };
647 
648     //this.position = 0;
649     this.needsUpdate = true;
650     this.update();
651     return this;
652 };
653 
654 /**
655  * Convert the point to CAS point and call update().
656  * @param {array} terms [[zterm], xterm, yterm] defining terms for the z, x and y coordinate.
657  * The z-coordinate is optional and it is used for homogeneaous coordinates.
658  * The coordinates may be either <ul>
659  *   <li>a JavaScript function,</li>
660  *   <li>a string containing GEONExT syntax. This string will be converted into a JavaScript 
661  *     function here,</li>
662  *   <li>a number</li>
663  *   <li>a pointer to a slider object. This will be converted into a call of the Value()-method 
664  *     of this slider.</li>
665  *   </ul>
666  * @see JXG.GeonextParser#geonext2JS
667  */
668 JXG.Point.prototype.addConstraint = function (terms) {
669     this.type = JXG.OBJECT_TYPE_CAS;
670     var elements = this.board.elementsByName;
671     var newfuncs = [];
672     var fs;
673     
674     for (var i=0;i<terms.length;i++) {
675         var v = terms[i];
676         if (typeof v=='string') {
677             // Convert GEONExT syntax into  JavaScript syntax
678             var t  = JXG.GeonextParser.geonext2JS(v, this.board);
679             newfuncs[i] = new Function('','return ' + t + ';');
680         } else if (typeof v=='function') {
681             newfuncs[i] = v;
682         } else if (typeof v=='number') {
683             newfuncs[i] = function(z){ return function() { return z; }; }(v);
684         } else if (typeof v == 'object' && typeof v.Value == 'function') {    // Slider
685             newfuncs[i] = (function(a) { return function() { return a.Value(); };})(v);
686         }
687     }
688     if (terms.length==1) { // Intersection function
689         this.updateConstraint = function() { 
690                 var c = newfuncs[0](); 
691                 if (JXG.isArray(c)) {      // Array
692                     this.coords.setCoordinates(JXG.COORDS_BY_USER,c);
693                 } else {                   // Coords object
694                     this.coords = c;
695                 }
696             };
697         // if (!this.board.isSuspendedUpdate) { this.update(); }
698         // return this;
699     } else if (terms.length==2) { // Euclidean coordinates
700         this.XEval = newfuncs[0];
701         this.YEval = newfuncs[1];
702         fs = 'this.coords.setCoordinates(JXG.COORDS_BY_USER,[this.XEval(),this.YEval()]);';
703         this.updateConstraint = new Function('',fs);
704     } else { // Homogeneous coordinates
705         this.ZEval = newfuncs[0];
706         this.XEval = newfuncs[1];
707         this.YEval = newfuncs[2];
708         fs = 'this.coords.setCoordinates(JXG.COORDS_BY_USER,[this.ZEval(),this.XEval(),this.YEval()]);';
709         this.updateConstraint = new Function('',fs);
710     }
711 
712     if (!this.board.isSuspendedUpdate) { this.update(); }
713     return this;
714 };
715 
716 /**
717  * TODO
718  */
719 JXG.Point.prototype.updateTransform = function () {
720     if (this.transformations.length==0 || this.baseElement==null) {
721         return;
722     }
723     var c, i;
724 
725     if (this===this.baseElement) {      // case of bindTo
726         c = this.transformations[0].apply(this.baseElement,'self');
727     } else {                           // case of board.create('point',[baseElement,transform]);
728         c = this.transformations[0].apply(this.baseElement);
729     }
730     this.coords.setCoordinates(JXG.COORDS_BY_USER,c);
731     for (i=1;i<this.transformations.length;i++) {
732         this.coords.setCoordinates(JXG.COORDS_BY_USER,this.transformations[i].apply(this));
733     }
734     return this;
735 };
736 
737 /**
738  * TODO
739  * @param el TODO
740  * @param transform TODO
741  */
742 JXG.Point.prototype.addTransform = function (el, transform) {
743     var list, i, len;
744     if (this.transformations.length==0) { // There is only one baseElement possible
745         this.baseElement = el;
746     }
747     if (JXG.isArray(transform)) {
748         list = transform;
749     } else {
750         list = [transform];
751     }
752     len = list.length;
753     for (i=0;i<len;i++) {
754         this.transformations.push(list[i]);
755     }
756     return this;
757 };
758 
759 /**
760  * Animate the point. 
761  * @param {number} direction The direction the glider is animated. Can be +1 or -1.
762  * @param {number} stepCount The number of steps.
763  * @name Glider#startAnimation
764  * @see Glider#stopAnimation
765  * @function
766  */
767 JXG.Point.prototype.startAnimation = function(direction, stepCount) {
768     if((this.type == JXG.OBJECT_TYPE_GLIDER) && (typeof this.intervalCode == 'undefined')) {
769         this.intervalCode = window.setInterval('JXG.JSXGraph.boards[\'' + this.board.id + '\'].objects[\'' + this.id + '\']._anim(' + direction + ', ' + stepCount + ')', 250);
770         if(typeof this.intervalCount == 'undefined')
771             this.intervalCount = 0;
772     }
773     return this;
774 };
775 
776 /**
777  * Stop animation.
778  * @name Glider#stopAnimation
779  * @see Glider#startAnimation
780  * @function
781  */
782 JXG.Point.prototype.stopAnimation = function() {
783     if(typeof this.intervalCode != 'undefined') {
784         window.clearInterval(this.intervalCode);
785         delete(this.intervalCode);
786     }
787     return this;
788 };
789 
790 /**
791  * Starts an animation which moves the point along a given path in given time.
792  * @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
793  * 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.
794  * In case of NaN the animation stops.
795  * @param {Number} time The time in milliseconds in which to finish the animation
796  */
797 JXG.Point.prototype.moveAlong = function(path, time) {
798     var interpath = [],
799         delay = 35,
800         makeFakeFunction = function (i, j) {
801             return function() {
802                 return path[i][j];
803             };
804         },
805         p = [], i, neville,
806         steps = time/delay;
807 
808     if (JXG.isArray(path)) {
809         for (i = 0; i < path.length; i++) {
810             if (JXG.isPoint(path[i])) {
811                 p[i] = path[i];
812             } else {
813                 p[i] = {
814                     elementClass: JXG.OBJECT_CLASS_POINT,
815                     X: makeFakeFunction(i, 0),
816                     Y: makeFakeFunction(i, 1)
817                 };
818             }
819         }
820 
821         time = time || 0;
822         if (time === 0) {
823             this.setPosition(JXG.COORDS_BY_USER, p[p.length - 1].X(), p[p.length - 1].Y());
824             return this.board.update(this);
825         }
826 
827         neville = JXG.Math.Numerics.Neville(p);
828         for (i = 0; i < steps; i++) {
829             interpath[i] = [];
830             interpath[i][0] = neville[0]((steps - i) / steps * neville[3]());
831             interpath[i][1] = neville[1]((steps - i) / steps * neville[3]());
832         }
833 
834         this.animationPath = interpath;
835     } else if (JXG.isFunction(path)) {
836         this.animationPath = path;
837         this.animationStart = new Date().getTime();
838     }
839 
840     this.board.addAnimation(this);
841     return this;
842 };
843 
844 /**
845  * Starts an animated point movement towards the given coordinates <tt>where</tt>. The animation is done after <tt>time</tt> milliseconds.
846  * @param {Array} where Array containing the x and y coordinate of the target location.
847  * @param {int} time Number of milliseconds the animation should last.
848  * If the second parameter is not given or is equal to 0, setPosition() is called, see #setPosition.
849  * @see #animate
850  */
851 JXG.Point.prototype.moveTo = function(where, time) {
852     if (typeof time == 'undefined' || time == 0) {
853         this.setPosition(JXG.COORDS_BY_USER, where[0], where[1]);
854         //this.prepareUpdate().update().updateRenderer();
855         return this.board.update(this);
856     }
857 	var delay = 35,
858 	    steps = Math.ceil(time/(delay * 1.0)),
859 		coords = new Array(steps+1),
860 		X = this.coords.usrCoords[1],
861 		Y = this.coords.usrCoords[2],
862 		dX = (where[0] - X),
863 		dY = (where[1] - Y),
864 	    i;
865     
866     if(Math.abs(dX) < JXG.Math.eps && Math.abs(dY) < JXG.Math.eps)
867         return this;
868 	
869 	for(i=steps; i>=0; i--) {
870 		coords[steps-i] = [X + dX * Math.sin((i/(steps*1.0))*Math.PI/2.), Y+ dY * Math.sin((i/(steps*1.0))*Math.PI/2.)];
871 	}
872 	this.animationPath = coords;
873     this.board.addAnimation(this);
874     return this;
875 };
876 
877 /**
878  * 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.
879  * The animation is done after <tt>time</tt> milliseconds.
880  * @param {Array} where Array containing the x and y coordinate of the target location.
881  * @param {int} time Number of milliseconds the animation should last.
882  * @param {int} repeat Optional: How often the animation should be repeated. The time value is then taken for one repeat.
883  * @see #animate
884  */
885 JXG.Point.prototype.visit = function(where, time, repeat) {
886     if(arguments.length == 2)
887         repeat = 1;
888 
889     var delay = 35,
890         steps = Math.ceil(time/(delay * 1.0)),
891         coords = new Array(repeat*(steps+1)),
892         X = this.coords.usrCoords[1],
893         Y = this.coords.usrCoords[2],
894         dX = (where[0] - X),
895         dY = (where[1] - Y),
896         i, j;
897     
898     for(j=0; j<repeat; j++) {
899         for(i=steps; i>=0; i--) {
900             coords[j*(steps+1) + steps-i] = [X + dX * Math.pow(Math.sin((i/(steps*1.0))*Math.PI), 2.), Y+ dY * Math.pow(Math.sin((i/(steps*1.0))*Math.PI), 2.)];
901         }
902     }
903     this.animationPath = coords;
904     this.board.addAnimation(this);
905     return this;
906 };
907 
908 /**
909  * Animates a glider. Is called by the browser after startAnimation is called.
910  * @param {number} direction The direction the glider is animated.
911  * @param {number} stepCount The number of steps.
912  * @see #startAnimation
913  * @see #stopAnimation
914  * @private
915  */
916 JXG.Point.prototype._anim = function(direction, stepCount) {
917     var distance, slope, dX, dY, alpha, startPoint,
918         factor = 1, newX, radius;
919     
920     this.intervalCount++;
921     if(this.intervalCount > stepCount)
922         this.intervalCount = 0;
923     
924     if(this.slideObject.elementClass == JXG.OBJECT_CLASS_LINE) {
925         distance = this.slideObject.point1.coords.distance(JXG.COORDS_BY_SCREEN, this.slideObject.point2.coords);
926         slope = this.slideObject.getSlope();
927         if(slope != 'INF') {
928             alpha = Math.atan(slope);
929             dX = Math.round((this.intervalCount/stepCount) * distance*Math.cos(alpha));
930             dY = Math.round((this.intervalCount/stepCount) * distance*Math.sin(alpha));
931         } else {
932             dX = 0;
933             dY = Math.round((this.intervalCount/stepCount) * distance);
934         }
935         
936         if(direction < 0) {
937             startPoint = this.slideObject.point2;
938             if(this.slideObject.point2.coords.scrCoords[1] - this.slideObject.point1.coords.scrCoords[1] > 0)
939                 factor = -1;
940             else if(this.slideObject.point2.coords.scrCoords[1] - this.slideObject.point1.coords.scrCoords[1] == 0) {
941                 if(this.slideObject.point2.coords.scrCoords[2] - this.slideObject.point1.coords.scrCoords[2] > 0)
942                     factor = -1;
943             }
944         } else {
945             startPoint = this.slideObject.point1;
946             if(this.slideObject.point1.coords.scrCoords[1] - this.slideObject.point2.coords.scrCoords[1] > 0)
947                 factor = -1;
948             else if(this.slideObject.point1.coords.scrCoords[1] - this.slideObject.point2.coords.scrCoords[1] == 0) {
949                 if(this.slideObject.point1.coords.scrCoords[2] - this.slideObject.point2.coords.scrCoords[2] > 0)
950                     factor = -1;
951             }
952         }
953         
954         this.coords.setCoordinates(JXG.COORDS_BY_SCREEN, [startPoint.coords.scrCoords[1] + factor*dX, startPoint.coords.scrCoords[2] + factor*dY]);
955     } else if(this.slideObject.elementClass == JXG.OBJECT_CLASS_CURVE) {
956         if(direction > 0) {
957             newX = Math.round(this.intervalCount/stepCount * this.board.canvasWidth);
958         } else {
959             newX = Math.round((stepCount - this.intervalCount)/stepCount * this.board.canvasWidth);
960         }
961   
962         this.coords.setCoordinates(JXG.COORDS_BY_SCREEN, [newX, 0]);
963         this.coords = JXG.Math.Geometry.projectPointToCurve(this, this.slideObject, this.board);
964     } else if(this.slideObject.elementClass == JXG.OBJECT_CLASS_CIRCLE) {
965         if(direction < 0) {
966             alpha = this.intervalCount/stepCount * 2*Math.PI;
967         } else {
968             alpha = (stepCount - this.intervalCount)/stepCount * 2*Math.PI;
969         }
970 
971         radius = this.slideObject.Radius();
972 
973         this.coords.setCoordinates(JXG.COORDS_BY_USER, [this.slideObject.midpoint.coords.usrCoords[1] + radius*Math.cos(alpha), this.slideObject.midpoint.coords.usrCoords[2] + radius*Math.sin(alpha)]);
974     }
975     
976     this.board.update(this);
977     return this;
978 };
979 
980 /**
981  * Set the style of a point.
982  * @param {int} i Integer to determine the style. See {@link JXG.GeometryElement#style} for a list of available styles.
983  * @see JXG.GeometryElement#style
984  * @private
985  * @deprecated
986  */
987 JXG.Point.prototype.setStyle = function(i) {
988     if(i == 0 || i == 1 || i == 2) { // x
989         this.visProp['face'] = 'cross';
990         if(i == 0) {
991             this.visProp['size'] = 2;
992         }
993         else if(i == 1) {
994             this.visProp['size'] = 3;
995         }
996         else {
997             this.visProp['size'] = 4;
998         }        
999     }
1000     else if(i == 3 || i == 4 || i == 5 || i == 6) { // circle
1001         this.visProp['face'] = 'circle';
1002         if(i == 3) {
1003             this.visProp['size'] = 1;
1004         }
1005         else if(i == 4) {
1006             this.visProp['size'] = 2;
1007         }
1008         else if(i == 5) {
1009             this.visProp['size'] = 3;
1010         }        
1011         else {
1012             this.visProp['size'] = 4;
1013         }            
1014     }
1015     else if(i == 7 || i == 8 || i == 9) { // rectangle
1016         this.visProp['face'] = 'square';
1017         if(i == 7) {
1018             this.visProp['size'] = 2;
1019         }
1020         else if(i == 8) {
1021             this.visProp['size'] = 3;
1022         }
1023         else {
1024             this.visProp['size'] = 4;
1025         }  
1026     }
1027     else if(i == 10 || i == 11 || i == 12) { // +
1028         this.visProp['face'] = 'plus';
1029         if(i == 10) {
1030             this.visProp['size'] = 2;
1031         }
1032         else if(i == 11) {
1033             this.visProp['size'] = 3;
1034         }
1035         else {
1036             this.visProp['size'] = 4;
1037         }  
1038     }    
1039     
1040     this.board.renderer.changePointStyle(this);
1041     return this;
1042 };
1043 
1044 /**
1045  * All point faces can be defined with more than one name, e.g. a cross faced point can be given
1046  * by face equal to 'cross' or equal to 'x'. This method maps all possible values to fixed ones to
1047  * simplify if- and switch-clauses regarding point faces. The translation table is as follows:
1048  * <table>
1049  * <tr><th>Input</th><th>Output</th></tr>
1050  * <tr><td>cross, x</td><td>x</td></tr>
1051  * <tr><td>circle, o</td><td>o</td></tr>
1052  * <tr><td>square, []</td><td>[]</td></tr>
1053  * <tr><td>plus, +</td><td>+</td></tr>
1054  * <tr><td>diamond, <></td><td><></td></tr>
1055  * <tr><td>triangleup, a, ^</td><td>A</td></tr>
1056  * <tr><td>triangledown, v</td><td>v</td></tr>
1057  * <tr><td>triangleleft, <</td><td><</td></tr>
1058  * <tr><td>triangleright, ></td><td>></td></tr>
1059  * </table>
1060  * @param {String} s A string which should determine a valid point face.
1061  * @returns {String} Returns a normalized string or undefined if the given string is not a valid
1062  * point face.
1063  */
1064 JXG.Point.prototype.normalizeFace = function(s) {
1065     var map = {
1066             cross: 'x',
1067             x: 'x',
1068             circle: 'o',
1069             o: 'o',
1070             square: '[]',
1071             '[]': '[]',
1072             plus: '+',
1073             '+': '+',
1074             diamond: '<>',
1075             '<>': '<>',
1076             triangleup: '^',
1077             a: '^',
1078             '^': '^',
1079             triangledown: 'v',
1080             v: 'v',
1081             triangleleft: '<',
1082             '<': '<',
1083             triangleright: '>',
1084             '>': '>'
1085         };
1086 
1087     return map[s];
1088 };
1089 
1090 /**
1091  * Set the face of a point.
1092  * @param {string} s String which determines the face of the point. See {@link JXG.GeometryElement#face} for a list of available faces.
1093  * @see JXG.GeometryElement#face
1094  * @private
1095  */
1096 JXG.Point.prototype.setFace = function(s) {
1097     s = s.toLowerCase();
1098     if(s == 'cross' || s == 'x' || s == 'plus' || s == '+' || s == 'circle' || s == 'o' || s == 'square' || s == '[]' 
1099        || s == 'diamond' || s == '<>' || s == 'triangleup' || s == 'a' || s == 'triangledown' || s == 'v' || 
1100        s == 'triangleleft' || s == '<' || s == 'triangleright' || s == '>') {
1101         this.visProp['face'] = s;
1102     }
1103     else {
1104         this.visProp['face'] = 'circle';
1105     }
1106     this.board.renderer.changePointStyle(this);
1107     return this;
1108 };
1109 
1110 /**
1111  * Remove the point from the drawing.
1112  */
1113 JXG.Point.prototype.remove = function() {    
1114     if (this.hasLabel) {
1115         this.board.renderer.remove(this.board.renderer.getElementById(this.label.content.id));
1116     }
1117     this.board.renderer.remove(this.board.renderer.getElementById(this.id));
1118 };
1119 
1120 /**
1121  * TODO
1122  * @return TODO
1123  * @type JXG.Coords
1124  * @private
1125  */
1126 JXG.Point.prototype.getTextAnchor = function() {
1127     return this.coords;
1128 };
1129 
1130 /**
1131  * TODO
1132  * @return TODO
1133  * @type JXG.Coords
1134  * @private
1135  */
1136 JXG.Point.prototype.getLabelAnchor = function() {
1137     return this.coords;
1138 };
1139 
1140 /**
1141  * Set the face of a point element
1142  * @param {string} f String which determines the face of the point. See {@link JXG.GeometryElement#face} for a list of available faces.
1143  * @see JXG.GeometryElement#face
1144  */
1145 JXG.Point.prototype.face = function(f) {
1146     this.setProperty({face:f});
1147 };
1148 
1149 /**
1150  * Set the size of a point element
1151  * @param {int} s Integer which determines the size of the point.
1152  * @see JXG.GeometryElement#size
1153  */
1154 JXG.Point.prototype.size = function(s) {
1155     this.setProperty({size:s});
1156 };
1157 
1158 /**
1159  * Copy the element to the background.
1160  * @param addToTrace If true the clone will be added to trace control and can be removed using {@link JXG.GeometryElement#clearTrace}.
1161  * Currently not used, and always true.
1162  */
1163 JXG.Point.prototype.cloneToBackground = function(/** boolean */ addToTrace) {
1164     var copy = {};
1165     copy.id = this.id + 'T' + this.numTraces;
1166     this.numTraces++;
1167     copy.coords = this.coords;
1168     copy.visProp = this.visProp;
1169     copy.elementClass = JXG.OBJECT_CLASS_POINT;
1170     JXG.clearVisPropOld(copy);
1171     
1172     this.board.renderer.drawPoint(copy);
1173     this.traces[copy.id] = copy.rendNode;
1174     delete copy;
1175 /*   
1176     this.board.renderer.cloneSubTree(this);
1177 */    
1178     return this;
1179 };
1180 
1181 /* old description of the following createPoint method.
1182  * There are several methods to construct a point.
1183  * The input parameter "parentArr" determines the point:
1184  * - 2 numbers: affine (Euclidean) coordinates of a free point
1185  * - 2 numbers and atts['slideObject'] : Glider with initial Euclidean coordinates
1186  * - 2 Strings or (1 String and 1 Number): constrained point
1187  * - 1 function: intersection of objects, this is just a constrained point too
1188  * - 1 transformation object: clone of a base point transformed by the given Transformation
1189  * - 3 numbers: homogeneous coordinates of a free point
1190  */
1191 
1192 /**
1193  * @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
1194  * 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
1195  * 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
1196  * position directly.
1197  * @pseudo
1198  * @description
1199  * @name Point
1200  * @augments JXG.Point
1201  * @constructor
1202  * @type JXG.Point
1203  * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
1204  * @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
1205  * constraint, or a function which takes no parameter and returns a number. Every parent element determines one coordinate. If a coordinate is
1206  * 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
1207  * 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
1208  * 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
1209  * parent elements are given they will be interpreted as homogeneous coordinates.
1210  * @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
1211  * point transformed by the given Transformation. {@see JXG.Transformation}.
1212  * @example
1213  * // Create a free point using affine euclidean coordinates 
1214  * var p1 = board.create('point', [3.5, 2.0]);
1215  * </pre><div id="672f1764-7dfa-4abc-a2c6-81fbbf83e44b" style="width: 200px; height: 200px;"></div>
1216  * <script type="text/javascript">
1217  *   var board = JXG.JSXGraph.initBoard('672f1764-7dfa-4abc-a2c6-81fbbf83e44b', {boundingbox: [-1, 5, 5, -1], axis: true, showcopyright: false, shownavigation: false});
1218  *   var p1 = board.create('point', [3.5, 2.0]);
1219  * </script><pre>
1220  * @example
1221  * // Create a constrained point using anonymous function 
1222  * var p2 = board.create('point', [3.5, function () { return p1.X(); }]);
1223  * </pre><div id="4fd4410c-3383-4e80-b1bb-961f5eeef224" style="width: 200px; height: 200px;"></div>
1224  * <script type="text/javascript">
1225  *   var fpex1_board = JXG.JSXGraph.initBoard('4fd4410c-3383-4e80-b1bb-961f5eeef224', {boundingbox: [-1, 5, 5, -1], axis: true, showcopyright: false, shownavigation: false});
1226  *   var fpex1_p1 = fpex1_board.create('point', [3.5, 2.0]);
1227  *   var fpex1_p2 = fpex1_board.create('point', [3.5, function () { return fpex1_p1.X(); }]);
1228  * </script><pre>
1229  * @example
1230  * // Create a point using transformations 
1231  * var trans = board.create('transform', [2, 0.5], {type:'scale'});
1232  * var p3 = board.create('point', [p2, trans]);
1233  * </pre><div id="630afdf3-0a64-46e0-8a44-f51bd197bb8d" style="width: 400px; height: 400px;"></div>
1234  * <script type="text/javascript">
1235  *   var fpex2_board = JXG.JSXGraph.initBoard('630afdf3-0a64-46e0-8a44-f51bd197bb8d', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false});
1236  *   var fpex2_trans = fpex2_board.create('transform', [2, 0.5], {type:'scale'});
1237  *   var fpex2_p2 = fpex2_board.create('point', [3.5, 2.0]);
1238  *   var fpex2_p3 = fpex2_board.create('point', [fpex2_p2, fpex2_trans]);
1239  * </script><pre>
1240  */
1241 JXG.createPoint = function(/** JXG.Board */ board, /** array */ parents, /** object */ atts) {
1242     var el, isConstrained = false, i, show;
1243     atts = JXG.checkAttributes(atts,{withLabel:JXG.readOption(board.options,'point','withLabel'), layer:null});
1244     show = (typeof atts['visible']=='undefined') || JXG.str2Bool(atts['visible']);
1245     
1246     for (i=0;i<parents.length;i++) {
1247         if (typeof parents[i]=='function' || typeof parents[i]=='string') {
1248             isConstrained = true;
1249         }
1250     }
1251     if (!isConstrained) {
1252         if ( (JXG.isNumber(parents[0])) && (JXG.isNumber(parents[1])) ) {
1253             el = new JXG.Point(board, parents, atts['id'], atts['name'], show, atts['withLabel'], atts['layer']);
1254             if ( atts["slideObject"] != null ) {
1255                 el.makeGlider(atts["slideObject"]);
1256             } else {
1257                 el.baseElement = el; // Free point
1258             }
1259         } else if ( (typeof parents[0]=='object') && (typeof parents[1]=='object') ) { // Transformation
1260             el = new JXG.Point(board, [0,0], atts['id'], atts['name'], show, atts['withLabel'], atts['layer']);   
1261             el.addTransform(parents[0],parents[1]);
1262         }
1263         else {// Failure
1264             throw new Error("JSXGraph: Can't create point with parent types '" + 
1265                             (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." +
1266                             "\nPossible parent types: [x,y], [z,x,y], [point,transformation]");
1267         }
1268     } else {
1269         el = new JXG.Point(board, [0,0], atts['id'], atts['name'], show, atts['withLabel'], atts['layer']);
1270         el.addConstraint(parents);
1271     }
1272     return el;
1273 };
1274 
1275 /**
1276  * @class This element is used to provide a constructor for a glider point. 
1277  * @pseudo
1278  * @description A glider is a point which lives on another geometric element like a line, circle, curve, turtle.
1279  * @name Glider
1280  * @augments JXG.Point
1281  * @constructor
1282  * @type JXG.Point
1283  * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
1284  * @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.
1285  * 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
1286  * coordinates, otherwise they will be interpreted as homogeneous coordinates. In any case the point will be projected on the glide object.
1287  * @example
1288  * // Create a glider with user defined coordinates. If the coordinates are not on
1289  * // the circle (like in this case) the point will be projected onto the circle.
1290  * var p1 = board.create('point', [2.0, 2.0]);
1291  * var c1 = board.create('circle', [p1, 2.0]);
1292  * var p2 = board.create('glider', [2.0, 1.5, c1]);
1293  * </pre><div id="4f65f32f-e50a-4b50-9b7c-f6ec41652930" style="width: 300px; height: 300px;"></div>
1294  * <script type="text/javascript">
1295  *   var gpex1_board = JXG.JSXGraph.initBoard('4f65f32f-e50a-4b50-9b7c-f6ec41652930', {boundingbox: [-1, 5, 5, -1], axis: true, showcopyright: false, shownavigation: false});
1296  *   var gpex1_p1 = gpex1_board.create('point', [2.0, 2.0]);
1297  *   var gpex1_c1 = gpex1_board.create('circle', [gpex1_p1, 2.0]);
1298  *   var gpex1_p2 = gpex1_board.create('glider', [2.0, 1.5, gpex1_c1]);
1299  * </script><pre>
1300  * @example
1301  * // Create a glider with default coordinates (1,0,0). Same premises as above.
1302  * var p1 = board.create('point', [2.0, 2.0]);
1303  * var c1 = board.create('circle', [p1, 2.0]);
1304  * var p2 = board.create('glider', [c1]);
1305  * </pre><div id="4de7f181-631a-44b1-a12f-bc4d995609e8" style="width: 200px; height: 200px;"></div>
1306  * <script type="text/javascript">
1307  *   var gpex2_board = JXG.JSXGraph.initBoard('4de7f181-631a-44b1-a12f-bc4d995609e8', {boundingbox: [-1, 5, 5, -1], axis: true, showcopyright: false, shownavigation: false});
1308  *   var gpex2_p1 = gpex2_board.create('point', [2.0, 2.0]);
1309  *   var gpex2_c1 = gpex2_board.create('circle', [gpex2_p1, 2.0]);
1310  *   var gpex2_p2 = gpex2_board.create('glider', [gpex2_c1]);
1311  * </script><pre>
1312  */
1313 JXG.createGlider = function(board, parents, atts) {
1314     var el, show;
1315     atts = JXG.checkAttributes(atts,{withLabel:JXG.readOption(board.options,'point','withLabel'), layer:null});
1316     show = (typeof atts['visible']=='undefined') || JXG.str2Bool(atts['visible']);
1317     
1318     if (parents.length==1) {
1319       el = new JXG.Point(board, [0,0], atts['id'], atts['name'], show, atts['withLabel']);
1320     } else {
1321       el = board.create('point',parents.slice(0,-1), atts);
1322     }
1323     el.makeGlider(parents[parents.length-1]);
1324     return el;
1325 };
1326 
1327 /**
1328  * @class This element is used to provide a constructor for an intersection point. 
1329  * @pseudo
1330  * @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.
1331  * an intersection point of the two elements.
1332  * @name Intersection
1333  * @augments JXG.Point
1334  * @constructor
1335  * @type JXG.Point
1336  * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
1337  * @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
1338  * intersection point if two points are available: <ul>
1339  *   <li>i==0: use the positive square root,</li> 
1340  *   <li>i==1: use the negative square root.</li></ul>
1341  * @example
1342  * // Create an intersection point of circle and line
1343  * var p1 = board.create('point', [2.0, 2.0]);
1344  * var c1 = board.create('circle', [p1, 2.0]);
1345  * 
1346  * var p2 = board.create('point', [2.0, 2.0]);
1347  * var p3 = board.create('point', [2.0, 2.0]);
1348  * var l1 = board.create('line', [p2, p3]);
1349  * 
1350  * var i = board.create('intersection', [c1, l1, 0]);
1351  * </pre><div id="e5b0e190-5200-4bc3-b995-b6cc53dc5dc0" style="width: 300px; height: 300px;"></div>
1352  * <script type="text/javascript">
1353  *   var ipex1_board = JXG.JSXGraph.initBoard('e5b0e190-5200-4bc3-b995-b6cc53dc5dc0', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false});
1354  *   var ipex1_p1 = ipex1_board.create('point', [4.0, 4.0]);
1355  *   var ipex1_c1 = ipex1_board.create('circle', [ipex1_p1, 2.0]);
1356  *   var ipex1_p2 = ipex1_board.create('point', [1.0, 1.0]);
1357  *   var ipex1_p3 = ipex1_board.create('point', [5.0, 3.0]);
1358  *   var ipex1_l1 = ipex1_board.create('line', [ipex1_p2, ipex1_p3]);
1359  *   var ipex1_i = ipex1_board.create('intersection', [ipex1_c1, ipex1_l1, 0]);
1360  * </script><pre>
1361  */
1362 JXG.createIntersectionPoint = function(board, parents, attributes) {
1363     var el;
1364     if (parents.length>=3) {
1365         if(parents.length == 3)
1366             parents.push(null);
1367         el = board.create('point', [board.intersection(parents[0], parents[1], parents[2], parents[3])], attributes);
1368     }
1369 
1370     parents[0].addChild(el);
1371     parents[1].addChild(el);
1372 
1373     el.generatePolynomial = function () {
1374         var poly1 = parents[0].generatePolynomial(el);
1375         var poly2 = parents[1].generatePolynomial(el);
1376 
1377         if((poly1.length == 0) || (poly2.length == 0))
1378             return [];
1379         else
1380             return [poly1[0], poly2[0]];
1381     };
1382     
1383     return el;
1384 };
1385 
1386 /**
1387  * @class This element is used to provide a constructor for the "other" intersection point.
1388  * @pseudo
1389  * @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.
1390  * an intersection point of the two elements. Additionally, one intersection point is provided. The function returns the other intersection point.
1391  * @name OtherIntersection
1392  * @augments JXG.Point
1393  * @constructor
1394  * @type JXG.Point
1395  * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
1396  * @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
1397  * intersection point different from p: 
1398  * @example
1399  * // Create an intersection point of circle and line
1400  * var p1 = board.create('point', [2.0, 2.0]);
1401  * var c1 = board.create('circle', [p1, 2.0]);
1402  * 
1403  * var p2 = board.create('point', [2.0, 2.0]);
1404  * var p3 = board.create('point', [2.0, 2.0]);
1405  * var l1 = board.create('line', [p2, p3]);
1406  * 
1407  * var i = board.create('intersection', [c1, l1, 0]);
1408  * var j = board.create('otherintersection', [c1, l1, i]);
1409  * </pre><div id="45e25f12-a1de-4257-a466-27a2ae73614c" style="width: 300px; height: 300px;"></div>
1410  * <script type="text/javascript">
1411  *   var ipex2_board = JXG.JSXGraph.initBoard('45e25f12-a1de-4257-a466-27a2ae73614c', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false});
1412  *   var ipex2_p1 = ipex2_board.create('point', [4.0, 4.0]);
1413  *   var ipex2_c1 = ipex2_board.create('circle', [ipex2_p1, 2.0]);
1414  *   var ipex2_p2 = ipex2_board.create('point', [1.0, 1.0]);
1415  *   var ipex2_p3 = ipex2_board.create('point', [5.0, 3.0]);
1416  *   var ipex2_l1 = ipex2_board.create('line', [ipex2_p2, ipex2_p3]);
1417  *   var ipex2_i = ipex2_board.create('intersection', [ipex2_c1, ipex2_l1, 0], {name:'D'});
1418  *   var ipex2_j = ipex2_board.create('otherintersection', [ipex2_c1, ipex2_l1, ipex2_i], {name:'E'});
1419  * </script><pre>
1420  */
1421 JXG.createOtherIntersectionPoint = function(board, parents, attributes) {
1422     var el;
1423     if (parents.length!=3 || 
1424         !JXG.isPoint(parents[2]) ||
1425         (parents[0].elementClass != JXG.OBJECT_CLASS_LINE && parents[0].elementClass != JXG.OBJECT_CLASS_CIRCLE) ||
1426         (parents[1].elementClass != JXG.OBJECT_CLASS_LINE && parents[1].elementClass != JXG.OBJECT_CLASS_CIRCLE) ) {
1427         // Failure
1428         throw new Error("JSXGraph: Can't create 'other intersection point' with parent types '" + 
1429                         (typeof parents[0]) + "',  '" + (typeof parents[1])+ "'and  '" + (typeof parents[2]) + "'." +
1430                         "\nPossible parent types: [circle|line,circle|line,point]");
1431     }
1432     else {
1433         el = board.create('point', [board.otherIntersection(parents[0], parents[1], parents[2])], attributes);
1434     }
1435     
1436     parents[0].addChild(el);
1437     parents[1].addChild(el);
1438 
1439     el.generatePolynomial = function () {
1440         var poly1 = parents[0].generatePolynomial(el);
1441         var poly2 = parents[1].generatePolynomial(el);
1442 
1443         if((poly1.length == 0) || (poly2.length == 0))
1444             return [];
1445         else
1446             return [poly1[0], poly2[0]];
1447     };
1448     
1449     return el;
1450 };
1451 
1452 
1453 JXG.JSXGraph.registerElement('point',JXG.createPoint);
1454 /*
1455 // Post-poned (A.W.)
1456 JXG.JSXGraph.registerElement('point', {
1457     icon:           'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAC0AAAAqCAIAAACofUV1AAAAu0lEQVR42u2Y2w7EIAhEYeL//zL7ttntRakyhjTw2MhwYDSaqplJgoDkiAAOFV0XaSGFD19MjMjh7/u70g8E6vBV1JmIQK2VHhp7A/5KdWxKf24Dh+HRxDaIvnJiX3jD6OhjM8RdlRfdc/Ece0y5rFW+FEdxFMerOCbe+9NxqFW+9Dn2WHOuAs8iNkT6/cEbyZ0yniYwIBL50ob4IY/F4XThkVj0yJOOQK2VHtpEW0OnuP+lqEep7rn/+AD75zNf8mTQTQAAAABJRU5ErkJggg%3D%3D',
1458     label:          'Free point',
1459     alttext:        'Constructs a free point',
1460     category:       'basic/points',
1461     description:    'Click on the board to place a free point or enter a pair of coordinates in the textbox.',
1462     showCoordsBox:  true,
1463     showInputbox:   false,
1464     checkInput:     function (draft, input) {
1465                        if(draft && input[input.length-1].usrCoords)
1466                            return true;
1467 
1468                        if(!draft && input.length == 1) {
1469                            return board.create('point', input[0].usrCoords.slice(1));
1470                        }
1471 
1472                        return false;
1473                     },
1474     creator:        JXG.createPoint
1475 });
1476 */
1477 JXG.JSXGraph.registerElement('glider', JXG.createGlider);
1478 JXG.JSXGraph.registerElement('intersection', JXG.createIntersectionPoint);
1479 JXG.JSXGraph.registerElement('otherintersection', JXG.createOtherIntersectionPoint);
1480