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