1 /*
  2     Copyright 2008-2013
  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         /**
661          * Apply a translation by <tt>tv = (x, y)</tt> to the line.
662          * @param {Number} method The type of coordinates used here. Possible values are {@link JXG.COORDS_BY_USER} and {@link JXG.COORDS_BY_SCREEN}.
663          * @param {Array} tv (x, y)
664          * @returns {JXG.Line} Reference to this line object.
665          */
666         setPosition: function (method, tv) {
667             var t;
668 
669             tv = new Coords(method, tv, this.board);
670             t = this.board.create('transform', tv.usrCoords.slice(1), {type: 'translate'});
671 
672             if (this.point1.transformations.length > 0 && this.point1.transformations[this.point1.transformations.length - 1].isNumericMatrix) {
673                 this.point1.transformations[this.point1.transformations.length - 1].melt(t);
674             } else {
675                 this.point1.addTransform(this.point1, t);
676             }
677             if (this.point2.transformations.length > 0 && this.point2.transformations[this.point2.transformations.length - 1].isNumericMatrix) {
678                 this.point2.transformations[this.point2.transformations.length - 1].melt(t);
679             } else {
680                 this.point2.addTransform(this.point2, t);
681             }
682 
683             return this;
684         },
685 
686         /**
687          * Moves the line by the difference of two coordinates.
688          * @param {Number} method The type of coordinates used here. Possible values are {@link JXG.COORDS_BY_USER} and {@link JXG.COORDS_BY_SCREEN}.
689          * @param {Array} coords coordinates in screen/user units
690          * @param {Array} oldcoords previous coordinates in screen/user units
691          * @returns {JXG.Line} this element
692          */
693         setPositionDirectly: function (method, coords, oldcoords) {
694             var dc, t,
695                 c = new Coords(method, coords, this.board),
696                 oldc = new Coords(method, oldcoords, this.board);
697 
698             if (!this.point1.draggable() || !this.point2.draggable()) {
699                 return this;
700             }
701 
702             dc = Statistics.subtract(c.usrCoords, oldc.usrCoords);
703             t = this.board.create('transform', dc.slice(1), {type: 'translate'});
704             t.applyOnce([this.point1, this.point2]);
705 
706             return this;
707         },
708 
709         // see GeometryElement.js
710         snapToGrid: function (pos) {
711             var c1, c2, dc, t, v, ticks,
712                 x, y, sX, sY;
713 
714             if (this.visProp.snaptogrid) {
715                 if (this.parents.length < 3) {    // Line through two points
716                     this.point1.handleSnapToGrid(true);
717                     this.point2.handleSnapToGrid(true);
718                 /*
719                 if (this.point1.visProp.snaptogrid || this.point2.visProp.snaptogrid) {
720                     this.point1.snapToGrid();
721                     this.point2.snapToGrid();
722                 */
723                 } else if (JXG.exists(pos)) {       // Free line
724                     sX = this.visProp.snapsizex;
725                     sY = this.visProp.snapsizey;
726 
727                     c1 = new Coords(Const.COORDS_BY_SCREEN, [pos.Xprev, pos.Yprev], this.board);
728 
729                     x = c1.usrCoords[1];
730                     y = c1.usrCoords[2];
731 
732                     if (sX <= 0 && this.board.defaultAxes && this.board.defaultAxes.x.defaultTicks) {
733                         ticks = this.board.defaultAxes.x.defaultTicks;
734                         sX = ticks.ticksDelta * (ticks.visProp.minorticks + 1);
735                     }
736                     if (sY <= 0 && this.board.defaultAxes && this.board.defaultAxes.y.defaultTicks) {
737                         ticks = this.board.defaultAxes.y.defaultTicks;
738                         sY = ticks.ticksDelta * (ticks.visProp.minorticks + 1);
739                     }
740 
741                     // if no valid snap sizes are available, don't change the coords.
742                     if (sX > 0 && sY > 0) {
743                         // projectCoordsToLine
744                         v = [0, this.stdform[1], this.stdform[2]];
745                         v = Mat.crossProduct(v, c1.usrCoords);
746                         c2 = Geometry.meetLineLine(v, this.stdform, 0, this.board);
747 
748                         dc = Statistics.subtract([1, Math.round(x / sX) * sX, Math.round(y / sY) * sY], c2.usrCoords);
749                         t = this.board.create('transform', dc.slice(1), {type: 'translate'});
750                         t.applyOnce([this.point1, this.point2]);
751                     }
752                 }
753             } else {
754                 this.point1.snapToGrid();
755                 this.point2.snapToGrid();
756             }
757 
758             return this;
759         },
760 
761         // see element.js
762         snapToPoints: function () {
763             var forceIt = this.visProp.snaptopoints;
764 
765             if (this.parents.length < 3) {    // Line through two points
766                 this.point1.handleSnapToPoints(forceIt);
767                 this.point2.handleSnapToPoints(forceIt);
768             }
769 
770             return this;
771         },
772 
773         /**
774          * Treat the line as parametric curve in homogeneous coordinates, where the parameter t runs from 0 to 1.
775          * First we transform the interval [0,1] to [-1,1].
776          * If the line has homogeneous coordinates [c,a,b] = stdform[] then the direction of the line is [b,-a].
777          * 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).
778          * Let the coordinates of that point be [z, x, y].
779          * Then, the curve runs linearly from
780          * [0, b, -a] (t=-1) to [z, x, y] (t=0)
781          * and
782          * [z, x, y] (t=0) to [0, -b, a] (t=1)
783          *
784          * @param {Number} t Parameter running from 0 to 1.
785          * @returns {Number} X(t) x-coordinate of the line treated as parametric curve.
786          * */
787         X: function (t) {
788             var x,
789                 b = this.stdform[2];
790 
791             x = (Math.abs(this.point1.coords.usrCoords[0]) > Mat.eps) ?
792                     this.point1.coords.usrCoords[1] :
793                     this.point2.coords.usrCoords[1];
794 
795             t = (t - 0.5) * 2;
796 
797             return (1 - Math.abs(t)) * x - t * b;
798         },
799 
800         /**
801          * Treat the line as parametric curve in homogeneous coordinates. See {@link #X} for a detailed description.
802          * @param {Number} t Parameter running from 0 to 1.
803          * @returns {Number} Y(t) y-coordinate of the line treated as parametric curve.
804          */
805         Y: function (t) {
806             var y,
807                 a = this.stdform[1];
808 
809             y = (Math.abs(this.point1.coords.usrCoords[0]) > Mat.eps) ?
810                     this.point1.coords.usrCoords[2] :
811                     this.point2.coords.usrCoords[2];
812 
813             t = (t - 0.5) * 2;
814 
815             return (1 - Math.abs(t)) * y + t * a;
816         },
817 
818         /**
819          * Treat the line as parametric curve in homogeneous coordinates. See {@link #X} for a detailed description.
820          * @param {Number} t Parameter running from 0 to 1.
821          * @returns {Number} Z(t) z-coordinate of the line treated as parametric curve.
822          */
823         Z: function (t) {
824             var z = (Math.abs(this.point1.coords.usrCoords[0]) > Mat.eps) ?
825                     this.point1.coords.usrCoords[0] :
826                     this.point2.coords.usrCoords[0];
827 
828             t = (t - 0.5) * 2;
829 
830             return (1 - Math.abs(t)) * z;
831         },
832 
833 
834         /**
835          * The distance between the two points defining the line.
836          * @returns {Number}
837          */
838         L: function () {
839             return this.point1.Dist(this.point2);
840         },
841 
842         /**
843          * Treat the element  as a parametric curve
844          * @private
845          */
846         minX: function () {
847             return 0.0;
848         },
849 
850         /**
851          * Treat the element as parametric curve
852          * @private
853          */
854         maxX: function () {
855             return 1.0;
856         },
857 
858         // documented in geometry element
859         bounds: function () {
860             var p1c = this.point1.coords.usrCoords,
861                 p2c = this.point2.coords.usrCoords;
862 
863             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])];
864         },
865 
866         /**
867          * Adds ticks to this line. Ticks can be added to any kind of line: line, arrow, and axis.
868          * @param {JXG.Ticks} ticks Reference to a ticks object which is describing the ticks (color, distance, how many, etc.).
869          * @returns {String} Id of the ticks object.
870          */
871         addTicks: function (ticks) {
872             if (ticks.id === '' || !Type.exists(ticks.id)) {
873                 ticks.id = this.id + '_ticks_' + (this.ticks.length + 1);
874             }
875 
876             this.board.renderer.drawTicks(ticks);
877             this.ticks.push(ticks);
878 
879             return ticks.id;
880         },
881 
882         // documented in GeometryElement.js
883         remove: function () {
884             this.removeAllTicks();
885             GeometryElement.prototype.remove.call(this);
886         },
887 
888         /**
889          * Removes all ticks from a line.
890          */
891         removeAllTicks: function () {
892             var i, t;
893 
894             for (t = this.ticks.length; t > 0; t--) {
895                 this.removeTicks(this.ticks[t - 1]);
896             }
897 
898             this.ticks = [];
899             this.board.update();
900         },
901 
902         /**
903          * Removes ticks identified by parameter named tick from this line.
904          * @param {JXG.Ticks} tick Reference to tick object to remove.
905          */
906         removeTicks: function (tick) {
907             var t, j;
908 
909             if (Type.exists(this.defaultTicks) && this.defaultTicks === tick) {
910                 this.defaultTicks = null;
911             }
912 
913             for (t = this.ticks.length; t > 0; t--) {
914                 if (this.ticks[t - 1] === tick) {
915                     this.board.removeObject(this.ticks[t - 1]);
916 
917                     if (this.ticks[t - 1].ticks) {
918                         for (j = 0; j < this.ticks[t - 1].ticks.length; j++) {
919                             if (Type.exists(this.ticks[t - 1].labels[j])) {
920                                 this.board.removeObject(this.ticks[t - 1].labels[j]);
921                             }
922                         }
923                     }
924 
925                     delete this.ticks[t - 1];
926                     break;
927                 }
928             }
929         },
930 
931         hideElement: function () {
932             var i;
933 
934             GeometryElement.prototype.hideElement.call(this);
935 
936             for (i = 0; i < this.ticks.length; i++) {
937                 this.ticks[i].hideElement();
938             }
939         },
940 
941         showElement: function () {
942             var i;
943 
944             GeometryElement.prototype.showElement.call(this);
945 
946             for (i = 0; i < this.ticks.length; i++) {
947                 this.ticks[i].showElement();
948             }
949         }
950     });
951 
952     /**
953      * @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
954      * a line can be used as an arrow and/or axis.
955      * @pseudo
956      * @description
957      * @name Line
958      * @augments JXG.Line
959      * @constructor
960      * @type JXG.Line
961      * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
962      * @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
963      * numbers describing the coordinates of a point. In the latter case the point will be constructed automatically as a fixed invisible point.
964      * It is possible to provide a function returning an array or a point, instead of providing an array or a point.
965      * @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
966      * 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.
967      * @param {function} f This function must return an array containing three numbers forming the line's homogeneous coordinates.
968      * @example
969      * // Create a line using point and coordinates/
970      * // The second point will be fixed and invisible.
971      * var p1 = board.create('point', [4.5, 2.0]);
972      * var l1 = board.create('line', [p1, [1.0, 1.0]]);
973      * </pre><div id="c0ae3461-10c4-4d39-b9be-81d74759d122" style="width: 300px; height: 300px;"></div>
974      * <script type="text/javascript">
975      *   var glex1_board = JXG.JSXGraph.initBoard('c0ae3461-10c4-4d39-b9be-81d74759d122', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false});
976      *   var glex1_p1 = glex1_board.create('point', [4.5, 2.0]);
977      *   var glex1_l1 = glex1_board.create('line', [glex1_p1, [1.0, 1.0]]);
978      * </script><pre>
979      * @example
980      * // Create a line using three coordinates
981      * var l1 = board.create('line', [1.0, -2.0, 3.0]);
982      * </pre><div id="cf45e462-f964-4ba4-be3a-c9db94e2593f" style="width: 300px; height: 300px;"></div>
983      * <script type="text/javascript">
984      *   var glex2_board = JXG.JSXGraph.initBoard('cf45e462-f964-4ba4-be3a-c9db94e2593f', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false});
985      *   var glex2_l1 = glex2_board.create('line', [1.0, -2.0, 3.0]);
986      * </script><pre>
987      */
988     JXG.createLine = function (board, parents, attributes) {
989         var ps, el, p1, p2, i, attr,
990             c = [],
991             constrained = false,
992             isDraggable;
993 
994         /**
995          * The line is defined by two points or coordinates of two points.
996          * In the latter case, the points are created.
997          */
998         if (parents.length === 2) {
999             // point 1 given by coordinates
1000             if (Type.isArray(parents[0]) && parents[0].length > 1) {
1001                 attr = Type.copyAttributes(attributes, board.options, 'line', 'point1');
1002                 p1 = board.create('point', parents[0], attr);
1003             } else if (Type.isString(parents[0]) || parents[0].elementClass === Const.OBJECT_CLASS_POINT) {
1004                 p1 =  board.select(parents[0]);
1005             } else if ((typeof parents[0] === 'function') && (parents[0]().elementClass === Const.OBJECT_CLASS_POINT)) {
1006                 p1 = parents[0]();
1007                 constrained = true;
1008             } else if ((typeof parents[0] === 'function') && (parents[0]().length && parents[0]().length === 2)) {
1009                 attr = Type.copyAttributes(attributes, board.options, 'line', 'point1');
1010                 p1 = Point.createPoint(board, parents[0](), attr);
1011                 constrained = true;
1012             } else {
1013                 throw new Error("JSXGraph: Can't create line with parent types '" +
1014                     (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." +
1015                     "\nPossible parent types: [point,point], [[x1,y1],[x2,y2]], [a,b,c]");
1016             }
1017 
1018             // point 2 given by coordinates
1019             if (Type.isArray(parents[1]) && parents[1].length > 1) {
1020                 attr = Type.copyAttributes(attributes, board.options, 'line', 'point2');
1021                 p2 = board.create('point', parents[1], attr);
1022             } else if (Type.isString(parents[1]) || parents[1].elementClass === Const.OBJECT_CLASS_POINT) {
1023                 p2 =  board.select(parents[1]);
1024             } else if ((typeof parents[1] === 'function') && (parents[1]().elementClass === Const.OBJECT_CLASS_POINT)) {
1025                 p2 = parents[1]();
1026                 constrained = true;
1027             } else if ((typeof parents[1] === 'function') && (parents[1]().length && parents[1]().length === 2)) {
1028                 attr = Type.copyAttributes(attributes, board.options, 'line', 'point2');
1029                 p2 = Point.createPoint(board, parents[1](), attr);
1030                 constrained = true;
1031             } else {
1032                 throw new Error("JSXGraph: Can't create line with parent types '" +
1033                     (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." +
1034                     "\nPossible parent types: [point,point], [[x1,y1],[x2,y2]], [a,b,c]");
1035             }
1036 
1037             attr = Type.copyAttributes(attributes, board.options, 'line');
1038 
1039             el = new JXG.Line(board, p1, p2, attr);
1040             if (constrained) {
1041                 el.constrained = true;
1042                 el.funp1 = parents[0];
1043                 el.funp2 = parents[1];
1044             } else {
1045                 el.isDraggable = true;
1046             }
1047 
1048             if (!el.constrained) {
1049                 el.parents = [p1.id, p2.id];
1050             }
1051          // Line is defined by three homogeneous coordinates.
1052          // Also in this case points are created.
1053         } else if (parents.length === 3) {
1054             // free line
1055             isDraggable = true;
1056             for (i = 0; i < 3; i++) {
1057                 if (typeof parents[i] === 'number') {
1058                     // createFunction will just wrap a function around our constant number
1059                     // that does nothing else but to return that number.
1060                     c[i] = Type.createFunction(parents[i]);
1061                 } else if (typeof parents[i] === 'function') {
1062                     c[i] = parents[i];
1063                     isDraggable = false;
1064                 } else {
1065                     throw new Error("JSXGraph: Can't create line with parent types '" +
1066                         (typeof parents[0]) + "' and '" + (typeof parents[1]) + "' and '" + (typeof parents[2]) + "'." +
1067                         "\nPossible parent types: [point,point], [[x1,y1],[x2,y2]], [a,b,c]");
1068                 }
1069             }
1070 
1071             // point 1 is the midpoint between (0,c,-b) and point 2. => point1 is finite.
1072             attr = Type.copyAttributes(attributes, board.options, 'line', 'point1');
1073             if (isDraggable) {
1074                 p1 = board.create('point', [
1075                     c[2]() * c[2]() + c[1]() * c[1](),
1076                     c[2]() - c[1]() * c[0]() + c[2](),
1077                     -c[1]() - c[2]() * c[0]() - c[1]()
1078                 ], attr);
1079             } else {
1080                 p1 = board.create('point', [
1081                     function () {
1082                         return (c[2]() * c[2]() + c[1]() * c[1]()) * 0.5;
1083                     },
1084                     function () {
1085                         return (c[2]() - c[1]() * c[0]() + c[2]()) * 0.5;
1086                     },
1087                     function () {
1088                         return (-c[1]() - c[2]() * c[0]() - c[1]()) * 0.5;
1089                     }], attr);
1090             }
1091 
1092             // point 2: (b^2+c^2,-ba+c,-ca-b)
1093             attr = Type.copyAttributes(attributes, board.options, 'line', 'point2');
1094             if (isDraggable) {
1095                 p2 = board.create('point', [
1096                     c[2]() * c[2]() + c[1]() * c[1](),
1097                     -c[1]() * c[0]() + c[2](),
1098                     -c[2]() * c[0]() - c[1]()
1099                 ], attr);
1100             } else {
1101                 p2 = board.create('point', [
1102                     function () {
1103                         return c[2]() * c[2]() + c[1]() * c[1]();
1104                     },
1105                     function () {
1106                         return -c[1]() * c[0]() + c[2]();
1107                     },
1108                     function () {
1109                         return -c[2]() * c[0]() - c[1]();
1110                     }], attr);
1111             }
1112 
1113             // If the line will have a glider and board.suspendUpdate() has been called, we
1114             // need to compute the initial position of the two points p1 and p2.
1115             p1.prepareUpdate().update();
1116             p2.prepareUpdate().update();
1117             attr = Type.copyAttributes(attributes, board.options, 'line');
1118             el = new JXG.Line(board, p1, p2, attr);
1119             // Not yet working, because the points are not draggable.
1120             el.isDraggable = isDraggable;
1121 
1122             if (isDraggable) {
1123                 el.parents = [c[0](), c[1](), c[2]()];
1124             }
1125         // The parent array contains a function which returns two points.
1126         } else if ((parents.length === 1) && (typeof parents[0] === 'function') && (parents[0]().length === 2) &&
1127                 (parents[0]()[0].elementClass === Const.OBJECT_CLASS_POINT) &&
1128                 (parents[0]()[1].elementClass === Const.OBJECT_CLASS_POINT)) {
1129             ps = parents[0]();
1130             attr = Type.copyAttributes(attributes, board.options, 'line');
1131             el = new JXG.Line(board, ps[0], ps[1], attr);
1132             el.constrained = true;
1133             el.funps = parents[0];
1134         } else if ((parents.length === 1) && (typeof parents[0] === 'function') && (parents[0]().length === 3) &&
1135                 (typeof parents[0]()[0] === 'number') &&
1136                 (typeof parents[0]()[1] === 'number') &&
1137                 (typeof parents[0]()[2] === 'number')) {
1138             ps = parents[0];
1139 
1140             attr = Type.copyAttributes(attributes, board.options, 'line', 'point1');
1141             p1 = board.create('point', [
1142                 function () {
1143                     var c = ps();
1144 
1145                     return [
1146                         (c[2] * c[2] + c[1] * c[1]) * 0.5,
1147                         (c[2] - c[1] * c[0] + c[2]) * 0.5,
1148                         (-c[1] - c[2] * c[0] - c[1]) * 0.5
1149                     ];
1150                 }], attr);
1151 
1152             attr = Type.copyAttributes(attributes, board.options, 'line', 'point2');
1153             p2 = board.create('point', [
1154                 function () {
1155                     var c = ps();
1156 
1157                     return [
1158                         c[2] * c[2] + c[1] * c[1],
1159                         -c[1] * c[0] + c[2],
1160                         -c[2] * c[0] - c[1]
1161                     ];
1162                 }], attr);
1163 
1164             attr = Type.copyAttributes(attributes, board.options, 'line');
1165             el = new JXG.Line(board, p1, p2, attr);
1166 
1167             el.constrained = true;
1168             el.funps = parents[0];
1169         } else {
1170             throw new Error("JSXGraph: Can't create line with parent types '" +
1171                 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." +
1172                 "\nPossible parent types: [point,point], [[x1,y1],[x2,y2]], [a,b,c]");
1173         }
1174 
1175         return el;
1176     };
1177 
1178     JXG.registerElement('line', JXG.createLine);
1179 
1180     /**
1181      * @class This element is used to provide a constructor for a segment.
1182      * It's strictly spoken just a wrapper for element {@link Line} with {@link JXG.Line#straightFirst}
1183      * and {@link JXG.Line#straightLast} properties set to false. If there is a third variable then the
1184      * segment has a fixed length (which may be a function, too).
1185      * @pseudo
1186      * @description
1187      * @name Segment
1188      * @augments JXG.Line
1189      * @constructor
1190      * @type JXG.Line
1191      * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
1192      * @param {JXG.Point,array_JXG.Point,array} point1, point2 Parent elements can be two elements either of type {@link JXG.Point}
1193      * or array of numbers describing the
1194      * coordinates of a point. In the latter case the point will be constructed automatically as a fixed invisible point.
1195      * @param {number,function} length (optional) The points are adapted - if possible - such that their distance
1196      * has a this value.
1197      * @see Line
1198      * @example
1199      * // Create a segment providing two points.
1200      *   var p1 = board.create('point', [4.5, 2.0]);
1201      *   var p2 = board.create('point', [1.0, 1.0]);
1202      *   var l1 = board.create('segment', [p1, p2]);
1203      * </pre><div id="d70e6aac-7c93-4525-a94c-a1820fa38e2f" style="width: 300px; height: 300px;"></div>
1204      * <script type="text/javascript">
1205      *   var slex1_board = JXG.JSXGraph.initBoard('d70e6aac-7c93-4525-a94c-a1820fa38e2f', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false});
1206      *   var slex1_p1 = slex1_board.create('point', [4.5, 2.0]);
1207      *   var slex1_p2 = slex1_board.create('point', [1.0, 1.0]);
1208      *   var slex1_l1 = slex1_board.create('segment', [slex1_p1, slex1_p2]);
1209      * </script><pre>
1210      *
1211      * @example
1212      * // Create a segment providing two points.
1213      *   var p1 = board.create('point', [4.0, 1.0]);
1214      *   var p2 = board.create('point', [1.0, 1.0]);
1215      *   var l1 = board.create('segment', [p1, p2]);
1216      *   var p3 = board.create('point', [4.0, 2.0]);
1217      *   var p4 = board.create('point', [1.0, 2.0]);
1218      *   var l2 = board.create('segment', [p3, p4, 3]);
1219      *   var p5 = board.create('point', [4.0, 3.0]);
1220      *   var p6 = board.create('point', [1.0, 4.0]);
1221      *   var l3 = board.create('segment', [p5, p6, function(){ return l1.L();} ]);
1222      * </pre><div id="617336ba-0705-4b2b-a236-c87c28ef25be" style="width: 300px; height: 300px;"></div>
1223      * <script type="text/javascript">
1224      *   var slex2_board = JXG.JSXGraph.initBoard('617336ba-0705-4b2b-a236-c87c28ef25be', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false});
1225      *   var slex2_p1 = slex1_board.create('point', [4.0, 1.0]);
1226      *   var slex2_p2 = slex1_board.create('point', [1.0, 1.0]);
1227      *   var slex2_l1 = slex1_board.create('segment', [slex1_p1, slex1_p2]);
1228      *   var slex2_p3 = slex1_board.create('point', [4.0, 2.0]);
1229      *   var slex2_p4 = slex1_board.create('point', [1.0, 2.0]);
1230      *   var slex2_l2 = slex1_board.create('segment', [slex1_p3, slex1_p4, 3]);
1231      *   var slex2_p5 = slex1_board.create('point', [4.0, 2.0]);
1232      *   var slex2_p6 = slex1_board.create('point', [1.0, 2.0]);
1233      *   var slex2_l3 = slex1_board.create('segment', [slex1_p5, slex1_p6, function(){ return slex2_l1.L();}]);
1234      * </script><pre>
1235      *
1236      */
1237     JXG.createSegment = function (board, parents, attributes) {
1238         var el, i, attr;
1239 
1240         attributes.straightFirst = false;
1241         attributes.straightLast = false;
1242         attr = Type.copyAttributes(attributes, board.options, 'segment');
1243 
1244         el = board.create('line', parents.slice(0, 2), attr);
1245 
1246         if (parents.length === 3) {
1247             el.hasFixedLength = true;
1248 
1249             if (Type.isNumber(parents[2])) {
1250                 el.fixedLength = function () {
1251                     return parents[2];
1252                 };
1253             } else if (Type.isFunction(parents[2])) {
1254                 el.fixedLength = parents[2];
1255             } else {
1256                 throw new Error("JSXGraph: Can't create segment with third parent type '" +
1257                     (typeof parents[2]) + "'." +
1258                     "\nPossible third parent types: number or function");
1259             }
1260 
1261             el.fixedLengthOldCoords = [];
1262             el.fixedLengthOldCoords[0] = new Coords(Const.COORDS_BY_USER, el.point1.coords.usrCoords.slice(1, 3), board);
1263             el.fixedLengthOldCoords[1] = new Coords(Const.COORDS_BY_USER, el.point2.coords.usrCoords.slice(1, 3), board);
1264         }
1265 
1266         el.elType = 'segment';
1267 
1268         return el;
1269     };
1270 
1271     JXG.registerElement('segment', JXG.createSegment);
1272 
1273     /**
1274      * @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}
1275      * and {@link JXG.Line#straightLast} properties set to false and {@link JXG.Line#lastArrow} set to true.
1276      * @pseudo
1277      * @description
1278      * @name Arrow
1279      * @augments JXG.Line
1280      * @constructor
1281      * @type JXG.Line
1282      * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
1283      * @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
1284      * coordinates of a point. In the latter case the point will be constructed automatically as a fixed invisible point.
1285      * @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
1286      * of the equation <tt>a*x+b*y+c*z = 0</tt>.
1287      * @see Line
1288      * @example
1289      * // Create an arrow providing two points.
1290      *   var p1 = board.create('point', [4.5, 2.0]);
1291      *   var p2 = board.create('point', [1.0, 1.0]);
1292      *   var l1 = board.create('arrow', [p1, p2]);
1293      * </pre><div id="1d26bd22-7d6d-4018-b164-4c8bc8d22ccf" style="width: 300px; height: 300px;"></div>
1294      * <script type="text/javascript">
1295      *   var alex1_board = JXG.JSXGraph.initBoard('1d26bd22-7d6d-4018-b164-4c8bc8d22ccf', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false});
1296      *   var alex1_p1 = alex1_board.create('point', [4.5, 2.0]);
1297      *   var alex1_p2 = alex1_board.create('point', [1.0, 1.0]);
1298      *   var alex1_l1 = alex1_board.create('arrow', [alex1_p1, alex1_p2]);
1299      * </script><pre>
1300      */
1301     JXG.createArrow = function (board, parents, attributes) {
1302         var el;
1303 
1304         attributes.firstArrow = false;
1305         attributes.lastArrow = true;
1306         el = board.create('line', parents, attributes).setStraight(false, false);
1307         //el.setArrow(false, true);
1308         el.type = Const.OBJECT_TYPE_VECTOR;
1309         el.elType = 'arrow';
1310 
1311         return el;
1312     };
1313 
1314     JXG.registerElement('arrow', JXG.createArrow);
1315 
1316     /**
1317      * @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}
1318      * 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.
1319      * @pseudo
1320      * @description
1321      * @name Axis
1322      * @augments JXG.Line
1323      * @constructor
1324      * @type JXG.Line
1325      * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
1326      * @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
1327      * coordinates of a point. In the latter case the point will be constructed automatically as a fixed invisible point.
1328      * @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
1329      * of the equation <tt>a*x+b*y+c*z = 0</tt>.
1330      * @example
1331      * // Create an axis providing two coord pairs.
1332      *   var l1 = board.create('axis', [[0.0, 1.0], [1.0, 1.3]]);
1333      * </pre><div id="4f414733-624c-42e4-855c-11f5530383ae" style="width: 300px; height: 300px;"></div>
1334      * <script type="text/javascript">
1335      *   var axex1_board = JXG.JSXGraph.initBoard('4f414733-624c-42e4-855c-11f5530383ae', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false});
1336      *   var axex1_l1 = axex1_board.create('axis', [[0.0, 1.0], [1.0, 1.3]]);
1337      * </script><pre>
1338      */
1339     JXG.createAxis = function (board, parents, attributes) {
1340         var attr, el, els, dist;
1341 
1342         // Arrays oder Punkte, mehr brauchen wir nicht.
1343         if ((Type.isArray(parents[0]) || Type.isPoint(parents[0])) && (Type.isArray(parents[1]) || Type.isPoint(parents[1]))) {
1344             attr = Type.copyAttributes(attributes, board.options, 'axis');
1345             el = board.create('line', parents, attr);
1346             el.type = Const.OBJECT_TYPE_AXIS;
1347             el.isDraggable = false;
1348             el.point1.isDraggable = false;
1349             el.point2.isDraggable = false;
1350 
1351             for (els in el.ancestors) {
1352                 if (el.ancestors.hasOwnProperty(els)) {
1353                     el.ancestors[els].type = Const.OBJECT_TYPE_AXISPOINT;
1354                 }
1355             }
1356 
1357             attr = Type.copyAttributes(attributes, board.options, 'axis', 'ticks');
1358             if (Type.exists(attr.ticksdistance)) {
1359                 dist = attr.ticksdistance;
1360             } else if (Type.isArray(attr.ticks)) {
1361                 dist = attr.ticks;
1362             } else {
1363                 dist = 1.0;
1364             }
1365 
1366             /**
1367              * The ticks attached to the axis.
1368              * @memberOf Axis.prototype
1369              * @name defaultTicks
1370              * @type JXG.Ticks
1371              */
1372             el.defaultTicks = board.create('ticks', [el, dist], attr);
1373 
1374             el.defaultTicks.dump = false;
1375 
1376             el.elType = 'axis';
1377             el.subs = {
1378                 ticks: el.defaultTicks
1379             };
1380         } else {
1381             throw new Error("JSXGraph: Can't create axis with parent types '" +
1382                 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." +
1383                 "\nPossible parent types: [point,point], [[x1,y1],[x2,y2]]");
1384         }
1385 
1386         return el;
1387     };
1388 
1389     JXG.registerElement('axis', JXG.createAxis);
1390 
1391     /**
1392      * @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
1393      * by a glider on a line, circle, or curve and describes the tangent in the glider point on that line, circle, or curve.
1394      * @pseudo
1395      * @description
1396      * @name Tangent
1397      * @augments JXG.Line
1398      * @constructor
1399      * @type JXG.Line
1400      * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
1401      * @param {Glider} g A glider on a line, circle, or curve.
1402      * @example
1403      * // Create a tangent providing a glider on a function graph
1404      *   var c1 = board.create('curve', [function(t){return t},function(t){return t*t*t;}]);
1405      *   var g1 = board.create('glider', [0.6, 1.2, c1]);
1406      *   var t1 = board.create('tangent', [g1]);
1407      * </pre><div id="7b7233a0-f363-47dd-9df5-4018d0d17a98" style="width: 400px; height: 400px;"></div>
1408      * <script type="text/javascript">
1409      *   var tlex1_board = JXG.JSXGraph.initBoard('7b7233a0-f363-47dd-9df5-4018d0d17a98', {boundingbox: [-6, 6, 6, -6], axis: true, showcopyright: false, shownavigation: false});
1410      *   var tlex1_c1 = tlex1_board.create('curve', [function(t){return t},function(t){return t*t*t;}]);
1411      *   var tlex1_g1 = tlex1_board.create('glider', [0.6, 1.2, tlex1_c1]);
1412      *   var tlex1_t1 = tlex1_board.create('tangent', [tlex1_g1]);
1413      * </script><pre>
1414      */
1415     JXG.createTangent = function (board, parents, attributes) {
1416         var p, c, g, f, i, j, el, tangent;
1417 
1418         // One arguments: glider on line, circle or curve
1419         if (parents.length === 1) {
1420             p = parents[0];
1421             c = p.slideObject;
1422         // Two arguments: (point,F"|conic) or (line|curve|circle|conic,point). // Not yet: curve!
1423         } else if (parents.length === 2) {
1424             // In fact, for circles and conics it is the polar
1425             if (Type.isPoint(parents[0])) {
1426                 p = parents[0];
1427                 c = parents[1];
1428             } else if (Type.isPoint(parents[1])) {
1429                 c = parents[0];
1430                 p = parents[1];
1431             } else {
1432                 throw new Error("JSXGraph: Can't create tangent with parent types '" +
1433                     (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." +
1434                     "\nPossible parent types: [glider], [point,line|curve|circle|conic]");
1435             }
1436         } else {
1437             throw new Error("JSXGraph: Can't create tangent with parent types '" +
1438                 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." +
1439                 "\nPossible parent types: [glider], [point,line|curve|circle|conic]");
1440         }
1441 
1442         if (c.elementClass === Const.OBJECT_CLASS_LINE) {
1443             tangent = board.create('line', [c.point1, c.point2], attributes);
1444             tangent.glider = p;
1445         } else if (c.elementClass === Const.OBJECT_CLASS_CURVE && c.type !== Const.OBJECT_TYPE_CONIC) {
1446             if (c.visProp.curvetype !== 'plot') {
1447                 g = c.X;
1448                 f = c.Y;
1449                 tangent = board.create('line', [
1450                     function () {
1451                         return -p.X() * Numerics.D(f)(p.position) + p.Y() * Numerics.D(g)(p.position);
1452                     },
1453                     function () {
1454                         return Numerics.D(f)(p.position);
1455                     },
1456                     function () {
1457                         return -Numerics.D(g)(p.position);
1458                     }
1459                 ], attributes);
1460                 p.addChild(tangent);
1461 
1462                 // this is required for the geogebra reader to display a slope
1463                 tangent.glider = p;
1464             } else {  // curveType 'plot'
1465                 // equation of the line segment: 0 = y*(x1-x2) + x*(y2-y1) + y1*x2-x1*y2
1466                 tangent = board.create('line', [
1467                     function () {
1468                         var i = Math.floor(p.position);
1469 
1470                         if (i === c.numberPoints - 1) {
1471                             i--;
1472                         }
1473 
1474                         if (i < 0) {
1475                             return 1;
1476                         }
1477 
1478                         return c.Y(i) * c.X(i + 1) - c.X(i) * c.Y(i + 1);
1479                     },
1480                     function () {
1481                         var i = Math.floor(p.position);
1482 
1483                         if (i === c.numberPoints - 1) {
1484                             i--;
1485                         }
1486 
1487                         if (i < 0) {
1488                             return 0;
1489                         }
1490 
1491                         return c.Y(i + 1) - c.Y(i);
1492                     },
1493                     function () {
1494                         var i = Math.floor(p.position);
1495 
1496                         if (i === c.numberPoints - 1) {
1497                             i--;
1498                         }
1499 
1500                         if (i < 0) {
1501                             return 0.0;
1502                         }
1503 
1504                         return c.X(i) - c.X(i + 1);
1505                     }], attributes);
1506 
1507                 p.addChild(tangent);
1508 
1509                 // this is required for the geogebra reader to display a slope
1510                 tangent.glider = p;
1511             }
1512         } else if (c.type === Const.OBJECT_TYPE_TURTLE) {
1513             tangent = board.create('line', [
1514                 function () {
1515                     var i = Math.floor(p.position);
1516 
1517                     // run through all curves of this turtle
1518                     for (j = 0; j < c.objects.length; j++) {
1519                         el = c.objects[j];
1520 
1521                         if (el.type === Const.OBJECT_TYPE_CURVE) {
1522                             if (i < el.numberPoints) {
1523                                 break;
1524                             }
1525 
1526                             i -= el.numberPoints;
1527                         }
1528                     }
1529 
1530                     if (i === el.numberPoints - 1) {
1531                         i--;
1532                     }
1533 
1534                     if (i < 0) {
1535                         return 1;
1536                     }
1537 
1538                     return el.Y(i) * el.X(i + 1) - el.X(i) * el.Y(i + 1);
1539                 },
1540                 function () {
1541                     var i = Math.floor(p.position);
1542 
1543                     // run through all curves of this turtle
1544                     for (j = 0; j < c.objects.length; j++) {
1545                         el = c.objects[j];
1546 
1547                         if (el.type === Const.OBJECT_TYPE_CURVE) {
1548                             if (i < el.numberPoints) {
1549                                 break;
1550                             }
1551 
1552                             i -= el.numberPoints;
1553                         }
1554                     }
1555 
1556                     if (i === el.numberPoints - 1) {
1557                         i--;
1558                     }
1559                     if (i < 0) {
1560                         return 0;
1561                     }
1562 
1563                     return el.Y(i + 1) - el.Y(i);
1564                 },
1565                 function () {
1566                     var i = Math.floor(p.position);
1567 
1568                     // run through all curves of this turtle
1569                     for (j = 0; j < c.objects.length; j++) {
1570                         el = c.objects[j];
1571                         if (el.type === Const.OBJECT_TYPE_CURVE) {
1572                             if (i < el.numberPoints) {
1573                                 break;
1574                             }
1575                             i -= el.numberPoints;
1576                         }
1577                     }
1578                     if (i === el.numberPoints - 1) {
1579                         i--;
1580                     }
1581 
1582                     if (i < 0) {
1583                         return 0;
1584                     }
1585 
1586                     return el.X(i) - el.X(i + 1);
1587                 }], attributes);
1588             p.addChild(tangent);
1589 
1590             // this is required for the geogebra reader to display a slope
1591             tangent.glider = p;
1592         } else if (c.elementClass === Const.OBJECT_CLASS_CIRCLE || c.type === Const.OBJECT_TYPE_CONIC) {
1593             // If p is not on c, the tangent is the polar.
1594             // This construction should work on conics, too. p has to lie on c.
1595             tangent = board.create('line', [
1596                 function () {
1597                     return Mat.matVecMult(c.quadraticform, p.coords.usrCoords)[0];
1598                 },
1599                 function () {
1600                     return Mat.matVecMult(c.quadraticform, p.coords.usrCoords)[1];
1601                 },
1602                 function () {
1603                     return Mat.matVecMult(c.quadraticform, p.coords.usrCoords)[2];
1604                 }], attributes);
1605 
1606             p.addChild(tangent);
1607             // this is required for the geogebra reader to display a slope
1608             tangent.glider = p;
1609         }
1610 
1611         if (!Type.exists(tangent)) {
1612             throw new Error('JSXGraph: Couldn\'t create tangent with the given parents.');
1613         }
1614 
1615         tangent.elType = 'tangent';
1616         tangent.type = Const.OBJECT_TYPE_TANGENT;
1617         tangent.parents = [];
1618         for (i = 0; i < parents.length; i++) {
1619             tangent.parents.push(parents[i].id);
1620         }
1621 
1622         return tangent;
1623     };
1624 
1625     /**
1626      * @class This element is used to provide a constructor for the radical axis with respect to two circles with distinct centers.
1627      * The angular bisector of the polar lines of the circle centers with respect to the other circle is always the radical axis.
1628      * The radical axis passes through the intersection points when the circles intersect.
1629      * 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.
1630      * @pseudo
1631      * @description
1632      * @name RadicalAxis
1633      * @augments JXG.Line
1634      * @constructor
1635      * @type JXG.Line
1636      * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
1637      * @param {JXG.Circle} circle Circle one of the two respective circles.
1638      * @param {JXG.Circle} circle Circle the other of the two respective circles.
1639      * @example
1640      * // Create the radical axis line with respect to two circles
1641      *   var board = JXG.JSXGraph.initBoard('7b7233a0-f363-47dd-9df5-5018d0d17a98', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false});
1642      *   var p1 = board.create('point', [2, 3]);
1643      *   var p2 = board.create('point', [1, 4]);
1644      *   var c1 = board.create('circle', [p1, p2]);
1645      *   var p3 = board.create('point', [6, 5]);
1646      *   var p4 = board.create('point', [8, 6]);
1647      *   var c2 = board.create('circle', [p3, p4]);
1648      *   var r1 = board.create('radicalaxis', [c1, c2]);
1649      * </pre><div id='7b7233a0-f363-47dd-9df5-5018d0d17a98' class='jxgbox' style='width:400px; height:400px;'></div>
1650      * <script type='text/javascript'>
1651      *   var rlex1_board = JXG.JSXGraph.initBoard('7b7233a0-f363-47dd-9df5-5018d0d17a98', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false});
1652      *   var rlex1_p1 = rlex1_board.create('point', [2, 3]);
1653      *   var rlex1_p2 = rlex1_board.create('point', [1, 4]);
1654      *   var rlex1_c1 = rlex1_board.create('circle', [rlex1_p1, rlex1_p2]);
1655      *   var rlex1_p3 = rlex1_board.create('point', [6, 5]);
1656      *   var rlex1_p4 = rlex1_board.create('point', [8, 6]);
1657      *   var rlex1_c2 = rlex1_board.create('circle', [rlex1_p3, rlex1_p4]);
1658      *   var rlex1_r1 = rlex1_board.create('radicalaxis', [rlex1_c1, rlex1_c2]);
1659      * </script><pre>
1660      */
1661     JXG.createRadicalAxis = function (board, parents, attributes) {
1662         var el, el1, el2;
1663 
1664         if (parents.length !== 2 ||
1665                 parents[0].elementClass !== Const.OBJECT_CLASS_CIRCLE ||
1666                 parents[1].elementClass !== Const.OBJECT_CLASS_CIRCLE) {
1667             // Failure
1668             throw new Error("JSXGraph: Can't create 'radical axis' with parent types '" +
1669                 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." +
1670                 "\nPossible parent type: [circle,circle]");
1671         }
1672 
1673         el1 = board.select(parents[0]);
1674         el2 = board.select(parents[1]);
1675 
1676         el = board.create('line', [function () {
1677                 var a = el1.stdform,
1678                     b = el2.stdform;
1679                     
1680                 return JXG.Math.matVecMult(
1681                     JXG.Math.transpose([a.slice(0,3), b.slice(0,3)]), [b[3] , -a[3]]);
1682              }], attributes);
1683 
1684         el.elType = 'radicalaxis';
1685         el.parents = [el1.id, el2.id];
1686 
1687         el1.addChild(el);
1688         el2.addChild(el);
1689 
1690         return el;
1691     };
1692 
1693     /**
1694      * @class This element is used to provide a constructor for the polar line of a point with respect to a conic or a circle.
1695      * @pseudo
1696      * @description The polar line is the unique reciprocal relationship of a point with respect to a conic.
1697      * 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.
1698      * 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.
1699      * See {@link http://en.wikipedia.org/wiki/Pole_and_polar} for more information on pole and polar.
1700      * @name PolarLine
1701      * @augments JXG.Line
1702      * @constructor
1703      * @type JXG.Line
1704      * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
1705      * @param {JXG.Conic,JXG.Circle_JXG.Point} el1,el2 or
1706      * @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.
1707      * @example
1708      * // Create the polar line of a point with respect to a conic
1709      * var p1 = board.create('point', [-1, 2]);
1710      * var p2 = board.create('point', [ 1, 4]);
1711      * var p3 = board.create('point', [-1,-2]);
1712      * var p4 = board.create('point', [ 0, 0]);
1713      * var p5 = board.create('point', [ 4,-2]);
1714      * var c1 = board.create('conic',[p1,p2,p3,p4,p5]);
1715      * var p6 = board.create('point', [-1, 1]);
1716      * var l1 = board.create('polarline', [c1, p6]);
1717      * </pre><div id='7b7233a0-f363-47dd-9df5-6018d0d17a98' class='jxgbox' style='width:400px; height:400px;'></div>
1718      * <script type='text/javascript'>
1719      * var plex1_board = JXG.JSXGraph.initBoard('7b7233a0-f363-47dd-9df5-6018d0d17a98', {boundingbox: [-3, 5, 5, -3], axis: true, showcopyright: false, shownavigation: false});
1720      * var plex1_p1 = plex1_board.create('point', [-1, 2]);
1721      * var plex1_p2 = plex1_board.create('point', [ 1, 4]);
1722      * var plex1_p3 = plex1_board.create('point', [-1,-2]);
1723      * var plex1_p4 = plex1_board.create('point', [ 0, 0]);
1724      * var plex1_p5 = plex1_board.create('point', [ 4,-2]);
1725      * var plex1_c1 = plex1_board.create('conic',[plex1_p1,plex1_p2,plex1_p3,plex1_p4,plex1_p5]);
1726      * var plex1_p6 = plex1_board.create('point', [-1, 1]);
1727      * var plex1_l1 = plex1_board.create('polarline', [plex1_c1, plex1_p6]);
1728      * </script><pre>
1729      * @example
1730      * // Create the polar line of a point with respect to a circle.
1731      * var p1 = board.create('point', [ 1, 1]);
1732      * var p2 = board.create('point', [ 2, 3]);
1733      * var c1 = board.create('circle',[p1,p2]);
1734      * var p3 = board.create('point', [ 6, 6]);
1735      * var l1 = board.create('polarline', [c1, p3]);
1736      * </pre><div id='7b7233a0-f363-47dd-9df5-7018d0d17a98' class='jxgbox' style='width:400px; height:400px;'></div>
1737      * <script type='text/javascript'>
1738      * var plex2_board = JXG.JSXGraph.initBoard('7b7233a0-f363-47dd-9df5-7018d0d17a98', {boundingbox: [-3, 7, 7, -3], axis: true, showcopyright: false, shownavigation: false});
1739      * var plex2_p1 = plex2_board.create('point', [ 1, 1]);
1740      * var plex2_p2 = plex2_board.create('point', [ 2, 3]);
1741      * var plex2_c1 = plex2_board.create('circle',[plex2_p1,plex2_p2]);
1742      * var plex2_p3 = plex2_board.create('point', [ 6, 6]);
1743      * var plex2_l1 = plex2_board.create('polarline', [plex2_c1, plex2_p3]);
1744      * </script><pre>
1745      */
1746     JXG.createPolarLine = function (board, parents, attributes) {
1747         var el, el1, el2;
1748 
1749         if (parents.length !== 2 || !((
1750                 parents[0].type === Const.OBJECT_TYPE_CONIC ||
1751                 parents[0].elementClass === Const.OBJECT_CLASS_CIRCLE) &&
1752                 parents[1].elementClass === Const.OBJECT_CLASS_POINT ||
1753                 parents[0].elementClass === Const.OBJECT_CLASS_POINT && (
1754                 parents[1].type === Const.OBJECT_TYPE_CONIC ||
1755                 parents[1].elementClass === Const.OBJECT_CLASS_CIRCLE))) {
1756             // Failure
1757             throw new Error("JSXGraph: Can't create 'polar line' with parent types '" +
1758                 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." +
1759                 "\nPossible parent type: [conic|circle,point], [point,conic|circle]");
1760         }
1761 
1762         if (parents[1].elementClass === Const.OBJECT_CLASS_POINT) {
1763             el1 = board.select(parents[0]);
1764             el2 = board.select(parents[1]);
1765         } else {
1766             el1 = board.select(parents[1]);
1767             el2 = board.select(parents[0]);
1768         }
1769 
1770         //el = board.create('line', [function () {return JXG.Math.matVecMult(el1.quadraticform.slice(0,3),el2.coords.usrCoords.slice(0,3));}]);
1771         // Polar lines have been already provided in the tangent element.
1772         el = board.create('tangent', [el1, el2], attributes);
1773 
1774         el.elType = 'polarline';
1775         return el;
1776     };
1777     
1778     /**
1779      * Register the element type tangent at JSXGraph
1780      * @private
1781      */
1782     JXG.registerElement('tangent', JXG.createTangent);
1783     JXG.registerElement('polar', JXG.createTangent);
1784     JXG.registerElement('radicalaxis', JXG.createRadicalAxis);
1785     JXG.registerElement('polarline', JXG.createPolarLine);
1786 
1787     return {
1788         Line: JXG.Line,
1789         createLine: JXG.createLine,
1790         createTangent: JXG.createTangent,
1791         createPolar: JXG.createTangent,
1792         createSegment: JXG.createSegment,
1793         createAxis: JXG.createAxis,
1794         createArrow: JXG.createArrow,
1795         createRadicalAxis: JXG.createRadicalAxis,
1796         createPolarLine: JXG.createPolarLine
1797     };
1798 });
1799