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