1 /*
  2     Copyright 2008-2016
  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*/
 34 /*jslint nomen: true, plusplus: true*/
 35 
 36 /* depends:
 37  jxg
 38  math/math
 39  math/geometry
 40  math/numerics
 41  math/statistics
 42  base/constants
 43  base/coords
 44  base/element
 45  utils/type
 46   elements:
 47    transform
 48    point
 49    ticks
 50  */
 51 
 52 /**
 53  * @fileoverview The geometry object Line is defined in this file. Line stores all
 54  * style and functional properties that are required to draw and move a line on
 55  * a board.
 56  */
 57 
 58 define([
 59     'jxg', 'math/math', 'math/geometry', 'math/numerics', 'math/statistics', 'base/constants', 'base/coords',
 60     'base/element', 'utils/type', 'base/point'
 61 ], function (JXG, Mat, Geometry, Numerics, Statistics, Const, Coords, GeometryElement, Type, Point) {
 62 
 63     "use strict";
 64 
 65     /**
 66      * The Line class is a basic class for all kind of line objects, e.g. line, arrow, and axis. It is usually defined by two points and can
 67      * be intersected with some other geometry elements.
 68      * @class Creates a new basic line object. Do not use this constructor to create a line.
 69      * Use {@link JXG.Board#create} with
 70      * type {@link Line}, {@link Arrow}, or {@link Axis} instead.
 71      * @constructor
 72      * @augments JXG.GeometryElement
 73      * @param {String,JXG.Board} board The board the new line is drawn on.
 74      * @param {Point} p1 Startpoint of the line.
 75      * @param {Point} p2 Endpoint of the line.
 76      * @param {String} id Unique identifier for this object. If null or an empty string is given,
 77      * an unique id will be generated by Board
 78      * @param {String} name Not necessarily unique name. If null or an
 79      * empty string is given, an unique name will be generated.
 80      * @param {Boolean} withLabel construct label, yes/no
 81      * @param {Number} layer display layer [0-9]
 82      * @see JXG.Board#generateName
 83      */
 84     JXG.Line = function (board, p1, p2, attributes) {
 85         this.constructor(board, attributes, Const.OBJECT_TYPE_LINE, Const.OBJECT_CLASS_LINE);
 86 
 87         /**
 88          * Startpoint of the line. You really should not set this field directly as it may break JSXGraph's
 89          * udpate system so your construction won't be updated properly.
 90          * @type JXG.Point
 91          */
 92         this.point1 = this.board.select(p1);
 93 
 94         /**
 95          * Endpoint of the line. Just like {@link JXG.Line.point1} you shouldn't write this field directly.
 96          * @type JXG.Point
 97          */
 98         this.point2 = this.board.select(p2);
 99 
100         /**
101          * Array of ticks storing all the ticks on this line. Do not set this field directly and use
102          * {@link JXG.Line#addTicks} and {@link JXG.Line#removeTicks} to add and remove ticks to and from the line.
103          * @type Array
104          * @see JXG.Ticks
105          */
106         this.ticks = [];
107 
108         /**
109          * Reference of the ticks created automatically when constructing an axis.
110          * @type JXG.Ticks
111          * @see JXG.Ticks
112          */
113         this.defaultTicks = null;
114 
115         /**
116          * If the line is the border of a polygon, the polygon object is stored, otherwise null.
117          * @type JXG.Polygon
118          * @default null
119          * @private
120          */
121         this.parentPolygon = null;
122 
123         /* Register line at board */
124         this.id = this.board.setId(this, 'L');
125         this.board.renderer.drawLine(this);
126         this.board.finalizeAdding(this);
127 
128         this.elType = 'line';
129 
130         /* Add arrow as child to defining points */
131         this.point1.addChild(this);
132         this.point2.addChild(this);
133 
134 
135         this.updateStdform(); // This is needed in the following situation:
136         // * the line is defined by three coordinates
137         // * and it will have a glider
138         // * and board.suspendUpdate() has been called.
139 
140         // create Label
141         this.createLabel();
142 
143         this.methodMap = JXG.deepCopy(this.methodMap, {
144             point1: 'point1',
145             point2: 'point2',
146             getSlope: 'getSlope',
147             getRise: 'getRise',
148             getYIntersect: 'getRise',
149             getAngle: 'getAngle',
150             L: 'L',
151             length: 'L',
152             addTicks: 'addTicks',
153             removeTicks: 'removeTicks',
154             removeAllTicks: 'removeAllTicks'
155         });
156     };
157 
158     JXG.Line.prototype = new GeometryElement();
159 
160 
161     JXG.extend(JXG.Line.prototype, /** @lends JXG.Line.prototype */ {
162         /**
163          * Checks whether (x,y) is near the line.
164          * @param {Number} x Coordinate in x direction, screen coordinates.
165          * @param {Number} y Coordinate in y direction, screen coordinates.
166          * @returns {Boolean} True if (x,y) is near the line, False otherwise.
167          */
168         hasPoint: function (x, y) {
169             // Compute the stdform of the line in screen coordinates.
170             var c = [], s,
171                 v = [1, x, y],
172                 vnew,
173                 p1c, p2c, d, pos, i;
174 
175             c[0] = this.stdform[0] -
176                 this.stdform[1] * this.board.origin.scrCoords[1] / this.board.unitX +
177                 this.stdform[2] * this.board.origin.scrCoords[2] / this.board.unitY;
178             c[1] = this.stdform[1] / this.board.unitX;
179             c[2] = this.stdform[2] / (-this.board.unitY);
180 
181             s = Geometry.distPointLine(v, c);
182             if (isNaN(s) || s > this.board.options.precision.hasPoint) {
183                 return false;
184             }
185 
186             if (this.visProp.straightfirst && this.visProp.straightlast) {
187                 return true;
188             }
189 
190             // If the line is a ray or segment we have to check if the projected point is between P1 and P2.
191             p1c = this.point1.coords;
192             p2c = this.point2.coords;
193 
194             // Project the point orthogonally onto the line
195             vnew = [0, c[1], c[2]];
196             // Orthogonal line to c through v
197             vnew = Mat.crossProduct(vnew, v);
198             // Intersect orthogonal line with line
199             vnew = Mat.crossProduct(vnew, c);
200 
201             // Normalize the projected point
202             vnew[1] /= vnew[0];
203             vnew[2] /= vnew[0];
204             vnew[0] = 1;
205 
206             vnew = (new Coords(Const.COORDS_BY_SCREEN, vnew.slice(1), this.board)).usrCoords;
207             d = p1c.distance(Const.COORDS_BY_USER, p2c);
208             p1c = p1c.usrCoords.slice(0);
209             p2c = p2c.usrCoords.slice(0);
210 
211             // The defining points are identical
212             if (d < Mat.eps) {
213                 pos = 0;
214             } else {
215                 /*
216                  * Handle the cases, where one of the defining points is an ideal point.
217                  * d is set to something close to infinity, namely 1/eps.
218                  * The ideal point is (temporarily) replaced by a finite point which has
219                  * distance d from the other point.
220                  * This is accomplishrd by extracting the x- and y-coordinates (x,y)=:v of the ideal point.
221                  * v determines the direction of the line. v is normalized, i.e. set to length 1 by deividing through its length.
222                  * Finally, the new point is the sum of the other point and v*d.
223                  *
224                  */
225 
226                 // At least one point is an ideal point
227                 if (d === Number.POSITIVE_INFINITY) {
228                     d = 1 / Mat.eps;
229 
230                     // The second point is an ideal point
231                     if (Math.abs(p2c[0]) < Mat.eps) {
232                         d /= Geometry.distance([0, 0, 0], p2c);
233                         p2c = [1, p1c[1] + p2c[1] * d, p1c[2] + p2c[2] * d];
234                     // The first point is an ideal point
235                     } else {
236                         d /= Geometry.distance([0, 0, 0], p1c);
237                         p1c = [1, p2c[1] + p1c[1] * d, p2c[2] + p1c[2] * d];
238                     }
239                 }
240                 i = 1;
241                 d = p2c[i] - p1c[i];
242 
243                 if (Math.abs(d) < Mat.eps) {
244                     i = 2;
245                     d = p2c[i] - p1c[i];
246                 }
247                 pos = (vnew[i] - p1c[i]) / d;
248             }
249 
250             if (!this.visProp.straightfirst && pos < 0) {
251                 return false;
252             }
253 
254             return !(!this.visProp.straightlast && pos > 1);
255 
256         },
257 
258         // documented in base/element
259         update: function () {
260             var funps;
261 
262             if (!this.needsUpdate) {
263                 return this;
264             }
265 
266             if (this.constrained) {
267                 if (Type.isFunction(this.funps)) {
268                     funps = this.funps();
269                     if (funps && funps.length && funps.length === 2) {
270                         this.point1 = funps[0];
271                         this.point2 = funps[1];
272                     }
273                 } else {
274                     if (Type.isFunction(this.funp1)) {
275                         funps = this.funp1();
276                         if (Type.isPoint(funps)) {
277                             this.point1 = funps;
278                         } else if (funps && funps.length && funps.length === 2) {
279                             this.point1.setPositionDirectly(Const.COORDS_BY_USER, funps);
280                         }
281                     }
282 
283                     if (Type.isFunction(this.funp2)) {
284                         funps = this.funp2();
285                         if (Type.isPoint(funps)) {
286                             this.point2 = funps;
287                         } else if (funps && funps.length && funps.length === 2) {
288                             this.point2.setPositionDirectly(Const.COORDS_BY_USER, funps);
289                         }
290                     }
291                 }
292             }
293 
294             this.updateSegmentFixedLength();
295             this.updateStdform();
296 
297             if (this.visProp.trace) {
298                 this.cloneToBackground(true);
299             }
300 
301             return this;
302         },
303 
304         /**
305          * Update segments with fixed length and at least one movable point.
306          * @private
307          */
308         updateSegmentFixedLength: function () {
309             var d, dnew, d1, d2, drag1, drag2, x, y;
310 
311             if (!this.hasFixedLength) {
312                 return this;
313             }
314 
315             // Compute the actual length of the segment
316             d = this.point1.Dist(this.point2);
317             // Determine the length the segment ought to have
318             dnew = this.fixedLength();
319             // Distances between the two points and their respective
320             // position before the update
321             d1 = this.fixedLengthOldCoords[0].distance(Const.COORDS_BY_USER, this.point1.coords);
322             d2 = this.fixedLengthOldCoords[1].distance(Const.COORDS_BY_USER, this.point2.coords);
323 
324             // If the position of the points or the fixed length function has been changed we have to work.
325             if (d1 > Mat.eps || d2 > Mat.eps || d !== dnew) {
326                 drag1 = this.point1.isDraggable && (this.point1.type !== Const.OBJECT_TYPE_GLIDER) && !this.point1.visProp.fixed;
327                 drag2 = this.point2.isDraggable && (this.point2.type !== Const.OBJECT_TYPE_GLIDER) && !this.point2.visProp.fixed;
328 
329                 // First case: the two points are different
330                 // Then we try to adapt the point that was not dragged
331                 // If this point can not be moved (e.g. because it is a glider)
332                 // we try move the other point
333                 if (d > Mat.eps) {
334                     if ((d1 > d2 && drag2) ||
335                             (d1 <= d2 && drag2 && !drag1)) {
336                         this.point2.setPositionDirectly(Const.COORDS_BY_USER, [
337                             this.point1.X() + (this.point2.X() - this.point1.X()) * dnew / d,
338                             this.point1.Y() + (this.point2.Y() - this.point1.Y()) * dnew / d
339                         ]);
340                         this.point2.prepareUpdate().updateRenderer();
341                     } else if ((d1 <= d2 && drag1) ||
342                             (d1 > d2 && drag1 && !drag2)) {
343                         this.point1.setPositionDirectly(Const.COORDS_BY_USER, [
344                             this.point2.X() + (this.point1.X() - this.point2.X()) * dnew / d,
345                             this.point2.Y() + (this.point1.Y() - this.point2.Y()) * dnew / d
346                         ]);
347                         this.point1.prepareUpdate().updateRenderer();
348                     }
349                     // Second case: the two points are identical. In this situation
350                     // we choose a random direction.
351                 } else {
352                     x = Math.random() - 0.5;
353                     y = Math.random() - 0.5;
354                     d = Math.sqrt(x * x + y * y);
355 
356                     if (drag2) {
357                         this.point2.setPositionDirectly(Const.COORDS_BY_USER, [
358                             this.point1.X() + x * dnew / d,
359                             this.point1.Y() + y * dnew / d
360                         ]);
361                         this.point2.prepareUpdate().updateRenderer();
362                     } else if (drag1) {
363                         this.point1.setPositionDirectly(Const.COORDS_BY_USER, [
364                             this.point2.X() + x * dnew / d,
365                             this.point2.Y() + y * dnew / d
366                         ]);
367                         this.point1.prepareUpdate().updateRenderer();
368                     }
369                 }
370                 // Finally, we save the position of the two points.
371                 this.fixedLengthOldCoords[0].setCoordinates(Const.COORDS_BY_USER, this.point1.coords.usrCoords);
372                 this.fixedLengthOldCoords[1].setCoordinates(Const.COORDS_BY_USER, this.point2.coords.usrCoords);
373             }
374             return this;
375         },
376 
377         /**
378          * Updates the stdform derived from the parent point positions.
379          * @private
380          */
381         updateStdform: function () {
382             var v = Mat.crossProduct(this.point1.coords.usrCoords, this.point2.coords.usrCoords);
383 
384             this.stdform[0] = v[0];
385             this.stdform[1] = v[1];
386             this.stdform[2] = v[2];
387             this.stdform[3] = 0;
388 
389             this.normalize();
390         },
391 
392         /**
393          * Uses the boards renderer to update the line.
394          * @private
395          */
396         updateRenderer: function () {
397             var wasReal;
398 
399             if (this.needsUpdate && this.visProp.visible) {
400                 wasReal = this.isReal;
401                 this.isReal = (!isNaN(this.point1.coords.usrCoords[1] + this.point1.coords.usrCoords[2] +
402                         this.point2.coords.usrCoords[1] + this.point2.coords.usrCoords[2]) &&
403                         (Mat.innerProduct(this.stdform, this.stdform, 3) >= Mat.eps * Mat.eps));
404 
405                 if (this.isReal) {
406                     if (wasReal !== this.isReal) {
407                         this.board.renderer.show(this);
408                         if (this.hasLabel && this.label.visProp.visible) {
409                             this.board.renderer.show(this.label);
410                         }
411                     }
412                     this.board.renderer.updateLine(this);
413                 } else {
414                     if (wasReal !== this.isReal) {
415                         this.board.renderer.hide(this);
416                         if (this.hasLabel && this.label.visProp.visible) {
417                             this.board.renderer.hide(this.label);
418                         }
419                     }
420                 }
421 
422                 this.needsUpdate = false;
423             }
424 
425             /* Update the label if visible. */
426             if (this.hasLabel && this.label.visProp.visible && this.isReal) {
427                 this.label.update();
428                 this.board.renderer.updateText(this.label);
429             }
430 
431             return this;
432         },
433 
434         /**
435          * Used to generate a polynomial for a point p that lies on this line, i.e. p is collinear to
436          * {@link JXG.Line#point1} and {@link JXG.Line#point2}.
437          *
438          * @param {JXG.Point} p The point for that the polynomial is generated.
439          * @returns {Array} An array containing the generated polynomial.
440          * @private
441          */
442         generatePolynomial: function (p) {
443             var u1 = this.point1.symbolic.x,
444                 u2 = this.point1.symbolic.y,
445                 v1 = this.point2.symbolic.x,
446                 v2 = this.point2.symbolic.y,
447                 w1 = p.symbolic.x,
448                 w2 = p.symbolic.y;
449 
450             /*
451              * The polynomial in this case is determined by three points being collinear:
452              *
453              *      U (u1,u2)      W (w1,w2)                V (v1,v2)
454              *  ----x--------------x------------------------x----------------
455              *
456              *  The collinearity condition is
457              *
458              *      u2-w2       w2-v2
459              *     -------  =  -------           (1)
460              *      u1-w1       w1-v1
461              *
462              * Multiplying (1) with denominators and simplifying is
463              *
464              *    u2w1 - u2v1 + w2v1 - u1w2 + u1v2 - w1v2 = 0
465              */
466 
467             return [['(', u2, ')*(', w1, ')-(', u2, ')*(', v1, ')+(', w2, ')*(', v1, ')-(', u1, ')*(', w2, ')+(', u1, ')*(', v2, ')-(', w1, ')*(', v2, ')'].join('')];
468         },
469 
470         /**
471          * Calculates the y intersect of the line.
472          * @returns {Number} The y intersect.
473          */
474         getRise: function () {
475             if (Math.abs(this.stdform[2]) >= Mat.eps) {
476                 return -this.stdform[0] / this.stdform[2];
477             }
478 
479             return Infinity;
480         },
481 
482         /**
483          * Calculates the slope of the line.
484          * @returns {Number} The slope of the line or Infinity if the line is parallel to the y-axis.
485          */
486         getSlope: function () {
487             if (Math.abs(this.stdform[2]) >= Mat.eps) {
488                 return -this.stdform[1] / this.stdform[2];
489             }
490 
491             return Infinity;
492         },
493 
494         /**
495          * Determines the angle between the positive x axis and the line.
496          * @returns {Number}
497          */
498         getAngle: function () {
499             return Math.atan2(-this.stdform[1], this.stdform[2]);
500         },
501 
502         /**
503          * Determines whether the line is drawn beyond {@link JXG.Line#point1} and
504          * {@link JXG.Line#point2} and updates the line.
505          * @param {Boolean} straightFirst True if the Line shall be drawn beyond
506          * {@link JXG.Line#point1}, false otherwise.
507          * @param {Boolean} straightLast True if the Line shall be drawn beyond
508          * {@link JXG.Line#point2}, false otherwise.
509          * @see #straightFirst
510          * @see #straightLast
511          * @private
512          */
513         setStraight: function (straightFirst, straightLast) {
514             this.visProp.straightfirst = straightFirst;
515             this.visProp.straightlast = straightLast;
516 
517             this.board.renderer.updateLine(this);
518             return this;
519         },
520 
521         // documented in geometry element
522         getTextAnchor: function () {
523             return new Coords(Const.COORDS_BY_USER, [0.5 * (this.point2.X() + this.point1.X()), 0.5 * (this.point2.Y() + this.point1.Y())], this.board);
524         },
525 
526         /**
527          * Adjusts Label coords relative to Anchor. DESCRIPTION
528          * @private
529          */
530         setLabelRelativeCoords: function (relCoords) {
531             if (Type.exists(this.label)) {
532                 this.label.relativeCoords = new Coords(Const.COORDS_BY_SCREEN, [relCoords[0], -relCoords[1]], this.board);
533             }
534         },
535 
536         // documented in geometry element
537         getLabelAnchor: function () {
538             var x, y,
539                 fs = 0,
540                 c1 = new Coords(Const.COORDS_BY_USER, this.point1.coords.usrCoords, this.board),
541                 c2 = new Coords(Const.COORDS_BY_USER, this.point2.coords.usrCoords, this.board);
542 
543             if (this.visProp.straightfirst || this.visProp.straightlast) {
544                 Geometry.calcStraight(this, c1, c2, 0);
545             }
546 
547             c1 = c1.scrCoords;
548             c2 = c2.scrCoords;
549 
550             if (!Type.exists(this.label)) {
551                 return new Coords(Const.COORDS_BY_SCREEN, [NaN, NaN], this.board);
552             }
553 
554             switch (this.label.visProp.position) {
555             case 'lft':
556             case 'llft':
557             case 'ulft':
558                 if (c1[1] <= c2[1]) {
559                     x = c1[1];
560                     y = c1[2];
561                 } else {
562                     x = c2[1];
563                     y = c2[2];
564                 }
565                 break;
566             case 'rt':
567             case 'lrt':
568             case 'urt':
569                 if (c1[1] > c2[1]) {
570                     x = c1[1];
571                     y = c1[2];
572                 } else {
573                     x = c2[1];
574                     y = c2[2];
575                 }
576                 break;
577             default:
578                 x = 0.5 * (c1[1] + c2[1]);
579                 y = 0.5 * (c1[2] + c2[2]);
580             }
581 
582             // Correct coordinates if the label seems to be outside of canvas.
583             if (this.visProp.straightfirst || this.visProp.straightlast) {
584                 if (Type.exists(this.label)) {  // Does not exist during createLabel
585                     fs = this.label.visProp.fontsize;
586                 }
587 
588                 if (Math.abs(x) < Mat.eps) {
589                     x = 0;
590                 } else if (this.board.canvasWidth + Mat.eps > x && x > this.board.canvasWidth - fs - Mat.eps) {
591                     x = this.board.canvasWidth - fs;
592                 }
593 
594                 if (Mat.eps + fs > y && y > -Mat.eps) {
595                     y = fs;
596                 } else if (this.board.canvasHeight + Mat.eps > y && y > this.board.canvasHeight - fs - Mat.eps) {
597                     y = this.board.canvasHeight;
598                 }
599             }
600 
601             return new Coords(Const.COORDS_BY_SCREEN, [x, y], this.board);
602         },
603 
604         // documented in geometry element
605         cloneToBackground: function () {
606             var copy = {}, r, s, er;
607 
608             copy.id = this.id + 'T' + this.numTraces;
609             copy.elementClass = Const.OBJECT_CLASS_LINE;
610             this.numTraces++;
611             copy.point1 = this.point1;
612             copy.point2 = this.point2;
613 
614             copy.stdform = this.stdform;
615 
616             copy.board = this.board;
617 
618             copy.visProp = Type.deepCopy(this.visProp, this.visProp.traceattributes, true);
619             copy.visProp.layer = this.board.options.layer.trace;
620             Type.clearVisPropOld(copy);
621 
622             s = this.getSlope();
623             r = this.getRise();
624             copy.getSlope = function () {
625                 return s;
626             };
627             copy.getRise = function () {
628                 return r;
629             };
630 
631             er = this.board.renderer.enhancedRendering;
632             this.board.renderer.enhancedRendering = true;
633             this.board.renderer.drawLine(copy);
634             this.board.renderer.enhancedRendering = er;
635             this.traces[copy.id] = copy.rendNode;
636 
637             return this;
638         },
639 
640         /**
641          * Add transformations to this line.
642          * @param {JXG.Transformation|Array} transform Either one {@link JXG.Transformation} or an array of
643          * {@link JXG.Transformation}s.
644          * @returns {JXG.Line} Reference to this line object.
645          */
646         addTransform: function (transform) {
647             var i,
648                 list = Type.isArray(transform) ? transform : [transform],
649                 len = list.length;
650 
651             for (i = 0; i < len; i++) {
652                 this.point1.transformations.push(list[i]);
653                 this.point2.transformations.push(list[i]);
654             }
655 
656             return this;
657         },
658 
659         // see GeometryElement.js
660         snapToGrid: function (pos) {
661             var c1, c2, dc, t, ticks,
662                 x, y, sX, sY;
663 
664             if (this.visProp.snaptogrid) {
665                 if (this.parents.length < 3) {    // Line through two points
666                     this.point1.handleSnapToGrid(true, true);
667                     this.point2.handleSnapToGrid(true, true);
668                 /*
669                 if (this.point1.visProp.snaptogrid || this.point2.visProp.snaptogrid) {
670                     this.point1.snapToGrid();
671                     this.point2.snapToGrid();
672                 */
673                 } else if (JXG.exists(pos)) {       // Free line
674                     sX = this.visProp.snapsizex;
675                     sY = this.visProp.snapsizey;
676 
677                     c1 = new Coords(Const.COORDS_BY_SCREEN, [pos.Xprev, pos.Yprev], this.board);
678 
679                     x = c1.usrCoords[1];
680                     y = c1.usrCoords[2];
681 
682                     if (sX <= 0 && this.board.defaultAxes && this.board.defaultAxes.x.defaultTicks) {
683                         ticks = this.board.defaultAxes.x.defaultTicks;
684                         sX = ticks.ticksDelta * (ticks.visProp.minorticks + 1);
685                     }
686                     if (sY <= 0 && this.board.defaultAxes && this.board.defaultAxes.y.defaultTicks) {
687                         ticks = this.board.defaultAxes.y.defaultTicks;
688                         sY = ticks.ticksDelta * (ticks.visProp.minorticks + 1);
689                     }
690 
691                     // if no valid snap sizes are available, don't change the coords.
692                     if (sX > 0 && sY > 0) {
693                         // projectCoordsToLine
694                         /*
695                         v = [0, this.stdform[1], this.stdform[2]];
696                         v = Mat.crossProduct(v, c1.usrCoords);
697                         c2 = Geometry.meetLineLine(v, this.stdform, 0, this.board);
698                         */
699                         c2 = Geometry.projectPointToLine({coords: c1}, this, this.board);
700 
701                         dc = Statistics.subtract([1, Math.round(x / sX) * sX, Math.round(y / sY) * sY], c2.usrCoords);
702                         t = this.board.create('transform', dc.slice(1), {type: 'translate'});
703                         t.applyOnce([this.point1, this.point2]);
704                     }
705                 }
706             } else {
707                 this.point1.handleSnapToGrid(false, true);
708                 this.point2.handleSnapToGrid(false, true);
709             }
710 
711             return this;
712         },
713 
714         // see element.js
715         snapToPoints: function () {
716             var forceIt = this.visProp.snaptopoints;
717 
718             if (this.parents.length < 3) {    // Line through two points
719                 this.point1.handleSnapToPoints(forceIt);
720                 this.point2.handleSnapToPoints(forceIt);
721             }
722 
723             return this;
724         },
725 
726         /**
727          * Treat the line as parametric curve in homogeneous coordinates, where the parameter t runs from 0 to 1.
728          * First we transform the interval [0,1] to [-1,1].
729          * If the line has homogeneous coordinates [c,a,b] = stdform[] then the direction of the line is [b,-a].
730          * Now, we take one finite point that defines the line, i.e. we take either point1 or point2 (in case the line is not the ideal line).
731          * Let the coordinates of that point be [z, x, y].
732          * Then, the curve runs linearly from
733          * [0, b, -a] (t=-1) to [z, x, y] (t=0)
734          * and
735          * [z, x, y] (t=0) to [0, -b, a] (t=1)
736          *
737          * @param {Number} t Parameter running from 0 to 1.
738          * @returns {Number} X(t) x-coordinate of the line treated as parametric curve.
739          * */
740         X: function (t) {
741             var x,
742                 b = this.stdform[2];
743 
744             x = (Math.abs(this.point1.coords.usrCoords[0]) > Mat.eps) ?
745                     this.point1.coords.usrCoords[1] :
746                     this.point2.coords.usrCoords[1];
747 
748             t = (t - 0.5) * 2;
749 
750             return (1 - Math.abs(t)) * x - t * b;
751         },
752 
753         /**
754          * Treat the line as parametric curve in homogeneous coordinates.
755          * See {@link JXG.Line#X} for a detailed description.
756          * @param {Number} t Parameter running from 0 to 1.
757          * @returns {Number} Y(t) y-coordinate of the line treated as parametric curve.
758          */
759         Y: function (t) {
760             var y,
761                 a = this.stdform[1];
762 
763             y = (Math.abs(this.point1.coords.usrCoords[0]) > Mat.eps) ?
764                     this.point1.coords.usrCoords[2] :
765                     this.point2.coords.usrCoords[2];
766 
767             t = (t - 0.5) * 2;
768 
769             return (1 - Math.abs(t)) * y + t * a;
770         },
771 
772         /**
773          * Treat the line as parametric curve in homogeneous coordinates.
774          * See {@link JXG.Line#X} for a detailed description.
775          *
776          * @param {Number} t Parameter running from 0 to 1.
777          * @returns {Number} Z(t) z-coordinate of the line treated as parametric curve.
778          */
779         Z: function (t) {
780             var z = (Math.abs(this.point1.coords.usrCoords[0]) > Mat.eps) ?
781                     this.point1.coords.usrCoords[0] :
782                     this.point2.coords.usrCoords[0];
783 
784             t = (t - 0.5) * 2;
785 
786             return (1 - Math.abs(t)) * z;
787         },
788 
789 
790         /**
791          * The distance between the two points defining the line.
792          * @returns {Number}
793          */
794         L: function () {
795             return this.point1.Dist(this.point2);
796         },
797 
798         /**
799          * Treat the element  as a parametric curve
800          * @private
801          */
802         minX: function () {
803             return 0.0;
804         },
805 
806         /**
807          * Treat the element as parametric curve
808          * @private
809          */
810         maxX: function () {
811             return 1.0;
812         },
813 
814         // documented in geometry element
815         bounds: function () {
816             var p1c = this.point1.coords.usrCoords,
817                 p2c = this.point2.coords.usrCoords;
818 
819             return [Math.min(p1c[1], p2c[1]), Math.max(p1c[2], p2c[2]), Math.max(p1c[1], p2c[1]), Math.min(p1c[2], p2c[2])];
820         },
821 
822         /**
823          * Adds ticks to this line. Ticks can be added to any kind of line: line, arrow, and axis.
824          * @param {JXG.Ticks} ticks Reference to a ticks object which is describing the ticks (color, distance, how many, etc.).
825          * @returns {String} Id of the ticks object.
826          */
827         addTicks: function (ticks) {
828             if (ticks.id === '' || !Type.exists(ticks.id)) {
829                 ticks.id = this.id + '_ticks_' + (this.ticks.length + 1);
830             }
831 
832             this.board.renderer.drawTicks(ticks);
833             this.ticks.push(ticks);
834 
835             return ticks.id;
836         },
837 
838         // documented in GeometryElement.js
839         remove: function () {
840             this.removeAllTicks();
841             GeometryElement.prototype.remove.call(this);
842         },
843 
844         /**
845          * Removes all ticks from a line.
846          */
847         removeAllTicks: function () {
848             var t;
849 
850             for (t = this.ticks.length; t > 0; t--) {
851                 this.removeTicks(this.ticks[t - 1]);
852             }
853 
854             this.ticks = [];
855             this.board.update();
856         },
857 
858         /**
859          * Removes ticks identified by parameter named tick from this line.
860          * @param {JXG.Ticks} tick Reference to tick object to remove.
861          */
862         removeTicks: function (tick) {
863             var t, j;
864 
865             if (Type.exists(this.defaultTicks) && this.defaultTicks === tick) {
866                 this.defaultTicks = null;
867             }
868 
869             for (t = this.ticks.length; t > 0; t--) {
870                 if (this.ticks[t - 1] === tick) {
871                     this.board.removeObject(this.ticks[t - 1]);
872 
873                     if (this.ticks[t - 1].ticks) {
874                         for (j = 0; j < this.ticks[t - 1].ticks.length; j++) {
875                             if (Type.exists(this.ticks[t - 1].labels[j])) {
876                                 this.board.removeObject(this.ticks[t - 1].labels[j]);
877                             }
878                         }
879                     }
880 
881                     delete this.ticks[t - 1];
882                     break;
883                 }
884             }
885         },
886 
887         hideElement: function () {
888             var i;
889 
890             GeometryElement.prototype.hideElement.call(this);
891 
892             for (i = 0; i < this.ticks.length; i++) {
893                 this.ticks[i].hideElement();
894             }
895         },
896 
897         showElement: function () {
898             var i;
899 
900             GeometryElement.prototype.showElement.call(this);
901 
902             for (i = 0; i < this.ticks.length; i++) {
903                 this.ticks[i].showElement();
904             }
905         }
906     });
907 
908     /**
909      * @class This element is used to provide a constructor for a general line. A general line is given by two points. By setting additional properties
910      * a line can be used as an arrow and/or axis.
911      * @pseudo
912      * @description
913      * @name Line
914      * @augments JXG.Line
915      * @constructor
916      * @type JXG.Line
917      * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
918      * @param {JXG.Point,array,function_JXG.Point,array,function} point1,point2 Parent elements can be two elements either of type {@link JXG.Point} or array of
919      * numbers describing the coordinates of a point. In the latter case the point will be constructed automatically as a fixed invisible point.
920      * It is possible to provide a function returning an array or a point, instead of providing an array or a point.
921      * @param {Number,function_Number,function_Number,function} c,a,b A line can also be created providing three numbers. The line is then described by
922      * the set of solutions of the equation <tt>a*x+b*y+c*z = 0</tt>. It is possible to provide three functions returning numbers, too.
923      * @param {function} f This function must return an array containing three numbers forming the line's homogeneous coordinates.
924      * @example
925      * // Create a line using point and coordinates/
926      * // The second point will be fixed and invisible.
927      * var p1 = board.create('point', [4.5, 2.0]);
928      * var l1 = board.create('line', [p1, [1.0, 1.0]]);
929      * </pre><div class="jxgbox"id="c0ae3461-10c4-4d39-b9be-81d74759d122" style="width: 300px; height: 300px;"></div>
930      * <script type="text/javascript">
931      *   var glex1_board = JXG.JSXGraph.initBoard('c0ae3461-10c4-4d39-b9be-81d74759d122', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false});
932      *   var glex1_p1 = glex1_board.create('point', [4.5, 2.0]);
933      *   var glex1_l1 = glex1_board.create('line', [glex1_p1, [1.0, 1.0]]);
934      * </script><pre>
935      * @example
936      * // Create a line using three coordinates
937      * var l1 = board.create('line', [1.0, -2.0, 3.0]);
938      * </pre><div class="jxgbox"id="cf45e462-f964-4ba4-be3a-c9db94e2593f" style="width: 300px; height: 300px;"></div>
939      * <script type="text/javascript">
940      *   var glex2_board = JXG.JSXGraph.initBoard('cf45e462-f964-4ba4-be3a-c9db94e2593f', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false});
941      *   var glex2_l1 = glex2_board.create('line', [1.0, -2.0, 3.0]);
942      * </script><pre>
943      */
944     JXG.createLine = function (board, parents, attributes) {
945         var ps, el, p1, p2, i, attr,
946             c = [],
947             constrained = false,
948             isDraggable;
949 
950         /**
951          * The line is defined by two points or coordinates of two points.
952          * In the latter case, the points are created.
953          */
954         if (parents.length === 2) {
955             // point 1 given by coordinates
956             if (Type.isArray(parents[0]) && parents[0].length > 1) {
957                 attr = Type.copyAttributes(attributes, board.options, 'line', 'point1');
958                 p1 = board.create('point', parents[0], attr);
959             } else if (Type.isString(parents[0]) || Type.isPoint(parents[0])) {
960                 p1 =  board.select(parents[0]);
961             } else if (Type.isFunction(parents[0]) && Type.isPoint(parents[0]())) {
962                 p1 = parents[0]();
963                 constrained = true;
964             } else if (Type.isFunction(parents[0]) && parents[0]().length && parents[0]().length >= 2) {
965                 attr = Type.copyAttributes(attributes, board.options, 'line', 'point1');
966                 p1 = Point.createPoint(board, parents[0](), attr);
967                 constrained = true;
968             } else {
969                 throw new Error("JSXGraph: Can't create line with parent types '" +
970                     (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." +
971                     "\nPossible parent types: [point,point], [[x1,y1],[x2,y2]], [a,b,c]");
972             }
973 
974             // point 2 given by coordinates
975             if (Type.isArray(parents[1]) && parents[1].length > 1) {
976                 attr = Type.copyAttributes(attributes, board.options, 'line', 'point2');
977                 p2 = board.create('point', parents[1], attr);
978             } else if (Type.isString(parents[1]) || Type.isPoint(parents[1])) {
979                 p2 =  board.select(parents[1]);
980             } else if (Type.isFunction(parents[1]) &&  Type.isPoint(parents[1]()) ) {
981                 p2 = parents[1]();
982                 constrained = true;
983             } else if (Type.isFunction(parents[1]) && parents[1]().length && parents[1]().length >= 2) {
984                 attr = Type.copyAttributes(attributes, board.options, 'line', 'point2');
985                 p2 = Point.createPoint(board, parents[1](), attr);
986                 constrained = true;
987             } else {
988                 throw new Error("JSXGraph: Can't create line with parent types '" +
989                     (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." +
990                     "\nPossible parent types: [point,point], [[x1,y1],[x2,y2]], [a,b,c]");
991             }
992 
993             attr = Type.copyAttributes(attributes, board.options, 'line');
994 
995             el = new JXG.Line(board, p1, p2, attr);
996             if (constrained) {
997                 el.constrained = true;
998                 el.funp1 = parents[0];
999                 el.funp2 = parents[1];
1000             } else {
1001                 el.isDraggable = true;
1002             }
1003 
1004             //if (!el.constrained) {
1005             el.setParents([p1.id, p2.id]);
1006             //}
1007 
1008          // Line is defined by three homogeneous coordinates.
1009          // Also in this case points are created.
1010         } else if (parents.length === 3) {
1011             // free line
1012             isDraggable = true;
1013             for (i = 0; i < 3; i++) {
1014                 if (Type.isNumber(parents[i])) {
1015                     // createFunction will just wrap a function around our constant number
1016                     // that does nothing else but to return that number.
1017                     c[i] = Type.createFunction(parents[i]);
1018                 } else if (Type.isFunction(parents[i])) {
1019                     c[i] = parents[i];
1020                     isDraggable = false;
1021                 } else {
1022                     throw new Error("JSXGraph: Can't create line with parent types '" +
1023                         (typeof parents[0]) + "' and '" + (typeof parents[1]) + "' and '" + (typeof parents[2]) + "'." +
1024                         "\nPossible parent types: [point,point], [[x1,y1],[x2,y2]], [a,b,c]");
1025                 }
1026             }
1027 
1028             // point 1 is the midpoint between (0,c,-b) and point 2. => point1 is finite.
1029             attr = Type.copyAttributes(attributes, board.options, 'line', 'point1');
1030             if (isDraggable) {
1031                 p1 = board.create('point', [
1032                     c[2]() * c[2]() + c[1]() * c[1](),
1033                     c[2]() - c[1]() * c[0]() + c[2](),
1034                     -c[1]() - c[2]() * c[0]() - c[1]()
1035                 ], attr);
1036             } else {
1037                 p1 = board.create('point', [
1038                     function () {
1039                         return (c[2]() * c[2]() + c[1]() * c[1]()) * 0.5;
1040                     },
1041                     function () {
1042                         return (c[2]() - c[1]() * c[0]() + c[2]()) * 0.5;
1043                     },
1044                     function () {
1045                         return (-c[1]() - c[2]() * c[0]() - c[1]()) * 0.5;
1046                     }], attr);
1047             }
1048 
1049             // point 2: (b^2+c^2,-ba+c,-ca-b)
1050             attr = Type.copyAttributes(attributes, board.options, 'line', 'point2');
1051             if (isDraggable) {
1052                 p2 = board.create('point', [
1053                     c[2]() * c[2]() + c[1]() * c[1](),
1054                     -c[1]() * c[0]() + c[2](),
1055                     -c[2]() * c[0]() - c[1]()
1056                 ], attr);
1057             } else {
1058                 p2 = board.create('point', [
1059                     function () {
1060                         return c[2]() * c[2]() + c[1]() * c[1]();
1061                     },
1062                     function () {
1063                         return -c[1]() * c[0]() + c[2]();
1064                     },
1065                     function () {
1066                         return -c[2]() * c[0]() - c[1]();
1067                     }], attr);
1068             }
1069 
1070             // If the line will have a glider and board.suspendUpdate() has been called, we
1071             // need to compute the initial position of the two points p1 and p2.
1072             p1.prepareUpdate().update();
1073             p2.prepareUpdate().update();
1074             attr = Type.copyAttributes(attributes, board.options, 'line');
1075             el = new JXG.Line(board, p1, p2, attr);
1076             // Not yet working, because the points are not draggable.
1077             el.isDraggable = isDraggable;
1078             el.setParents([p1, p2]);
1079 
1080         // The parent array contains a function which returns two points.
1081         } else if (parents.length === 1 && Type.isFunction(parents[0]) && parents[0]().length === 2 &&
1082                 Type.isPoint(parents[0]()[0]) &&
1083                 Type.isPoint(parents[0]()[1])) {
1084             ps = parents[0]();
1085             attr = Type.copyAttributes(attributes, board.options, 'line');
1086             el = new JXG.Line(board, ps[0], ps[1], attr);
1087             el.constrained = true;
1088             el.funps = parents[0];
1089             el.setParents(ps);
1090 
1091         } else if (parents.length === 1 && Type.isFunction(parents[0]) && parents[0]().length === 3 &&
1092                 Type.isNumber(parents[0]()[0]) &&
1093                 Type.isNumber(parents[0]()[1]) &&
1094                 Type.isNumber(parents[0]()[2])) {
1095             ps = parents[0];
1096 
1097             attr = Type.copyAttributes(attributes, board.options, 'line', 'point1');
1098             p1 = board.create('point', [
1099                 function () {
1100                     var c = ps();
1101 
1102                     return [
1103                         (c[2] * c[2] + c[1] * c[1]) * 0.5,
1104                         (c[2] - c[1] * c[0] + c[2]) * 0.5,
1105                         (-c[1] - c[2] * c[0] - c[1]) * 0.5
1106                     ];
1107                 }], attr);
1108 
1109             attr = Type.copyAttributes(attributes, board.options, 'line', 'point2');
1110             p2 = board.create('point', [
1111                 function () {
1112                     var c = ps();
1113 
1114                     return [
1115                         c[2] * c[2] + c[1] * c[1],
1116                         -c[1] * c[0] + c[2],
1117                         -c[2] * c[0] - c[1]
1118                     ];
1119                 }], attr);
1120 
1121             attr = Type.copyAttributes(attributes, board.options, 'line');
1122             el = new JXG.Line(board, p1, p2, attr);
1123 
1124             el.constrained = true;
1125             el.funps = parents[0];
1126             el.setParents([p1, p2]);
1127 
1128         } else {
1129             throw new Error("JSXGraph: Can't create line with parent types '" +
1130                 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." +
1131                 "\nPossible parent types: [point,point], [[x1,y1],[x2,y2]], [a,b,c]");
1132         }
1133 
1134         return el;
1135     };
1136 
1137     JXG.registerElement('line', JXG.createLine);
1138 
1139     /**
1140      * @class This element is used to provide a constructor for a segment.
1141      * It's strictly spoken just a wrapper for element {@link Line} with {@link JXG.Line#straightFirst}
1142      * and {@link JXG.Line#straightLast} properties set to false. If there is a third variable then the
1143      * segment has a fixed length (which may be a function, too).
1144      * @pseudo
1145      * @description
1146      * @name Segment
1147      * @augments JXG.Line
1148      * @constructor
1149      * @type JXG.Line
1150      * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
1151      * @param {JXG.Point,array_JXG.Point,array} point1,point2 Parent elements can be two elements either of type {@link JXG.Point}
1152      * or array of numbers describing the
1153      * coordinates of a point. In the latter case the point will be constructed automatically as a fixed invisible point.
1154      * @param {number,function} length (optional) The points are adapted - if possible - such that their distance
1155      * has a this value.
1156      * @see Line
1157      * @example
1158      * // Create a segment providing two points.
1159      *   var p1 = board.create('point', [4.5, 2.0]);
1160      *   var p2 = board.create('point', [1.0, 1.0]);
1161      *   var l1 = board.create('segment', [p1, p2]);
1162      * </pre><div class="jxgbox"id="d70e6aac-7c93-4525-a94c-a1820fa38e2f" style="width: 300px; height: 300px;"></div>
1163      * <script type="text/javascript">
1164      *   var slex1_board = JXG.JSXGraph.initBoard('d70e6aac-7c93-4525-a94c-a1820fa38e2f', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false});
1165      *   var slex1_p1 = slex1_board.create('point', [4.5, 2.0]);
1166      *   var slex1_p2 = slex1_board.create('point', [1.0, 1.0]);
1167      *   var slex1_l1 = slex1_board.create('segment', [slex1_p1, slex1_p2]);
1168      * </script><pre>
1169      *
1170      * @example
1171      * // Create a segment providing two points.
1172      *   var p1 = board.create('point', [4.0, 1.0]);
1173      *   var p2 = board.create('point', [1.0, 1.0]);
1174      *   var l1 = board.create('segment', [p1, p2]);
1175      *   var p3 = board.create('point', [4.0, 2.0]);
1176      *   var p4 = board.create('point', [1.0, 2.0]);
1177      *   var l2 = board.create('segment', [p3, p4, 3]);
1178      *   var p5 = board.create('point', [4.0, 3.0]);
1179      *   var p6 = board.create('point', [1.0, 4.0]);
1180      *   var l3 = board.create('segment', [p5, p6, function(){ return l1.L();} ]);
1181      * </pre><div class="jxgbox"id="617336ba-0705-4b2b-a236-c87c28ef25be" style="width: 300px; height: 300px;"></div>
1182      * <script type="text/javascript">
1183      *   var slex2_board = JXG.JSXGraph.initBoard('617336ba-0705-4b2b-a236-c87c28ef25be', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false});
1184      *   var slex2_p1 = slex2_board.create('point', [4.0, 1.0]);
1185      *   var slex2_p2 = slex2_board.create('point', [1.0, 1.0]);
1186      *   var slex2_l1 = slex2_board.create('segment', [slex2_p1, slex2_p2]);
1187      *   var slex2_p3 = slex2_board.create('point', [4.0, 2.0]);
1188      *   var slex2_p4 = slex2_board.create('point', [1.0, 2.0]);
1189      *   var slex2_l2 = slex2_board.create('segment', [slex2_p3, slex2_p4, 3]);
1190      *   var slex2_p5 = slex2_board.create('point', [4.0, 2.0]);
1191      *   var slex2_p6 = slex2_board.create('point', [1.0, 2.0]);
1192      *   var slex2_l3 = slex2_board.create('segment', [slex2_p5, slex2_p6, function(){ return slex2_l1.L();}]);
1193      * </script><pre>
1194      *
1195      */
1196     JXG.createSegment = function (board, parents, attributes) {
1197         var el, attr;
1198 
1199         attributes.straightFirst = false;
1200         attributes.straightLast = false;
1201         attr = Type.copyAttributes(attributes, board.options, 'segment');
1202 
1203         el = board.create('line', parents.slice(0, 2), attr);
1204 
1205         if (parents.length === 3) {
1206             el.hasFixedLength = true;
1207 
1208             if (Type.isNumber(parents[2])) {
1209                 el.fixedLength = function () {
1210                     return parents[2];
1211                 };
1212             } else if (Type.isFunction(parents[2])) {
1213                 el.fixedLength = parents[2];
1214             } else {
1215                 throw new Error("JSXGraph: Can't create segment with third parent type '" +
1216                     (typeof parents[2]) + "'." +
1217                     "\nPossible third parent types: number or function");
1218             }
1219 
1220             el.getParents = function() {
1221                 return this.parents.concat(this.fixedLength());
1222             };
1223 
1224             el.fixedLengthOldCoords = [];
1225             el.fixedLengthOldCoords[0] = new Coords(Const.COORDS_BY_USER, el.point1.coords.usrCoords.slice(1, 3), board);
1226             el.fixedLengthOldCoords[1] = new Coords(Const.COORDS_BY_USER, el.point2.coords.usrCoords.slice(1, 3), board);
1227         }
1228 
1229         el.elType = 'segment';
1230 
1231         return el;
1232     };
1233 
1234     JXG.registerElement('segment', JXG.createSegment);
1235 
1236     /**
1237      * @class This element is used to provide a constructor for arrow, which is just a wrapper for element {@link Line} with {@link JXG.Line#straightFirst}
1238      * and {@link JXG.Line#straightLast} properties set to false and {@link JXG.Line#lastArrow} set to true.
1239      * @pseudo
1240      * @description
1241      * @name Arrow
1242      * @augments JXG.Line
1243      * @constructor
1244      * @type JXG.Line
1245      * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
1246      * @param {JXG.Point,array_JXG.Point,array} point1,point2 Parent elements can be two elements either of type {@link JXG.Point} or array of numbers describing the
1247      * coordinates of a point. In the latter case the point will be constructed automatically as a fixed invisible point.
1248      * @param {Number_Number_Number} a,b,c A line can also be created providing three numbers. The line is then described by the set of solutions
1249      * of the equation <tt>a*x+b*y+c*z = 0</tt>.
1250      * @see Line
1251      * @example
1252      * // Create an arrow providing two points.
1253      *   var p1 = board.create('point', [4.5, 2.0]);
1254      *   var p2 = board.create('point', [1.0, 1.0]);
1255      *   var l1 = board.create('arrow', [p1, p2]);
1256      * </pre><div class="jxgbox"id="1d26bd22-7d6d-4018-b164-4c8bc8d22ccf" style="width: 300px; height: 300px;"></div>
1257      * <script type="text/javascript">
1258      *   var alex1_board = JXG.JSXGraph.initBoard('1d26bd22-7d6d-4018-b164-4c8bc8d22ccf', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false});
1259      *   var alex1_p1 = alex1_board.create('point', [4.5, 2.0]);
1260      *   var alex1_p2 = alex1_board.create('point', [1.0, 1.0]);
1261      *   var alex1_l1 = alex1_board.create('arrow', [alex1_p1, alex1_p2]);
1262      * </script><pre>
1263      */
1264     JXG.createArrow = function (board, parents, attributes) {
1265         var el;
1266 
1267         attributes.firstArrow = false;
1268         attributes.lastArrow = true;
1269         el = board.create('line', parents, attributes).setStraight(false, false);
1270         //el.setArrow(false, true);
1271         el.type = Const.OBJECT_TYPE_VECTOR;
1272         el.elType = 'arrow';
1273 
1274         return el;
1275     };
1276 
1277     JXG.registerElement('arrow', JXG.createArrow);
1278 
1279     /**
1280      * @class This element is used to provide a constructor for an axis. It's strictly spoken just a wrapper for element {@link Line} with {@link JXG.Line#straightFirst}
1281      * and {@link JXG.Line#straightLast} properties set to true. Additionally {@link JXG.Line#lastArrow} is set to true and default {@link Ticks} will be created.
1282      * @pseudo
1283      * @description
1284      * @name Axis
1285      * @augments JXG.Line
1286      * @constructor
1287      * @type JXG.Line
1288      * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
1289      * @param {JXG.Point,array_JXG.Point,array} point1,point2 Parent elements can be two elements either of type {@link JXG.Point} or array of numbers describing the
1290      * coordinates of a point. In the latter case the point will be constructed automatically as a fixed invisible point.
1291      * @param {Number_Number_Number} a,b,c A line can also be created providing three numbers. The line is then described by the set of solutions
1292      * of the equation <tt>a*x+b*y+c*z = 0</tt>.
1293      * @example
1294      * // Create an axis providing two coord pairs.
1295      *   var l1 = board.create('axis', [[0.0, 1.0], [1.0, 1.3]]);
1296      * </pre><div class="jxgbox"id="4f414733-624c-42e4-855c-11f5530383ae" style="width: 300px; height: 300px;"></div>
1297      * <script type="text/javascript">
1298      *   var axex1_board = JXG.JSXGraph.initBoard('4f414733-624c-42e4-855c-11f5530383ae', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false});
1299      *   var axex1_l1 = axex1_board.create('axis', [[0.0, 1.0], [1.0, 1.3]]);
1300      * </script><pre>
1301      */
1302     JXG.createAxis = function (board, parents, attributes) {
1303         var attr, el, els, dist;
1304 
1305         // Arrays oder Punkte, mehr brauchen wir nicht.
1306         if ((Type.isArray(parents[0]) || Type.isPoint(parents[0])) && (Type.isArray(parents[1]) || Type.isPoint(parents[1]))) {
1307             attr = Type.copyAttributes(attributes, board.options, 'axis');
1308             el = board.create('line', parents, attr);
1309             el.type = Const.OBJECT_TYPE_AXIS;
1310             el.isDraggable = false;
1311             el.point1.isDraggable = false;
1312             el.point2.isDraggable = false;
1313 
1314             for (els in el.ancestors) {
1315                 if (el.ancestors.hasOwnProperty(els)) {
1316                     el.ancestors[els].type = Const.OBJECT_TYPE_AXISPOINT;
1317                 }
1318             }
1319 
1320             attr = Type.copyAttributes(attributes, board.options, 'axis', 'ticks');
1321             if (Type.exists(attr.ticksdistance)) {
1322                 dist = attr.ticksdistance;
1323             } else if (Type.isArray(attr.ticks)) {
1324                 dist = attr.ticks;
1325             } else {
1326                 dist = 1.0;
1327             }
1328 
1329             /**
1330              * The ticks attached to the axis.
1331              * @memberOf Axis.prototype
1332              * @name defaultTicks
1333              * @type JXG.Ticks
1334              */
1335             el.defaultTicks = board.create('ticks', [el, dist], attr);
1336 
1337             el.defaultTicks.dump = false;
1338 
1339             el.elType = 'axis';
1340             el.subs = {
1341                 ticks: el.defaultTicks
1342             };
1343         } else {
1344             throw new Error("JSXGraph: Can't create axis with parent types '" +
1345                 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." +
1346                 "\nPossible parent types: [point,point], [[x1,y1],[x2,y2]]");
1347         }
1348 
1349         return el;
1350     };
1351 
1352     JXG.registerElement('axis', JXG.createAxis);
1353 
1354     /**
1355      * @class With the element tangent the slope of a line, circle, or curve in a certain point can be visualized. A tangent is always constructed
1356      * by a glider on a line, circle, or curve and describes the tangent in the glider point on that line, circle, or curve.
1357      * @pseudo
1358      * @description
1359      * @name Tangent
1360      * @augments JXG.Line
1361      * @constructor
1362      * @type JXG.Line
1363      * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
1364      * @param {Glider} g A glider on a line, circle, or curve.
1365      * @example
1366      * // Create a tangent providing a glider on a function graph
1367      *   var c1 = board.create('curve', [function(t){return t},function(t){return t*t*t;}]);
1368      *   var g1 = board.create('glider', [0.6, 1.2, c1]);
1369      *   var t1 = board.create('tangent', [g1]);
1370      * </pre><div class="jxgbox"id="7b7233a0-f363-47dd-9df5-4018d0d17a98" style="width: 400px; height: 400px;"></div>
1371      * <script type="text/javascript">
1372      *   var tlex1_board = JXG.JSXGraph.initBoard('7b7233a0-f363-47dd-9df5-4018d0d17a98', {boundingbox: [-6, 6, 6, -6], axis: true, showcopyright: false, shownavigation: false});
1373      *   var tlex1_c1 = tlex1_board.create('curve', [function(t){return t},function(t){return t*t*t;}]);
1374      *   var tlex1_g1 = tlex1_board.create('glider', [0.6, 1.2, tlex1_c1]);
1375      *   var tlex1_t1 = tlex1_board.create('tangent', [tlex1_g1]);
1376      * </script><pre>
1377      */
1378     JXG.createTangent = function (board, parents, attributes) {
1379         var p, c, g, f, j, el, tangent;
1380 
1381         // One arguments: glider on line, circle or curve
1382         if (parents.length === 1) {
1383             p = parents[0];
1384             c = p.slideObject;
1385         // Two arguments: (point,F"|conic) or (line|curve|circle|conic,point). // Not yet: curve!
1386         } else if (parents.length === 2) {
1387             // In fact, for circles and conics it is the polar
1388             if (Type.isPoint(parents[0])) {
1389                 p = parents[0];
1390                 c = parents[1];
1391             } else if (Type.isPoint(parents[1])) {
1392                 c = parents[0];
1393                 p = parents[1];
1394             } else {
1395                 throw new Error("JSXGraph: Can't create tangent with parent types '" +
1396                     (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." +
1397                     "\nPossible parent types: [glider], [point,line|curve|circle|conic]");
1398             }
1399         } else {
1400             throw new Error("JSXGraph: Can't create tangent with parent types '" +
1401                 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." +
1402                 "\nPossible parent types: [glider], [point,line|curve|circle|conic]");
1403         }
1404 
1405         if (c.elementClass === Const.OBJECT_CLASS_LINE) {
1406             tangent = board.create('line', [c.point1, c.point2], attributes);
1407             tangent.glider = p;
1408         } else if (c.elementClass === Const.OBJECT_CLASS_CURVE && c.type !== Const.OBJECT_TYPE_CONIC) {
1409             if (c.visProp.curvetype !== 'plot') {
1410                 g = c.X;
1411                 f = c.Y;
1412                 tangent = board.create('line', [
1413                     function () {
1414                         return -p.X() * Numerics.D(f)(p.position) + p.Y() * Numerics.D(g)(p.position);
1415                     },
1416                     function () {
1417                         return Numerics.D(f)(p.position);
1418                     },
1419                     function () {
1420                         return -Numerics.D(g)(p.position);
1421                     }
1422                 ], attributes);
1423                 p.addChild(tangent);
1424 
1425                 // this is required for the geogebra reader to display a slope
1426                 tangent.glider = p;
1427             } else {  // curveType 'plot'
1428                 // equation of the line segment: 0 = y*(x1-x2) + x*(y2-y1) + y1*x2-x1*y2
1429                 tangent = board.create('line', [
1430                     function () {
1431                         var i = Math.floor(p.position);
1432 
1433                         if (i === c.numberPoints - 1) {
1434                             i--;
1435                         }
1436 
1437                         if (i < 0) {
1438                             return 1;
1439                         }
1440 
1441                         return c.Y(i) * c.X(i + 1) - c.X(i) * c.Y(i + 1);
1442                     },
1443                     function () {
1444                         var i = Math.floor(p.position);
1445 
1446                         if (i === c.numberPoints - 1) {
1447                             i--;
1448                         }
1449 
1450                         if (i < 0) {
1451                             return 0;
1452                         }
1453 
1454                         return c.Y(i + 1) - c.Y(i);
1455                     },
1456                     function () {
1457                         var i = Math.floor(p.position);
1458 
1459                         if (i === c.numberPoints - 1) {
1460                             i--;
1461                         }
1462 
1463                         if (i < 0) {
1464                             return 0.0;
1465                         }
1466 
1467                         return c.X(i) - c.X(i + 1);
1468                     }], attributes);
1469 
1470                 p.addChild(tangent);
1471 
1472                 // this is required for the geogebra reader to display a slope
1473                 tangent.glider = p;
1474             }
1475         } else if (c.type === Const.OBJECT_TYPE_TURTLE) {
1476             tangent = board.create('line', [
1477                 function () {
1478                     var i = Math.floor(p.position);
1479 
1480                     // run through all curves of this turtle
1481                     for (j = 0; j < c.objects.length; j++) {
1482                         el = c.objects[j];
1483 
1484                         if (el.type === Const.OBJECT_TYPE_CURVE) {
1485                             if (i < el.numberPoints) {
1486                                 break;
1487                             }
1488 
1489                             i -= el.numberPoints;
1490                         }
1491                     }
1492 
1493                     if (i === el.numberPoints - 1) {
1494                         i--;
1495                     }
1496 
1497                     if (i < 0) {
1498                         return 1;
1499                     }
1500 
1501                     return el.Y(i) * el.X(i + 1) - el.X(i) * el.Y(i + 1);
1502                 },
1503                 function () {
1504                     var i = Math.floor(p.position);
1505 
1506                     // run through all curves of this turtle
1507                     for (j = 0; j < c.objects.length; j++) {
1508                         el = c.objects[j];
1509 
1510                         if (el.type === Const.OBJECT_TYPE_CURVE) {
1511                             if (i < el.numberPoints) {
1512                                 break;
1513                             }
1514 
1515                             i -= el.numberPoints;
1516                         }
1517                     }
1518 
1519                     if (i === el.numberPoints - 1) {
1520                         i--;
1521                     }
1522                     if (i < 0) {
1523                         return 0;
1524                     }
1525 
1526                     return el.Y(i + 1) - el.Y(i);
1527                 },
1528                 function () {
1529                     var i = Math.floor(p.position);
1530 
1531                     // run through all curves of this turtle
1532                     for (j = 0; j < c.objects.length; j++) {
1533                         el = c.objects[j];
1534                         if (el.type === Const.OBJECT_TYPE_CURVE) {
1535                             if (i < el.numberPoints) {
1536                                 break;
1537                             }
1538                             i -= el.numberPoints;
1539                         }
1540                     }
1541                     if (i === el.numberPoints - 1) {
1542                         i--;
1543                     }
1544 
1545                     if (i < 0) {
1546                         return 0;
1547                     }
1548 
1549                     return el.X(i) - el.X(i + 1);
1550                 }], attributes);
1551             p.addChild(tangent);
1552 
1553             // this is required for the geogebra reader to display a slope
1554             tangent.glider = p;
1555         } else if (c.elementClass === Const.OBJECT_CLASS_CIRCLE || c.type === Const.OBJECT_TYPE_CONIC) {
1556             // If p is not on c, the tangent is the polar.
1557             // This construction should work on conics, too. p has to lie on c.
1558             tangent = board.create('line', [
1559                 function () {
1560                     return Mat.matVecMult(c.quadraticform, p.coords.usrCoords)[0];
1561                 },
1562                 function () {
1563                     return Mat.matVecMult(c.quadraticform, p.coords.usrCoords)[1];
1564                 },
1565                 function () {
1566                     return Mat.matVecMult(c.quadraticform, p.coords.usrCoords)[2];
1567                 }], attributes);
1568 
1569             p.addChild(tangent);
1570             // this is required for the geogebra reader to display a slope
1571             tangent.glider = p;
1572         }
1573 
1574         if (!Type.exists(tangent)) {
1575             throw new Error('JSXGraph: Couldn\'t create tangent with the given parents.');
1576         }
1577 
1578         tangent.elType = 'tangent';
1579         tangent.type = Const.OBJECT_TYPE_TANGENT;
1580         tangent.setParents(parents);
1581 
1582         return tangent;
1583     };
1584 
1585     /**
1586      * @class This element is used to provide a constructor for the radical axis with respect to two circles with distinct centers.
1587      * The angular bisector of the polar lines of the circle centers with respect to the other circle is always the radical axis.
1588      * The radical axis passes through the intersection points when the circles intersect.
1589      * When a circle about the midpoint of circle centers, passing through the circle centers, intersects the circles, the polar lines pass through those intersection points.
1590      * @pseudo
1591      * @description
1592      * @name RadicalAxis
1593      * @augments JXG.Line
1594      * @constructor
1595      * @type JXG.Line
1596      * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
1597      * @param {JXG.Circle} circle Circle one of the two respective circles.
1598      * @param {JXG.Circle} circle Circle the other of the two respective circles.
1599      * @example
1600      * // Create the radical axis line with respect to two circles
1601      *   var board = JXG.JSXGraph.initBoard('7b7233a0-f363-47dd-9df5-5018d0d17a98', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false});
1602      *   var p1 = board.create('point', [2, 3]);
1603      *   var p2 = board.create('point', [1, 4]);
1604      *   var c1 = board.create('circle', [p1, p2]);
1605      *   var p3 = board.create('point', [6, 5]);
1606      *   var p4 = board.create('point', [8, 6]);
1607      *   var c2 = board.create('circle', [p3, p4]);
1608      *   var r1 = board.create('radicalaxis', [c1, c2]);
1609      * </pre><div class="jxgbox"id='7b7233a0-f363-47dd-9df5-5018d0d17a98' class='jxgbox' style='width:400px; height:400px;'></div>
1610      * <script type='text/javascript'>
1611      *   var rlex1_board = JXG.JSXGraph.initBoard('7b7233a0-f363-47dd-9df5-5018d0d17a98', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false});
1612      *   var rlex1_p1 = rlex1_board.create('point', [2, 3]);
1613      *   var rlex1_p2 = rlex1_board.create('point', [1, 4]);
1614      *   var rlex1_c1 = rlex1_board.create('circle', [rlex1_p1, rlex1_p2]);
1615      *   var rlex1_p3 = rlex1_board.create('point', [6, 5]);
1616      *   var rlex1_p4 = rlex1_board.create('point', [8, 6]);
1617      *   var rlex1_c2 = rlex1_board.create('circle', [rlex1_p3, rlex1_p4]);
1618      *   var rlex1_r1 = rlex1_board.create('radicalaxis', [rlex1_c1, rlex1_c2]);
1619      * </script><pre>
1620      */
1621     JXG.createRadicalAxis = function (board, parents, attributes) {
1622         var el, el1, el2;
1623 
1624         if (parents.length !== 2 ||
1625                 parents[0].elementClass !== Const.OBJECT_CLASS_CIRCLE ||
1626                 parents[1].elementClass !== Const.OBJECT_CLASS_CIRCLE) {
1627             // Failure
1628             throw new Error("JSXGraph: Can't create 'radical axis' with parent types '" +
1629                 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." +
1630                 "\nPossible parent type: [circle,circle]");
1631         }
1632 
1633         el1 = board.select(parents[0]);
1634         el2 = board.select(parents[1]);
1635 
1636         el = board.create('line', [function () {
1637             var a = el1.stdform,
1638                 b = el2.stdform;
1639 
1640             return Mat.matVecMult(Mat.transpose([a.slice(0, 3), b.slice(0, 3)]), [b[3], -a[3]]);
1641         }], attributes);
1642 
1643         el.elType = 'radicalaxis';
1644         el.setParents([el1.id, el2.id]);
1645 
1646         el1.addChild(el);
1647         el2.addChild(el);
1648 
1649         return el;
1650     };
1651 
1652     /**
1653      * @class This element is used to provide a constructor for the polar line of a point with respect to a conic or a circle.
1654      * @pseudo
1655      * @description The polar line is the unique reciprocal relationship of a point with respect to a conic.
1656      * The lines through the intersections of a conic and the polar line of a point with respect to that conic and through that point are tangent to the conic.
1657      * A point on a conic has the polar line of that point with respect to that conic as the tangent line to that conic at that point.
1658      * See {@link http://en.wikipedia.org/wiki/Pole_and_polar} for more information on pole and polar.
1659      * @name PolarLine
1660      * @augments JXG.Line
1661      * @constructor
1662      * @type JXG.Line
1663      * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
1664      * @param {JXG.Conic,JXG.Circle_JXG.Point} el1,el2 or
1665      * @param {JXG.Point_JXG.Conic,JXG.Circle} el1,el2 The result will be the polar line of the point with respect to the conic or the circle.
1666      * @example
1667      * // Create the polar line of a point with respect to a conic
1668      * var p1 = board.create('point', [-1, 2]);
1669      * var p2 = board.create('point', [ 1, 4]);
1670      * var p3 = board.create('point', [-1,-2]);
1671      * var p4 = board.create('point', [ 0, 0]);
1672      * var p5 = board.create('point', [ 4,-2]);
1673      * var c1 = board.create('conic',[p1,p2,p3,p4,p5]);
1674      * var p6 = board.create('point', [-1, 1]);
1675      * var l1 = board.create('polarline', [c1, p6]);
1676      * </pre><div class="jxgbox"id='7b7233a0-f363-47dd-9df5-6018d0d17a98' class='jxgbox' style='width:400px; height:400px;'></div>
1677      * <script type='text/javascript'>
1678      * var plex1_board = JXG.JSXGraph.initBoard('7b7233a0-f363-47dd-9df5-6018d0d17a98', {boundingbox: [-3, 5, 5, -3], axis: true, showcopyright: false, shownavigation: false});
1679      * var plex1_p1 = plex1_board.create('point', [-1, 2]);
1680      * var plex1_p2 = plex1_board.create('point', [ 1, 4]);
1681      * var plex1_p3 = plex1_board.create('point', [-1,-2]);
1682      * var plex1_p4 = plex1_board.create('point', [ 0, 0]);
1683      * var plex1_p5 = plex1_board.create('point', [ 4,-2]);
1684      * var plex1_c1 = plex1_board.create('conic',[plex1_p1,plex1_p2,plex1_p3,plex1_p4,plex1_p5]);
1685      * var plex1_p6 = plex1_board.create('point', [-1, 1]);
1686      * var plex1_l1 = plex1_board.create('polarline', [plex1_c1, plex1_p6]);
1687      * </script><pre>
1688      * @example
1689      * // Create the polar line of a point with respect to a circle.
1690      * var p1 = board.create('point', [ 1, 1]);
1691      * var p2 = board.create('point', [ 2, 3]);
1692      * var c1 = board.create('circle',[p1,p2]);
1693      * var p3 = board.create('point', [ 6, 6]);
1694      * var l1 = board.create('polarline', [c1, p3]);
1695      * </pre><div class="jxgbox"id='7b7233a0-f363-47dd-9df5-7018d0d17a98' class='jxgbox' style='width:400px; height:400px;'></div>
1696      * <script type='text/javascript'>
1697      * var plex2_board = JXG.JSXGraph.initBoard('7b7233a0-f363-47dd-9df5-7018d0d17a98', {boundingbox: [-3, 7, 7, -3], axis: true, showcopyright: false, shownavigation: false});
1698      * var plex2_p1 = plex2_board.create('point', [ 1, 1]);
1699      * var plex2_p2 = plex2_board.create('point', [ 2, 3]);
1700      * var plex2_c1 = plex2_board.create('circle',[plex2_p1,plex2_p2]);
1701      * var plex2_p3 = plex2_board.create('point', [ 6, 6]);
1702      * var plex2_l1 = plex2_board.create('polarline', [plex2_c1, plex2_p3]);
1703      * </script><pre>
1704      */
1705     JXG.createPolarLine = function (board, parents, attributes) {
1706         var el, el1, el2,
1707             firstParentIsConic, secondParentIsConic,
1708             firstParentIsPoint, secondParentIsPoint;
1709 
1710         if (parents.length > 1) {
1711             firstParentIsConic = (parents[0].type === Const.OBJECT_TYPE_CONIC ||
1712                 parents[0].elementClass === Const.OBJECT_CLASS_CIRCLE);
1713             secondParentIsConic = (parents[1].type === Const.OBJECT_TYPE_CONIC ||
1714                 parents[1].elementClass === Const.OBJECT_CLASS_CIRCLE);
1715 
1716             firstParentIsPoint = (Type.isPoint(parents[0]));
1717             secondParentIsPoint = (Type.isPoint(parents[1]));
1718         }
1719 
1720         if (parents.length !== 2 ||
1721                 !((firstParentIsConic && secondParentIsPoint) ||
1722                     (firstParentIsPoint && secondParentIsConic))) {
1723             // Failure
1724             throw new Error("JSXGraph: Can't create 'polar line' with parent types '" +
1725                 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." +
1726                 "\nPossible parent type: [conic|circle,point], [point,conic|circle]");
1727         }
1728 
1729         if (secondParentIsPoint) {
1730             el1 = board.select(parents[0]);
1731             el2 = board.select(parents[1]);
1732         } else {
1733             el1 = board.select(parents[1]);
1734             el2 = board.select(parents[0]);
1735         }
1736 
1737         // Polar lines have been already provided in the tangent element.
1738         el = board.create('tangent', [el1, el2], attributes);
1739 
1740         el.elType = 'polarline';
1741         return el;
1742     };
1743 
1744     /**
1745      * Register the element type tangent at JSXGraph
1746      * @private
1747      */
1748     JXG.registerElement('tangent', JXG.createTangent);
1749     JXG.registerElement('polar', JXG.createTangent);
1750     JXG.registerElement('radicalaxis', JXG.createRadicalAxis);
1751     JXG.registerElement('polarline', JXG.createPolarLine);
1752 
1753     return {
1754         Line: JXG.Line,
1755         createLine: JXG.createLine,
1756         createTangent: JXG.createTangent,
1757         createPolar: JXG.createTangent,
1758         createSegment: JXG.createSegment,
1759         createAxis: JXG.createAxis,
1760         createArrow: JXG.createArrow,
1761         createRadicalAxis: JXG.createRadicalAxis,
1762         createPolarLine: JXG.createPolarLine
1763     };
1764 });
1765