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