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