1 /*
  2     Copyright 2008-2014
  3         Matthias Ehmann,
  4         Michael Gerhaeuser,
  5         Carsten Miller,
  6         Bianca Valentin,
  7         Alfred Wassermann,
  8         Peter Wilfahrt
  9 
 10     This file is part of JSXGraph.
 11 
 12     JSXGraph is free software dual licensed under the GNU LGPL or MIT License.
 13 
 14     You can redistribute it and/or modify it under the terms of the
 15 
 16       * GNU Lesser General Public License as published by
 17         the Free Software Foundation, either version 3 of the License, or
 18         (at your option) any later version
 19       OR
 20       * MIT License: https://github.com/jsxgraph/jsxgraph/blob/master/LICENSE.MIT
 21 
 22     JSXGraph is distributed in the hope that it will be useful,
 23     but WITHOUT ANY WARRANTY; without even the implied warranty of
 24     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 25     GNU Lesser General Public License for more details.
 26 
 27     You should have received a copy of the GNU Lesser General Public License and
 28     the MIT License along with JSXGraph. If not, see <http://www.gnu.org/licenses/>
 29     and <http://opensource.org/licenses/MIT/>.
 30  */
 31 
 32 
 33 /*global JXG: true, define: true, console: true, window: true*/
 34 /*jslint nomen: true, plusplus: true*/
 35 
 36 /* depends:
 37  jxg
 38  options
 39  math/math
 40  math/geometry
 41  math/numerics
 42  base/coords
 43  base/constants
 44  base/element
 45  parser/geonext
 46  utils/type
 47   elements:
 48    transform
 49  */
 50 
 51 /**
 52  * @fileoverview The geometry object Point is defined in this file. Point stores all
 53  * style and functional properties that are required to draw and move a point on
 54  * a board.
 55  */
 56 
 57 define([
 58     'jxg', 'options', 'math/math', 'math/geometry', 'math/numerics', 'base/coords', 'base/constants', 'base/element',
 59     'parser/geonext', 'utils/type', 'base/transformation'
 60 ], function (JXG, Options, Mat, Geometry, Numerics, Coords, Const, GeometryElement, GeonextParser, Type, Transform) {
 61 
 62     "use strict";
 63 
 64     /**
 65      * A point is the basic geometric element. Based on points lines and circles can be constructed which can be intersected
 66      * which in turn are points again which can be used to construct new lines, circles, polygons, etc. This class holds methods for
 67      * all kind of points like free points, gliders, and intersection points.
 68      * @class Creates a new point object. Do not use this constructor to create a point. Use {@link JXG.Board#create} with
 69      * type {@link Point}, {@link Glider}, or {@link Intersection} instead.
 70      * @augments JXG.GeometryElement
 71      * @param {string|JXG.Board} board The board the new point is drawn on.
 72      * @param {Array} coordinates An array with the affine user coordinates of the point.
 73      * @param {Object} attributes An object containing visual properties like in {@link JXG.Options#point} and
 74      * {@link JXG.Options#elements}, and optional a name and a id.
 75      * @see JXG.Board#generateName
 76      * @see JXG.Board#addPoint
 77      */
 78     JXG.Point = function (board, coordinates, attributes) {
 79         this.constructor(board, attributes, Const.OBJECT_TYPE_POINT, Const.OBJECT_CLASS_POINT);
 80 
 81         if (!Type.exists(coordinates)) {
 82             coordinates = [0, 0];
 83         }
 84 
 85         /**
 86          * Coordinates of the point.
 87          * @type JXG.Coords
 88          * @private
 89          */
 90         this.coords = new Coords(Const.COORDS_BY_USER, coordinates, this.board);
 91         this.initialCoords = new Coords(Const.COORDS_BY_USER, coordinates, this.board);
 92 
 93         /**
 94          * Relative position on a line if point is a glider on a line.
 95          * @type Number
 96          * @private
 97          */
 98         this.position = null;
 99 
100         /**
101          * Determines whether the point slides on a polygon if point is a glider.
102          * @type boolean
103          * @default false
104          * @private
105          */
106         this.onPolygon = false;
107 
108         /**
109          * When used as a glider this member stores the object, where to glide on. To set the object to glide on use the method
110          * {@link JXG.Point#makeGlider} and DO NOT set this property directly as it will break the dependency tree.
111          * @type JXG.GeometryElement
112          * @name Glider#slideObject
113          */
114         this.slideObject = null;
115 
116         /**
117          * List of elements the point is bound to, i.e. the point glides on.
118          * Only the last entry is active.
119          * Use {@link JXG.Point#popSlideObject} to remove the currently active slideObject.
120          */
121         this.slideObjects = [];
122 
123         /**
124          * A {@link JXG.Point#updateGlider} call is usually followed by a general {@link JXG.Board#update} which calls
125          * {@link JXG.Point#updateGliderFromParent}. To prevent double updates, {@link JXG.Point#needsUpdateFromParent}
126          * is set to false in updateGlider() and reset to true in the following call to
127          * {@link JXG.Point#updateGliderFromParent}
128          * @type {Boolean}
129          */
130         this.needsUpdateFromParent = true;
131 
132         this.Xjc = null;
133         this.Yjc = null;
134 
135         // documented in GeometryElement
136         this.methodMap = Type.deepCopy(this.methodMap, {
137             move: 'moveTo',
138             moveTo: 'moveTo',
139             moveAlong: 'moveAlong',
140             visit: 'visit',
141             glide: 'makeGlider',
142             makeGlider: 'makeGlider',
143             X: 'X',
144             Y: 'Y',
145             free: 'free',
146             setPosition: 'setGliderPosition',
147             setGliderPosition: 'setGliderPosition',
148             addConstraint: 'addConstraint',
149             dist: 'Dist',
150             onPolygon: 'onPolygon'
151         });
152 
153         /**
154          * Stores the groups of this point in an array of Group.
155          * @type array
156          * @see JXG.Group
157          * @private
158          */
159         this.group = [];
160 
161         this.elType = 'point';
162 
163         /* Register point at board. */
164         this.id = this.board.setId(this, 'P');
165         this.board.renderer.drawPoint(this);
166         this.board.finalizeAdding(this);
167 
168         this.createLabel();
169     };
170 
171     /**
172      * Inherits here from {@link JXG.GeometryElement}.
173      */
174     JXG.Point.prototype = new GeometryElement();
175 
176     JXG.extend(JXG.Point.prototype, /** @lends JXG.Point.prototype */ {
177         /**
178          * Checks whether (x,y) is near the point.
179          * @param {Number} x Coordinate in x direction, screen coordinates.
180          * @param {Number} y Coordinate in y direction, screen coordinates.
181          * @returns {Boolean} True if (x,y) is near the point, False otherwise.
182          * @private
183          */
184         hasPoint: function (x, y) {
185             var coordsScr = this.coords.scrCoords, r;
186             r = parseFloat(this.visProp.size) + parseFloat(this.visProp.strokewidth) * 0.5;
187             if (r < this.board.options.precision.hasPoint) {
188                 r = this.board.options.precision.hasPoint;
189             }
190 
191             return ((Math.abs(coordsScr[1] - x) < r + 2) && (Math.abs(coordsScr[2] - y) < r + 2));
192         },
193 
194         /**
195          * Dummy function for unconstrained points or gliders.
196          * @private
197          */
198         updateConstraint: function () {
199             return this;
200         },
201 
202         /**
203          * Updates the position of the point.
204          */
205         update: function (fromParent) {
206             if (!this.needsUpdate) {
207                 return this;
208             }
209 
210             if (!Type.exists(fromParent)) {
211                 fromParent = false;
212             }
213 
214             /*
215              * We need to calculate the new coordinates no matter of the points visibility because
216              * a child could be visible and depend on the coordinates of the point (e.g. perpendicular).
217              *
218              * Check if point is a glider and calculate new coords in dependency of this.slideObject.
219              * This function is called with fromParent==true for example if
220              * the defining elements of the line or circle have been changed.
221              */
222             if (this.type === Const.OBJECT_TYPE_GLIDER) {
223                 if (fromParent) {
224                     this.updateGliderFromParent();
225                 } else {
226                     this.updateGlider();
227                 }
228             }
229 
230             /**
231              * If point is a calculated point, call updateConstraint() to calculate new coords.
232              * The second test is for dynamic axes.
233              */
234             if (this.type === Const.OBJECT_TYPE_CAS || this.type === Const.OBJECT_TYPE_INTERSECTION || this.type === Const.OBJECT_TYPE_AXISPOINT) {
235                 this.updateConstraint();
236             }
237 
238             this.updateTransform();
239 
240             if (this.visProp.trace) {
241                 this.cloneToBackground(true);
242             }
243 
244             return this;
245         },
246 
247         /**
248          * Update of glider in case of dragging the glider or setting the postion of the glider.
249          * The relative position of the glider has to be updated.
250          * If the second point is an ideal point, then -1 < this.position < 1,
251          * this.position==+/-1 equals point2, this.position==0 equals point1
252          *
253          * If the first point is an ideal point, then 0 < this.position < 2
254          * this.position==0  or 2 equals point1, this.position==1 equals point2
255          *
256          * @private
257          */
258         updateGlider: function () {
259             var i, p1c, p2c, d, v, poly, cc, pos, sgn,
260                 alpha, beta, angle,
261                 cp, c, invMat, newCoords, newPos,
262                 doRound = false,
263                 slide = this.slideObject;
264 
265             this.needsUpdateFromParent = false;
266 
267             if (slide.elementClass === Const.OBJECT_CLASS_CIRCLE) {
268                 //this.coords.setCoordinates(Const.COORDS_BY_USER, Geometry.projectPointToCircle(this, slide, this.board).usrCoords, false);
269                 newCoords = Geometry.projectPointToCircle(this, slide, this.board);
270                 newPos = Geometry.rad([slide.center.X() + 1.0, slide.center.Y()], slide.center, this);
271             } else if (slide.elementClass === Const.OBJECT_CLASS_LINE) {
272                 /*
273                  * onPolygon==true: the point is a slider on a segment and this segment is one of the
274                  * "borders" of a polygon.
275                  * This is a GEONExT feature.
276                  */
277                 if (this.onPolygon) {
278                     p1c = slide.point1.coords.usrCoords;
279                     p2c = slide.point2.coords.usrCoords;
280                     i = 1;
281                     d = p2c[i] - p1c[i];
282 
283                     if (Math.abs(d) < Mat.eps) {
284                         i = 2;
285                         d = p2c[i] - p1c[i];
286                     }
287 
288                     cc = Geometry.projectPointToLine(this, slide, this.board);
289                     pos = (cc.usrCoords[i] - p1c[i]) / d;
290                     poly = slide.parentPolygon;
291 
292                     if (pos < 0) {
293                         for (i = 0; i < poly.borders.length; i++) {
294                             if (slide === poly.borders[i]) {
295                                 slide = poly.borders[(i - 1 + poly.borders.length) % poly.borders.length];
296                                 break;
297                             }
298                         }
299                     } else if (pos > 1.0) {
300                         for (i = 0; i < poly.borders.length; i++) {
301                             if (slide === poly.borders[i]) {
302                                 slide = poly.borders[(i + 1 + poly.borders.length) % poly.borders.length];
303                                 break;
304                             }
305                         }
306                     }
307 
308                     // If the slide object has changed, save the change to the glider.
309                     if (slide.id !== this.slideObject.id) {
310                         this.slideObject = slide;
311                     }
312                 }
313 
314                 p1c = slide.point1.coords;
315                 p2c = slide.point2.coords;
316 
317                 // Distance between the two defining points
318                 d = p1c.distance(Const.COORDS_BY_USER, p2c);
319 
320                 // The defining points are identical
321                 if (d < Mat.eps) {
322                     //this.coords.setCoordinates(Const.COORDS_BY_USER, p1c);
323                     newCoords = p1c;
324                     doRound = true;
325                     newPos = 0.0;
326                 } else {
327                     //this.coords.setCoordinates(Const.COORDS_BY_USER, Geometry.projectPointToLine(this, slide, this.board).usrCoords, false);
328                     newCoords = Geometry.projectPointToLine(this, slide, this.board);
329                     p1c = p1c.usrCoords.slice(0);
330                     p2c = p2c.usrCoords.slice(0);
331 
332                     // The second point is an ideal point
333                     if (Math.abs(p2c[0]) < Mat.eps) {
334                         i = 1;
335                         d = p2c[i];
336 
337                         if (Math.abs(d) < Mat.eps) {
338                             i = 2;
339                             d = p2c[i];
340                         }
341 
342                         d = (newCoords.usrCoords[i] - p1c[i]) / d;
343                         sgn = (d >= 0) ? 1 : -1;
344                         d = Math.abs(d);
345                         newPos = sgn * d / (d + 1);
346 
347                     // The first point is an ideal point
348                     } else if (Math.abs(p1c[0]) < Mat.eps) {
349                         i = 1;
350                         d = p1c[i];
351 
352                         if (Math.abs(d) < Mat.eps) {
353                             i = 2;
354                             d = p1c[i];
355                         }
356 
357                         d = (newCoords.usrCoords[i] - p2c[i]) / d;
358 
359                         // 1.0 - d/(1-d);
360                         if (d < 0.0) {
361                             newPos = (1 - 2.0 * d) / (1.0 - d);
362                         } else {
363                             newPos = 1 / (d + 1);
364                         }
365                     } else {
366                         i = 1;
367                         d = p2c[i] - p1c[i];
368 
369                         if (Math.abs(d) < Mat.eps) {
370                             i = 2;
371                             d = p2c[i] - p1c[i];
372                         }
373                         newPos = (newCoords.usrCoords[i] - p1c[i]) / d;
374                     }
375                 }
376 
377                 // Snap the glider point of the slider into its appropiate position
378                 // First, recalculate the new value of this.position
379                 // Second, call update(fromParent==true) to make the positioning snappier.
380                 if (this.visProp.snapwidth > 0.0 && Math.abs(this._smax - this._smin) >= Mat.eps) {
381                     newPos = Math.max(Math.min(newPos, 1), 0);
382 
383                     v = newPos * (this._smax - this._smin) + this._smin;
384                     v = Math.round(v / this.visProp.snapwidth) * this.visProp.snapwidth;
385                     newPos = (v - this._smin) / (this._smax - this._smin);
386                     this.update(true);
387                 }
388 
389                 p1c = slide.point1.coords;
390                 if (!slide.visProp.straightfirst && Math.abs(p1c.usrCoords[0]) > Mat.eps && newPos < 0) {
391                     //this.coords.setCoordinates(Const.COORDS_BY_USER, p1c);
392                     newCoords = p1c;
393                     doRound = true;
394                     newPos = 0;
395                 }
396 
397                 p2c = slide.point2.coords;
398                 if (!slide.visProp.straightlast && Math.abs(p2c.usrCoords[0]) > Mat.eps && newPos > 1) {
399                     //this.coords.setCoordinates(Const.COORDS_BY_USER, p2c);
400                     newCoords = p2c;
401                     doRound = true;
402                     newPos = 1;
403                 }
404             } else if (slide.type === Const.OBJECT_TYPE_TURTLE) {
405                 // In case, the point is a constrained glider.
406                 // side-effect: this.position is overwritten
407                 this.updateConstraint();
408                 //this.coords.setCoordinates(Const.COORDS_BY_USER, Geometry.projectPointToTurtle(this, slide, this.board).usrCoords, false);
409                 newCoords = Geometry.projectPointToTurtle(this, slide, this.board);
410                 newPos = this.position;     // save position for the overwriting below
411             } else if (slide.elementClass === Const.OBJECT_CLASS_CURVE) {
412                 if ((slide.type === Const.OBJECT_TYPE_ARC ||
413                         slide.type === Const.OBJECT_TYPE_SECTOR)) {
414                     //this.coords.setCoordinates(Const.COORDS_BY_USER, Geometry.projectPointToCircle(this, slide, this.board).usrCoords, false);
415                     newCoords = Geometry.projectPointToCircle(this, slide, this.board);
416 
417                     angle = Geometry.rad(slide.radiuspoint, slide.center, this);
418                     alpha = 0.0;
419                     beta = Geometry.rad(slide.radiuspoint, slide.center, slide.anglepoint);
420                     newPos = angle;
421 
422                     if ((slide.visProp.type === 'minor' && beta > Math.PI) ||
423                             (slide.visProp.type === 'major' && beta < Math.PI)) {
424                         alpha = beta;
425                         beta = 2 * Math.PI;
426                     }
427 
428                     // Correct the position if we are outside of the sector/arc
429                     if (angle < alpha || angle > beta) {
430                         newPos = beta;
431 
432                         if ((angle < alpha && angle > alpha * 0.5) || (angle > beta && angle > beta * 0.5 + Math.PI)) {
433                             newPos = alpha;
434                         }
435                         this.needsUpdateFromParent = true;
436                         this.updateGliderFromParent();
437                     }
438 
439                 } else {
440                     // In case, the point is a constrained glider.
441                     this.updateConstraint();
442 
443                     if (slide.transformations.length > 0) {
444                         slide.updateTransformMatrix();
445                         invMat = Mat.inverse(slide.transformMat);
446                         c = Mat.matVecMult(invMat, this.coords.usrCoords);
447 
448                         cp = (new Coords(Const.COORDS_BY_USER, c, this.board)).usrCoords;
449                         c = Geometry.projectCoordsToCurve(cp[1], cp[2], this.position || 0, slide, this.board);
450 
451                         newCoords = c[0];
452                         newPos = c[1];
453                     } else {
454                         // side-effect: this.position is overwritten
455                         //this.coords.setCoordinates(Const.COORDS_BY_USER, Geometry.projectPointToCurve(this, slide, this.board).usrCoords, false);
456                         newCoords = Geometry.projectPointToCurve(this, slide, this.board);
457                         newPos = this.position; // save position for the overwriting below
458                     }
459                 }
460             } else if (slide.elementClass === Const.OBJECT_CLASS_POINT) {
461                 //this.coords.setCoordinates(Const.COORDS_BY_USER, Geometry.projectPointToPoint(this, slide, this.board).usrCoords, false);
462                 newCoords = Geometry.projectPointToPoint(this, slide, this.board);
463                 newPos = this.position; // save position for the overwriting below
464             }
465 
466             this.coords.setCoordinates(Const.COORDS_BY_USER, newCoords.usrCoords, doRound);
467             this.position = newPos;
468         },
469 
470         /**
471          * Update of a glider in case a parent element has been updated. That means the
472          * relative position of the glider stays the same.
473          * @private
474          */
475         updateGliderFromParent: function () {
476             var p1c, p2c, r, lbda, c,
477                 slide = this.slideObject, 
478                 baseangle, alpha, angle, beta, newPos;
479 
480             if (!this.needsUpdateFromParent) {
481                 this.needsUpdateFromParent = true;
482                 return;
483             }
484 
485             if (slide.elementClass === Const.OBJECT_CLASS_CIRCLE) {
486                 r = slide.Radius();
487                 c = [
488                     slide.center.X() + r * Math.cos(this.position),
489                     slide.center.Y() + r * Math.sin(this.position)
490                 ];
491             } else if (slide.elementClass === Const.OBJECT_CLASS_LINE) {
492                 p1c = slide.point1.coords.usrCoords;
493                 p2c = slide.point2.coords.usrCoords;
494 
495                 // The second point is an ideal point
496                 if (Math.abs(p2c[0]) < Mat.eps) {
497                     lbda = Math.min(Math.abs(this.position), 1 - Mat.eps);
498                     lbda /= (1.0 - lbda);
499 
500                     if (this.position < 0) {
501                         lbda = -lbda;
502                     }
503 
504                     c = [
505                         p1c[0] + lbda * p2c[0],
506                         p1c[1] + lbda * p2c[1],
507                         p1c[2] + lbda * p2c[2]
508                     ];
509                 // The first point is an ideal point
510                 } else if (Math.abs(p1c[0]) < Mat.eps) {
511                     lbda = Math.max(this.position, Mat.eps);
512                     lbda = Math.min(lbda, 2 - Mat.eps);
513 
514                     if (lbda > 1) {
515                         lbda = (lbda - 1) / (lbda - 2);
516                     } else {
517                         lbda = (1 - lbda) / lbda;
518                     }
519 
520                     c = [
521                         p2c[0] + lbda * p1c[0],
522                         p2c[1] + lbda * p1c[1],
523                         p2c[2] + lbda * p1c[2]
524                     ];
525                 } else {
526                     lbda = this.position;
527                     c = [
528                         p1c[0] + lbda * (p2c[0] - p1c[0]),
529                         p1c[1] + lbda * (p2c[1] - p1c[1]),
530                         p1c[2] + lbda * (p2c[2] - p1c[2])
531                     ];
532                 }
533             } else if (slide.type === Const.OBJECT_TYPE_TURTLE) {
534                 this.coords.setCoordinates(Const.COORDS_BY_USER, [slide.Z(this.position), slide.X(this.position), slide.Y(this.position)]);
535                 // In case, the point is a constrained glider.
536                 // side-effect: this.position is overwritten:
537                 this.updateConstraint();
538                 c  = Geometry.projectPointToTurtle(this, slide, this.board).usrCoords;
539             } else if (slide.elementClass === Const.OBJECT_CLASS_CURVE) {
540                 this.coords.setCoordinates(Const.COORDS_BY_USER, [slide.Z(this.position), slide.X(this.position), slide.Y(this.position)]);
541 
542                 if (slide.type === Const.OBJECT_TYPE_ARC || slide.type === Const.OBJECT_TYPE_SECTOR) {
543                     
544                     baseangle = Geometry.rad([slide.center.X() + 1, slide.center.Y()], slide.center, slide.radiuspoint);
545 
546                     alpha = 0.0;
547                     beta = Geometry.rad(slide.radiuspoint, slide.center, slide.anglepoint);
548 
549                     if ((slide.visProp.type === 'minor' && beta > Math.PI) ||
550                             (slide.visProp.type === 'major' && beta < Math.PI)) {
551                         alpha = beta;
552                         beta = 2 * Math.PI;
553                     }
554 
555                     // Correct the position if we are outside of the sector/arc
556                     if (this.position < alpha || this.position > beta) {
557                         this.position = beta;
558 
559                         if ((this.position < alpha && this.position > alpha * 0.5) || 
560                             (this.position > beta && this.position > beta * 0.5 + Math.PI)) {
561                             this.position = alpha;
562                         }
563                     }
564 
565                     r = slide.Radius();
566                     c = [
567                         slide.center.X() + r * Math.cos(this.position + baseangle),
568                         slide.center.Y() + r * Math.sin(this.position + baseangle)
569                     ];
570                     
571                 } else {
572                     // In case, the point is a constrained glider.
573                     // side-effect: this.position is overwritten
574                     this.updateConstraint();
575                     c = Geometry.projectPointToCurve(this, slide, this.board).usrCoords;
576                 }
577 
578             } else if (slide.elementClass === Const.OBJECT_CLASS_POINT) {
579                 c = Geometry.projectPointToPoint(this, slide, this.board).usrCoords;
580             }
581 
582             this.coords.setCoordinates(Const.COORDS_BY_USER, c, false);
583         },
584 
585         /**
586          * Calls the renderer to update the drawing.
587          * @private
588          */
589         updateRenderer: function () {
590             var wasReal;
591 
592             if (!this.needsUpdate) {
593                 return this;
594             }
595 
596             /* Call the renderer only if point is visible. */
597             if (this.visProp.visible && this.visProp.size > 0) {
598                 wasReal = this.isReal;
599                 this.isReal = (!isNaN(this.coords.usrCoords[1] + this.coords.usrCoords[2]));
600                 //Homogeneous coords: ideal point
601                 this.isReal = (Math.abs(this.coords.usrCoords[0]) > Mat.eps) ? this.isReal : false;
602 
603                 if (this.isReal) {
604                     if (wasReal !== this.isReal) {
605                         this.board.renderer.show(this);
606 
607                         if (this.hasLabel && this.label.visProp.visible) {
608                             this.board.renderer.show(this.label);
609                         }
610                     }
611                     this.board.renderer.updatePoint(this);
612                 } else {
613                     if (wasReal !== this.isReal) {
614                         this.board.renderer.hide(this);
615 
616                         if (this.hasLabel && this.label.visProp.visible) {
617                             this.board.renderer.hide(this.label);
618                         }
619                     }
620                 }
621             }
622 
623             /* Update the label if visible. */
624             if (this.hasLabel && this.visProp.visible && this.label && this.label.visProp.visible && this.isReal) {
625                 this.label.update();
626                 this.board.renderer.updateText(this.label);
627             }
628 
629             this.needsUpdate = false;
630             return this;
631         },
632 
633         /**
634          * Getter method for x, this is used by for CAS-points to access point coordinates.
635          * @returns {Number} User coordinate of point in x direction.
636          */
637         X: function () {
638             return this.coords.usrCoords[1];
639         },
640 
641         /**
642          * Getter method for y, this is used by CAS-points to access point coordinates.
643          * @returns {Number} User coordinate of point in y direction.
644          */
645         Y: function () {
646             return this.coords.usrCoords[2];
647         },
648 
649         /**
650          * Getter method for z, this is used by CAS-points to access point coordinates.
651          * @returns {Number} User coordinate of point in z direction.
652          */
653         Z: function () {
654             return this.coords.usrCoords[0];
655         },
656 
657         /**
658          * New evaluation of the function term.
659          * This is required for CAS-points: Their XTerm() method is overwritten in {@link #addConstraint}
660          * @returns {Number} User coordinate of point in x direction.
661          * @private
662          */
663         XEval: function () {
664             return this.coords.usrCoords[1];
665         },
666 
667         /**
668          * New evaluation of the function term.
669          * This is required for CAS-points: Their YTerm() method is overwritten in {@link #addConstraint}
670          * @returns {Number} User coordinate of point in y direction.
671          * @private
672          */
673         YEval: function () {
674             return this.coords.usrCoords[2];
675         },
676 
677         /**
678          * New evaluation of the function term.
679          * This is required for CAS-points: Their ZTerm() method is overwritten in {@link #addConstraint}
680          * @returns {Number} User coordinate of point in z direction.
681          * @private
682          */
683         ZEval: function () {
684             return this.coords.usrCoords[0];
685         },
686 
687         // documented in JXG.GeometryElement
688         bounds: function () {
689             return this.coords.usrCoords.slice(1).concat(this.coords.usrCoords.slice(1));
690         },
691 
692         /**
693          * Getter method for the distance to a second point, this is required for CAS-elements.
694          * Here, function inlining seems to be worthwile  (for plotting).
695          * @param {JXG.Point} point2 The point to which the distance shall be calculated.
696          * @returns {Number} Distance in user coordinate to the given point
697          */
698         Dist: function (point2) {
699             var sum, f,
700                 r = NaN,
701                 c = point2.coords.usrCoords,
702                 ucr = this.coords.usrCoords;
703 
704             if (this.isReal && point2.isReal) {
705                 f = ucr[0] - c[0];
706                 sum = f * f;
707                 f = ucr[1] - c[1];
708                 sum += f * f;
709                 f = ucr[2] - c[2];
710                 sum += f * f;
711 
712                 r = Math.sqrt(sum);
713             }
714 
715             return r;
716         },
717 
718         /**
719          * Alias for {@link JXG.Element#handleSnapToGrid}
720          * @param {Boolean} force force snapping independent from what the snaptogrid attribute says
721          * @returns {JXG.Point} Reference to this element
722          */
723         snapToGrid: function (force) {
724             return this.handleSnapToGrid(force);
725         },
726 
727         /**
728          * Let a point snap to the nearest point in distance of
729          * {@link JXG.Point#attractorDistance}.
730          * The function uses the coords object of the point as
731          * its actual position.
732          * @param {Boolean} force force snapping independent from what the snaptogrid attribute says
733          * @returns {JXG.Point} Reference to this element
734          */
735         handleSnapToPoints: function (force) {
736             var i, pEl, pCoords,
737                 d = 0,
738                 dMax = Infinity,
739                 c = null;
740 
741             if (this.visProp.snaptopoints || force) {
742                 for (i = 0; i < this.board.objectsList.length; i++) {
743                     pEl = this.board.objectsList[i];
744 
745                     if (pEl.elementClass === Const.OBJECT_CLASS_POINT && pEl !== this && pEl.visProp.visible) {
746                         pCoords = Geometry.projectPointToPoint(this, pEl, this.board);
747                         if (this.visProp.attractorunit === 'screen') {
748                             d = pCoords.distance(Const.COORDS_BY_SCREEN, this.coords);
749                         } else {
750                             d = pCoords.distance(Const.COORDS_BY_USER, this.coords);
751                         }
752 
753                         if (d < this.visProp.attractordistance && d < dMax) {
754                             dMax = d;
755                             c = pCoords;
756                         }
757                     }
758                 }
759 
760                 if (c !== null) {
761                     this.coords.setCoordinates(Const.COORDS_BY_USER, c.usrCoords);
762                 }
763             }
764 
765             return this;
766         },
767 
768         /**
769          * Alias for {@link #handleSnapToPoints}.
770          * @param {Boolean} force force snapping independent from what the snaptogrid attribute says
771          * @returns {JXG.Point} Reference to this element
772          */
773         snapToPoints: function (force) {
774             return this.handleSnapToPoints(force);
775         },
776 
777         /**
778          * A point can change its type from free point to glider
779          * and vice versa. If it is given an array of attractor elements
780          * (attribute attractors) and the attribute attractorDistance
781          * then the pint will be made a glider if it less than attractorDistance
782          * apart from one of its attractor elements.
783          * If attractorDistance is equal to zero, the point stays in its
784          * current form.
785          * @returns {JXG.Point} Reference to this element
786          */
787         handleAttractors: function () {
788             var i, el, projCoords,
789                 d = 0.0,
790                 len = this.visProp.attractors.length;
791 
792             if (this.visProp.attractordistance === 0.0) {
793                 return;
794             }
795 
796             for (i = 0; i < len; i++) {
797                 el = this.board.select(this.visProp.attractors[i]);
798 
799                 if (Type.exists(el) && el !== this) {
800                     if (el.elementClass === Const.OBJECT_CLASS_POINT) {
801                         projCoords = Geometry.projectPointToPoint(this, el, this.board);
802                     } else if (el.elementClass === Const.OBJECT_CLASS_LINE) {
803                         projCoords = Geometry.projectPointToLine(this, el, this.board);
804                     } else if (el.elementClass === Const.OBJECT_CLASS_CIRCLE) {
805                         projCoords = Geometry.projectPointToCircle(this, el, this.board);
806                     } else if (el.elementClass === Const.OBJECT_CLASS_CURVE) {
807                         projCoords = Geometry.projectPointToCurve(this, el, this.board);
808                     } else if (el.type === Const.OBJECT_TYPE_TURTLE) {
809                         projCoords = Geometry.projectPointToTurtle(this, el, this.board);
810                     }
811 
812                     if (this.visProp.attractorunit === 'screen') {
813                         d = projCoords.distance(Const.COORDS_BY_SCREEN, this.coords);
814                     } else {
815                         d = projCoords.distance(Const.COORDS_BY_USER, this.coords);
816                     }
817 
818                     if (d < this.visProp.attractordistance) {
819                         if (!(this.type === Const.OBJECT_TYPE_GLIDER && this.slideObject === el)) {
820                             this.makeGlider(el);
821                         }
822 
823                         break;       // bind the point to the first attractor in its list.
824                     } else {
825                         if (el === this.slideObject && d >= this.visProp.snatchdistance) {
826                             this.popSlideObject();
827                         }
828                     }
829                 }
830             }
831 
832             return this;
833         },
834 
835         /**
836          * Sets coordinates and calls the point's update() method.
837          * @param {Number} method The type of coordinates used here. Possible values are {@link JXG.COORDS_BY_USER} and {@link JXG.COORDS_BY_SCREEN}.
838          * @param {Array} coords coordinates <tt>(z, x, y)</tt> in screen/user units
839          * @returns {JXG.Point} this element
840          */
841         setPositionDirectly: function (method, coords) {
842             var i, dx, dy, dz, el, p,
843                 oldCoords = this.coords,
844                 newCoords;
845 
846             this.coords.setCoordinates(method, coords);
847             this.handleSnapToGrid();
848             this.handleSnapToPoints();
849             this.handleAttractors();
850 
851             if (this.group.length === 0) {
852                 // Here used to be the udpate of the groups. I'm not sure why we don't need to execute
853                 // the else branch if there are groups defined on this point, hence I'll let the if live.
854 
855                 // Update the initial coordinates. This is needed for free points
856                 // that have a transformation bound to it.
857                 for (i = this.transformations.length - 1; i >= 0; i--) {
858                     if (method === Const.COORDS_BY_SCREEN) {
859                         newCoords = (new Coords(method, coords, this.board)).usrCoords;
860                     } else {
861                         if (coords.length === 2) {
862                             coords = [1].concat(coords);
863                         }
864                         newCoords = coords;
865                     }
866                     this.initialCoords.setCoordinates(Const.COORDS_BY_USER, Mat.matVecMult(Mat.inverse(this.transformations[i].matrix), newCoords));
867                 }
868                 this.update();
869             }
870 
871             // if the user suspends the board updates we need to recalculate the relative position of
872             // the point on the slide object. this is done in updateGlider() which is NOT called during the
873             // update process triggered by unsuspendUpdate.
874             if (this.board.isSuspendedUpdate && this.type === Const.OBJECT_TYPE_GLIDER) {
875                 this.updateGlider();
876             }
877 
878             return coords;
879         },
880 
881         /**
882          * Translates the point by <tt>tv = (x, y)</tt>.
883          * @param {Number} method The type of coordinates used here. Possible values are {@link JXG.COORDS_BY_USER} and {@link JXG.COORDS_BY_SCREEN}.
884          * @param {Number} tv (x, y)
885          * @returns {JXG.Point}
886          */
887         setPositionByTransform: function (method, tv) {
888             var t;
889 
890             tv = new Coords(method, tv, this.board);
891             t = this.board.create('transform', tv.usrCoords.slice(1), {type: 'translate'});
892 
893             if (this.transformations.length > 0 && this.transformations[this.transformations.length - 1].isNumericMatrix) {
894                 this.transformations[this.transformations.length - 1].melt(t);
895             } else {
896                 this.addTransform(this, t);
897             }
898 
899             this.update();
900 
901             return this;
902         },
903 
904         /**
905          * Sets coordinates and calls the point's update() method.
906          * @param {Number} method The type of coordinates used here. Possible values are {@link JXG.COORDS_BY_USER} and {@link JXG.COORDS_BY_SCREEN}.
907          * @param {Array} coords coordinates in screen/user units
908          * @returns {JXG.Point}
909          */
910         setPosition: function (method, coords) {
911             return this.setPositionDirectly(method, coords);
912         },
913 
914         /**
915          * Sets the position of a glider relative to the defining elements of the {@link JXG.Point#slideObject}.
916          * @param {Number} x
917          * @returns {JXG.Point} Reference to the point element.
918          */
919         setGliderPosition: function (x) {
920             if (this.type === Const.OBJECT_TYPE_GLIDER) {
921                 this.position = x;
922                 this.board.update();
923             }
924 
925             return this;
926         },
927 
928         /**
929          * Convert the point to glider and update the construction.
930          * @param {String|Object} slide The Object the point will be bound to.
931          */
932         makeGlider: function (slide) {
933             var slideobj = this.board.select(slide);
934             
935             /* Gliders on Ticks are forbidden */
936             if (!Type.exists(slideobj)) {
937                 throw new Error("JSXGraph: slide object undefined.");
938             } else if (slideobj.type === Const.OBJECT_TYPE_TICKS) {
939                 throw new Error("JSXGraph: gliders on ticks are not possible.");
940             }
941             
942             this.slideObject = this.board.select(slide);
943             this.slideObjects.push(this.slideObject);
944 
945             this.type = Const.OBJECT_TYPE_GLIDER;
946             this.elType = 'glider';
947             this.visProp.snapwidth = -1;          // By default, deactivate snapWidth
948             this.slideObject.addChild(this);
949             this.isDraggable = true;
950 
951             this.generatePolynomial = function () {
952                 return this.slideObject.generatePolynomial(this);
953             };
954 
955             // Determine the initial value of this.position
956             this.updateGlider();
957             this.needsUpdateFromParent = true;
958             this.updateGliderFromParent();
959 
960             return this;
961         },
962 
963         /**
964          * Remove the last slideObject. If there are more than one elements the point is bound to,
965          * the second last element is the new active slideObject.
966          */
967         popSlideObject: function () {
968             if (this.slideObjects.length > 0) {
969                 this.slideObjects.pop();
970 
971                 // It may not be sufficient to remove the point from
972                 // the list of childElement. For complex dependencies
973                 // one may have to go to the list of ancestor and descendants.  A.W.
974                 // yes indeed, see #51 on github bugtracker
975                 //delete this.slideObject.childElements[this.id];
976                 this.slideObject.removeChild(this);
977 
978                 if (this.slideObjects.length === 0) {
979                     this.elType = 'point';
980                     this.type = Const.OBJECT_TYPE_POINT;
981                     this.slideObject = null;
982                 } else {
983                     this.slideObject = this.slideObjects[this.slideObjects.length - 1];
984                 }
985             }
986         },
987 
988         /**
989          * Converts a calculated point into a free point, i.e. it will delete all ancestors and transformations and,
990          * if the point is currently a glider, will remove the slideObject reference.
991          */
992         free: function () {
993             var ancestorId, ancestor, child;
994 
995             if (this.type !== Const.OBJECT_TYPE_GLIDER) {
996                 // remove all transformations
997                 this.transformations.length = 0;
998 
999                 if (!this.isDraggable) {
1000                     this.isDraggable = true;
1001                     this.type = Const.OBJECT_TYPE_POINT;
1002 
1003                     this.XEval = function () {
1004                         return this.coords.usrCoords[1];
1005                     };
1006 
1007                     this.YEval = function () {
1008                         return this.coords.usrCoords[2];
1009                     };
1010 
1011                     this.ZEval = function () {
1012                         return this.coords.usrCoords[0];
1013                     };
1014 
1015                     this.Xjc = null;
1016                     this.Yjc = null;
1017                 } else {
1018                     return;
1019                 }
1020             }
1021 
1022             // a free point does not depend on anything. And instead of running through tons of descendants and ancestor
1023             // structures, where we eventually are going to visit a lot of objects twice or thrice with hard to read and
1024             // comprehend code, just run once through all objects and delete all references to this point and its label.
1025             for (ancestorId in this.board.objects) {
1026                 if (this.board.objects.hasOwnProperty(ancestorId)) {
1027                     ancestor = this.board.objects[ancestorId];
1028 
1029                     if (ancestor.descendants) {
1030                         delete ancestor.descendants[this.id];
1031                         delete ancestor.childElements[this.id];
1032 
1033                         if (this.hasLabel) {
1034                             delete ancestor.descendants[this.label.id];
1035                             delete ancestor.childElements[this.label.id];
1036                         }
1037                     }
1038                 }
1039             }
1040 
1041             // A free point does not depend on anything. Remove all ancestors.
1042             this.ancestors = {}; // only remove the reference
1043 
1044             // Completely remove all slideObjects of the point
1045             this.slideObject = null;
1046             this.slideObjects = [];
1047             this.elType = 'point';
1048             this.type = Const.OBJECT_TYPE_POINT;
1049         },
1050 
1051         /**
1052          * Convert the point to CAS point and call update().
1053          * @param {Array} terms [[zterm], xterm, yterm] defining terms for the z, x and y coordinate.
1054          * The z-coordinate is optional and it is used for homogeneous coordinates.
1055          * The coordinates may be either <ul>
1056          *   <li>a JavaScript function,</li>
1057          *   <li>a string containing GEONExT syntax. This string will be converted into a JavaScript
1058          *     function here,</li>
1059          *   <li>a Number</li>
1060          *   <li>a pointer to a slider object. This will be converted into a call of the Value()-method
1061          *     of this slider.</li>
1062          *   </ul>
1063          * @see JXG.GeonextParser#geonext2JS
1064          */
1065         addConstraint: function (terms) {
1066             var fs, i, v, t,
1067                 newfuncs = [],
1068                 what = ['X', 'Y'],
1069 
1070                 makeConstFunction = function (z) {
1071                     return function () {
1072                         return z;
1073                     };
1074                 },
1075 
1076                 makeSliderFunction = function (a) {
1077                     return function () {
1078                         return a.Value();
1079                     };
1080                 };
1081 
1082             this.type = Const.OBJECT_TYPE_CAS;
1083             this.isDraggable = false;
1084 
1085             for (i = 0; i < terms.length; i++) {
1086                 v = terms[i];
1087 
1088                 if (typeof v === 'string') {
1089                     // Convert GEONExT syntax into  JavaScript syntax
1090                     //t  = JXG.GeonextParser.geonext2JS(v, this.board);
1091                     //newfuncs[i] = new Function('','return ' + t + ';');
1092                     //v = GeonextParser.replaceNameById(v, this.board);
1093                     newfuncs[i] = this.board.jc.snippet(v, true, null, true);
1094 
1095                     if (terms.length === 2) {
1096                         this[what[i] + 'jc'] = terms[i];
1097                     }
1098                 } else if (typeof v === 'function') {
1099                     newfuncs[i] = v;
1100                 } else if (typeof v === 'number') {
1101                     newfuncs[i] = makeConstFunction(v);
1102                 // Slider
1103                 } else if (typeof v === 'object' && typeof v.Value === 'function') {
1104                     newfuncs[i] = makeSliderFunction(v);
1105                 }
1106 
1107                 newfuncs[i].origin = v;
1108             }
1109 
1110             // Intersection function
1111             if (terms.length === 1) {
1112                 this.updateConstraint = function () {
1113                     var c = newfuncs[0]();
1114 
1115                     // Array
1116                     if (Type.isArray(c)) {
1117                         this.coords.setCoordinates(Const.COORDS_BY_USER, c);
1118                     // Coords object
1119                     } else {
1120                         this.coords = c;
1121                     }
1122                 };
1123             // Euclidean coordinates
1124             } else if (terms.length === 2) {
1125                 this.XEval = newfuncs[0];
1126                 this.YEval = newfuncs[1];
1127 
1128                 this.parents = [newfuncs[0].origin, newfuncs[1].origin];
1129 
1130                 this.updateConstraint = function () {
1131                     this.coords.setCoordinates(Const.COORDS_BY_USER, [this.XEval(), this.YEval()]);
1132                 };
1133             // Homogeneous coordinates
1134             } else {
1135                 this.ZEval = newfuncs[0];
1136                 this.XEval = newfuncs[1];
1137                 this.YEval = newfuncs[2];
1138 
1139                 this.parents = [newfuncs[0].origin, newfuncs[1].origin, newfuncs[2].origin];
1140 
1141                 this.updateConstraint = function () {
1142                     this.coords.setCoordinates(Const.COORDS_BY_USER, [this.ZEval(), this.XEval(), this.YEval()]);
1143                 };
1144             }
1145 
1146             /**
1147             * We have to do an update. Otherwise, elements relying on this point will receive NaN.
1148             */
1149             this.update();
1150             if (!this.board.isSuspendedUpdate) {
1151                 this.updateRenderer();
1152             }
1153 
1154             return this;
1155         },
1156 
1157         /**
1158          * Applies the transformations of the curve to {@link JXG.Point#baseElement}.
1159          * @returns {JXG.Point} Reference to this point object.
1160          */
1161         updateTransform: function () {
1162             var c, i;
1163 
1164             if (this.transformations.length === 0 || this.baseElement === null) {
1165                 return this;
1166             }
1167 
1168             // case of bindTo
1169             if (this === this.baseElement) {
1170                 c = this.transformations[0].apply(this.baseElement, 'self');
1171             // case of board.create('point',[baseElement,transform]);
1172             } else {
1173                 c = this.transformations[0].apply(this.baseElement);
1174             }
1175 
1176             this.coords.setCoordinates(Const.COORDS_BY_USER, c);
1177 
1178             for (i = 1; i < this.transformations.length; i++) {
1179                 this.coords.setCoordinates(Const.COORDS_BY_USER, this.transformations[i].apply(this));
1180             }
1181             return this;
1182         },
1183 
1184         /**
1185          * Add transformations to this point.
1186          * @param {JXG.GeometryElement} el
1187          * @param {JXG.Transformation|Array} transform Either one {@link JXG.Transformation} or an array of {@link JXG.Transformation}s.
1188          * @returns {JXG.Point} Reference to this point object.
1189          */
1190         addTransform: function (el, transform) {
1191             var i,
1192                 list = Type.isArray(transform) ? transform : [transform],
1193                 len = list.length;
1194 
1195             // There is only one baseElement possible
1196             if (this.transformations.length === 0) {
1197                 this.baseElement = el;
1198             }
1199 
1200             for (i = 0; i < len; i++) {
1201                 this.transformations.push(list[i]);
1202             }
1203 
1204             return this;
1205         },
1206 
1207         /**
1208          * Animate the point.
1209          * @param {Number} direction The direction the glider is animated. Can be +1 or -1.
1210          * @param {Number} stepCount The number of steps.
1211          * @name Glider#startAnimation
1212          * @see Glider#stopAnimation
1213          * @function
1214          */
1215         startAnimation: function (direction, stepCount) {
1216             var that = this;
1217 
1218             if ((this.type === Const.OBJECT_TYPE_GLIDER) && !Type.exists(this.intervalCode)) {
1219                 this.intervalCode = window.setInterval(function () {
1220                     that._anim(direction, stepCount);
1221                 }, 250);
1222 
1223                 if (!Type.exists(this.intervalCount)) {
1224                     this.intervalCount = 0;
1225                 }
1226             }
1227             return this;
1228         },
1229 
1230         /**
1231          * Stop animation.
1232          * @name Glider#stopAnimation
1233          * @see Glider#startAnimation
1234          * @function
1235          */
1236         stopAnimation: function () {
1237             if (Type.exists(this.intervalCode)) {
1238                 window.clearInterval(this.intervalCode);
1239                 delete this.intervalCode;
1240             }
1241 
1242             return this;
1243         },
1244 
1245         /**
1246          * Starts an animation which moves the point along a given path in given time.
1247          * @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
1248          * 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.
1249          * In case of NaN the animation stops.
1250          * @param {Number} time The time in milliseconds in which to finish the animation
1251          * @param {Object} [options] Optional settings for the animation.
1252          * @param {function} [options.callback] A function that is called as soon as the animation is finished.
1253          * @param {Boolean} [options.interpolate=true] If <tt>path</tt> is an array moveAlong() will interpolate the path
1254          * using {@link JXG.Math.Numerics#Neville}. Set this flag to false if you don't want to use interpolation.
1255          * @returns {JXG.Point} Reference to the point.
1256          */
1257         moveAlong: function (path, time, options) {
1258             options = options || {};
1259 
1260             var i, neville,
1261                 interpath = [],
1262                 p = [],
1263                 delay = this.board.attr.animationdelay,
1264                 steps = time / delay,
1265 
1266                 makeFakeFunction = function (i, j) {
1267                     return function () {
1268                         return path[i][j];
1269                     };
1270                 };
1271 
1272             if (Type.isArray(path)) {
1273                 for (i = 0; i < path.length; i++) {
1274                     if (Type.isPoint(path[i])) {
1275                         p[i] = path[i];
1276                     } else {
1277                         p[i] = {
1278                             elementClass: Const.OBJECT_CLASS_POINT,
1279                             X: makeFakeFunction(i, 0),
1280                             Y: makeFakeFunction(i, 1)
1281                         };
1282                     }
1283                 }
1284 
1285                 time = time || 0;
1286                 if (time === 0) {
1287                     this.setPosition(Const.COORDS_BY_USER, [p[p.length - 1].X(), p[p.length - 1].Y()]);
1288                     return this.board.update(this);
1289                 }
1290 
1291                 if (!Type.exists(options.interpolate) || options.interpolate) {
1292                     neville = Numerics.Neville(p);
1293                     for (i = 0; i < steps; i++) {
1294                         interpath[i] = [];
1295                         interpath[i][0] = neville[0]((steps - i) / steps * neville[3]());
1296                         interpath[i][1] = neville[1]((steps - i) / steps * neville[3]());
1297                     }
1298                 } else {
1299                     for (i = 0; i < steps; i++) {
1300                         interpath[i] = [];
1301                         interpath[i][0] = path[Math.floor((steps - i) / steps * (path.length - 1))][0];
1302                         interpath[i][1] = path[Math.floor((steps - i) / steps * (path.length - 1))][1];
1303                     }
1304                 }
1305 
1306                 this.animationPath = interpath;
1307             } else if (Type.isFunction(path)) {
1308                 this.animationPath = path;
1309                 this.animationStart = new Date().getTime();
1310             }
1311 
1312             this.animationCallback = options.callback;
1313             this.board.addAnimation(this);
1314 
1315             return this;
1316         },
1317 
1318         /**
1319          * Starts an animated point movement towards the given coordinates <tt>where</tt>. The animation is done after <tt>time</tt> milliseconds.
1320          * If the second parameter is not given or is equal to 0, setPosition() is called, see #setPosition.
1321          * @param {Array} where Array containing the x and y coordinate of the target location.
1322          * @param {Number} [time] Number of milliseconds the animation should last.
1323          * @param {Object} [options] Optional settings for the animation
1324          * @param {function} [options.callback] A function that is called as soon as the animation is finished.
1325          * @param {String} [options.effect='<>'] animation effects like speed fade in and out. possible values are
1326          * '<>' for speed increase on start and slow down at the end (default) and '--' for constant speed during
1327          * the whole animation.
1328          * @returns {JXG.Point} Reference to itself.
1329          * @see #animate
1330          */
1331         moveTo: function (where, time, options) {
1332             options = options || {};
1333             where = new Coords(Const.COORDS_BY_USER, where, this.board);
1334 
1335             var i,
1336                 delay = this.board.attr.animationdelay,
1337                 steps = Math.ceil(time / delay),
1338                 coords = [],
1339                 X = this.coords.usrCoords[1],
1340                 Y = this.coords.usrCoords[2],
1341                 dX = (where.usrCoords[1] - X),
1342                 dY = (where.usrCoords[2] - Y),
1343 
1344                 /** @ignore */
1345                 stepFun = function (i) {
1346                     if (options.effect && options.effect === '<>') {
1347                         return Math.pow(Math.sin((i / steps) * Math.PI / 2), 2);
1348                     }
1349                     return i / steps;
1350                 };
1351 
1352             if (!Type.exists(time) || time === 0 || (Math.abs(where.usrCoords[0] - this.coords.usrCoords[0]) > Mat.eps)) {
1353                 this.setPosition(Const.COORDS_BY_USER, where.usrCoords);
1354                 return this.board.update(this);
1355             }
1356 
1357             if (Math.abs(dX) < Mat.eps && Math.abs(dY) < Mat.eps) {
1358                 return this;
1359             }
1360 
1361             for (i = steps; i >= 0; i--) {
1362                 coords[steps - i] = [where.usrCoords[0], X + dX * stepFun(i), Y + dY * stepFun(i)];
1363             }
1364 
1365             this.animationPath = coords;
1366             this.animationCallback = options.callback;
1367             this.board.addAnimation(this);
1368 
1369             return this;
1370         },
1371 
1372         /**
1373          * Starts an animated point movement towards the given coordinates <tt>where</tt>. After arriving at
1374          * <tt>where</tt> the point moves back to where it started. The animation is done after <tt>time</tt>
1375          * milliseconds.
1376          * @param {Array} where Array containing the x and y coordinate of the target location.
1377          * @param {Number} time Number of milliseconds the animation should last.
1378          * @param {Object} [options] Optional settings for the animation
1379          * @param {function} [options.callback] A function that is called as soon as the animation is finished.
1380          * @param {String} [options.effect='<>'] animation effects like speed fade in and out. possible values are
1381          * '<>' for speed increase on start and slow down at the end (default) and '--' for constant speed during
1382          * the whole animation.
1383          * @param {Number} [options.repeat=1] How often this animation should be repeated.
1384          * @returns {JXG.Point} Reference to itself.
1385          * @see #animate
1386          */
1387         visit: function (where, time, options) {
1388             where = new Coords(Const.COORDS_BY_USER, where, this.board);
1389 
1390             var i, j, steps,
1391                 delay = this.board.attr.animationdelay,
1392                 coords = [],
1393                 X = this.coords.usrCoords[1],
1394                 Y = this.coords.usrCoords[2],
1395                 dX = (where.usrCoords[1] - X),
1396                 dY = (where.usrCoords[2] - Y),
1397 
1398                 /** @ignore */
1399                 stepFun = function (i) {
1400                     var x = (i < steps / 2 ? 2 * i / steps : 2 * (steps - i) / steps);
1401 
1402                     if (options.effect && options.effect === '<>') {
1403                         return Math.pow(Math.sin(x * Math.PI / 2), 2);
1404                     }
1405 
1406                     return x;
1407                 };
1408 
1409             // support legacy interface where the third parameter was the number of repeats
1410             if (typeof options === 'number') {
1411                 options = {repeat: options};
1412             } else {
1413                 options = options || {};
1414                 if (!Type.exists(options.repeat)) {
1415                     options.repeat = 1;
1416                 }
1417             }
1418 
1419             steps = Math.ceil(time / (delay * options.repeat));
1420 
1421             for (j = 0; j < options.repeat; j++) {
1422                 for (i = steps; i >= 0; i--) {
1423                     coords[j * (steps + 1) + steps - i] = [where.usrCoords[0], X + dX * stepFun(i), Y + dY * stepFun(i)];
1424                 }
1425             }
1426             this.animationPath = coords;
1427             this.animationCallback = options.callback;
1428             this.board.addAnimation(this);
1429 
1430             return this;
1431         },
1432 
1433         /**
1434          * Animates a glider. Is called by the browser after startAnimation is called.
1435          * @param {Number} direction The direction the glider is animated.
1436          * @param {Number} stepCount The number of steps.
1437          * @see #startAnimation
1438          * @see #stopAnimation
1439          * @private
1440          */
1441         _anim: function (direction, stepCount) {
1442             var distance, slope, dX, dY, alpha, startPoint, newX, radius,
1443                 factor = 1;
1444 
1445             this.intervalCount += 1;
1446             if (this.intervalCount > stepCount) {
1447                 this.intervalCount = 0;
1448             }
1449 
1450             if (this.slideObject.elementClass === Const.OBJECT_CLASS_LINE) {
1451                 distance = this.slideObject.point1.coords.distance(Const.COORDS_BY_SCREEN, this.slideObject.point2.coords);
1452                 slope = this.slideObject.getSlope();
1453                 if (slope !== Infinity) {
1454                     alpha = Math.atan(slope);
1455                     dX = Math.round((this.intervalCount / stepCount) * distance * Math.cos(alpha));
1456                     dY = Math.round((this.intervalCount / stepCount) * distance * Math.sin(alpha));
1457                 } else {
1458                     dX = 0;
1459                     dY = Math.round((this.intervalCount / stepCount) * distance);
1460                 }
1461 
1462                 if (direction < 0) {
1463                     startPoint = this.slideObject.point2;
1464 
1465                     if (this.slideObject.point2.coords.scrCoords[1] - this.slideObject.point1.coords.scrCoords[1] > 0) {
1466                         factor = -1;
1467                     } else if (this.slideObject.point2.coords.scrCoords[1] - this.slideObject.point1.coords.scrCoords[1] === 0) {
1468                         if (this.slideObject.point2.coords.scrCoords[2] - this.slideObject.point1.coords.scrCoords[2] > 0) {
1469                             factor = -1;
1470                         }
1471                     }
1472                 } else {
1473                     startPoint = this.slideObject.point1;
1474 
1475                     if (this.slideObject.point1.coords.scrCoords[1] - this.slideObject.point2.coords.scrCoords[1] > 0) {
1476                         factor = -1;
1477                     } else if (this.slideObject.point1.coords.scrCoords[1] - this.slideObject.point2.coords.scrCoords[1] === 0) {
1478                         if (this.slideObject.point1.coords.scrCoords[2] - this.slideObject.point2.coords.scrCoords[2] > 0) {
1479                             factor = -1;
1480                         }
1481                     }
1482                 }
1483 
1484                 this.coords.setCoordinates(Const.COORDS_BY_SCREEN, [
1485                     startPoint.coords.scrCoords[1] + factor * dX,
1486                     startPoint.coords.scrCoords[2] + factor * dY
1487                 ]);
1488             } else if (this.slideObject.elementClass === Const.OBJECT_CLASS_CURVE) {
1489                 if (direction > 0) {
1490                     newX = Math.round(this.intervalCount / stepCount * this.board.canvasWidth);
1491                 } else {
1492                     newX = Math.round((stepCount - this.intervalCount) / stepCount * this.board.canvasWidth);
1493                 }
1494 
1495                 this.coords.setCoordinates(Const.COORDS_BY_SCREEN, [newX, 0]);
1496                 this.coords = Geometry.projectPointToCurve(this, this.slideObject, this.board);
1497             } else if (this.slideObject.elementClass === Const.OBJECT_CLASS_CIRCLE) {
1498                 if (direction < 0) {
1499                     alpha = this.intervalCount / stepCount * 2 * Math.PI;
1500                 } else {
1501                     alpha = (stepCount - this.intervalCount) / stepCount * 2 * Math.PI;
1502                 }
1503 
1504                 radius = this.slideObject.Radius();
1505 
1506                 this.coords.setCoordinates(Const.COORDS_BY_USER, [
1507                     this.slideObject.center.coords.usrCoords[1] + radius * Math.cos(alpha),
1508                     this.slideObject.center.coords.usrCoords[2] + radius * Math.sin(alpha)
1509                 ]);
1510             }
1511 
1512             this.board.update(this);
1513             return this;
1514         },
1515 
1516         /**
1517          * Set the style of a point. Used for GEONExT import and should not be used to set the point's face and size.
1518          * @param {Number} i Integer to determine the style.
1519          * @private
1520          */
1521         setStyle: function (i) {
1522             var facemap = [
1523                 // 0-2
1524                 'cross', 'cross', 'cross',
1525                 // 3-6
1526                 'circle', 'circle', 'circle', 'circle',
1527                 // 7-9
1528                 'square', 'square', 'square',
1529                 // 10-12
1530                 'plus', 'plus', 'plus'
1531             ], sizemap = [
1532                 // 0-2
1533                 2, 3, 4,
1534                 // 3-6
1535                 1, 2, 3, 4,
1536                 // 7-9
1537                 2, 3, 4,
1538                 // 10-12
1539                 2, 3, 4
1540             ];
1541 
1542             this.visProp.face = facemap[i];
1543             this.visProp.size = sizemap[i];
1544 
1545             this.board.renderer.changePointStyle(this);
1546             return this;
1547         },
1548 
1549         /**
1550          * @deprecated Use JXG#normalizePointFace instead
1551          * @param s
1552          * @return {*}
1553          */
1554         normalizeFace: function (s) {
1555             return Options.normalizePointFace(s);
1556         },
1557 
1558         /**
1559          * 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
1560          * the object completely you should use {@link JXG.Board#removeObject}.
1561          */
1562         remove: function () {
1563             if (this.hasLabel) {
1564                 this.board.renderer.remove(this.board.renderer.getElementById(this.label.id));
1565             }
1566             this.board.renderer.remove(this.board.renderer.getElementById(this.id));
1567         },
1568 
1569         // documented in GeometryElement
1570         getTextAnchor: function () {
1571             return this.coords;
1572         },
1573 
1574         // documented in GeometryElement
1575         getLabelAnchor: function () {
1576             return this.coords;
1577         },
1578 
1579         /**
1580          * Set the face of a point element.
1581          * @param {String} f String which determines the face of the point. See {@link JXG.GeometryElement#face} for a list of available faces.
1582          * @see JXG.GeometryElement#face
1583          * @deprecated Use setAttribute()
1584          */
1585         face: function (f) {
1586             this.setAttribute({face: f});
1587         },
1588 
1589         /**
1590          * Set the size of a point element
1591          * @param {Number} s Integer which determines the size of the point.
1592          * @see JXG.GeometryElement#size
1593          * @deprecated Use setAttribute()
1594          */
1595         size: function (s) {
1596             this.setAttribute({size: s});
1597         },
1598 
1599         // already documented in GeometryElement
1600         cloneToBackground: function () {
1601             var copy = {};
1602 
1603             copy.id = this.id + 'T' + this.numTraces;
1604             this.numTraces += 1;
1605 
1606             copy.coords = this.coords;
1607             copy.visProp = Type.deepCopy(this.visProp, this.visProp.traceattributes, true);
1608             copy.visProp.layer = this.board.options.layer.trace;
1609             copy.elementClass = Const.OBJECT_CLASS_POINT;
1610             copy.board = this.board;
1611             Type.clearVisPropOld(copy);
1612 
1613             this.board.renderer.drawPoint(copy);
1614             this.traces[copy.id] = copy.rendNode;
1615 
1616             return this;
1617         },
1618 
1619         getParents: function () {
1620             var p = [this.Z(), this.X(), this.Y()];
1621 
1622             if (this.parents) {
1623                 p = this.parents;
1624             }
1625 
1626             if (this.type === Const.OBJECT_TYPE_GLIDER) {
1627                 p = [this.X(), this.Y(), this.slideObject.id];
1628 
1629             }
1630 
1631             return p;
1632         }
1633     });
1634 
1635 
1636     /**
1637      * @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
1638      * 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
1639      * 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
1640      * position directly.
1641      * @pseudo
1642      * @description
1643      * @name Point
1644      * @augments JXG.Point
1645      * @constructor
1646      * @type JXG.Point
1647      * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
1648      * @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
1649      * constraint, or a function which takes no parameter and returns a number. Every parent element determines one coordinate. If a coordinate is
1650      * 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
1651      * 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
1652      * 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
1653      * parent elements are given they will be interpreted as homogeneous coordinates.
1654      * @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
1655      * point transformed by the given Transformation. {@see JXG.Transformation}.
1656      * @example
1657      * // Create a free point using affine euclidean coordinates
1658      * var p1 = board.create('point', [3.5, 2.0]);
1659      * </pre><div id="672f1764-7dfa-4abc-a2c6-81fbbf83e44b" style="width: 200px; height: 200px;"></div>
1660      * <script type="text/javascript">
1661      *   var board = JXG.JSXGraph.initBoard('672f1764-7dfa-4abc-a2c6-81fbbf83e44b', {boundingbox: [-1, 5, 5, -1], axis: true, showcopyright: false, shownavigation: false});
1662      *   var p1 = board.create('point', [3.5, 2.0]);
1663      * </script><pre>
1664      * @example
1665      * // Create a constrained point using anonymous function
1666      * var p2 = board.create('point', [3.5, function () { return p1.X(); }]);
1667      * </pre><div id="4fd4410c-3383-4e80-b1bb-961f5eeef224" style="width: 200px; height: 200px;"></div>
1668      * <script type="text/javascript">
1669      *   var fpex1_board = JXG.JSXGraph.initBoard('4fd4410c-3383-4e80-b1bb-961f5eeef224', {boundingbox: [-1, 5, 5, -1], axis: true, showcopyright: false, shownavigation: false});
1670      *   var fpex1_p1 = fpex1_board.create('point', [3.5, 2.0]);
1671      *   var fpex1_p2 = fpex1_board.create('point', [3.5, function () { return fpex1_p1.X(); }]);
1672      * </script><pre>
1673      * @example
1674      * // Create a point using transformations
1675      * var trans = board.create('transform', [2, 0.5], {type:'scale'});
1676      * var p3 = board.create('point', [p2, trans]);
1677      * </pre><div id="630afdf3-0a64-46e0-8a44-f51bd197bb8d" style="width: 400px; height: 400px;"></div>
1678      * <script type="text/javascript">
1679      *   var fpex2_board = JXG.JSXGraph.initBoard('630afdf3-0a64-46e0-8a44-f51bd197bb8d', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false});
1680      *   var fpex2_trans = fpex2_board.create('transform', [2, 0.5], {type:'scale'});
1681      *   var fpex2_p2 = fpex2_board.create('point', [3.5, 2.0]);
1682      *   var fpex2_p3 = fpex2_board.create('point', [fpex2_p2, fpex2_trans]);
1683      * </script><pre>
1684      */
1685     JXG.createPoint = function (board, parents, attributes) {
1686         var el, isConstrained = false, i, attr;
1687 
1688         attr = Type.copyAttributes(attributes, board.options, 'point');
1689 
1690         if (parents.length === 1 && Type.isArray(parents[0]) && parents[0].length > 1 && parents[0].length < 4) {
1691             parents = parents[0];
1692         }
1693 
1694         for (i = 0; i < parents.length; i++) {
1695             if (typeof parents[i] === 'function' || typeof parents[i] === 'string') {
1696                 isConstrained = true;
1697             }
1698         }
1699 
1700         if (!isConstrained) {
1701             if ((Type.isNumber(parents[0])) && (Type.isNumber(parents[1]))) {
1702                 el = new JXG.Point(board, parents, attr);
1703 
1704                 if (Type.exists(attr.slideobject)) {
1705                     el.makeGlider(attr.slideobject);
1706                 } else {
1707                     // Free point
1708                     el.baseElement = el;
1709                 }
1710                 el.isDraggable = true;
1711             } else if ((typeof parents[0] === 'object') && (typeof parents[1] === 'object')) {
1712                 // Transformation
1713                 el = new JXG.Point(board, [0, 0], attr);
1714                 el.addTransform(parents[0], parents[1]);
1715                 el.isDraggable = false;
1716 
1717                 el.parents = [parents[0].id];
1718             } else {
1719                 throw new Error("JSXGraph: Can't create point with parent types '" +
1720                     (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." +
1721                     "\nPossible parent types: [x,y], [z,x,y], [point,transformation]");
1722             }
1723         } else {
1724             el = new JXG.Point(board, [NaN, NaN], attr);
1725             el.addConstraint(parents);
1726         }
1727 
1728         if (!board.isSuspendedUpdate) {
1729             el.handleSnapToGrid();
1730             el.handleSnapToPoints();
1731             el.handleAttractors();
1732         }
1733 
1734         return el;
1735     };
1736 
1737     /**
1738      * @class This element is used to provide a constructor for a glider point.
1739      * @pseudo
1740      * @description A glider is a point which lives on another geometric element like a line, circle, curve, turtle.
1741      * @name Glider
1742      * @augments JXG.Point
1743      * @constructor
1744      * @type JXG.Point
1745      * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
1746      * @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.
1747      * 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
1748      * coordinates, otherwise they will be interpreted as homogeneous coordinates. In any case the point will be projected on the glide object.
1749      * @example
1750      * // Create a glider with user defined coordinates. If the coordinates are not on
1751      * // the circle (like in this case) the point will be projected onto the circle.
1752      * var p1 = board.create('point', [2.0, 2.0]);
1753      * var c1 = board.create('circle', [p1, 2.0]);
1754      * var p2 = board.create('glider', [2.0, 1.5, c1]);
1755      * </pre><div id="4f65f32f-e50a-4b50-9b7c-f6ec41652930" style="width: 300px; height: 300px;"></div>
1756      * <script type="text/javascript">
1757      *   var gpex1_board = JXG.JSXGraph.initBoard('4f65f32f-e50a-4b50-9b7c-f6ec41652930', {boundingbox: [-1, 5, 5, -1], axis: true, showcopyright: false, shownavigation: false});
1758      *   var gpex1_p1 = gpex1_board.create('point', [2.0, 2.0]);
1759      *   var gpex1_c1 = gpex1_board.create('circle', [gpex1_p1, 2.0]);
1760      *   var gpex1_p2 = gpex1_board.create('glider', [2.0, 1.5, gpex1_c1]);
1761      * </script><pre>
1762      * @example
1763      * // Create a glider with default coordinates (1,0,0). Same premises as above.
1764      * var p1 = board.create('point', [2.0, 2.0]);
1765      * var c1 = board.create('circle', [p1, 2.0]);
1766      * var p2 = board.create('glider', [c1]);
1767      * </pre><div id="4de7f181-631a-44b1-a12f-bc4d995609e8" style="width: 200px; height: 200px;"></div>
1768      * <script type="text/javascript">
1769      *   var gpex2_board = JXG.JSXGraph.initBoard('4de7f181-631a-44b1-a12f-bc4d995609e8', {boundingbox: [-1, 5, 5, -1], axis: true, showcopyright: false, shownavigation: false});
1770      *   var gpex2_p1 = gpex2_board.create('point', [2.0, 2.0]);
1771      *   var gpex2_c1 = gpex2_board.create('circle', [gpex2_p1, 2.0]);
1772      *   var gpex2_p2 = gpex2_board.create('glider', [gpex2_c1]);
1773      * </script><pre>
1774      */
1775     JXG.createGlider = function (board, parents, attributes) {
1776         var el,
1777             attr = Type.copyAttributes(attributes, board.options, 'glider');
1778 
1779         if (parents.length === 1) {
1780             el = board.create('point', [0, 0], attr);
1781         } else {
1782             el = board.create('point', parents.slice(0, 2), attr);
1783         }
1784 
1785         // eltype is set in here
1786         el.makeGlider(parents[parents.length - 1]);
1787 
1788         return el;
1789     };
1790 
1791     /**
1792      * @class This element is used to provide a constructor for an intersection point.
1793      * @pseudo
1794      * @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.
1795      * an intersection point of the two elements.
1796      * @name Intersection
1797      * @augments JXG.Point
1798      * @constructor
1799      * @type JXG.Point
1800      * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
1801      * @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
1802      * intersection point if two points are available: <ul>
1803      *   <li>i==0: use the positive square root,</li>
1804      *   <li>i==1: use the negative square root.</li></ul>
1805      * @example
1806      * // Create an intersection point of circle and line
1807      * var p1 = board.create('point', [2.0, 2.0]);
1808      * var c1 = board.create('circle', [p1, 2.0]);
1809      *
1810      * var p2 = board.create('point', [2.0, 2.0]);
1811      * var p3 = board.create('point', [2.0, 2.0]);
1812      * var l1 = board.create('line', [p2, p3]);
1813      *
1814      * var i = board.create('intersection', [c1, l1, 0]);
1815      * </pre><div id="e5b0e190-5200-4bc3-b995-b6cc53dc5dc0" style="width: 300px; height: 300px;"></div>
1816      * <script type="text/javascript">
1817      *   var ipex1_board = JXG.JSXGraph.initBoard('e5b0e190-5200-4bc3-b995-b6cc53dc5dc0', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false});
1818      *   var ipex1_p1 = ipex1_board.create('point', [4.0, 4.0]);
1819      *   var ipex1_c1 = ipex1_board.create('circle', [ipex1_p1, 2.0]);
1820      *   var ipex1_p2 = ipex1_board.create('point', [1.0, 1.0]);
1821      *   var ipex1_p3 = ipex1_board.create('point', [5.0, 3.0]);
1822      *   var ipex1_l1 = ipex1_board.create('line', [ipex1_p2, ipex1_p3]);
1823      *   var ipex1_i = ipex1_board.create('intersection', [ipex1_c1, ipex1_l1, 0]);
1824      * </script><pre>
1825      */
1826     JXG.createIntersectionPoint = function (board, parents, attributes) {
1827         var el, el1, el2, func, i, j,
1828             attr = Type.copyAttributes(attributes, board.options, 'intersection');
1829 
1830 
1831         // make sure we definitely have the indices
1832         parents.push(0, 0);
1833 
1834         el = board.create('point', [0, 0, 0], attr);
1835 
1836         el1 = board.select(parents[0]);
1837         el2 = board.select(parents[1]);
1838 
1839         i = parents[2] || 0;
1840         j = parents[3] || 0;
1841 
1842         if (el1.elementClass === Const.OBJECT_CLASS_CURVE &&
1843                 el2.elementClass === Const.OBJECT_CLASS_CURVE) {
1844             // curve - curve
1845             /** @ignore */
1846             func = function () {
1847                 return Geometry.meetCurveCurve(el1, el2, i, j, el1.board);
1848             };
1849 
1850         //} else if ((el1.type === Const.OBJECT_TYPE_ARC && el2.elementClass === Const.OBJECT_CLASS_LINE) ||
1851 //                (el2.type === Const.OBJECT_TYPE_ARC && el1.elementClass === Const.OBJECT_CLASS_LINE)) {
1852             // arc - line   (arcs are of class curve, but are intersected like circles)
1853             // TEMPORARY FIX!!!
1854             /** @ignore */
1855 //            func = function () {
1856                 //return Geometry.meet(el1.stdform, el2.stdform, i, el1.board);
1857             //};
1858 
1859         } else if ((el1.elementClass === Const.OBJECT_CLASS_CURVE && el2.elementClass === Const.OBJECT_CLASS_LINE) ||
1860                 (el2.elementClass === Const.OBJECT_CLASS_CURVE && el1.elementClass === Const.OBJECT_CLASS_LINE)) {
1861             // curve - line (this includes intersections between conic sections and lines
1862             /** @ignore */
1863             func = function () {
1864                 return Geometry.meetCurveLine(el1, el2, i, el1.board, el.visProp.alwaysintersect);
1865             };
1866 
1867         } else if (el1.elementClass === Const.OBJECT_CLASS_LINE && el2.elementClass === Const.OBJECT_CLASS_LINE) {
1868             // line - line, lines may also be segments.
1869             /** @ignore */
1870             func = function () {
1871                 var res, c,
1872                     first1 = el1.visProp.straightfirst,
1873                     first2 = el2.visProp.straightfirst,
1874                     last1 = el1.visProp.straightlast,
1875                     last2 = el2.visProp.straightlast;
1876 
1877                 /**
1878                  * If one of the lines is a segment or ray and
1879                  * the the intersection point shpould disappear if outside
1880                  * of the segment or ray we call
1881                  * meetSegmentSegment
1882                  */
1883                 if (!el.visProp.alwaysintersect && (!first1 || !last1 || !first2 || !last2)) {
1884                     res = Geometry.meetSegmentSegment(
1885                         el1.point1.coords.usrCoords,
1886                         el1.point2.coords.usrCoords,
1887                         el2.point1.coords.usrCoords,
1888                         el2.point2.coords.usrCoords,
1889                         el1.board
1890                     );
1891 
1892                     if ((!first1 && res[1] < 0) || (!last1 && res[1] > 1) ||
1893                             (!first2 && res[2] < 0) || (!last2 && res[2] > 1)) {
1894                         // Non-existent
1895                         c = [0, NaN, NaN];
1896                     } else {
1897                         c = res[0];
1898                     }
1899 
1900                     return (new Coords(Const.COORDS_BY_USER, c, el1.board));
1901                 }
1902 
1903                 return Geometry.meet(el1.stdform, el2.stdform, i, el1.board);
1904             };
1905         } else {
1906             // All other combinations of circles and lines
1907             /** @ignore */
1908             func = function () {
1909                 return Geometry.meet(el1.stdform, el2.stdform, i, el1.board);
1910             };
1911         }
1912 
1913         el.addConstraint([func]);
1914 
1915         try {
1916             el1.addChild(el);
1917             el2.addChild(el);
1918         } catch (e) {
1919             throw new Error("JSXGraph: Can't create 'intersection' with parent types '" +
1920                 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'.");
1921         }
1922 
1923         el.type = Const.OBJECT_TYPE_INTERSECTION;
1924         el.elType = 'intersection';
1925         el.parents = [el1.id, el2.id, i, j];
1926 
1927         el.generatePolynomial = function () {
1928             var poly1 = el1.generatePolynomial(el),
1929                 poly2 = el2.generatePolynomial(el);
1930 
1931             if ((poly1.length === 0) || (poly2.length === 0)) {
1932                 return [];
1933             }
1934 
1935             return [poly1[0], poly2[0]];
1936         };
1937 
1938         return el;
1939     };
1940 
1941     /**
1942      * @class This element is used to provide a constructor for the "other" intersection point.
1943      * @pseudo
1944      * @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.
1945      * an intersection point of the two elements. Additionally, one intersection point is provided. The function returns the other intersection point.
1946      * @name OtherIntersection
1947      * @augments JXG.Point
1948      * @constructor
1949      * @type JXG.Point
1950      * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
1951      * @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
1952      * intersection point different from p:
1953      * @example
1954      * // Create an intersection point of circle and line
1955      * var p1 = board.create('point', [2.0, 2.0]);
1956      * var c1 = board.create('circle', [p1, 2.0]);
1957      *
1958      * var p2 = board.create('point', [2.0, 2.0]);
1959      * var p3 = board.create('point', [2.0, 2.0]);
1960      * var l1 = board.create('line', [p2, p3]);
1961      *
1962      * var i = board.create('intersection', [c1, l1, 0]);
1963      * var j = board.create('otherintersection', [c1, l1, i]);
1964      * </pre><div id="45e25f12-a1de-4257-a466-27a2ae73614c" style="width: 300px; height: 300px;"></div>
1965      * <script type="text/javascript">
1966      *   var ipex2_board = JXG.JSXGraph.initBoard('45e25f12-a1de-4257-a466-27a2ae73614c', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false});
1967      *   var ipex2_p1 = ipex2_board.create('point', [4.0, 4.0]);
1968      *   var ipex2_c1 = ipex2_board.create('circle', [ipex2_p1, 2.0]);
1969      *   var ipex2_p2 = ipex2_board.create('point', [1.0, 1.0]);
1970      *   var ipex2_p3 = ipex2_board.create('point', [5.0, 3.0]);
1971      *   var ipex2_l1 = ipex2_board.create('line', [ipex2_p2, ipex2_p3]);
1972      *   var ipex2_i = ipex2_board.create('intersection', [ipex2_c1, ipex2_l1, 0], {name:'D'});
1973      *   var ipex2_j = ipex2_board.create('otherintersection', [ipex2_c1, ipex2_l1, ipex2_i], {name:'E'});
1974      * </script><pre>
1975      */
1976     JXG.createOtherIntersectionPoint = function (board, parents, attributes) {
1977         var el, el1, el2, other;
1978 
1979         if (parents.length !== 3 ||
1980                 !Type.isPoint(parents[2]) ||
1981                 (parents[0].elementClass !== Const.OBJECT_CLASS_LINE && parents[0].elementClass !== Const.OBJECT_CLASS_CIRCLE) ||
1982                 (parents[1].elementClass !== Const.OBJECT_CLASS_LINE && parents[1].elementClass !== Const.OBJECT_CLASS_CIRCLE)) {
1983             // Failure
1984             throw new Error("JSXGraph: Can't create 'other intersection point' with parent types '" +
1985                 (typeof parents[0]) + "',  '" + (typeof parents[1]) + "'and  '" + (typeof parents[2]) + "'." +
1986                 "\nPossible parent types: [circle|line,circle|line,point]");
1987         }
1988 
1989         el1 = board.select(parents[0]);
1990         el2 = board.select(parents[1]);
1991         other = board.select(parents[2]);
1992 
1993         el = board.create('point', [function () {
1994             var c = Geometry.meet(el1.stdform, el2.stdform, 0, el1.board);
1995 
1996             if (Math.abs(other.X() - c.usrCoords[1]) > Mat.eps ||
1997                     Math.abs(other.Y() - c.usrCoords[2]) > Mat.eps ||
1998                     Math.abs(other.Z() - c.usrCoords[0]) > Mat.eps) {
1999                 return c;
2000             }
2001 
2002             return Geometry.meet(el1.stdform, el2.stdform, 1, el1.board);
2003         }], attributes);
2004 
2005         el.type = Const.OBJECT_TYPE_INTERSECTION;
2006         el.elType = 'otherintersection';
2007         el.parents = [el1.id, el2.id, other];
2008 
2009         el1.addChild(el);
2010         el2.addChild(el);
2011 
2012         el.generatePolynomial = function () {
2013             var poly1 = el1.generatePolynomial(el),
2014                 poly2 = el2.generatePolynomial(el);
2015 
2016             if ((poly1.length === 0) || (poly2.length === 0)) {
2017                 return [];
2018             }
2019 
2020             return [poly1[0], poly2[0]];
2021         };
2022 
2023         return el;
2024     };
2025 
2026     /**
2027      * @class This element is used to provide a constructor for the pole point of a line with respect to a conic or a circle.
2028      * @pseudo
2029      * @description The pole point is the unique reciprocal relationship of a line with respect to a conic.
2030      * The lines tangent to the intersections of a conic and a line intersect at the pole point of that line with respect to that conic.
2031      * A line tangent to a conic has the pole point of that line with respect to that conic as the tangent point.
2032      * See {@link http://en.wikipedia.org/wiki/Pole_and_polar} for more information on pole and polar.
2033      * @name PolePoint
2034      * @augments JXG.Point
2035      * @constructor
2036      * @type JXG.Point
2037      * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
2038      * @param {JXG.Conic,JXG.Circle_JXG.Point} el1,el2 or
2039      * @param {JXG.Point_JXG.Conic,JXG.Circle} el1,el2 The result will be the pole point of the line with respect to the conic or the circle.
2040      * @example
2041      * // Create the pole point of a line with respect to a conic
2042      * var p1 = board.create('point', [-1, 2]);
2043      * var p2 = board.create('point', [ 1, 4]);
2044      * var p3 = board.create('point', [-1,-2]);
2045      * var p4 = board.create('point', [ 0, 0]);
2046      * var p5 = board.create('point', [ 4,-2]);
2047      * var c1 = board.create('conic',[p1,p2,p3,p4,p5]);
2048      * var p6 = board.create('point', [-1, 4]);
2049      * var p7 = board.create('point', [2, -2]);
2050      * var l1 = board.create('line', [p6, p7]);
2051      * var p8 = board.create('polepoint', [c1, l1]);
2052      * </pre><div id='7b7233a0-f363-47dd-9df5-8018d0d17a98' class='jxgbox' style='width:400px; height:400px;'></div>
2053      * <script type='text/javascript'>
2054      * var ppex1_board = JXG.JSXGraph.initBoard('7b7233a0-f363-47dd-9df5-8018d0d17a98', {boundingbox: [-3, 5, 5, -3], axis: true, showcopyright: false, shownavigation: false});
2055      * var ppex1_p1 = ppex1_board.create('point', [-1, 2]);
2056      * var ppex1_p2 = ppex1_board.create('point', [ 1, 4]);
2057      * var ppex1_p3 = ppex1_board.create('point', [-1,-2]);
2058      * var ppex1_p4 = ppex1_board.create('point', [ 0, 0]);
2059      * var ppex1_p5 = ppex1_board.create('point', [ 4,-2]);
2060      * var ppex1_c1 = ppex1_board.create('conic',[ppex1_p1,ppex1_p2,ppex1_p3,ppex1_p4,ppex1_p5]);
2061      * var ppex1_p6 = ppex1_board.create('point', [-1, 4]);
2062      * var ppex1_p7 = ppex1_board.create('point', [2, -2]);
2063      * var ppex1_l1 = ppex1_board.create('line', [ppex1_p6, ppex1_p7]);
2064      * var ppex1_p8 = ppex1_board.create('polepoint', [ppex1_c1, ppex1_l1]);
2065      * </script><pre>
2066      * @example
2067      * // Create the pole point of a line with respect to a circle
2068      * var p1 = board.create('point', [1, 1]);
2069      * var p2 = board.create('point', [2, 3]);
2070      * var c1 = board.create('circle',[p1,p2]);
2071      * var p3 = board.create('point', [-1, 4]);
2072      * var p4 = board.create('point', [4, -1]);
2073      * var l1 = board.create('line', [p3, p4]);
2074      * var p5 = board.create('polepoint', [c1, l1]);
2075      * </pre><div id='7b7233a0-f363-47dd-9df5-9018d0d17a98' class='jxgbox' style='width:400px; height:400px;'></div>
2076      * <script type='text/javascript'>
2077      * var ppex2_board = JXG.JSXGraph.initBoard('7b7233a0-f363-47dd-9df5-9018d0d17a98', {boundingbox: [-3, 7, 7, -3], axis: true, showcopyright: false, shownavigation: false});
2078      * var ppex2_p1 = ppex2_board.create('point', [1, 1]);
2079      * var ppex2_p2 = ppex2_board.create('point', [2, 3]);
2080      * var ppex2_c1 = ppex2_board.create('circle',[ppex2_p1,ppex2_p2]);
2081      * var ppex2_p3 = ppex2_board.create('point', [-1, 4]);
2082      * var ppex2_p4 = ppex2_board.create('point', [4, -1]);
2083      * var ppex2_l1 = ppex2_board.create('line', [ppex2_p3, ppex2_p4]);
2084      * var ppex2_p5 = ppex2_board.create('polepoint', [ppex2_c1, ppex2_l1]);
2085      * </script><pre>
2086      */
2087     JXG.createPolePoint = function (board, parents, attributes) {
2088         var el, el1, el2;
2089 
2090         if (parents.length !== 2 || !((
2091                 parents[0].type === Const.OBJECT_TYPE_CONIC ||
2092                 parents[0].elementClass === Const.OBJECT_CLASS_CIRCLE) &&
2093                 parents[1].elementClass === Const.OBJECT_CLASS_LINE ||
2094                 parents[0].elementClass === Const.OBJECT_CLASS_LINE && (
2095                 parents[1].type === Const.OBJECT_TYPE_CONIC ||
2096                 parents[1].elementClass === Const.OBJECT_CLASS_CIRCLE))) {
2097             // Failure
2098             throw new Error("JSXGraph: Can't create 'pole point' with parent types '" +
2099                 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." +
2100                 "\nPossible parent type: [conic|circle,line], [line,conic|circle]");
2101         }
2102 
2103         if (parents[1].elementClass === Const.OBJECT_CLASS_LINE) {
2104             el1 = board.select(parents[0]);
2105             el2 = board.select(parents[1]);
2106         } else {
2107             el1 = board.select(parents[1]);
2108             el2 = board.select(parents[0]);
2109         }
2110 
2111         el = board.create('point', 
2112             [function () {
2113                 var q = el1.quadraticform,
2114                     s = el2.stdform.slice(0,3);
2115                     
2116                 return [JXG.Math.Numerics.det([s, q[1], q[2]]),
2117                         JXG.Math.Numerics.det([q[0], s, q[2]]),
2118                         JXG.Math.Numerics.det([q[0], q[1], s])];
2119             }], attributes);
2120 
2121         el.elType = 'polepoint';
2122         el.parents = [el1.id, el2.id];
2123 
2124         el1.addChild(el);
2125         el2.addChild(el);
2126 
2127         return el;
2128     };
2129 
2130     JXG.registerElement('point', JXG.createPoint);
2131     JXG.registerElement('glider', JXG.createGlider);
2132     JXG.registerElement('intersection', JXG.createIntersectionPoint);
2133     JXG.registerElement('otherintersection', JXG.createOtherIntersectionPoint);
2134     JXG.registerElement('polepoint', JXG.createPolePoint);
2135 
2136     return {
2137         Point: JXG.Point,
2138         createPoint: JXG.createPoint,
2139         createGlider: JXG.createGlider,
2140         createIntersection: JXG.createIntersectionPoint,
2141         createOtherIntersection: JXG.createOtherIntersectionPoint,
2142         createPolePoint: JXG.createPolePoint
2143     };
2144 });
2145