      1 /*
      2     Copyright 2008-2023
      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 <https://www.gnu.org/licenses/>
     29     and <https://opensource.org/licenses/MIT/>.
     30  */
     31
     32 /*global JXG: true, define: true*/
     33 /*jslint nomen: true, plusplus: true*/
     34
     35 /**
     36  * @fileoverview This file contains our composition elements, i.e. these elements are mostly put together
     37  * from one or more {@link JXG.GeometryElement} but with a special meaning. E.g. the midpoint element is contained here
     38  * and this is just a {@link JXG.Point} with coordinates dependent from two other points. Currently in this file the
     39  * following compositions can be found: <ul>
     40  *   <li>{@link Arrowparallel} (currently private)</li>
     41  *   <li>{@link Bisector}</li>
     42  *   <li>{@link Msector}</li>
     43  *   <li>{@link Circumcircle}</li>
     44  *   <li>{@link Circumcirclemidpoint}</li>
     45  *   <li>{@link Integral}</li>
     46  *   <li>{@link Midpoint}</li>
     47  *   <li>{@link Mirrorpoint}</li>
     48  *   <li>{@link Normal}</li>
     49  *   <li>{@link Orthogonalprojection}</li>
     50  *   <li>{@link Parallel}</li>
     51  *   <li>{@link Perpendicular}</li>
     52  *   <li>{@link Perpendicularpoint}</li>
     53  *   <li>{@link Perpendicularsegment}</li>
     54  *   <li>{@link Reflection}</li></ul>
     55  */
     56
     57 import JXG from "../jxg";
     58 import Mat from "../math/math";
     59 import Geometry from "../math/geometry";
     60 import Numerics from "../math/numerics";
     61 import Coords from "../base/coords";
     62 import Type from "../utils/type";
     63 import Const from "../base/constants";
     64 // import Point from "../base/point";
     65 // import Line from "../base/line";
     66 // import Circle from "../base/circle";
     67 // import Transform from "../base/transformation";
     68 import Composition from "../base/composition";
     69 // import Curve from "../base/curve";
     70 // import Polygon from "../base/polygon";
     71
     72 /**
     73  * @class This is used to construct a point that is the orthogonal projection of a point to a line.
     74  * @pseudo
     75  * @description An orthogonal projection is given by a point and a line. It is determined by projecting the given point
     76  * orthogonal onto the given line.
     77  * @constructor
     78  * @name Orthogonalprojection
     79  * @type JXG.Point
     80  * @augments JXG.Point
     81  * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown.
     82  * @param {JXG.Line_JXG.Point} p,l The constructed point is the orthogonal projection of p onto l.
     83  * @example
     84  * var p1 = board.create('point', [0.0, 4.0]);
     85  * var p2 = board.create('point', [6.0, 1.0]);
     86  * var l1 = board.create('line', [p1, p2]);
     87  * var p3 = board.create('point', [3.0, 3.0]);
     88  *
     89  * var pp1 = board.create('orthogonalprojection', [p3, l1]);
     90  * </pre><div class="jxgbox" id="JXG7708b215-39fa-41b6-b972-19d73d77d791" style="width: 400px; height: 400px;"></div>
     91  * <script type="text/javascript">
     92  *   var ppex1_board = JXG.JSXGraph.initBoard('JXG7708b215-39fa-41b6-b972-19d73d77d791', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false});
     93  *   var ppex1_p1 = ppex1_board.create('point', [0.0, 4.0]);
     94  *   var ppex1_p2 = ppex1_board.create('point', [6.0, 1.0]);
     95  *   var ppex1_l1 = ppex1_board.create('line', [ppex1_p1, ppex1_p2]);
     96  *   var ppex1_p3 = ppex1_board.create('point', [3.0, 3.0]);
     97  *   var ppex1_pp1 = ppex1_board.create('orthogonalprojection', [ppex1_p3, ppex1_l1]);
     98  * </script><pre>
     99  */
    100 JXG.createOrthogonalProjection = function (board, parents, attributes) {
    101     var l, p, t, attr;
    102
    103     parents[0] = board.select(parents[0]);
    104     parents[1] = board.select(parents[1]);
    105
    106     if (
    107         Type.isPointType(board, parents[0]) &&
    108         parents[1].elementClass === Const.OBJECT_CLASS_LINE
    109     ) {
    110         p = Type.providePoints(board, [parents[0]], attributes, "point")[0];
    111         l = parents[1];
    112     } else if (
    113         Type.isPointType(board, parents[1]) &&
    114         parents[0].elementClass === Const.OBJECT_CLASS_LINE
    115     ) {
    116         p = Type.providePoints(board, [parents[1]], attributes, "point")[0];
    117         l = parents[0];
    118     } else {
    119         throw new Error(
    120             "JSXGraph: Can't create perpendicular point with parent types '" +
    121                 typeof parents[0] +
    122                 "' and '" +
    123                 typeof parents[1] +
    124                 "'." +
    125                 "\nPossible parent types: [point,line]"
    126         );
    127     }
    128
    129     attr = Type.copyAttributes(attributes, board.options, "orthogonalprojection");
    130
    131     /**
    132      * @type JXG.Element
    133      * @ignore
    134      */
    135     t = board.create(
    136         "point",
    137         [
    138             function () {
    139                 return Geometry.projectPointToLine(p, l, board);
    140             }
    141         ],
    142         attr
    143     );
    144
    145     if (Type.exists(p._is_new)) {
    146         t.addChild(p);
    147         delete p._is_new;
    148     } else {
    149         p.addChild(t);
    150     }
    151     l.addChild(t);
    152
    153     t.elType = "orthogonalprojection";
    154     t.setParents([p.id, t.id]);
    155
    156     t.update();
    157
    158     /**
    159      * Used to generate a polynomial for the orthogonal projection
    160      * @name Orthogonalprojection#generatePolynomial
    161      * @returns {Array} An array containing the generated polynomial.
    162      * @private
    163      * @function
    164      * @ignore
    165      */
    166     t.generatePolynomial = function () {
    167         /*
    168          *  Perpendicular takes point P and line L and creates point T and line M:
    169          *
    170          *                          | M
    171          *                          |
    172          *                          x P (p1,p2)
    173          *                          |
    174          *                          |
    175          *  L                       |
    176          *  ----------x-------------x------------------------x--------
    177          *            A (a1,a2)     |T (t1,t2)               B (b1,b2)
    178          *                          |
    179          *                          |
    180          *
    181          * So we have two conditions:
    182          *
    183          *   (a)  AT  || TB          (collinearity condition)
    184          *   (b)  PT _|_ AB          (orthogonality condition)
    185          *
    186          *      a2-t2       t2-b2
    187          *     -------  =  -------           (1)
    188          *      a1-t1       t1-b1
    189          *
    190          *      p2-t2         a1-b1
    191          *     -------  =  - -------         (2)
    192          *      p1-t1         a2-b2
    193          *
    194          * Multiplying (1) and (2) with denominators and simplifying gives
    195          *
    196          *    a2t1 - a2b1 + t2b1 - a1t2 + a1b2 - t1b2 = 0                  (1')
    197          *
    198          *    p2a2 - p2b2 - t2a2 + t2b2 + p1a1 - p1b1 - t1a1 + t1b1 = 0    (2')
    199          *
    200          */
    201
    202         var a1 = l.point1.symbolic.x,
    203             a2 = l.point1.symbolic.y,
    204             b1 = l.point2.symbolic.x,
    205             b2 = l.point2.symbolic.y,
    206             p1 = p.symbolic.x,
    207             p2 = p.symbolic.y,
    208             t1 = t.symbolic.x,
    209             t2 = t.symbolic.y,
    210             poly1 = "(" + a2 + ")*(" + t1 + ")-(" + a2 + ")*(" + b1 + ")+(" + t2 + ")*(" + b1 + ")-(" + a1 + ")*(" + t2 + ")+(" + a1 + ")*(" +
    211                 b2 + ")-(" + t1 + ")*(" + b2 + ")",
    212             poly2 = "(" + p2 + ")*(" + a2 + ")-(" + p2 + ")*(" + b2 + ")-(" + t2 + ")*(" + a2 + ")+(" + t2 + ")*(" + b2 + ")+(" + p1 + ")*(" +
    213                 a1 + ")-(" + p1 + ")*(" + b1 + ")-(" + t1 + ")*(" + a1 + ")+(" + t1 + ")*(" + b1 + ")";
    214
    215         return [poly1, poly2];
    216     };
    217
    218     return t;
    219 };
    220
    221 /**
    222
    223      * @class This element is used to provide a constructor for a perpendicular.
    224      * @pseudo
    225      * @description  A perpendicular is a composition of two elements: a line and a point. The line is orthogonal
    226      * to a given line and contains a given point.
    227      * @name Perpendicular
    228      * @constructor
    229      * @type JXG.Line
    230      * @augments Segment
    231      * @returns A {@link JXG.Line} object through the given point that is orthogonal to the given line.
    232      * @throws {Error} If the elements cannot be constructed with the given parent objects an exception is thrown.
    233      * @param {JXG.Line_JXG.Point} l,p The perpendicular line will be orthogonal to l and
    234      * will contain p.
    235      * @example
    236      * // Create a perpendicular
    237      * var p1 = board.create('point', [0.0, 2.0]);
    238      * var p2 = board.create('point', [2.0, 1.0]);
    239      * var l1 = board.create('line', [p1, p2]);
    240      *
    241      * var p3 = board.create('point', [3.0, 3.0]);
    242      * var perp1 = board.create('perpendicular', [l1, p3]);
    243      * </pre><div class="jxgbox" id="JXGd5b78842-7b27-4d37-b608-d02519e6cd03" style="width: 400px; height: 400px;"></div>
    244      * <script type="text/javascript">
    245      *   var pex1_board = JXG.JSXGraph.initBoard('JXGd5b78842-7b27-4d37-b608-d02519e6cd03', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false});
    246      *   var pex1_p1 = pex1_board.create('point', [0.0, 2.0]);
    247      *   var pex1_p2 = pex1_board.create('point', [2.0, 1.0]);
    248      *   var pex1_l1 = pex1_board.create('line', [pex1_p1, pex1_p2]);
    249      *   var pex1_p3 = pex1_board.create('point', [3.0, 3.0]);
    250      *   var pex1_perp1 = pex1_board.create('perpendicular', [pex1_l1, pex1_p3]);
    251      * </script><pre>
    252      */
    253 JXG.createPerpendicular = function (board, parents, attributes) {
    254     var p, l, pd, attr;
    255
    256     parents[0] = board.select(parents[0]);
    257     parents[1] = board.select(parents[1]);
    258
    259     if (
    260         Type.isPointType(board, parents[0]) &&
    261         parents[1].elementClass === Const.OBJECT_CLASS_LINE
    262     ) {
    263         l = parents[1];
    264         p = Type.providePoints(board, [parents[0]], attributes, "point")[0];
    265     } else if (
    266         Type.isPointType(board, parents[1]) &&
    267         parents[0].elementClass === Const.OBJECT_CLASS_LINE
    268     ) {
    269         l = parents[0];
    270         p = Type.providePoints(board, [parents[1]], attributes, "point")[0];
    271     } else {
    272         throw new Error(
    273             "JSXGraph: Can't create perpendicular with parent types '" +
    274                 typeof parents[0] +
    275                 "' and '" +
    276                 typeof parents[1] +
    277                 "'." +
    278                 "\nPossible parent types: [line,point]"
    279         );
    280     }
    281
    282     attr = Type.copyAttributes(attributes, board.options, "perpendicular");
    283     pd = JXG.createLine(
    284         board,
    285         [
    286             function () {
    287                 return l.stdform[2] * p.X() - l.stdform[1] * p.Y();
    288             },
    289             function () {
    290                 return -l.stdform[2] * p.Z();
    291             },
    292             function () {
    293                 return l.stdform[1] * p.Z();
    294             }
    295         ],
    296         attr
    297     );
    298
    299     pd.elType = "perpendicular";
    300     pd.setParents([l.id, p.id]);
    301
    302     if (Type.exists(p._is_new)) {
    303         pd.addChild(p);
    304         delete p._is_new;
    305     } else {
    306         p.addChild(pd);
    307     }
    308     l.addChild(pd);
    309
    310     return pd;
    311 };
    312
    313 /**
    314  * @class This is used to construct a perpendicular point.
    315  * @pseudo
    316  * @description A perpendicular point is given by a point and a line. It is determined by projecting the given point
    317  * orthogonal onto the given line. This element should be used in GEONExTReader only. All other applications should
    318  * use orthogonal projection {@link Orthogonalprojection}.
    319  * @constructor
    320  * @name PerpendicularPoint
    321  * @type JXG.Point
    322  * @augments JXG.Point
    323  * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown.
    324  * @param {JXG.Line_JXG.Point} p,l The constructed point is the orthogonal projection of p onto l.
    325  * @example
    326  * var p1 = board.create('point', [0.0, 4.0]);
    327  * var p2 = board.create('point', [6.0, 1.0]);
    328  * var l1 = board.create('line', [p1, p2]);
    329  * var p3 = board.create('point', [3.0, 3.0]);
    330  *
    331  * var pp1 = board.create('perpendicularpoint', [p3, l1]);
    332  * </pre><div class="jxgbox" id="JXGded148c9-3536-44c0-ab81-1bb8fa48f3f4" style="width: 400px; height: 400px;"></div>
    333  * <script type="text/javascript">
    334  *   var ppex1_board = JXG.JSXGraph.initBoard('JXGded148c9-3536-44c0-ab81-1bb8fa48f3f4', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false});
    335  *   var ppex1_p1 = ppex1_board.create('point', [0.0, 4.0]);
    336  *   var ppex1_p2 = ppex1_board.create('point', [6.0, 1.0]);
    337  *   var ppex1_l1 = ppex1_board.create('line', [ppex1_p1, ppex1_p2]);
    338  *   var ppex1_p3 = ppex1_board.create('point', [3.0, 3.0]);
    339  *   var ppex1_pp1 = ppex1_board.create('perpendicularpoint', [ppex1_p3, ppex1_l1]);
    340  * </script><pre>
    341  */
    342 JXG.createPerpendicularPoint = function (board, parents, attributes) {
    343     var l, p, t;
    344
    345     parents[0] = board.select(parents[0]);
    346     parents[1] = board.select(parents[1]);
    347     if (
    348         Type.isPointType(board, parents[0]) &&
    349         parents[1].elementClass === Const.OBJECT_CLASS_LINE
    350     ) {
    351         p = Type.providePoints(board, [parents[0]], attributes, "point")[0];
    352         l = parents[1];
    353     } else if (
    354         Type.isPointType(board, parents[1]) &&
    355         parents[0].elementClass === Const.OBJECT_CLASS_LINE
    356     ) {
    357         p = Type.providePoints(board, [parents[1]], attributes, "point")[0];
    358         l = parents[0];
    359     } else {
    360         throw new Error(
    361             "JSXGraph: Can't create perpendicular point with parent types '" +
    362                 typeof parents[0] +
    363                 "' and '" +
    364                 typeof parents[1] +
    365                 "'." +
    366                 "\nPossible parent types: [point,line]"
    367         );
    368     }
    369
    370     /**
    371      * @class
    372      * @ignore
    373      */
    374     t = board.create(
    375         "point",
    376         [
    377             function () {
    378                 return Geometry.perpendicular(l, p, board)[0];
    379             }
    380         ],
    381         attributes
    382     );
    383
    384     if (Type.exists(p._is_new)) {
    385         t.addChild(p);
    386         delete p._is_new;
    387     } else {
    388         p.addChild(t);
    389     }
    390     l.addChild(t);
    391
    392     t.elType = "perpendicularpoint";
    393     t.setParents([p.id, l.id]);
    394
    395     t.update();
    396
    397     /**
    398      * Used to generate a polynomial for the perpendicular point
    399      * @name PerpendicularPoint#generatePolynomial
    400      * @returns {Array} An array containing the generated polynomial.
    401      * @private
    402      * @function
    403      * @ignore
    404      */
    405     t.generatePolynomial = function () {
    406         /*
    407          *  Perpendicular takes point P and line L and creates point T and line M:
    408          *
    409          *                          | M
    410          *                          |
    411          *                          x P (p1,p2)
    412          *                          |
    413          *                          |
    414          *  L                       |
    415          *  ----------x-------------x------------------------x--------
    416          *            A (a1,a2)     |T (t1,t2)               B (b1,b2)
    417          *                          |
    418          *                          |
    419          *
    420          * So we have two conditions:
    421          *
    422          *   (a)  AT  || TB          (collinearity condition)
    423          *   (b)  PT _|_ AB          (orthogonality condition)
    424          *
    425          *      a2-t2       t2-b2
    426          *     -------  =  -------           (1)
    427          *      a1-t1       t1-b1
    428          *
    429          *      p2-t2         a1-b1
    430          *     -------  =  - -------         (2)
    431          *      p1-t1         a2-b2
    432          *
    433          * Multiplying (1) and (2) with denominators and simplifying gives
    434          *
    435          *    a2t1 - a2b1 + t2b1 - a1t2 + a1b2 - t1b2 = 0                  (1')
    436          *
    437          *    p2a2 - p2b2 - t2a2 + t2b2 + p1a1 - p1b1 - t1a1 + t1b1 = 0    (2')
    438          *
    439          */
    440         var a1 = l.point1.symbolic.x,
    441             a2 = l.point1.symbolic.y,
    442             b1 = l.point2.symbolic.x,
    443             b2 = l.point2.symbolic.y,
    444             p1 = p.symbolic.x,
    445             p2 = p.symbolic.y,
    446             t1 = t.symbolic.x,
    447             t2 = t.symbolic.y,
    448             poly1 = "(" + a2 + ")*(" + t1 + ")-(" + a2 + ")*(" + b1 + ")+(" + t2 + ")*(" + b1 + ")-(" + a1 + ")*(" + t2 + ")+(" + a1 + ")*(" + b2 + ")-(" + t1 +
    449                 ")*(" + b2 + ")",
    450             poly2 = "(" + p2 + ")*(" + a2 + ")-(" + p2 + ")*(" + b2 + ")-(" + t2 + ")*(" + a2 + ")+(" + t2 + ")*(" + b2 + ")+(" + p1 + ")*(" + a1 + ")-(" + p1 +
    451                 ")*(" + b1 + ")-(" + t1 + ")*(" + a1 + ")+(" + t1 + ")*(" + b1 + ")";
    452
    453         return [poly1, poly2];
    454     };
    455
    456     return t;
    457 };
    458
    459 /**
    460  * @class This element is used to provide a constructor for a perpendicular segment.
    461  * @pseudo
    462  * @description  A perpendicular is a composition of two elements: a line segment and a point. The line segment is orthogonal
    463  * to a given line and contains a given point and meets the given line in the perpendicular point.
    464  * @name PerpendicularSegment
    465  * @constructor
    466  * @type JXG.Line
    467  * @augments Segment
    468  * @returns An array containing two elements: A {@link JXG.Line} object in the first component and a
    469  * {@link JXG.Point} element in the second component. The line segment is orthogonal to the given line and meets it
    470  * in the returned point.
    471  * @throws {Error} If the elements cannot be constructed with the given parent objects an exception is thrown.
    472  * @param {JXG.Line_JXG.Point} l,p The perpendicular line will be orthogonal to l and
    473  * will contain p. The perpendicular point is the intersection point of the two lines.
    474  * @example
    475  * // Create a perpendicular
    476  * var p1 = board.create('point', [0.0, 2.0]);
    477  * var p2 = board.create('point', [2.0, 1.0]);
    478  * var l1 = board.create('line', [p1, p2]);
    479  *
    480  * var p3 = board.create('point', [3.0, 3.0]);
    481  * var perp1 = board.create('perpendicularsegment', [l1, p3]);
    482  * </pre><div class="jxgbox" id="JXG037a6eb2-781d-4b71-b286-763619a63f22" style="width: 400px; height: 400px;"></div>
    483  * <script type="text/javascript">
    484  *   var pex1_board = JXG.JSXGraph.initBoard('JXG037a6eb2-781d-4b71-b286-763619a63f22', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false});
    485  *   var pex1_p1 = pex1_board.create('point', [0.0, 2.0]);
    486  *   var pex1_p2 = pex1_board.create('point', [2.0, 1.0]);
    487  *   var pex1_l1 = pex1_board.create('line', [pex1_p1, pex1_p2]);
    488  *   var pex1_p3 = pex1_board.create('point', [3.0, 3.0]);
    489  *   var pex1_perp1 = pex1_board.create('perpendicularsegment', [pex1_l1, pex1_p3]);
    490  * </script><pre>
    491  */
    492 JXG.createPerpendicularSegment = function (board, parents, attributes) {
    493     var p, l, pd, t, attr;
    494
    495     parents[0] = board.select(parents[0]);
    496     parents[1] = board.select(parents[1]);
    497     if (
    498         Type.isPointType(board, parents[0]) &&
    499         parents[1].elementClass === Const.OBJECT_CLASS_LINE
    500     ) {
    501         l = parents[1];
    502         p = Type.providePoints(board, [parents[0]], attributes, "point")[0];
    503     } else if (
    504         Type.isPointType(board, parents[1]) &&
    505         parents[0].elementClass === Const.OBJECT_CLASS_LINE
    506     ) {
    507         l = parents[0];
    508         p = Type.providePoints(board, [parents[1]], attributes, "point")[0];
    509     } else {
    510         throw new Error(
    511             "JSXGraph: Can't create perpendicular with parent types '" +
    512                 typeof parents[0] +
    513                 "' and '" +
    514                 typeof parents[1] +
    515                 "'." +
    516                 "\nPossible parent types: [line,point]"
    517         );
    518     }
    519     attr = Type.copyAttributes(attributes, board.options, "perpendicularsegment", "point");
    520     t = JXG.createPerpendicularPoint(board, [l, p], attr);
    521     t.dump = false;
    522
    523     if (!Type.exists(attributes.layer)) {
    524         attributes.layer = board.options.layer.line;
    525     }
    526
    527     attr = Type.copyAttributes(attributes, board.options, "perpendicularsegment");
    528     pd = JXG.createLine(
    529         board,
    530         [
    531             function () {
    532                 return Geometry.perpendicular(l, p, board)[1] ? [t, p] : [p, t];
    533             }
    534         ],
    535         attr
    536     );
    537
    538     /**
    539      * Helper point
    540      * @memberOf PerpendicularSegment.prototype
    541      * @type PerpendicularPoint
    542      * @name point
    543      */
    544     pd.point = t;
    545
    546     if (Type.exists(p._is_new)) {
    547         pd.addChild(p);
    548         delete p._is_new;
    549     } else {
    550         p.addChild(pd);
    551     }
    552     l.addChild(pd);
    553
    554     pd.elType = "perpendicularsegment";
    555     pd.setParents([p.id, l.id]);
    556     pd.subs = {
    557         point: t
    558     };
    559     pd.inherits.push(t);
    560
    561     return pd;
    562 };
    563
    564 /**
    565  * @class The midpoint element constructs a point in the middle of two given points.
    566  * @pseudo
    567  * @description A midpoint is given by two points. It is collinear to the given points and the distance
    568  * is the same to each of the given points, i.e. it is in the middle of the given points.
    569  * @constructor
    570  * @name Midpoint
    571  * @type JXG.Point
    572  * @augments JXG.Point
    573  * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown.
    574  * @param {JXG.Point_JXG.Point} p1,p2 The constructed point will be in the middle of p1 and p2.
    575  * @param {JXG.Line} l The midpoint will be in the middle of {@link JXG.Line#point1} and {@link JXG.Line#point2} of
    576  * the given line l.
    577  * @example
    578  * // Create base elements: 2 points and 1 line
    579  * var p1 = board.create('point', [0.0, 2.0]);
    580  * var p2 = board.create('point', [2.0, 1.0]);
    581  * var l1 = board.create('segment', [[0.0, 3.0], [3.0, 3.0]]);
    582  *
    583  * var mp1 = board.create('midpoint', [p1, p2]);
    584  * var mp2 = board.create('midpoint', [l1]);
    585  * </pre><div class="jxgbox" id="JXG7927ef86-24ae-40cc-afb0-91ff61dd0de7" style="width: 400px; height: 400px;"></div>
    586  * <script type="text/javascript">
    587  *   var mpex1_board = JXG.JSXGraph.initBoard('JXG7927ef86-24ae-40cc-afb0-91ff61dd0de7', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false});
    588  *   var mpex1_p1 = mpex1_board.create('point', [0.0, 2.0]);
    589  *   var mpex1_p2 = mpex1_board.create('point', [2.0, 1.0]);
    590  *   var mpex1_l1 = mpex1_board.create('segment', [[0.0, 3.0], [3.0, 3.0]]);
    591  *   var mpex1_mp1 = mpex1_board.create('midpoint', [mpex1_p1, mpex1_p2]);
    592  *   var mpex1_mp2 = mpex1_board.create('midpoint', [mpex1_l1]);
    593  * </script><pre>
    594  */
    595 JXG.createMidpoint = function (board, parents, attributes) {
    596     var a, b, t, i, attr;
    597
    598     for (i = 0; i < parents.length; ++i) {
    599         parents[i] = board.select(parents[i]);
    600     }
    601     if (
    602         parents.length === 2 &&
    603         Type.isPointType(board, parents[0]) &&
    604         Type.isPointType(board, parents[1])
    605     ) {
    606         parents = Type.providePoints(board, parents, attributes, "point");
    607         a = parents[0];
    608         b = parents[1];
    609     } else if (parents.length === 1 && parents[0].elementClass === Const.OBJECT_CLASS_LINE) {
    610         a = parents[0].point1;
    611         b = parents[0].point2;
    612     } else {
    613         throw new Error(
    614             "JSXGraph: Can't create midpoint." +
    615                 "\nPossible parent types: [point,point], [line]"
    616         );
    617     }
    618
    619     attr = Type.copyAttributes(attributes, board.options, "midpoint");
    620     /**
    621      * @type JXG.Element
    622      * @ignore
    623      */
    624     t = board.create(
    625         "point",
    626         [
    627             function () {
    628                 var x = a.coords.usrCoords[1] + b.coords.usrCoords[1];
    629                 if (
    630                     isNaN(x) ||
    631                     Math.abs(a.coords.usrCoords[0]) < Mat.eps ||
    632                     Math.abs(b.coords.usrCoords[0]) < Mat.eps
    633                 ) {
    634                     return NaN;
    635                 }
    636
    637                 return x * 0.5;
    638             },
    639             function () {
    640                 var y = a.coords.usrCoords[2] + b.coords.usrCoords[2];
    641                 if (
    642                     isNaN(y) ||
    643                     Math.abs(a.coords.usrCoords[0]) < Mat.eps ||
    644                     Math.abs(b.coords.usrCoords[0]) < Mat.eps
    645                 ) {
    646                     return NaN;
    647                 }
    648
    649                 return y * 0.5;
    650             }
    651         ],
    652         attr
    653     );
    654     if (Type.exists(a._is_new)) {
    655         t.addChild(a);
    656         delete a._is_new;
    657     } else {
    658         a.addChild(t);
    659     }
    660     if (Type.exists(b._is_new)) {
    661         t.addChild(b);
    662         delete b._is_new;
    663     } else {
    664         b.addChild(t);
    665     }
    666
    667     t.elType = "midpoint";
    668     t.setParents([a.id, b.id]);
    669
    670     t.prepareUpdate().update();
    671
    672     /**
    673      * Used to generate a polynomial for the midpoint.
    674      * @name Midpoint#generatePolynomial
    675      * @returns {Array} An array containing the generated polynomial.
    676      * @private
    677      * @function
    678      * @ignore
    679      */
    680     t.generatePolynomial = function () {
    681         /*
    682          *  Midpoint takes two point A and B or line L (with points P and Q) and creates point T:
    683          *
    684          *  L (not necessarily)
    685          *  ----------x------------------x------------------x--------
    686          *            A (a1,a2)          T (t1,t2)          B (b1,b2)
    687          *
    688          * So we have two conditions:
    689          *
    690          *   (a)   AT  ||  TB           (collinearity condition)
    691          *   (b)  [AT] == [TB]          (equidistant condition)
    692          *
    693          *      a2-t2       t2-b2
    694          *     -------  =  -------                                         (1)
    695          *      a1-t1       t1-b1
    696          *
    697          *     (a1 - t1)^2 + (a2 - t2)^2 = (b1 - t1)^2 + (b2 - t2)^2       (2)
    698          *
    699          *
    700          * Multiplying (1) with denominators and simplifying (1) and (2) gives
    701          *
    702          *    a2t1 - a2b1 + t2b1 - a1t2 + a1b2 - t1b2 = 0                      (1')
    703          *
    704          *    a1^2 - 2a1t1 + a2^2 - 2a2t2 - b1^2 + 2b1t1 - b2^2 + 2b2t2 = 0    (2')
    705          *
    706          */
    707         var a1 = a.symbolic.x,
    708             a2 = a.symbolic.y,
    709             b1 = b.symbolic.x,
    710             b2 = b.symbolic.y,
    711             t1 = t.symbolic.x,
    712             t2 = t.symbolic.y,
    713             poly1 = "(" + a2 + ")*(" + t1 + ")-(" + a2 + ")*(" + b1 + ")+(" + t2 + ")*(" + b1 + ")-(" + a1 + ")*(" + t2 + ")+(" + a1 + ")*(" + b2 +
    714                 ")-(" + t1 + ")*(" + b2 + ")",
    715             poly2 = "(" + a1 + ")^2 - 2*(" + a1 + ")*(" + t1 + ")+(" + a2 + ")^2-2*(" + a2 + ")*(" + t2 + ")-(" + b1 + ")^2+2*(" + b1 + ")*(" + t1 +
    716                 ")-(" + b2 + ")^2+2*(" + b2 + ")*(" + t2 + ")";
    717
    718         return [poly1, poly2];
    719     };
    720
    721     return t;
    722 };
    723
    724 /**
    725  * @class This element is used to construct a parallel point.
    726  * @pseudo
    727  * @description A parallel point is given by three points. Taking the Euclidean vector from the first to the
    728  * second point, the parallel point is determined by adding that vector to the third point.
    729  * The line determined by the first two points is parallel to the line determined by the third point and the constructed point.
    730  * @constructor
    731  * @name Parallelpoint
    732  * @type JXG.Point
    733  * @augments JXG.Point
    734  * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown.
    735  * @param {JXG.Point_JXG.Point_JXG.Point} p1,p2,p3 Taking the Euclidean vector <tt>v=p2-p1</tt> the parallel point is determined by
    736  * <tt>p4 = p3+v</tt>
    737  * @param {JXG.Line_JXG.Point} l,p The resulting point will together with p specify a line which is parallel to l.
    738  * @example
    739  * var p1 = board.create('point', [0.0, 2.0]);
    740  * var p2 = board.create('point', [2.0, 1.0]);
    741  * var p3 = board.create('point', [3.0, 3.0]);
    742  *
    743  * var pp1 = board.create('parallelpoint', [p1, p2, p3]);
    744  * </pre><div class="jxgbox" id="JXG488c4be9-274f-40f0-a469-c5f70abe1f0e" style="width: 400px; height: 400px;"></div>
    745  * <script type="text/javascript">
    746  *   var ppex1_board = JXG.JSXGraph.initBoard('JXG488c4be9-274f-40f0-a469-c5f70abe1f0e', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false});
    747  *   var ppex1_p1 = ppex1_board.create('point', [0.0, 2.0]);
    748  *   var ppex1_p2 = ppex1_board.create('point', [2.0, 1.0]);
    749  *   var ppex1_p3 = ppex1_board.create('point', [3.0, 3.0]);
    750  *   var ppex1_pp1 = ppex1_board.create('parallelpoint', [ppex1_p1, ppex1_p2, ppex1_p3]);
    751  * </script><pre>
    752  */
    753 JXG.createParallelPoint = function (board, parents, attributes) {
    754     var a, b, c, p, i, attr;
    755
    756     for (i = 0; i < parents.length; ++i) {
    757         parents[i] = board.select(parents[i]);
    758     }
    759     if (
    760         parents.length === 3 &&
    761         Type.isPointType(board, parents[0]) &&
    762         Type.isPointType(board, parents[1]) &&
    763         Type.isPointType(board, parents[2])
    764     ) {
    765         parents = Type.providePoints(board, parents, attributes, "point");
    766         a = parents[0];
    767         b = parents[1];
    768         c = parents[2];
    769     } else if (
    770         Type.isPointType(board, parents[0]) &&
    771         parents[1].elementClass === Const.OBJECT_CLASS_LINE
    772     ) {
    773         c = Type.providePoints(board, [parents[0]], attributes, "point")[0];
    774         a = parents[1].point1;
    775         b = parents[1].point2;
    776     } else if (
    777         Type.isPointType(board, parents[1]) &&
    778         parents[0].elementClass === Const.OBJECT_CLASS_LINE
    779     ) {
    780         c = Type.providePoints(board, [parents[1]], attributes, "point")[0];
    781         a = parents[0].point1;
    782         b = parents[0].point2;
    783     } else {
    784         throw new Error(
    785             "JSXGraph: Can't create parallel point with parent types '" +
    786                 typeof parents[0] +
    787                 "', '" +
    788                 typeof parents[1] +
    789                 "' and '" +
    790                 typeof parents[2] +
    791                 "'." +
    792                 "\nPossible parent types: [line,point], [point,point,point]"
    793         );
    794     }
    795
    796     attr = Type.copyAttributes(attributes, board.options, 'parallelpoint');
    797     /**
    798      * @type {JXG.Element}
    799      * @ignore
    800      */
    801     p = board.create(
    802         "point",
    803         [
    804             function () {
    805                 return c.coords.usrCoords[1] + b.coords.usrCoords[1] - a.coords.usrCoords[1];
    806             },
    807             function () {
    808                 return c.coords.usrCoords[2] + b.coords.usrCoords[2] - a.coords.usrCoords[2];
    809             }
    810         ],
    811         attr
    812     );
    813
    814     // required for algorithms requiring dependencies between elements
    815     if (Type.exists(a._is_new)) {
    816         p.addChild(a);
    817         delete a._is_new;
    818     } else {
    819         a.addChild(p);
    820     }
    821     if (Type.exists(b._is_new)) {
    822         p.addChild(b);
    823         delete b._is_new;
    824     } else {
    825         b.addChild(p);
    826     }
    827     if (Type.exists(c._is_new)) {
    828         p.addChild(c);
    829         delete c._is_new;
    830     } else {
    831         c.addChild(p);
    832     }
    833
    834     p.elType = "parallelpoint";
    835     p.setParents([a.id, b.id, c.id]);
    836
    837     // required to set the coordinates because functions are considered as constraints. hence, the coordinates get set first after an update.
    838     // can be removed if the above issue is resolved.
    839     p.prepareUpdate().update();
    840
    841     /**
    842      * @function
    843      * @ignore
    844      */
    845     p.generatePolynomial = function () {
    846         /*
    847          *  Parallelpoint takes three points A, B and C or line L (with points B and C) and creates point T:
    848          *
    849          *
    850          *                     C (c1,c2)                             T (t1,t2)
    851          *                      x                                     x
    852          *                     /                                     /
    853          *                    /                                     /
    854          *                   /                                     /
    855          *                  /                                     /
    856          *                 /                                     /
    857          *                /                                     /
    858          *               /                                     /
    859          *              /                                     /
    860          *  L (opt)    /                                     /
    861          *  ----------x-------------------------------------x--------
    862          *            A (a1,a2)                             B (b1,b2)
    863          *
    864          * So we have two conditions:
    865          *
    866          *   (a)   CT  ||  AB           (collinearity condition I)
    867          *   (b)   BT  ||  AC           (collinearity condition II)
    868          *
    869          * The corresponding equations are
    870          *
    871          *    (b2 - a2)(t1 - c1) - (t2 - c2)(b1 - a1) = 0         (1)
    872          *    (t2 - b2)(a1 - c1) - (t1 - b1)(a2 - c2) = 0         (2)
    873          *
    874          * Simplifying (1) and (2) gives
    875          *
    876          *    b2t1 - b2c1 - a2t1 + a2c1 - t2b1 + t2a1 + c2b1 - c2a1 = 0      (1')
    877          *    t2a1 - t2c1 - b2a1 + b2c1 - t1a2 + t1c2 + b1a2 - b1c2 = 0      (2')
    878          *
    879          */
    880         var a1 = a.symbolic.x,
    881             a2 = a.symbolic.y,
    882             b1 = b.symbolic.x,
    883             b2 = b.symbolic.y,
    884             c1 = c.symbolic.x,
    885             c2 = c.symbolic.y,
    886             t1 = p.symbolic.x,
    887             t2 = p.symbolic.y,
    888             poly1 = "(" + b2 + ")*(" + t1 + ")-(" + b2 + ")*(" + c1 + ")-(" + a2 + ")*(" + t1 + ")+(" + a2 + ")*(" + c1 + ")-(" + t2 + ")*(" + b1 + ")+(" + t2 + ")*(" +
    889                 a1 + ")+(" + c2 + ")*(" + b1 + ")-(" + c2 + ")*(" + a1 + ")",
    890             poly2 = "(" + t2 + ")*(" + a1 + ")-(" + t2 + ")*(" + c1 + ")-(" + b2 + ")*(" + a1 + ")+(" + b2 + ")*(" + c1 + ")-(" + t1 + ")*(" + a2 + ")+(" + t1 + ")*(" +
    891                 c2 + ")+(" + b1 + ")*(" + a2 + ")-(" + b1 + ")*(" + c2 + ")";
    892
    893         return [poly1, poly2];
    894     };
    895
    896     return p;
    897 };
    898
    899 /**
    900  * @class A parallel is a line through a given point with the same slope as a given line or
    901  * the line through two given point.
    902  * <p>
    903  * If original line is given as a JSXGraph line object, the resulting parallel line will be defined by the given point and an
    904  * infinitely far away point (an ideal point). That means, the line can not be shortened to a segment.
    905  * <p>
    906  * If the original line is given as two points, the resulting parallel line can be shortened to a a segment.
    907  * @pseudo
    908  * @name Parallel
    909  * @augments Line
    910  * @constructor
    911  * @type JXG.Line
    912  * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown.
    913  * @param {JXG.Line_JXG.Point} l,p The constructed line contains p and has the same slope as l. Alternative parameters are p1, p2, p: The
    914  * constructed line contains p and has the same slope as the line through p1 and p2.
    915  * @example
    916  * // Create a parallel
    917  * var p1 = board.create('point', [0.0, 2.0]);
    918  * var p2 = board.create('point', [2.0, 1.0]);
    919  * var l1 = board.create('line', [p1, p2]);
    920  *
    921  * var p3 = board.create('point', [3.0, 3.0]);
    922  * var pl1 = board.create('parallel', [l1, p3]);
    923  * </pre><div class="jxgbox" id="JXG24e54f9e-5c4e-4afb-9228-0ef27a59d627" style="width: 400px; height: 400px;"></div>
    924  * <script type="text/javascript">
    925  *   var plex1_board = JXG.JSXGraph.initBoard('JXG24e54f9e-5c4e-4afb-9228-0ef27a59d627', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false});
    926  *   var plex1_p1 = plex1_board.create('point', [0.0, 2.0]);
    927  *   var plex1_p2 = plex1_board.create('point', [2.0, 1.0]);
    928  *   var plex1_l1 = plex1_board.create('line', [plex1_p1, plex1_p2]);
    929  *   var plex1_p3 = plex1_board.create('point', [3.0, 3.0]);
    930  *   var plex1_pl1 = plex1_board.create('parallel', [plex1_l1, plex1_p3]);
    931  * </script><pre>
    932  * @example
    933  * var p1, p2, p3, l1, pl1;
    934  *
    935  * p1 = board.create('point', [0.0, 2.0]);
    936  * p2 = board.create('point', [2.0, 1.0]);
    937  * l1 = board.create('line', [p1, p2]);
    938  *
    939  * p3 = board.create('point', [1.0, 3.0]);
    940  * pl1 = board.create('parallel', [p1, p2, p3], {straightFirst: false, straightLast: false});
    941  *
    942  * </pre><div id="JXGd643305d-20c3-4a88-91f9-8d0c4448594f" class="jxgbox" style="width: 300px; height: 300px;"></div>
    943  * <script type="text/javascript">
    944  *     (function() {
    945  *         var board = JXG.JSXGraph.initBoard('JXGd643305d-20c3-4a88-91f9-8d0c4448594f',
    946  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
    947  *     var p1, p2, p3, l1, pl1;
    948  *
    949  *     p1 = board.create('point', [0.0, 2.0]);
    950  *     p2 = board.create('point', [2.0, 1.0]);
    951  *     l1 = board.create('line', [p1, p2]);
    952  *
    953  *     p3 = board.create('point', [1.0, 3.0]);
    954  *     pl1 = board.create('parallel', [p1, p2, p3], {straightFirst: false, straightLast: false});
    955  *
    956  *     })();
    957  *
    958  * </script><pre>
    959  *
    960  */
    961 JXG.createParallel = function (board, parents, attributes) {
    962     var p,
    963         pp,
    964         pl,
    965         li,
    966         i,
    967         attr,
    968         ty = 1;
    969
    970     for (i = 0; i < parents.length; ++i) {
    971         parents[i] = board.select(parents[i]);
    972     }
    973     p = null;
    974     if (parents.length === 3) {
    975         // Line / segment through point parents[2] which is parallel to line through parents[0] and parents[1]
    976         parents = Type.providePoints(board, parents, attributes, "point");
    977         p = parents[2];
    978         ty = 0;
    979     } else if (Type.isPointType(board, parents[0])) {
    980         // Parallel to line parents[1] through point parents[0]
    981         p = Type.providePoints(board, [parents[0]], attributes, "point")[0];
    982         /** @ignore */
    983         li = function () {
    984             return parents[1].stdform;
    985         };
    986     } else if (Type.isPointType(board, parents[1])) {
    987         // Parallel to line parents[0] through point parents[1]
    988         p = Type.providePoints(board, [parents[1]], attributes, "point")[0];
    989         /** @ignore */
    990         li = function () {
    991             return parents[0].stdform;
    992         };
    993     }
    994
    995     if (!Type.exists(attributes.layer)) {
    996         attributes.layer = board.options.layer.line;
    997     }
    998
    999     attr = Type.copyAttributes(attributes, board.options, "parallel", "point");
    1000     if (ty === 1) {
    1001         // Line is given by line element. The parallel line is
    1002         // constructed as line through an ideal point.
    1003         pp = board.create(
    1004             "point",
    1005             [
    1006                 function () {
    1007                     return Mat.crossProduct([1, 0, 0], li());
    1008                 }
    1009             ],
    1010             attr
    1011         );
    1012     } else {
    1013         // Line is given by two points. The parallel line is
    1014         // constructed as line through two finite point.
    1015         pp = board.create("parallelpoint", parents, attr);
    1016     }
    1017     pp.isDraggable = true;
    1018
    1019     attr = Type.copyAttributes(attributes, board.options, "parallel");
    1020     // line creator also calls addChild
    1021     pl = board.create("line", [p, pp], attr);
    1022
    1023     pl.elType = "parallel";
    1024     pl.subs = {
    1025         point: pp
    1026     };
    1027
    1028     pl.inherits.push(pp);
    1029     pl.setParents([parents[0].id, parents[1].id]);
    1030     if (parents.length === 3) {
    1031         pl.addParents(parents[2].id);
    1032     }
    1033
    1034     // p.addChild(pl);
    1035
    1036     /**
    1037      * Helper point used to create the parallel line. This point lies on the line at infinity, hence it's not visible,
    1038      * not even with visible set to <tt>true</tt>. Creating another line through this point would make that other line
    1039      * parallel to the create parallel.
    1040      * @memberOf Parallel.prototype
    1041      * @name point
    1042      * @type JXG.Point
    1043      */
    1044     pl.point = pp;
    1045
    1046     return pl;
    1047 };
    1048
    1049 /**
    1050  * @class An arrow parallel is a segment with an arrow attached which is parallel through a given segment, given by its defining two points,
    1051  * through a given point.
    1052  * <p>
    1053  * @pseudo
    1054  * @constructor
    1055  * @name Arrowparallel
    1056  * @type Parallel
    1057  * @augments Parallel
    1058  * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown.
    1059  * @param {JXG.Point_JXG.Point_JXG.Point} p1,p2,p3 The constructed arrow contains p3 and has the same slope as the line through p1 and p2.
    1060  * @example
    1061  * // Create a parallel
    1062  * var p1 = board.create('point', [0.0, 2.0]);
    1063  * var p2 = board.create('point', [2.0, 1.0]);
    1064  * var l1 = board.create('segment', [p1, p2]);
    1065  *
    1066  * var p3 = board.create('point', [3.0, 3.0]);
    1067  * var pl1 = board.create('arrowparallel', [p1, p2, p3]);
    1068  * </pre><div class="jxgbox" id="JXGeeacdf99-036f-4e83-aeb6-f7388423e369" style="width: 400px; height: 400px;"></div>
    1069  * <script type="text/javascript">
    1070  * (function () {
    1071  *   var plex1_board = JXG.JSXGraph.initBoard('JXGeeacdf99-036f-4e83-aeb6-f7388423e369', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false});
    1072  *   var plex1_p1 = plex1_board.create('point', [0.0, 2.0]);
    1073  *   var plex1_p2 = plex1_board.create('point', [2.0, 1.0]);
    1074  *   var plex1_l1 = plex1_board.create('segment', [plex1_p1, plex1_p2]);
    1075  *   var plex1_p3 = plex1_board.create('point', [3.0, 3.0]);
    1076  *   var plex1_pl1 = plex1_board.create('arrowparallel', [plex1_p1, plex1_p2, plex1_p3]);
    1077  * })();
    1078  * </script><pre>
    1079  */
    1080 JXG.createArrowParallel = function (board, parents, attributes) {
    1081     var p, attr;
    1082
    1083     /* parallel arrow point polynomials are done in createParallelPoint */
    1084     try {
    1085         attr = Type.copyAttributes(attributes, board.options, 'arrowparallel');
    1086         attr.firstArrow = false;
    1087         attr.lastArrow = true;
    1088         p = JXG.createParallel(board, parents, attr).setAttribute({
    1089             straightFirst: false,
    1090             straightLast: false
    1091         });
    1092         p.elType = "arrowparallel";
    1093
    1094         // parents are set in createParallel
    1095
    1096         return p;
    1097     } catch (e) {
    1098         throw new Error(
    1099             "JSXGraph: Can't create arrowparallel with parent types '" +
    1100                 typeof parents[0] +
    1101                 "' and '" +
    1102                 typeof parents[1] +
    1103                 "'." +
    1104                 "\nPossible parent types: [line,point], [point,point,point]"
    1105         );
    1106     }
    1107 };
    1108
    1109 /**
    1110  * @class Constructs a normal.
    1111  * @pseudo
    1112  * @description A normal is a line through a given point on a element of type line, circle, curve, or turtle and orthogonal to that object.
    1113  * @constructor
    1114  * @name Normal
    1115  * @type JXG.Line
    1116  * @augments JXG.Line
    1117  * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown.
    1118  * @param {JXG.Line,JXG.Circle,JXG.Curve,JXG.Turtle_JXG.Point} o,p The constructed line contains p which lies on the object and is orthogonal
    1119  * to the tangent to the object in the given point.
    1120  * @param {Glider} p Works like above, however the object is given by {@link JXG.CoordsElement#slideObject}.
    1121  * @example
    1122  * // Create a normal to a circle.
    1123  * var p1 = board.create('point', [2.0, 2.0]);
    1124  * var p2 = board.create('point', [3.0, 2.0]);
    1125  * var c1 = board.create('circle', [p1, p2]);
    1126  *
    1127  * var norm1 = board.create('normal', [c1, p2]);
    1128  * </pre><div class="jxgbox" id="JXG4154753d-3d29-40fb-a860-0b08aa4f3743" style="width: 400px; height: 400px;"></div>
    1129  * <script type="text/javascript">
    1130  *   var nlex1_board = JXG.JSXGraph.initBoard('JXG4154753d-3d29-40fb-a860-0b08aa4f3743', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false});
    1131  *   var nlex1_p1 = nlex1_board.create('point', [2.0, 2.0]);
    1132  *   var nlex1_p2 = nlex1_board.create('point', [3.0, 2.0]);
    1133  *   var nlex1_c1 = nlex1_board.create('circle', [nlex1_p1, nlex1_p2]);
    1134  *
    1135  *   // var nlex1_p3 = nlex1_board.create('point', [1.0, 2.0]);
    1136  *   var nlex1_norm1 = nlex1_board.create('normal', [nlex1_c1, nlex1_p2]);
    1137  * </script><pre>
    1138  */
    1139 JXG.createNormal = function (board, parents, attributes) {
    1140     var p, c, l, i, g, f, attr, pp, attrp;
    1141
    1142     for (i = 0; i < parents.length; ++i) {
    1143         parents[i] = board.select(parents[i]);
    1144     }
    1145     // One arguments: glider on line, circle or curve
    1146     if (parents.length === 1) {
    1147         p = parents[0];
    1148         c = p.slideObject;
    1149         // Two arguments: (point,line), (point,circle), (line,point) or (circle,point)
    1150     } else if (parents.length === 2) {
    1151         if (Type.isPointType(board, parents[0])) {
    1152             p = Type.providePoints(board, [parents[0]], attributes, "point")[0];
    1153             c = parents[1];
    1154         } else if (Type.isPointType(board, parents[1])) {
    1155             c = parents[0];
    1156             p = Type.providePoints(board, [parents[1]], attributes, "point")[0];
    1157         } else {
    1158             throw new Error(
    1159                 "JSXGraph: Can't create normal with parent types '" +
    1160                     typeof parents[0] +
    1161                     "' and '" +
    1162                     typeof parents[1] +
    1163                     "'." +
    1164                     "\nPossible parent types: [point,line], [point,circle], [glider]"
    1165             );
    1166         }
    1167     } else {
    1168         throw new Error(
    1169             "JSXGraph: Can't create normal with parent types '" +
    1170                 typeof parents[0] +
    1171                 "' and '" +
    1172                 typeof parents[1] +
    1173                 "'." +
    1174                 "\nPossible parent types: [point,line], [point,circle], [glider]"
    1175         );
    1176     }
    1177
    1178     attr = Type.copyAttributes(attributes, board.options, "normal");
    1179     if (c.elementClass === Const.OBJECT_CLASS_LINE) {
    1180         // Private point
    1181         attrp = Type.copyAttributes(attributes, board.options, "normal", "point");
    1182         pp = board.create(
    1183             "point",
    1184             [
    1185                 function () {
    1186                     var p = Mat.crossProduct([1, 0, 0], c.stdform);
    1187                     return [p[0], -p[2], p[1]];
    1188                 }
    1189             ],
    1190             attrp
    1191         );
    1192         pp.isDraggable = true;
    1193
    1194         l = board.create("line", [p, pp], attr);
    1195
    1196         /**
    1197          * A helper point used to create a normal to a {@link JXG.Line} object. For normals to circles or curves this
    1198          * element is <tt>undefined</tt>.
    1199          * @type JXG.Point
    1200          * @name point
    1201          * @memberOf Normal.prototype
    1202          */
    1203         l.point = pp;
    1204         l.subs = {
    1205             point: pp
    1206         };
    1207         l.inherits.push(pp);
    1208     } else if (c.elementClass === Const.OBJECT_CLASS_CIRCLE) {
    1209         l = board.create("line", [c.midpoint, p], attr);
    1210     } else if (c.elementClass === Const.OBJECT_CLASS_CURVE) {
    1211         if (Type.evaluate(c.visProp.curvetype) !== "plot") {
    1212             g = c.X;
    1213             f = c.Y;
    1214             l = board.create(
    1215                 "line",
    1216                 [
    1217                     function () {
    1218                         return (
    1219                             -p.X() * Numerics.D(g)(p.position) -
    1220                             p.Y() * Numerics.D(f)(p.position)
    1221                         );
    1222                     },
    1223                     function () {
    1224                         return Numerics.D(g)(p.position);
    1225                     },
    1226                     function () {
    1227                         return Numerics.D(f)(p.position);
    1228                     }
    1229                 ],
    1230                 attr
    1231             );
    1232         } else {
    1233             // curveType 'plot'
    1234             l = board.create(
    1235                 "line",
    1236                 [
    1237                     function () {
    1238                         var i = Math.floor(p.position),
    1239                             lbda = p.position - i,
    1240                             p1,
    1241                             p2,
    1242                             t,
    1243                             A,
    1244                             B,
    1245                             C,
    1246                             D,
    1247                             dx,
    1248                             dy,
    1249                             d;
    1250
    1251                         if (c.bezierdegree === 1) {
    1252                             if (i === c.numberPoints - 1) {
    1253                                 i -= 1;
    1254                                 lbda = 1;
    1255                             }
    1256                         } else if (c.bezierDegree === 3) {
    1257                             // i is start of the Bezier segment
    1258                             // t is the position in the Bezier segment
    1259                             i = Math.floor((p.position * (c.numberPoints - 1)) / 3) * 3;
    1260                             t = (p.position * (c.numberPoints - 1) - i) / 3;
    1261                             if (i >= c.numberPoints - 1) {
    1262                                 i = c.numberPoints - 4;
    1263                                 t = 1;
    1264                             }
    1265                         } else {
    1266                             return 0;
    1267                         }
    1268
    1269                         if (i < 0) {
    1270                             return 1;
    1271                         }
    1272
    1273                         if (c.bezierDegree === 1) {
    1274                             return (
    1275                                 (c.Y(i) + lbda * (c.Y(i + 1) - c.Y(i))) *
    1276                                     (c.Y(i) - c.Y(i + 1)) -
    1277                                 (c.X(i) + lbda * (c.X(i + 1) - c.X(i))) * (c.X(i + 1) - c.X(i))
    1278                             );
    1279                         } else {
    1280                             A = c.points[i].usrCoords;
    1281                             B = c.points[i + 1].usrCoords;
    1282                             C = c.points[i + 2].usrCoords;
    1283                             D = c.points[i + 3].usrCoords;
    1284                             dx =
    1285                                 (1 - t) * (1 - t) * (B[1] - A[1]) +
    1286                                 2 * (1 - t) * t * (C[1] - B[1]) +
    1287                                 t * t * (D[1] - C[1]);
    1288                             dy =
    1289                                 (1 - t) * (1 - t) * (B[2] - A[2]) +
    1290                                 2 * (1 - t) * t * (C[2] - B[2]) +
    1291                                 t * t * (D[2] - C[2]);
    1292                             d = Mat.hypot(dx, dy);
    1293                             dx /= d;
    1294                             dy /= d;
    1295                             p1 = p.coords.usrCoords;
    1296                             p2 = [1, p1[1] - dy, p1[2] + dx];
    1297                             return p1[2] * p2[1] - p1[1] * p2[2];
    1298                         }
    1299                     },
    1300                     function () {
    1301                         var i = Math.floor(p.position),
    1302                             p1,
    1303                             p2,
    1304                             t,
    1305                             A,
    1306                             B,
    1307                             C,
    1308                             D,
    1309                             dx,
    1310                             dy,
    1311                             d;
    1312
    1313                         if (c.bezierdegree === 1) {
    1314                             if (i === c.numberPoints - 1) {
    1315                                 i -= 1;
    1316                             }
    1317                         } else if (c.bezierDegree === 3) {
    1318                             // i is start of the Bezier segment
    1319                             // t is the position in the Bezier segment
    1320                             i = Math.floor((p.position * (c.numberPoints - 1)) / 3) * 3;
    1321                             t = (p.position * (c.numberPoints - 1) - i) / 3;
    1322                             if (i >= c.numberPoints - 1) {
    1323                                 i = c.numberPoints - 4;
    1324                                 t = 1;
    1325                             }
    1326                         } else {
    1327                             return 0;
    1328                         }
    1329
    1330                         if (i < 0) {
    1331                             return 0;
    1332                         }
    1333                         if (c.bezierDegree === 1) {
    1334                             return c.X(i + 1) - c.X(i);
    1335                         } else {
    1336                             A = c.points[i].usrCoords;
    1337                             B = c.points[i + 1].usrCoords;
    1338                             C = c.points[i + 2].usrCoords;
    1339                             D = c.points[i + 3].usrCoords;
    1340                             dx =
    1341                                 (1 - t) * (1 - t) * (B[1] - A[1]) +
    1342                                 2 * (1 - t) * t * (C[1] - B[1]) +
    1343                                 t * t * (D[1] - C[1]);
    1344                             dy =
    1345                                 (1 - t) * (1 - t) * (B[2] - A[2]) +
    1346                                 2 * (1 - t) * t * (C[2] - B[2]) +
    1347                                 t * t * (D[2] - C[2]);
    1348                             d = Mat.hypot(dx, dy);
    1349                             dx /= d;
    1350                             dy /= d;
    1351                             p1 = p.coords.usrCoords;
    1352                             p2 = [1, p1[1] - dy, p1[2] + dx];
    1353                             return p2[2] - p1[2];
    1354                         }
    1355                     },
    1356                     function () {
    1357                         var i = Math.floor(p.position),
    1358                             p1,
    1359                             p2,
    1360                             t,
    1361                             A,
    1362                             B,
    1363                             C,
    1364                             D,
    1365                             dx,
    1366                             dy,
    1367                             d;
    1368
    1369                         if (c.bezierdegree === 1) {
    1370                             if (i === c.numberPoints - 1) {
    1371                                 i -= 1;
    1372                             }
    1373                         } else if (c.bezierDegree === 3) {
    1374                             // i is start of the Bezier segment
    1375                             // t is the position in the Bezier segment
    1376                             i = Math.floor((p.position * (c.numberPoints - 1)) / 3) * 3;
    1377                             t = (p.position * (c.numberPoints - 1) - i) / 3;
    1378                             if (i >= c.numberPoints - 1) {
    1379                                 i = c.numberPoints - 4;
    1380                                 t = 1;
    1381                             }
    1382                         } else {
    1383                             return 0;
    1384                         }
    1385
    1386                         if (i < 0) {
    1387                             return 0;
    1388                         }
    1389
    1390                         if (c.bezierDegree === 1) {
    1391                             return c.Y(i + 1) - c.Y(i);
    1392                         } else {
    1393                             A = c.points[i].usrCoords;
    1394                             B = c.points[i + 1].usrCoords;
    1395                             C = c.points[i + 2].usrCoords;
    1396                             D = c.points[i + 3].usrCoords;
    1397                             dx =
    1398                                 (1 - t) * (1 - t) * (B[1] - A[1]) +
    1399                                 2 * (1 - t) * t * (C[1] - B[1]) +
    1400                                 t * t * (D[1] - C[1]);
    1401                             dy =
    1402                                 (1 - t) * (1 - t) * (B[2] - A[2]) +
    1403                                 2 * (1 - t) * t * (C[2] - B[2]) +
    1404                                 t * t * (D[2] - C[2]);
    1405                             d = Mat.hypot(dx, dy);
    1406                             dx /= d;
    1407                             dy /= d;
    1408                             p1 = p.coords.usrCoords;
    1409                             p2 = [1, p1[1] - dy, p1[2] + dx];
    1410                             return p1[1] - p2[1];
    1411                         }
    1412                     }
    1413                 ],
    1414                 attr
    1415             );
    1416         }
    1417     } else if (c.type === Const.OBJECT_TYPE_TURTLE) {
    1418         l = board.create(
    1419             "line",
    1420             [
    1421                 function () {
    1422                     var el,
    1423                         j,
    1424                         i = Math.floor(p.position),
    1425                         lbda = p.position - i;
    1426
    1427                     // run through all curves of this turtle
    1428                     for (j = 0; j < c.objects.length; j++) {
    1429                         el = c.objects[j];
    1430
    1431                         if (el.type === Const.OBJECT_TYPE_CURVE) {
    1432                             if (i < el.numberPoints) {
    1433                                 break;
    1434                             }
    1435
    1436                             i -= el.numberPoints;
    1437                         }
    1438                     }
    1439
    1440                     if (i === el.numberPoints - 1) {
    1441                         i -= 1;
    1442                         lbda = 1;
    1443                     }
    1444
    1445                     if (i < 0) {
    1446                         return 1;
    1447                     }
    1448
    1449                     return (
    1450                         (el.Y(i) + lbda * (el.Y(i + 1) - el.Y(i))) * (el.Y(i) - el.Y(i + 1)) -
    1451                         (el.X(i) + lbda * (el.X(i + 1) - el.X(i))) * (el.X(i + 1) - el.X(i))
    1452                     );
    1453                 },
    1454                 function () {
    1455                     var el,
    1456                         j,
    1457                         i = Math.floor(p.position);
    1458
    1459                     // run through all curves of this turtle
    1460                     for (j = 0; j < c.objects.length; j++) {
    1461                         el = c.objects[j];
    1462                         if (el.type === Const.OBJECT_TYPE_CURVE) {
    1463                             if (i < el.numberPoints) {
    1464                                 break;
    1465                             }
    1466
    1467                             i -= el.numberPoints;
    1468                         }
    1469                     }
    1470
    1471                     if (i === el.numberPoints - 1) {
    1472                         i -= 1;
    1473                     }
    1474
    1475                     if (i < 0) {
    1476                         return 0;
    1477                     }
    1478
    1479                     return el.X(i + 1) - el.X(i);
    1480                 },
    1481                 function () {
    1482                     var el,
    1483                         j,
    1484                         i = Math.floor(p.position);
    1485
    1486                     // run through all curves of this turtle
    1487                     for (j = 0; j < c.objects.length; j++) {
    1488                         el = c.objects[j];
    1489                         if (el.type === Const.OBJECT_TYPE_CURVE) {
    1490                             if (i < el.numberPoints) {
    1491                                 break;
    1492                             }
    1493
    1494                             i -= el.numberPoints;
    1495                         }
    1496                     }
    1497
    1498                     if (i === el.numberPoints - 1) {
    1499                         i -= 1;
    1500                     }
    1501
    1502                     if (i < 0) {
    1503                         return 0;
    1504                     }
    1505
    1506                     return el.Y(i + 1) - el.Y(i);
    1507                 }
    1508             ],
    1509             attr
    1510         );
    1511     } else {
    1512         throw new Error(
    1513             "JSXGraph: Can't create normal with parent types '" +
    1514                 typeof parents[0] +
    1515                 "' and '" +
    1516                 typeof parents[1] +
    1517                 "'." +
    1518                 "\nPossible parent types: [point,line], [point,circle], [glider]"
    1519         );
    1520     }
    1521
    1522     l.elType = "normal";
    1523     l.setParents(parents);
    1524
    1525     if (Type.exists(p._is_new)) {
    1526         l.addChild(p);
    1527         delete p._is_new;
    1528     } else {
    1529         p.addChild(l);
    1530     }
    1531     c.addChild(l);
    1532
    1533     return l;
    1534 };
    1535
    1536 /**
    1537  * @class A bisector is a line which divides an angle into two equal angles. It is given by three points A, B, and
    1538  * C and divides the angle ABC into two equal sized parts.
    1539  * @pseudo
    1540  * @constructor
    1541  * @name Bisector
    1542  * @type JXG.Line
    1543  * @augments JXG.Line
    1544  * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown.
    1545  * @param {JXG.Point_JXG.Point_JXG.Point} p1,p2,p3 The angle described by <tt>p1</tt>, <tt>p2</tt> and <tt>p3</tt> will
    1546  * be divided into two equal angles.
    1547  * @example
    1548  * var p1 = board.create('point', [6.0, 4.0]);
    1549  * var p2 = board.create('point', [3.0, 2.0]);
    1550  * var p3 = board.create('point', [1.0, 7.0]);
    1551  *
    1552  * var bi1 = board.create('bisector', [p1, p2, p3]);
    1553  * </pre><div class="jxgbox" id="JXG0d58cea8-b06a-407c-b27c-0908f508f5a4" style="width: 400px; height: 400px;"></div>
    1554  * <script type="text/javascript">
    1555  * (function () {
    1556  *   var board = JXG.JSXGraph.initBoard('JXG0d58cea8-b06a-407c-b27c-0908f508f5a4', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false});
    1557  *   var p1 = board.create('point', [6.0, 4.0]);
    1558  *   var p2 = board.create('point', [3.0, 2.0]);
    1559  *   var p3 = board.create('point', [1.0, 7.0]);
    1560  *   var bi1 = board.create('bisector', [p1, p2, p3]);
    1561  * })();
    1562  * </script><pre>
    1563  */
    1564 JXG.createBisector = function (board, parents, attributes) {
    1565     var p, l, i, attr;
    1566
    1567     parents = Type.providePoints(board, parents, attributes, "point");
    1568     if (Type.isPoint(parents[0]) && Type.isPoint(parents[1]) && Type.isPoint(parents[2])) {
    1569         // hidden and fixed helper
    1570         attr = Type.copyAttributes(attributes, board.options, "bisector", "point");
    1571         attr.snapToGrid = false;
    1572
    1573         p = board.create(
    1574             "point",
    1575             [
    1576                 function () {
    1577                     return Geometry.angleBisector(parents[0], parents[1], parents[2], board);
    1578                 }
    1579             ],
    1580             attr
    1581         );
    1582         p.dump = false;
    1583
    1584         for (i = 0; i < 3; i++) {
    1585             // required for algorithm requiring dependencies between elements
    1586             if (Type.exists(parents[i]._is_new)) {
    1587                 p.addChild(parents[i]);
    1588                 delete parents[i]._is_new;
    1589             } else {
    1590                 parents[i].addChild(p);
    1591             }
    1592         }
    1593
    1594         if (!Type.exists(attributes.layer)) {
    1595             attributes.layer = board.options.layer.line;
    1596         }
    1597
    1598         attr = Type.copyAttributes(attributes, board.options, "bisector");
    1599         l = JXG.createLine(board, [parents[1], p], attr);
    1600
    1601         /**
    1602          * Helper point
    1603          * @memberOf Bisector.prototype
    1604          * @type Point
    1605          * @name point
    1606          */
    1607         l.point = p;
    1608
    1609         l.elType = "bisector";
    1610         l.setParents(parents);
    1611         l.subs = {
    1612             point: p
    1613         };
    1614         l.inherits.push(p);
    1615
    1616         return l;
    1617     }
    1618
    1619     throw new Error(
    1620         "JSXGraph: Can't create angle bisector with parent types '" +
    1621             typeof parents[0] +
    1622             "' and '" +
    1623             typeof parents[1] +
    1624             "'." +
    1625             "\nPossible parent types: [point,point,point]"
    1626     );
    1627 };
    1628
    1629 /**
    1630  * @class Bisector lines are similar to {@link Bisector} but take two lines as parent elements. The resulting element is
    1631  * a composition of two lines.
    1632  * @pseudo
    1633  * @constructor
    1634  * @name Bisectorlines
    1635  * @type JXG.Composition
    1636  * @augments JXG.Composition
    1637  * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown.
    1638  * @param {JXG.Line_JXG.Line} l1,l2 The four angles described by the lines <tt>l1</tt> and <tt>l2</tt> will each
    1639  * be divided into two equal angles.
    1640  * @example
    1641  * var p1 = board.create('point', [6.0, 4.0]);
    1642  * var p2 = board.create('point', [3.0, 2.0]);
    1643  * var p3 = board.create('point', [1.0, 7.0]);
    1644  * var p4 = board.create('point', [3.0, 0.0]);
    1645  * var l1 = board.create('line', [p1, p2]);
    1646  * var l2 = board.create('line', [p3, p4]);
    1647  *
    1648  * var bi1 = board.create('bisectorlines', [l1, l2]);
    1649  * </pre><div class="jxgbox" id="JXG3121ff67-44f0-4dda-bb10-9cda0b80bf18" style="width: 400px; height: 400px;"></div>
    1650  * <script type="text/javascript">
    1651  * (function () {
    1652  *   var board = JXG.JSXGraph.initBoard('JXG3121ff67-44f0-4dda-bb10-9cda0b80bf18', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false});
    1653  *   var p1 = board.create('point', [6.0, 4.0]);
    1654  *   var p2 = board.create('point', [3.0, 2.0]);
    1655  *   var p3 = board.create('point', [1.0, 7.0]);
    1656  *   var p4 = board.create('point', [3.0, 0.0]);
    1657  *   var l1 = board.create('line', [p1, p2]);
    1658  *   var l2 = board.create('line', [p3, p4]);
    1659  *   var bi1 = board.create('bisectorlines', [l1, l2]);
    1660  * })();
    1661  * </script><pre>
    1662  */
    1663 JXG.createAngularBisectorsOfTwoLines = function (board, parents, attributes) {
    1664     // The angular bisectors of two line [c1,a1,b1] and [c2,a2,b2] are determined by the equation:
    1665     // (a1*x+b1*y+c1*z)/sqrt(a1^2+b1^2) = +/- (a2*x+b2*y+c2*z)/sqrt(a2^2+b2^2)
    1666
    1667     var g1,
    1668         g2,
    1669         attr,
    1670         ret,
    1671         l1 = board.select(parents[0]),
    1672         l2 = board.select(parents[1]);
    1673
    1674     if (
    1675         l1.elementClass !== Const.OBJECT_CLASS_LINE ||
    1676         l2.elementClass !== Const.OBJECT_CLASS_LINE
    1677     ) {
    1678         throw new Error(
    1679             "JSXGraph: Can't create angle bisectors of two lines with parent types '" +
    1680                 typeof parents[0] +
    1681                 "' and '" +
    1682                 typeof parents[1] +
    1683                 "'." +
    1684                 "\nPossible parent types: [line,line]"
    1685         );
    1686     }
    1687
    1688     if (!Type.exists(attributes.layer)) {
    1689         attributes.layer = board.options.layer.line;
    1690     }
    1691
    1692     attr = Type.copyAttributes(attributes, board.options, "bisectorlines", "line1");
    1693     g1 = board.create(
    1694         "line",
    1695         [
    1696             function () {
    1697                 var d1 = Mat.hypot(l1.stdform[1], l1.stdform[2]),
    1698                     d2 = Mat.hypot(l2.stdform[1], l2.stdform[2]);
    1699
    1700                 return l1.stdform[0] / d1 - l2.stdform[0] / d2;
    1701             },
    1702             function () {
    1703                 var d1 = Mat.hypot(l1.stdform[1], l1.stdform[2]),
    1704                     d2 = Mat.hypot(l2.stdform[1], l2.stdform[2]);
    1705
    1706                 return l1.stdform[1] / d1 - l2.stdform[1] / d2;
    1707             },
    1708             function () {
    1709                 var d1 = Mat.hypot(l1.stdform[1], l1.stdform[2]),
    1710                     d2 = Mat.hypot(l2.stdform[1], l2.stdform[2]);
    1711
    1712                 return l1.stdform[2] / d1 - l2.stdform[2] / d2;
    1713             }
    1714         ],
    1715         attr
    1716     );
    1717
    1718     if (!Type.exists(attributes.layer)) {
    1719         attributes.layer = board.options.layer.line;
    1720     }
    1721     attr = Type.copyAttributes(attributes, board.options, "bisectorlines", "line2");
    1722     g2 = board.create(
    1723         "line",
    1724         [
    1725             function () {
    1726                 var d1 = Mat.hypot(l1.stdform[1], l1.stdform[2]),
    1727                     d2 = Mat.hypot(l2.stdform[1], l2.stdform[2]);
    1728
    1729                 return l1.stdform[0] / d1 + l2.stdform[0] / d2;
    1730             },
    1731             function () {
    1732                 var d1 = Mat.hypot(l1.stdform[1], l1.stdform[2]),
    1733                     d2 = Mat.hypot(l2.stdform[1], l2.stdform[2]);
    1734
    1735                 return l1.stdform[1] / d1 + l2.stdform[1] / d2;
    1736             },
    1737             function () {
    1738                 var d1 = Mat.hypot(l1.stdform[1], l1.stdform[2]),
    1739                     d2 = Mat.hypot(l2.stdform[1], l2.stdform[2]);
    1740
    1741                 return l1.stdform[2] / d1 + l2.stdform[2] / d2;
    1742             }
    1743         ],
    1744         attr
    1745     );
    1746
    1747     // documentation
    1748     /**
    1749      * First line.
    1750      * @memberOf Bisectorlines.prototype
    1751      * @name line1
    1752      * @type Line
    1753      */
    1754
    1755     /**
    1756      * Second line.
    1757      * @memberOf Bisectorlines.prototype
    1758      * @name line2
    1759      * @type Line
    1760      */
    1761
    1762     ret = new Composition({ line1: g1, line2: g2 });
    1763
    1764     g1.dump = false;
    1765     g2.dump = false;
    1766
    1767     ret.elType = "bisectorlines";
    1768     ret.setParents([l1.id, l2.id]);
    1769     ret.subs = {
    1770         line1: g1,
    1771         line2: g2
    1772     };
    1773     // ret.inherits.push(g1, g2);
    1774
    1775     return ret;
    1776 };
    1777
    1778 // /**
    1779 //  * @class An m-sector is a line which divides an angle into two angles. It is given by three points A, B, and
    1780 //  * C and a real number m, and divides an angle into two angles, an angle with amplitude m and an angle with
    1781 //  * amplitude (1-m)
    1782 //  * @pseudo
    1783 //  * @constructor
    1784 //  * @name Msector
    1785 //  * @type JXG.Line
    1786 //  * @augments JXG.Line
    1787 //  * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown.
    1788 //  * @param {JXG.Point_JXG.Point_JXG.Point} p1,p2,p3 The angle described by <tt>p1</tt>, <tt>p2</tt> and <tt>p3</tt> will
    1789 //  * be divided into two angles according to the value of <tt>m</tt>.
    1790 //  * @example
    1791 //  * var p1 = board.create('point', [6.0, 4.0]);
    1792 //  * var p2 = board.create('point', [3.0, 2.0]);
    1793 //  * var p3 = board.create('point', [1.0, 7.0]);
    1794 //  *
    1795 //  * var bi1 = board.create('msector', [p1, p2, p3], 1/5);
    1796 //  * </pre><div id="JXG0d58cea8-b06a-407c-b27c-0908f508f5a4" style="width: 400px; height: 400px;"></div>
    1797 //  * <script type="text/javascript">
    1798 //  * (function () {
    1799 //  *   var board = JXG.JSXGraph.initBoard('JXG0d58cea8-b06a-407c-b27c-0908f508f5a4', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false});
    1800 //  *   var p1 = board.create('point', [6.0, 4.0]);
    1801 //  *   var p2 = board.create('point', [3.0, 2.0]);
    1802 //  *   var p3 = board.create('point', [1.0, 7.0]);
    1803 //  *   var bi1 = board.create('msector', [p1, p2, p3], 1/5);
    1804 //  * })();
    1805 //  * </script><pre>
    1806 //  */
    1807 // JXG.createMsector = function (board, parents, attributes) {
    1808 //     var p, l, i, attr;
    1809
    1810 //     if (parents[0].elementClass === Const.OBJECT_CLASS_POINT &&
    1811 //             parents[1].elementClass === Const.OBJECT_CLASS_POINT &&
    1812 //             parents[2].elementClass === Const.OBJECT_CLASS_POINT) {
    1813 //         // hidden and fixed helper
    1814 //         attr = Type.copyAttributes(attributes, board.options, 'msector', 'point');
    1815 //         p = board.create('point', [
    1816 //             function () {
    1817 //                 return Geometry.angleMsector(parents[0], parents[1], parents[2], parents[3], board);
    1818 //             }
    1819 //         ], attr);
    1820 //         p.dump = false;
    1821
    1822 //         for (i = 0; i < 3; i++) {
    1823 //             // required for algorithm requiring dependencies between elements
    1824 //             parents[i].addChild(p);
    1825 //         }
    1826
    1827 //         if (!Type.exists(attributes.layer)) {
    1828 //             attributes.layer = board.options.layer.line;
    1829 //         }
    1830
    1831 //         attr = Type.copyAttributes(attributes, board.options, 'msector');
    1832 //         l = JXG.createLine(board, [parents[1], p], attr);
    1833
    1834 //         /**
    1835 //          * Helper point
    1836 //          * @memberOf Msector.prototype
    1837 //          * @type Point
    1838 //          * @name point
    1839 //          */
    1840 //         l.point = p;
    1841
    1842 //         l.elType = 'msector';
    1843 //         l.parents = [parents[0].id, parents[1].id, parents[2].id];
    1844 //         l.subs = {
    1845 //             point: p
    1846 //         };
    1847 //         l.inherits.push(p);
    1848
    1849 //         return l;
    1850 //     }
    1851
    1852 //     throw new Error("JSXGraph: Can't create angle msector with parent types '" +
    1853 //         (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." +
    1854 //         "\nPossible parent types: [point,point,point,Number]");
    1855 // };
    1856
    1857 /**
    1858  * @class Constructs the midpoint of a {@link Circumcircle}. Like the circumcircle the circumcenter
    1859  * is constructed by providing three points.
    1860  * @pseudo
    1861  * @description A circumcenter is given by three points which are all lying on the circle with the
    1862  * constructed circumcenter as the midpoint.
    1863  * @constructor
    1864  * @name Circumcenter
    1865  * @type JXG.Point
    1866  * @augments JXG.Point
    1867  * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown.
    1868  * @param {JXG.Point_JXG.Point_JXG.Point} p1,p2,p3 The constructed point is the midpoint of the circle determined
    1869  * by p1, p2, and p3.
    1870  * @example
    1871  * var p1 = board.create('point', [0.0, 2.0]);
    1872  * var p2 = board.create('point', [2.0, 1.0]);
    1873  * var p3 = board.create('point', [3.0, 3.0]);
    1874  *
    1875  * var cc1 = board.create('circumcenter', [p1, p2, p3]);
    1876  * </pre><div class="jxgbox" id="JXGe8a40f95-bf30-4eb4-88a8-f4d5495261fd" style="width: 400px; height: 400px;"></div>
    1877  * <script type="text/javascript">
    1878  *   var ccmex1_board = JXG.JSXGraph.initBoard('JXGe8a40f95-bf30-4eb4-88a8-f4d5495261fd', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false});
    1879  *   var ccmex1_p1 = ccmex1_board.create('point', [0.0, 2.0]);
    1880  *   var ccmex1_p2 = ccmex1_board.create('point', [6.0, 1.0]);
    1881  *   var ccmex1_p3 = ccmex1_board.create('point', [3.0, 7.0]);
    1882  *   var ccmex1_cc1 = ccmex1_board.create('circumcenter', [ccmex1_p1, ccmex1_p2, ccmex1_p3]);
    1883  * </script><pre>
    1884  */
    1885 JXG.createCircumcenter = function (board, parents, attributes) {
    1886     var p, i, a, b, c;
    1887
    1888     parents = Type.providePoints(board, parents, attributes, "point");
    1889     if (Type.isPoint(parents[0]) && Type.isPoint(parents[1]) && Type.isPoint(parents[2])) {
    1890         a = parents[0];
    1891         b = parents[1];
    1892         c = parents[2];
    1893
    1894         p = JXG.createPoint(
    1895             board,
    1896             [
    1897                 function () {
    1898                     return Geometry.circumcenter(a, b, c, board);
    1899                 }
    1900             ],
    1901             attributes
    1902         );
    1903
    1904         for (i = 0; i < 3; i++) {
    1905             if (Type.exists(parents[i]._is_new)) {
    1906                 p.addChild(parents[i]);
    1907                 delete parents[i]._is_new;
    1908             } else {
    1909                 parents[i].addChild(p);
    1910             }
    1911         }
    1912
    1913         p.elType = "circumcenter";
    1914         p.setParents(parents);
    1915
    1916         p.generatePolynomial = function () {
    1917             /*
    1918              *  CircumcircleMidpoint takes three points A, B and C  and creates point M, which is the circumcenter of A, B, and C.
    1919              *
    1920              *
    1921              * So we have two conditions:
    1922              *
    1923              *   (a)   CT  ==  AT           (distance condition I)
    1924              *   (b)   BT  ==  AT           (distance condition II)
    1925              *
    1926              */
    1927             var a1 = a.symbolic.x,
    1928                 a2 = a.symbolic.y,
    1929                 b1 = b.symbolic.x,
    1930                 b2 = b.symbolic.y,
    1931                 c1 = c.symbolic.x,
    1932                 c2 = c.symbolic.y,
    1933                 t1 = p.symbolic.x,
    1934                 t2 = p.symbolic.y,
    1935                 poly1 = ["((", t1, ")-(", a1, "))^2+((", t2, ")-(", a2, "))^2-((", t1, ")-(", b1, "))^2-((", t2, ")-(", b2, "))^2"].join(""),
    1936                 poly2 = ["((", t1, ")-(", a1, "))^2+((", t2, ")-(", a2, "))^2-((", t1, ")-(", c1, "))^2-((", t2, ")-(", c2, "))^2"].join("");
    1937
    1938             return [poly1, poly2];
    1939         };
    1940
    1941         return p;
    1942     }
    1943
    1944     throw new Error(
    1945         "JSXGraph: Can't create circumcircle midpoint with parent types '" +
    1946             typeof parents[0] +
    1947             "', '" +
    1948             typeof parents[1] +
    1949             "' and '" +
    1950             typeof parents[2] +
    1951             "'." +
    1952             "\nPossible parent types: [point,point,point]"
    1953     );
    1954 };
    1955
    1956 /**
    1957  * @class Constructs the incenter of the triangle described by the three given points.
    1958  * {@link https://mathworld.wolfram.com/Incenter.html}
    1959  * @pseudo
    1960  * @constructor
    1961  * @name Incenter
    1962  * @type JXG.Point
    1963  * @augments JXG.Point
    1964  * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown.
    1965  * @param {JXG.Point_JXG.Point_JXG.Point} p1,p2,p3 The constructed point is the incenter of the triangle described
    1966  * by p1, p2, and p3.
    1967  * @example
    1968  * var p1 = board.create('point', [0.0, 2.0]);
    1969  * var p2 = board.create('point', [2.0, 1.0]);
    1970  * var p3 = board.create('point', [3.0, 3.0]);
    1971  *
    1972  * var ic1 = board.create('incenter', [p1, p2, p3]);
    1973  * </pre><div class="jxgbox" id="JXGe8a40f95-bf30-4eb4-88a8-a2d5495261fd" style="width: 400px; height: 400px;"></div>
    1974  * <script type="text/javascript">
    1975  *   var icmex1_board = JXG.JSXGraph.initBoard('JXGe8a40f95-bf30-4eb4-88a8-a2d5495261fd', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false});
    1976  *   var icmex1_p1 = icmex1_board.create('point', [0.0, 2.0]);
    1977  *   var icmex1_p2 = icmex1_board.create('point', [6.0, 1.0]);
    1978  *   var icmex1_p3 = icmex1_board.create('point', [3.0, 7.0]);
    1979  *   var icmex1_ic1 = icmex1_board.create('incenter', [icmex1_p1, icmex1_p2, icmex1_p3]);
    1980  * </script><pre>
    1981  */
    1982 JXG.createIncenter = function (board, parents, attributes) {
    1983     var p, A, B, C, i;
    1984
    1985     parents = Type.providePoints(board, parents, attributes, "point");
    1986     if (
    1987         parents.length >= 3 &&
    1988         Type.isPoint(parents[0]) &&
    1989         Type.isPoint(parents[1]) &&
    1990         Type.isPoint(parents[2])
    1991     ) {
    1992         A = parents[0];
    1993         B = parents[1];
    1994         C = parents[2];
    1995
    1996         p = board.create(
    1997             "point",
    1998             [
    1999                 function () {
    2000                     var a, b, c;
    2001
    2002                     a = Mat.hypot(B.X() - C.X(), B.Y() - C.Y());
    2003                     b = Mat.hypot(A.X() - C.X(), A.Y() - C.Y());
    2004                     c = Mat.hypot(B.X() - A.X(), B.Y() - A.Y());
    2005
    2006                     return new Coords(
    2007                         Const.COORDS_BY_USER,
    2008                         [
    2009                             (a * A.X() + b * B.X() + c * C.X()) / (a + b + c),
    2010                             (a * A.Y() + b * B.Y() + c * C.Y()) / (a + b + c)
    2011                         ],
    2012                         board
    2013                     );
    2014                 }
    2015             ],
    2016             attributes
    2017         );
    2018
    2019         for (i = 0; i < 3; i++) {
    2020             if (Type.exists(parents[i]._is_new)) {
    2021                 p.addChild(parents[i]);
    2022                 delete parents[i]._is_new;
    2023             } else {
    2024                 parents[i].addChild(p);
    2025             }
    2026         }
    2027
    2028         p.elType = "incenter";
    2029         p.setParents(parents);
    2030     } else {
    2031         throw new Error(
    2032             "JSXGraph: Can't create incenter with parent types '" +
    2033                 typeof parents[0] +
    2034                 "', '" +
    2035                 typeof parents[1] +
    2036                 "' and '" +
    2037                 typeof parents[2] +
    2038                 "'." +
    2039                 "\nPossible parent types: [point,point,point]"
    2040         );
    2041     }
    2042
    2043     return p;
    2044 };
    2045
    2046 /**
    2047  * @class A circumcircle is given by three points which are all lying on the circle.
    2048  * @pseudo
    2049  * @constructor
    2050  * @name Circumcircle
    2051  * @type JXG.Circle
    2052  * @augments JXG.Circle
    2053  * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown.
    2054  * @param {JXG.Point_JXG.Point_JXG.Point} p1,p2,p3 The constructed element is the circle determined by <tt>p1</tt>, <tt>p2</tt>, and <tt>p3</tt>.
    2055  * @example
    2056  * var p1 = board.create('point', [0.0, 2.0]);
    2057  * var p2 = board.create('point', [2.0, 1.0]);
    2058  * var p3 = board.create('point', [3.0, 3.0]);
    2059  *
    2060  * var cc1 = board.create('circumcircle', [p1, p2, p3]);
    2061  * </pre><div class="jxgbox" id="JXGe65c9861-0bf0-402d-af57-3ab11962f5ac" style="width: 400px; height: 400px;"></div>
    2062  * <script type="text/javascript">
    2063  *   var ccex1_board = JXG.JSXGraph.initBoard('JXGe65c9861-0bf0-402d-af57-3ab11962f5ac', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false});
    2064  *   var ccex1_p1 = ccex1_board.create('point', [0.0, 2.0]);
    2065  *   var ccex1_p2 = ccex1_board.create('point', [6.0, 1.0]);
    2066  *   var ccex1_p3 = ccex1_board.create('point', [3.0, 7.0]);
    2067  *   var ccex1_cc1 = ccex1_board.create('circumcircle', [ccex1_p1, ccex1_p2, ccex1_p3]);
    2068  * </script><pre>
    2069  */
    2070 JXG.createCircumcircle = function (board, parents, attributes) {
    2071     var p, c, attr, i;
    2072
    2073     parents = Type.providePoints(board, parents, attributes, "point");
    2074     if (parents === false) {
    2075         throw new Error(
    2076             "JSXGraph: Can't create circumcircle with parent types '" +
    2077                 typeof parents[0] +
    2078                 "', '" +
    2079                 typeof parents[1] +
    2080                 "' and '" +
    2081                 typeof parents[2] +
    2082                 "'." +
    2083                 "\nPossible parent types: [point,point,point]"
    2084         );
    2085     }
    2086
    2087     try {
    2088         attr = Type.copyAttributes(attributes, board.options, "circumcircle", "center");
    2089         p = JXG.createCircumcenter(board, parents, attr);
    2090
    2091         p.dump = false;
    2092
    2093         if (!Type.exists(attributes.layer)) {
    2094             attributes.layer = board.options.layer.circle;
    2095         }
    2096         attr = Type.copyAttributes(attributes, board.options, "circumcircle");
    2097         c = JXG.createCircle(board, [p, parents[0]], attr);
    2098
    2099         c.elType = "circumcircle";
    2100         c.setParents(parents);
    2101         c.subs = {
    2102             center: p
    2103         };
    2104         c.inherits.push(c);
    2105         for (i = 0; i < 3; i++) {
    2106             if (Type.exists(parents[i]._is_new)) {
    2107                 c.addChild(parents[i]);
    2108                 delete parents[i]._is_new;
    2109             } else {
    2110                 parents[i].addChild(c);
    2111             }
    2112         }
    2113     } catch (e) {
    2114         throw new Error(
    2115             "JSXGraph: Can't create circumcircle with parent types '" +
    2116                 typeof parents[0] +
    2117                 "', '" +
    2118                 typeof parents[1] +
    2119                 "' and '" +
    2120                 typeof parents[2] +
    2121                 "'." +
    2122                 "\nPossible parent types: [point,point,point]"
    2123         );
    2124     }
    2125
    2126     // p is already stored as midpoint in c so there's no need to store it explicitly.
    2127
    2128     return c;
    2129 };
    2130
    2131 /**
    2132  * @class An incircle is given by three points.
    2133  * @pseudo
    2134  * @constructor
    2135  * @name Incircle
    2136  * @type JXG.Circle
    2137  * @augments JXG.Circle
    2138  * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown.
    2139  * @param {JXG.Point_JXG.Point_JXG.Point} p1,p2,p3 The constructed point is the midpoint of the incircle of
    2140  * <tt>p1</tt>, <tt>p2</tt>, and <tt>p3</tt>.
    2141  * @example
    2142  * var p1 = board.create('point', [0.0, 2.0]);
    2143  * var p2 = board.create('point', [2.0, 1.0]);
    2144  * var p3 = board.create('point', [3.0, 3.0]);
    2145  *
    2146  * var ic1 = board.create('incircle', [p1, p2, p3]);
    2147  * </pre><div class="jxgbox" id="JXGe65c9861-0bf0-402d-af57-2ab12962f8ac" style="width: 400px; height: 400px;"></div>
    2148  * <script type="text/javascript">
    2149  *   var icex1_board = JXG.JSXGraph.initBoard('JXGe65c9861-0bf0-402d-af57-2ab12962f8ac', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false});
    2150  *   var icex1_p1 = icex1_board.create('point', [0.0, 2.0]);
    2151  *   var icex1_p2 = icex1_board.create('point', [6.0, 1.0]);
    2152  *   var icex1_p3 = icex1_board.create('point', [3.0, 7.0]);
    2153  *   var icex1_ic1 = icex1_board.create('incircle', [icex1_p1, icex1_p2, icex1_p3]);
    2154  * </script><pre>
    2155  */
    2156 JXG.createIncircle = function (board, parents, attributes) {
    2157     var i, p, c, attr;
    2158
    2159     parents = Type.providePoints(board, parents, attributes, "point");
    2160     if (parents === false) {
    2161         throw new Error(
    2162             "JSXGraph: Can't create circumcircle with parent types '" +
    2163                 typeof parents[0] +
    2164                 "', '" +
    2165                 typeof parents[1] +
    2166                 "' and '" +
    2167                 typeof parents[2] +
    2168                 "'." +
    2169                 "\nPossible parent types: [point,point,point]"
    2170         );
    2171     }
    2172     try {
    2173         attr = Type.copyAttributes(attributes, board.options, "incircle", "center");
    2174         p = JXG.createIncenter(board, parents, attr);
    2175
    2176         p.dump = false;
    2177
    2178         if (!Type.exists(attributes.layer)) {
    2179             attributes.layer = board.options.layer.circle;
    2180         }
    2181         attr = Type.copyAttributes(attributes, board.options, "incircle");
    2182         c = JXG.createCircle(
    2183             board,
    2184             [
    2185                 p,
    2186                 function () {
    2187                     var a = Mat.hypot(parents[1].X() - parents[2].X(), parents[1].Y() - parents[2].Y()),
    2188                         b = Mat.hypot(parents[0].X() - parents[2].X(), parents[0].Y() - parents[2].Y()),
    2189                         c = Mat.hypot(parents[1].X() - parents[0].X(), parents[1].Y() - parents[0].Y()),
    2190                         s = (a + b + c) / 2;
    2191
    2192                     return Math.sqrt(((s - a) * (s - b) * (s - c)) / s);
    2193                 }
    2194             ],
    2195             attr
    2196         );
    2197
    2198         c.elType = "incircle";
    2199         c.setParents(parents);
    2200         for (i = 0; i < 3; i++) {
    2201             if (Type.exists(parents[i]._is_new)) {
    2202                 c.addChild(parents[i]);
    2203                 delete parents[i]._is_new;
    2204             } else {
    2205                 parents[i].addChild(c);
    2206             }
    2207         }
    2208
    2209         /**
    2210          * The center of the incircle
    2211          * @memberOf Incircle.prototype
    2212          * @type Incenter
    2213          * @name center
    2214          */
    2215         c.center = p;
    2216
    2217         c.subs = {
    2218             center: c.center
    2219         };
    2220         c.inherits.push(p);
    2221     } catch (e) {
    2222         throw new Error(
    2223             "JSXGraph: Can't create circumcircle with parent types '" +
    2224                 typeof parents[0] +
    2225                 "', '" +
    2226                 typeof parents[1] +
    2227                 "' and '" +
    2228                 typeof parents[2] +
    2229                 "'." +
    2230                 "\nPossible parent types: [point,point,point]"
    2231         );
    2232     }
    2233
    2234     // p is already stored as midpoint in c so there's no need to store it explicitly.
    2235
    2236     return c;
    2237 };
    2238
    2239 /**
    2240  * @class This element is used to construct reflected elements (points, lines, circles, curves, polygons).
    2241  * @pseudo
    2242  * @description A reflected element (point, polygon, line or curve) is given by a given
    2243  * object of the same type and a line of reflection.
    2244  * It is determined by the reflection of the given element
    2245  * across the given line.
    2246  * @constructor
    2247  * @name Reflection
    2248  * @type JXG.GeometryElement
    2249  * @augments JXG.GeometryElement
    2250  * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown.
    2251  * @param {JXG.Point|JXG.Line|JXG.Curve|JXG.Polygon_JXG.Line} p,l The reflection element is the reflection of p across the line l.
    2252  * @example
    2253  * var p1 = board.create('point', [0.0, 4.0]);
    2254  * var p2 = board.create('point', [6.0, 1.0]);
    2255  * var l1 = board.create('line', [p1, p2]);
    2256  * var p3 = board.create('point', [3.0, 3.0]);
    2257  *
    2258  * var rp1 = board.create('reflection', [p3, l1]);
    2259  * </pre><div class="jxgbox" id="JXG087a798e-a36a-4f52-a2b4-29a23a69393b" style="width: 400px; height: 400px;"></div>
    2260  * <script type="text/javascript">
    2261  *   var rpex1_board = JXG.JSXGraph.initBoard('JXG087a798e-a36a-4f52-a2b4-29a23a69393b', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false});
    2262  *   var rpex1_p1 = rpex1_board.create('point', [0.0, 4.0]);
    2263  *   var rpex1_p2 = rpex1_board.create('point', [6.0, 1.0]);
    2264  *   var rpex1_l1 = rpex1_board.create('line', [rpex1_p1, rpex1_p2]);
    2265  *   var rpex1_p3 = rpex1_board.create('point', [3.0, 3.0]);
    2266  *   var rpex1_rp1 = rpex1_board.create('reflection', [rpex1_p3, rpex1_l1]);
    2267  * </script><pre>
    2268  * @example
    2269  *         // Reflection of more elements
    2270  *         // reflection line
    2271  *         var li = board.create('line', [1,1,1], {strokeColor: '#aaaaaa'});
    2272  *
    2273  *         var p1 = board.create('point', [-3,-1], {name: "A"});
    2274  *         var q1 = board.create('reflection', [p1, li], {name: "A'"});
    2275  *
    2276  *         var l1 = board.create('line', [1,-5,1]);
    2277  *         var l2 = board.create('reflection', [l1, li]);
    2278  *
    2279  *         var cu1 = board.create('curve', [[-3, -3, -2.5, -3, -3, -2.5], [-3, -2, -2, -2, -2.5, -2.5]], {strokeWidth:3});
    2280  *         var cu2 = board.create('reflection', [cu1, li], {strokeColor: 'red', strokeWidth:3});
    2281  *
    2282  *         var pol1 = board.create('polygon', [[-6,-3], [-4,-5], [-5,-1.5]]);
    2283  *         var pol2 = board.create('reflection', [pol1, li]);
    2284  *
    2285  *         var c1 = board.create('circle', [[-2,-2], [-2, -1]]);
    2286  *         var c2 = board.create('reflection', [c1, li]);
    2287  *
    2288  *         var a1 = board.create('arc', [[1, 1], [0, 1], [1, 0]], {strokeColor: 'red'});
    2289  *         var a2 = board.create('reflection', [a1, li], {strokeColor: 'red'});
    2290  *
    2291  *         var s1 = board.create('sector', [[-3.5,-3], [-3.5, -2], [-3.5,-4]], {
    2292  *                           anglePoint: {visible:true}, center: {visible: true}, radiusPoint: {visible: true},
    2293  *                           fillColor: 'yellow', strokeColor: 'black'});
    2294  *         var s2 = board.create('reflection', [s1, li], {fillColor: 'yellow', strokeColor: 'black', fillOpacity: 0.5});
    2295  *
    2296  *         var an1 = board.create('angle', [[-4,3.9], [-3, 4], [-3, 3]]);
    2297  *         var an2 = board.create('reflection', [an1, li]);
    2298  *
    2299  * </pre><div id="JXG8f763af4-d449-11e7-93b3-901b0e1b8723" class="jxgbox" style="width: 300px; height: 300px;"></div>
    2300  * <script type="text/javascript">
    2301  *     (function() {
    2302  *         var board = JXG.JSXGraph.initBoard('JXG8f763af4-d449-11e7-93b3-901b0e1b8723',
    2303  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
    2304  *             // reflection line
    2305  *             var li = board.create('line', [1,1,1], {strokeColor: '#aaaaaa'});
    2306  *
    2307  *             var p1 = board.create('point', [-3,-1], {name: "A"});
    2308  *             var q1 = board.create('reflection', [p1, li], {name: "A'"});
    2309  *
    2310  *             var l1 = board.create('line', [1,-5,1]);
    2311  *             var l2 = board.create('reflection', [l1, li]);
    2312  *
    2313  *             var cu1 = board.create('curve', [[-3, -3, -2.5, -3, -3, -2.5], [-3, -2, -2, -2, -2.5, -2.5]], {strokeWidth:3});
    2314  *             var cu2 = board.create('reflection', [cu1, li], {strokeColor: 'red', strokeWidth:3});
    2315  *
    2316  *             var pol1 = board.create('polygon', [[-6,-3], [-4,-5], [-5,-1.5]]);
    2317  *             var pol2 = board.create('reflection', [pol1, li]);
    2318  *
    2319  *             var c1 = board.create('circle', [[-2,-2], [-2, -1]]);
    2320  *             var c2 = board.create('reflection', [c1, li]);
    2321  *
    2322  *         var a1 = board.create('arc', [[1, 1], [0, 1], [1, 0]], {strokeColor: 'red'});
    2323  *         var a2 = board.create('reflection', [a1, li], {strokeColor: 'red'});
    2324  *
    2325  *         var s1 = board.create('sector', [[-3.5,-3], [-3.5, -2], [-3.5,-4]], {
    2326  *                           anglePoint: {visible:true}, center: {visible: true}, radiusPoint: {visible: true},
    2327  *                           fillColor: 'yellow', strokeColor: 'black'});
    2328  *         var s2 = board.create('reflection', [s1, li], {fillColor: 'yellow', strokeColor: 'black', fillOpacity: 0.5});
    2329  *
    2330  *         var an1 = board.create('angle', [[-4,3.9], [-3, 4], [-3, 3]]);
    2331  *         var an2 = board.create('reflection', [an1, li]);
    2332  *
    2333  *     })();
    2334  *
    2335  * </script><pre>
    2336  *
    2337  */
    2338 JXG.createReflection = function (board, parents, attributes) {
    2339     var l, org, r, r_c,
    2340         t, i, attr, attr2,
    2341         errStr = "\nPossible parent types: [point|line|curve|polygon|circle|arc|sector, line]";
    2342
    2343     for (i = 0; i < parents.length; ++i) {
    2344         parents[i] = board.select(parents[i]);
    2345     }
    2346
    2347     attr = Type.copyAttributes(attributes, board.options, "reflection");
    2348
    2349     if (Type.isPoint(parents[0])) {
    2350         org = Type.providePoints(board, [parents[0]], attr2)[0];
    2351     } else if (
    2352         parents[0].elementClass === Const.OBJECT_CLASS_CURVE ||
    2353         parents[0].elementClass === Const.OBJECT_CLASS_LINE ||
    2354         parents[0].type === Const.OBJECT_TYPE_POLYGON ||
    2355         parents[0].elementClass === Const.OBJECT_CLASS_CIRCLE
    2356     ) {
    2357         org = parents[0];
    2358     } else {
    2359         throw new Error(
    2360             "JSXGraph: Can't create reflection element with parent types '" +
    2361                 typeof parents[0] +
    2362                 "' and '" +
    2363                 typeof parents[1] +
    2364                 "'." +
    2365                 errStr
    2366         );
    2367     }
    2368
    2369     if (parents[1].elementClass === Const.OBJECT_CLASS_LINE) {
    2370         l = parents[1];
    2371     } else {
    2372         throw new Error(
    2373             "JSXGraph: Can't create reflected element with parent types '" +
    2374                 typeof parents[0] +
    2375                 "' and '" +
    2376                 typeof parents[1] +
    2377                 "'." +
    2378                 errStr
    2379         );
    2380     }
    2381     t = JXG.createTransform(board, [l], { type: "reflect" });
    2382
    2383     if (Type.isPoint(org)) {
    2384         r = JXG.createPoint(board, [org, t], attr);
    2385
    2386         // Arcs and sectors are treated as curves
    2387     } else if (org.elementClass === Const.OBJECT_CLASS_CURVE) {
    2388         r = JXG.createCurve(board, [org, t], attr);
    2389     } else if (org.elementClass === Const.OBJECT_CLASS_LINE) {
    2390         r = JXG.createLine(board, [org, t], attr);
    2391     } else if (org.type === Const.OBJECT_TYPE_POLYGON) {
    2392         r = JXG.createPolygon(board, [org, t], attr);
    2393     } else if (org.elementClass === Const.OBJECT_CLASS_CIRCLE) {
    2394         if (attr.type.toLowerCase() === "euclidean") {
    2395             // Create a circle element from a circle and a Euclidean transformation
    2396             attr2 = Type.copyAttributes(attributes, board.options, "reflection", "center");
    2397             r_c = JXG.createPoint(board, [org.center, t], attr2);
    2398             r_c.prepareUpdate()
    2399                 .update()
    2400                 .updateVisibility(Type.evaluate(r_c.visProp.visible))
    2401                 .updateRenderer();
    2402             r = JXG.createCircle(
    2403                 board,
    2404                 [
    2405                     r_c,
    2406                     function () {
    2407                         return org.Radius();
    2408                     }
    2409                 ],
    2410                 attr
    2411             );
    2412         } else {
    2413             // Create a conic element from a circle and a projective transformation
    2414             r = JXG.createCircle(board, [org, t], attr);
    2415         }
    2416     } else {
    2417         throw new Error(
    2418             "JSXGraph: Can't create reflected element with parent types '" +
    2419                 typeof parents[0] +
    2420                 "' and '" +
    2421                 typeof parents[1] +
    2422                 "'." +
    2423                 errStr
    2424         );
    2425     }
    2426
    2427     if (Type.exists(org._is_new)) {
    2428         r.addChild(org);
    2429         delete org._is_new;
    2430     } else {
    2431         // org.addChild(r);
    2432     }
    2433     l.addChild(r);
    2434
    2435     r.elType = "reflection";
    2436     r.addParents(l);
    2437     r.prepareUpdate().update(); //.updateVisibility(Type.evaluate(r.visProp.visible)).updateRenderer();
    2438
    2439     if (Type.isPoint(r)) {
    2440         r.generatePolynomial = function () {
    2441             /*
    2442              *  Reflection takes a point R and a line L and creates point P, which is the reflection of R on L.
    2443              *  L is defined by two points A and B.
    2444              *
    2445              * So we have two conditions:
    2446              *
    2447              *   (a)   RP  _|_  AB            (orthogonality condition)
    2448              *   (b)   AR  ==   AP            (distance condition)
    2449              *
    2450              */
    2451             var a1 = l.point1.symbolic.x,
    2452                 a2 = l.point1.symbolic.y,
    2453                 b1 = l.point2.symbolic.x,
    2454                 b2 = l.point2.symbolic.y,
    2455                 p1 = org.symbolic.x,
    2456                 p2 = org.symbolic.y,
    2457                 r1 = r.symbolic.x,
    2458                 r2 = r.symbolic.y,
    2459                 poly1 = ["((", r2, ")-(", p2, "))*((", a2, ")-(", b2, "))+((", a1, ")-(", b1, "))*((", r1, ")-(", p1, "))"].join(""),
    2460                 poly2 = ["((", r1, ")-(", a1, "))^2+((", r2, ")-(", a2, "))^2-((", p1, ")-(", a1, "))^2-((", p2, ")-(", a2, "))^2"].join("");
    2461
    2462             return [poly1, poly2];
    2463         };
    2464     }
    2465
    2466     return r;
    2467 };
    2468
    2469 /**
    2470  * @class A mirror element of a point, line, circle, curve, polygon will be constructed.
    2471  * @pseudo
    2472  * @description A mirror element is determined by the reflection of a given point, line, circle, curve, polygon across another given point.
    2473  * @constructor
    2474  * @name mirrorelement
    2475  * @type JXG.GeometryElement
    2476  * @augments JXG.GeometryElement
    2477  * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown.
    2478  * @param {JXG.Point|JXG.Line|JXG.Curve|JXG.Ppolygon_JXG.Point} p1,p2 The constructed element is the mirror image of p2 across p1.
    2479  * @example
    2480  *         // point of reflection
    2481  *         var mirr = board.create('point', [-1,-1], {color: '#aaaaaa'});
    2482  *
    2483  *         var p1 = board.create('point', [-3,-1], {name: "A"});
    2484  *         var q1 = board.create('mirrorelement', [p1, mirr], {name: "A'"});
    2485  *
    2486  *         var l1 = board.create('line', [1, -5, 1]);
    2487  *         var l2 = board.create('mirrorelement', [l1, mirr]);
    2488  *
    2489  *         var cu1 = board.create('curve', [[-3, -3, -2.5, -3, -3, -2.5], [-3, -2, -2, -2, -2.5, -2.5]], {strokeWidth:3});
    2490  *         var cu2 = board.create('mirrorelement', [cu1, mirr], {strokeColor: 'red', strokeWidth:3});
    2491  *
    2492  *         var pol1 = board.create('polygon', [[-6,-2], [-4,-4], [-5,-0.5]]);
    2493  *         var pol2 = board.create('mirrorelement', [pol1, mirr]);
    2494  *
    2495  *         var c1 = board.create('circle', [[-6,-6], [-6, -5]]);
    2496  *         var c2 = board.create('mirrorelement', [c1, mirr]);
    2497  *
    2498  *         var a1 = board.create('arc', [[1, 1], [0, 1], [1, 0]], {strokeColor: 'red'});
    2499  *         var a2 = board.create('mirrorelement', [a1, mirr], {strokeColor: 'red'});
    2500  *
    2501  *         var s1 = board.create('sector', [[-3.5,-3], [-3.5, -2], [-3.5,-4]], {
    2502  *                           anglePoint: {visible:true}, center: {visible: true}, radiusPoint: {visible: true},
    2503  *                           fillColor: 'yellow', strokeColor: 'black'});
    2504  *         var s2 = board.create('mirrorelement', [s1, mirr], {fillColor: 'yellow', strokeColor: 'black', fillOpacity: 0.5});
    2505  *
    2506  *         var an1 = board.create('angle', [[-4,3.9], [-3, 4], [-3, 3]]);
    2507  *         var an2 = board.create('mirrorelement', [an1, mirr]);
    2508  *
    2509  *
    2510  * </pre><div id="JXG026c779c-d8d9-11e7-93b3-901b0e1b8723" class="jxgbox" style="width: 300px; height: 300px;"></div>
    2511  * <script type="text/javascript">
    2512  *     (function() {
    2513  *         var board = JXG.JSXGraph.initBoard('JXG026c779c-d8d9-11e7-93b3-901b0e1b8723',
    2514  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
    2515  *             // point of reflection
    2516  *             var mirr = board.create('point', [-1,-1], {color: '#aaaaaa'});
    2517  *
    2518  *             var p1 = board.create('point', [-3,-1], {name: "A"});
    2519  *             var q1 = board.create('mirrorelement', [p1, mirr], {name: "A'"});
    2520  *
    2521  *             var l1 = board.create('line', [1,-5, 1]);
    2522  *             var l2 = board.create('mirrorelement', [l1, mirr]);
    2523  *
    2524  *             var cu1 = board.create('curve', [[-3, -3, -2.5, -3, -3, -2.5], [-3, -2, -2, -2, -2.5, -2.5]], {strokeWidth:3});
    2525  *             var cu2 = board.create('mirrorelement', [cu1, mirr], {strokeColor: 'red', strokeWidth:3});
    2526  *
    2527  *             var pol1 = board.create('polygon', [[-6,-2], [-4,-4], [-5,-0.5]]);
    2528  *             var pol2 = board.create('mirrorelement', [pol1, mirr]);
    2529  *
    2530  *             var c1 = board.create('circle', [[-6,-6], [-6, -5]]);
    2531  *             var c2 = board.create('mirrorelement', [c1, mirr]);
    2532  *
    2533  *         var a1 = board.create('arc', [[1, 1], [0, 1], [1, 0]], {strokeColor: 'red'});
    2534  *         var a2 = board.create('mirrorelement', [a1, mirr], {strokeColor: 'red'});
    2535  *
    2536  *         var s1 = board.create('sector', [[-3.5,-3], [-3.5, -2], [-3.5,-4]], {
    2537  *                           anglePoint: {visible:true}, center: {visible: true}, radiusPoint: {visible: true},
    2538  *                           fillColor: 'yellow', strokeColor: 'black'});
    2539  *         var s2 = board.create('mirrorelement', [s1, mirr], {fillColor: 'yellow', strokeColor: 'black', fillOpacity: 0.5});
    2540  *
    2541  *         var an1 = board.create('angle', [[-4,3.9], [-3, 4], [-3, 3]]);
    2542  *         var an2 = board.create('mirrorelement', [an1, mirr]);
    2543  *
    2544  *     })();
    2545  *
    2546  * </script><pre>
    2547  */
    2548 JXG.createMirrorElement = function (board, parents, attributes) {
    2549     var org, i, m, r, r_c, t,
    2550         attr, attr2,
    2551         errStr = "\nPossible parent types: [point|line|curve|polygon|circle|arc|sector, point]";
    2552
    2553     for (i = 0; i < parents.length; ++i) {
    2554         parents[i] = board.select(parents[i]);
    2555     }
    2556
    2557     attr = Type.copyAttributes(attributes, board.options, "mirrorelement");
    2558     if (Type.isPoint(parents[0])) {
    2559         // Create point to be mirrored if supplied by coords array.
    2560         org = Type.providePoints(board, [parents[0]], attr)[0];
    2561     } else if (
    2562         parents[0].elementClass === Const.OBJECT_CLASS_CURVE ||
    2563         parents[0].elementClass === Const.OBJECT_CLASS_LINE ||
    2564         parents[0].type === Const.OBJECT_TYPE_POLYGON ||
    2565         parents[0].elementClass === Const.OBJECT_CLASS_CIRCLE
    2566     ) {
    2567         org = parents[0];
    2568     } else {
    2569         throw new Error(
    2570             "JSXGraph: Can't create mirror element with parent types '" +
    2571                 typeof parents[0] +
    2572                 "' and '" +
    2573                 typeof parents[1] +
    2574                 "'." +
    2575                 errStr
    2576         );
    2577     }
    2578
    2579     if (Type.isPoint(parents[1])) {
    2580         attr2 = Type.copyAttributes(attributes, board.options, "mirrorelement", "point");
    2581         // Create mirror point if supplied by coords array.
    2582         m = Type.providePoints(board, [parents[1]], attr2)[0];
    2583     } else {
    2584         throw new Error(
    2585             "JSXGraph: Can't create mirror element with parent types '" +
    2586                 typeof parents[0] +
    2587                 "' and '" +
    2588                 typeof parents[1] +
    2589                 "'." +
    2590                 errStr
    2591         );
    2592     }
    2593
    2594     t = JXG.createTransform(board, [Math.PI, m], { type: "rotate" });
    2595     if (Type.isPoint(org)) {
    2596         r = JXG.createPoint(board, [org, t], attr);
    2597
    2598         // Arcs and sectors are treated as curves
    2599     } else if (org.elementClass === Const.OBJECT_CLASS_CURVE) {
    2600         r = JXG.createCurve(board, [org, t], attr);
    2601     } else if (org.elementClass === Const.OBJECT_CLASS_LINE) {
    2602         r = JXG.createLine(board, [org, t], attr);
    2603     } else if (org.type === Const.OBJECT_TYPE_POLYGON) {
    2604         r = JXG.createPolygon(board, [org, t], attr);
    2605     } else if (org.elementClass === Const.OBJECT_CLASS_CIRCLE) {
    2606         if (attr.type.toLowerCase() === "euclidean") {
    2607             // Create a circle element from a circle and a Euclidean transformation
    2608             attr2 = Type.copyAttributes(attributes, board.options, "mirrorelement", "center");
    2609             r_c = JXG.createPoint(board, [org.center, t], attr2);
    2610             r_c.prepareUpdate()
    2611                 .update()
    2612                 .updateVisibility(Type.evaluate(r_c.visProp.visible))
    2613                 .updateRenderer();
    2614             r = JXG.createCircle(
    2615                 board,
    2616                 [
    2617                     r_c,
    2618                     function () {
    2619                         return org.Radius();
    2620                     }
    2621                 ],
    2622                 attr
    2623             );
    2624         } else {
    2625             // Create a conic element from a circle and a projective transformation
    2626             r = JXG.createCircle(board, [org, t], attr);
    2627         }
    2628     } else {
    2629         throw new Error(
    2630             "JSXGraph: Can't create mirror element with parent types '" +
    2631                 typeof parents[0] +
    2632                 "' and '" +
    2633                 typeof parents[1] +
    2634                 "'." +
    2635                 errStr
    2636         );
    2637     }
    2638
    2639     if (Type.exists(org._is_new)) {
    2640         r.addChild(org);
    2641         delete org._is_new;
    2642     } else {
    2643         // org.addChild(r);
    2644     }
    2645     m.addChild(r);
    2646
    2647     r.elType = "mirrorelement";
    2648     r.addParents(m);
    2649     r.prepareUpdate().update();
    2650
    2651     return r;
    2652 };
    2653
    2654 /**
    2655  * @class A mirror point will be constructed.
    2656  * @pseudo
    2657  * @description A mirror point is determined by the reflection of a given point against another given point.
    2658  * @constructor
    2659  * @name Mirrorpoint
    2660  * @type JXG.Point
    2661  * @augments JXG.Point
    2662  * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown.
    2663  * @param {JXG.Point_JXG.Point} p1,p2 The constructed point is the reflection of p2 against p1.
    2664  *
    2665  * This method is superseeded by the more general {@link JXG.createMirrorElement}.
    2666  * @example
    2667  * var p1 = board.create('point', [3.0, 3.0]);
    2668  * var p2 = board.create('point', [6.0, 1.0]);
    2669  *
    2670  * var mp1 = board.create('mirrorpoint', [p1, p2]);
    2671  * </pre><div class="jxgbox" id="JXG7eb2a814-6c4b-4caa-8cfa-4183a948d25b" style="width: 400px; height: 400px;"></div>
    2672  * <script type="text/javascript">
    2673  *   var mpex1_board = JXG.JSXGraph.initBoard('JXG7eb2a814-6c4b-4caa-8cfa-4183a948d25b', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false});
    2674  *   var mpex1_p1 = mpex1_board.create('point', [3.0, 3.0]);
    2675  *   var mpex1_p2 = mpex1_board.create('point', [6.0, 1.0]);
    2676  *   var mpex1_mp1 = mpex1_board.create('mirrorpoint', [mpex1_p1, mpex1_p2]);
    2677  * </script><pre>
    2678  */
    2679 JXG.createMirrorPoint = function (board, parents, attributes) {
    2680     var el = JXG.createMirrorElement(board, parents, attributes);
    2681     el.elType = "mirrorpoint";
    2682     return el;
    2683 };
    2684
    2685 /**
    2686  * @class This element is used to visualize the integral of a given curve over a given interval.
    2687  * @pseudo
    2688  * @description The Integral element is used to visualize the area under a given curve over a given interval
    2689  * and to calculate the area's value. For that a polygon and gliders are used. The polygon displays the area,
    2690  * the gliders are used to change the interval dynamically.
    2691  * @constructor
    2692  * @name Integral
    2693  * @type JXG.Curve
    2694  * @augments JXG.Curve
    2695  * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown.
    2696  * @param {Array_JXG.Curve} i,c The constructed element covers the area between the curve <tt>c</tt> and the x-axis
    2697  * within the interval <tt>i</tt>.
    2698  * @example
    2699  * var c1 = board.create('functiongraph', [function (t) { return Math.cos(t)*t; }]);
    2700  * var i1 = board.create('integral', [[-2.0, 2.0], c1]);
    2701  * </pre><div class="jxgbox" id="JXGd45d7188-6624-4d6e-bebb-1efa2a305c8a" style="width: 400px; height: 400px;"></div>
    2702  * <script type="text/javascript">
    2703  *   var intex1_board = JXG.JSXGraph.initBoard('JXGd45d7188-6624-4d6e-bebb-1efa2a305c8a', {boundingbox: [-5, 5, 5, -5], axis: true, showcopyright: false, shownavigation: false});
    2704  *   var intex1_c1 = intex1_board.create('functiongraph', [function (t) { return Math.cos(t)*t; }]);
    2705  *   var intex1_i1 = intex1_board.create('integral', [[-2.0, 2.0], intex1_c1]);
    2706  * </script><pre>
    2707  */
    2708 JXG.createIntegral = function (board, parents, attributes) {
    2709     var interval, curve, attr, start, end,
    2710         startx, starty, endx, endy,
    2711         pa_on_curve, pa_on_axis, pb_on_curve, pb_on_axis,
    2712         txt_fun,
    2713         t = null, p;
    2714
    2715     if (Type.isArray(parents[0]) && parents[1].elementClass === Const.OBJECT_CLASS_CURVE) {
    2716         interval = parents[0];
    2717         curve = parents[1];
    2718     } else if (
    2719         Type.isArray(parents[1]) &&
    2720         parents[0].elementClass === Const.OBJECT_CLASS_CURVE
    2721     ) {
    2722         interval = parents[1];
    2723         curve = parents[0];
    2724     } else {
    2725         throw new Error(
    2726             "JSXGraph: Can't create integral with parent types '" +
    2727                 typeof parents[0] +
    2728                 "' and '" +
    2729                 typeof parents[1] +
    2730                 "'." +
    2731                 "\nPossible parent types: [[number|function,number|function],curve]"
    2732         );
    2733     }
    2734
    2735     attr = Type.copyAttributes(attributes, board.options, "integral");
    2736     attr.withLabel = false; // There is a custom 'label' below.
    2737     p = board.create("curve", [[0], [0]], attr);
    2738
    2739     // Correct the interval if necessary - NOT ANYMORE, GGB's fault
    2740     start = interval[0];
    2741     end = interval[1];
    2742
    2743     if (Type.isFunction(start)) {
    2744         startx = start;
    2745         starty = function () {
    2746             return curve.Y(startx());
    2747         };
    2748         start = startx();
    2749     } else {
    2750         startx = start;
    2751         starty = curve.Y(start);
    2752     }
    2753
    2754     if (Type.isFunction(end)) {
    2755         endx = end;
    2756         endy = function () {
    2757             return curve.Y(endx());
    2758         };
    2759         end = endx();
    2760     } else {
    2761         endx = end;
    2762         endy = curve.Y(end);
    2763     }
    2764
    2765     attr = Type.copyAttributes(attributes, board.options, "integral", "curveLeft");
    2766     pa_on_curve = board.create("glider", [startx, starty, curve], attr);
    2767     if (Type.isFunction(startx)) {
    2768         pa_on_curve.hideElement();
    2769     }
    2770
    2771     attr = Type.copyAttributes(attributes, board.options, 'integral', 'baseLeft');
    2772     pa_on_axis = board.create('point', [
    2773             function () {
    2774                 if (Type.evaluate(p.visProp.axis) === "y") {
    2775                     return 0;
    2776                 }
    2777                 return pa_on_curve.X();
    2778             },
    2779             function () {
    2780                 if (Type.evaluate(p.visProp.axis) === "y") {
    2781                     return pa_on_curve.Y();
    2782                 }
    2783                 return 0;
    2784             }
    2785         ], attr);
    2786
    2787     attr = Type.copyAttributes(attributes, board.options, "integral", "curveRight");
    2788     pb_on_curve = board.create("glider", [endx, endy, curve], attr);
    2789     if (Type.isFunction(endx)) {
    2790         pb_on_curve.hideElement();
    2791     }
    2792
    2793     attr = Type.copyAttributes(attributes, board.options, "integral", "baseRight");
    2794     pb_on_axis = board.create('point', [
    2795             function () {
    2796                 if (Type.evaluate(p.visProp.axis) === "y") {
    2797                     return 0;
    2798                 }
    2799                 return pb_on_curve.X();
    2800             },
    2801             function () {
    2802                 if (Type.evaluate(p.visProp.axis) === "y") {
    2803                     return pb_on_curve.Y();
    2804                 }
    2805
    2806                 return 0;
    2807             }
    2808         ], attr);
    2809
    2810     attr = Type.copyAttributes(attributes, board.options, "integral");
    2811     if (attr.withlabel !== false && attr.axis !== "y") {
    2812         attr = Type.copyAttributes(attributes, board.options, "integral", "label");
    2813         attr = Type.copyAttributes(attr, board.options, "label");
    2814
    2815         t = board.create('text', [
    2816                 function () {
    2817                     var off = new Coords(
    2818                             Const.COORDS_BY_SCREEN,
    2819                             [
    2820                                 Type.evaluate(this.visProp.offset[0]) +
    2821                                     this.board.origin.scrCoords[1],
    2822                                 0
    2823                             ],
    2824                             this.board,
    2825                             false
    2826                         ),
    2827                         bb = this.board.getBoundingBox(),
    2828                         dx = (bb[2] - bb[0]) * 0.1,
    2829                         x = pb_on_curve.X();
    2830
    2831                     if (x < bb[0]) {
    2832                         x = bb[0] + dx;
    2833                     } else if (x > bb[2]) {
    2834                         x = bb[2] - dx;
    2835                     }
    2836
    2837                     return x + off.usrCoords[1];
    2838                 },
    2839                 function () {
    2840                     var off = new Coords(
    2841                             Const.COORDS_BY_SCREEN,
    2842                             [
    2843                                 0,
    2844                                 Type.evaluate(this.visProp.offset[1]) +
    2845                                     this.board.origin.scrCoords[2]
    2846                             ],
    2847                             this.board,
    2848                             false
    2849                         ),
    2850                         bb = this.board.getBoundingBox(),
    2851                         dy = (bb[1] - bb[3]) * 0.1,
    2852                         y = pb_on_curve.Y();
    2853
    2854                     if (y > bb[1]) {
    2855                         y = bb[1] - dy;
    2856                     } else if (y < bb[3]) {
    2857                         y = bb[3] + dy;
    2858                     }
    2859
    2860                     return y + off.usrCoords[2];
    2861                 },
    2862                 ''
    2863             ], attr);
    2864
    2865         txt_fun = function () {
    2866             var Int = Numerics.NewtonCotes([pa_on_axis.X(), pb_on_axis.X()], curve.Y),
    2867                 digits = Type.evaluate(t.visProp.digits),
    2868                 val;
    2869
    2870             if (t.useLocale()) {
    2871                 val = t.formatNumberLocale(Int, digits);
    2872             } else {
    2873                 val = Type.toFixed(Int, digits);
    2874             }
    2875             return '∫ = ' + val;
    2876         };
    2877         t.setText(txt_fun);
    2878         t.dump = false;
    2879
    2880         pa_on_curve.addChild(t);
    2881         pb_on_curve.addChild(t);
    2882     }
    2883
    2884     // dump stuff
    2885     pa_on_curve.dump = false;
    2886     pa_on_axis.dump = false;
    2887
    2888     pb_on_curve.dump = false;
    2889     pb_on_axis.dump = false;
    2890
    2891     p.elType = "integral";
    2892     p.setParents([curve.id, interval]);
    2893     p.subs = {
    2894         curveLeft: pa_on_curve,
    2895         baseLeft: pa_on_axis,
    2896         curveRight: pb_on_curve,
    2897         baseRight: pb_on_axis
    2898     };
    2899     p.inherits.push(pa_on_curve, pa_on_axis, pb_on_curve, pb_on_axis);
    2900
    2901     if (attr.withLabel) {
    2902         p.subs.label = t;
    2903         p.inherits.push(t);
    2904     }
    2905
    2906     /**
    2907      * Returns the current value of the integral.
    2908      * @memberOf Integral
    2909      * @name Value
    2910      * @function
    2911      * @returns {Number}
    2912      */
    2913     p.Value = function () {
    2914         return Numerics.I([pa_on_axis.X(), pb_on_axis.X()], curve.Y);
    2915     };
    2916
    2917     /**
    2918      * documented in JXG.Curve
    2919      * @class
    2920      * @ignore
    2921      */
    2922     p.updateDataArray = function () {
    2923         var x, y, i, left, right, lowx, upx, lowy, upy;
    2924
    2925         if (Type.evaluate(this.visProp.axis) === "y") {
    2926             if (pa_on_curve.Y() < pb_on_curve.Y()) {
    2927                 lowx = pa_on_curve.X();
    2928                 lowy = pa_on_curve.Y();
    2929                 upx = pb_on_curve.X();
    2930                 upy = pb_on_curve.Y();
    2931             } else {
    2932                 lowx = pb_on_curve.X();
    2933                 lowy = pb_on_curve.Y();
    2934                 upx = pa_on_curve.X();
    2935                 upy = pa_on_curve.Y();
    2936             }
    2937             left = Math.min(lowx, upx);
    2938             right = Math.max(lowx, upx);
    2939
    2940             x = [0, lowx];
    2941             y = [lowy, lowy];
    2942
    2943             for (i = 0; i < curve.numberPoints; i++) {
    2944                 if (
    2945                     lowy <= curve.points[i].usrCoords[2] &&
    2946                     left <= curve.points[i].usrCoords[1] &&
    2947                     curve.points[i].usrCoords[2] <= upy &&
    2948                     curve.points[i].usrCoords[1] <= right
    2949                 ) {
    2950                     x.push(curve.points[i].usrCoords[1]);
    2951                     y.push(curve.points[i].usrCoords[2]);
    2952                 }
    2953             }
    2954             x.push(upx);
    2955             y.push(upy);
    2956             x.push(0);
    2957             y.push(upy);
    2958
    2959             // close the curve
    2960             x.push(0);
    2961             y.push(lowy);
    2962         } else {
    2963             if (pa_on_axis.X() < pb_on_axis.X()) {
    2964                 left = pa_on_axis.X();
    2965                 right = pb_on_axis.X();
    2966             } else {
    2967                 left = pb_on_axis.X();
    2968                 right = pa_on_axis.X();
    2969             }
    2970
    2971             x = [left, left];
    2972             y = [0, curve.Y(left)];
    2973
    2974             for (i = 0; i < curve.numberPoints; i++) {
    2975                 if (
    2976                     left <= curve.points[i].usrCoords[1] &&
    2977                     curve.points[i].usrCoords[1] <= right
    2978                 ) {
    2979                     x.push(curve.points[i].usrCoords[1]);
    2980                     y.push(curve.points[i].usrCoords[2]);
    2981                 }
    2982             }
    2983             x.push(right);
    2984             y.push(curve.Y(right));
    2985             x.push(right);
    2986             y.push(0);
    2987
    2988             // close the curve
    2989             x.push(left);
    2990             y.push(0);
    2991         }
    2992
    2993         this.dataX = x;
    2994         this.dataY = y;
    2995     };
    2996
    2997     pa_on_curve.addChild(p);
    2998     pb_on_curve.addChild(p);
    2999     pa_on_axis.addChild(p);
    3000     pb_on_axis.addChild(p);
    3001
    3002     /**
    3003      * The point on the axis initially corresponding to the lower value of the interval.
    3004      *
    3005      * @name baseLeft
    3006      * @memberOf Integral
    3007      * @type JXG.Point
    3008      */
    3009     p.baseLeft = pa_on_axis;
    3010
    3011     /**
    3012      * The point on the axis initially corresponding to the higher value of the interval.
    3013      *
    3014      * @name baseRight
    3015      * @memberOf Integral
    3016      * @type JXG.Point
    3017      */
    3018     p.baseRight = pb_on_axis;
    3019
    3020     /**
    3021      * The glider on the curve corresponding to the lower value of the interval.
    3022      *
    3023      * @name curveLeft
    3024      * @memberOf Integral
    3025      * @type Glider
    3026      */
    3027     p.curveLeft = pa_on_curve;
    3028
    3029     /**
    3030      * The glider on the axis corresponding to the higher value of the interval.
    3031      *
    3032      * @name curveRight
    3033      * @memberOf Integral
    3034      * @type Glider
    3035      */
    3036     p.curveRight = pb_on_curve;
    3037
    3038     p.methodMap = JXG.deepCopy(p.methodMap, {
    3039         curveLeft: "curveLeft",
    3040         baseLeft: "baseLeft",
    3041         curveRight: "curveRight",
    3042         baseRight: "baseRight",
    3043         Value: "Value"
    3044     });
    3045
    3046     /**
    3047      * documented in GeometryElement
    3048      * @ignore
    3049      */
    3050     p.label = t;
    3051
    3052     return p;
    3053 };
    3054
    3055 /**
    3056  * @class Creates a grid to support the user with element placement.
    3057  * @pseudo
    3058  * @description A grid is a set of vertical and horizontal lines to support the user with element placement. This method
    3059  * draws such a grid on the given board. This method does not
    3060  * take any parent elements. It is usually instantiated on the board's creation via the attribute <tt>grid</tt> set
    3061  * to true.
    3062  * @parameter None.
    3063  * @constructor
    3064  * @name Grid
    3065  * @type JXG.Curve
    3066  * @augments JXG.Curve
    3067  * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown.
    3068  * @example
    3069  * grid = board.create('grid', []);
    3070  * </pre><div class="jxgbox" id="JXGa9a0671f-7a51-4fa2-8697-241142c00940" style="width: 400px; height: 400px;"></div>
    3071  * <script type="text/javascript">
    3072  * (function () {
    3073  *  board = JXG.JSXGraph.initBoard('JXGa9a0671f-7a51-4fa2-8697-241142c00940', {boundingbox:[-4, 6, 10, -6], axis: false, grid: false, keepaspectratio: true});
    3074  *  grid = board.create('grid', []);
    3075  * })();
    3076  * </script><pre>
    3077  */
    3078 JXG.createGrid = function (board, parents, attributes) {
    3079     var c, attr;
    3080
    3081     attr = Type.copyAttributes(attributes, board.options, "grid");
    3082     c = board.create("curve", [[null], [null]], attr);
    3083
    3084     c.elType = "grid";
    3085     c.type = Const.OBJECT_TYPE_GRID;
    3086
    3087     /**
    3088      * @ignore
    3089      */
    3090     c.updateDataArray = function () {
    3091         var start, end, i,
    3092             max_lines = 5000,
    3093             is_finite,
    3094             topLeft, bottomRight,
    3095             gridX = Type.evaluate(this.visProp.gridx),
    3096             gridY = Type.evaluate(this.visProp.gridy);
    3097
    3098         if (Type.isArray(this.visProp.topleft)) {
    3099             topLeft = new Coords(
    3100                 Type.evaluate(this.visProp.tltype) || Const.COORDS_BY_USER,
    3101                 this.visProp.topleft,
    3102                 board
    3103             );
    3104         } else {
    3105             topLeft = new Coords(Const.COORDS_BY_SCREEN, [0, 0], board);
    3106         }
    3107
    3108         if (Type.isArray(this.visProp.bottomright)) {
    3109             bottomRight = new Coords(
    3110                 Type.evaluate(this.visProp.brtype) || Const.COORDS_BY_USER,
    3111                 this.visProp.bottomright,
    3112                 board
    3113             );
    3114         } else {
    3115             bottomRight = new Coords(
    3116                 Const.COORDS_BY_SCREEN,
    3117                 [board.canvasWidth, board.canvasHeight],
    3118                 board
    3119             );
    3120         }
    3121
    3122         //
    3123         //      |         |         |
    3124         //  ----+---------+---------+-----
    3125         //      |        /|         |
    3126         //      |    gridY|     <---+------   Grid Cell
    3127         //      |        \|         |
    3128         //  ----+---------+---------+-----
    3129         //      |         |\ gridX /|
    3130         //      |         |         |
    3131         //
    3132         // uc: usercoordinates
    3133         //
    3134         // currently one grid cell is 1/JXG.Options.grid.gridX uc wide and 1/JXG.Options.grid.gridY uc high.
    3135         // this may work perfectly with GeonextReader (#readGeonext, initialization of gridX and gridY) but it
    3136         // is absolutely not user friendly when it comes to use it as an API interface.
    3137         // i changed this to use gridX and gridY as the actual width and height of the grid cell. for this i
    3138         // had to refactor these methods:
    3139         //
    3140         //  DONE JXG.Board.calculateSnapSizes (init p1, p2)
    3141         //  DONE JXG.GeonextReader.readGeonext (init gridX, gridY)
    3142         //
    3143
    3144         board.options.grid.hasGrid = true;
    3145
    3146         // fix_grid: adding integer function to calculation of start and end values, and adding to calculation of start and end values below
    3147         // To allow this:
    3148         // (axes on the outside, min value of grid = 0.25)
    3149         //
    3150         //      |    |         |          |
    3151         // 1.5 -+----+---------+----------+-----
    3152         //      |    |         |          |
    3153         //      |    |         |          |
    3154         //      |    |         |          |
    3155         //   1 -+----+---------+----------+-----
    3156         //      |    |         |          |
    3157         //      |    |         |          |
    3158         //      |    |         |          |
    3159         // 0.5 -+----+---------+----------+-----
    3160         //      |    |         |          |
    3161         //      +----+---------+----------+-----
    3162         //           |         |          |
    3163         //          0.5        1         1.5
    3164         //
    3165         // fix_grid: these lines disabled:
    3166         // topLeft.setCoordinates(Const.COORDS_BY_USER, [Math.ceil(topLeft.usrCoords[1] / gridX) * gridX, Math.floor(topLeft.usrCoords[2] / gridY) * gridY]);
    3167         // bottomRight.setCoordinates(Const.COORDS_BY_USER, [Math.floor(bottomRight.usrCoords[1] / gridX) * gridX, Math.ceil(bottomRight.usrCoords[2] / gridY) * gridY]);
    3168
    3169         c.dataX = [];
    3170         c.dataY = [];
    3171
    3172         // Sometimes the bounding box is used to invert the axis. We have to take this into account here.
    3173         // fix_grid: adding integer function to calculation of start and end values
    3174         start = Math.floor(topLeft.usrCoords[2] / gridY) * gridY;
    3175         end = Math.ceil(bottomRight.usrCoords[2] / gridY) * gridY;
    3176
    3177         if (topLeft.usrCoords[2] < bottomRight.usrCoords[2]) {
    3178             start = Math.ceil(bottomRight.usrCoords[2] / gridY) * gridY; // bottomRight.usrCoords[2];
    3179             end = Math.floor(topLeft.usrCoords[2] / gridY) * gridY;
    3180         }
    3181
    3182         // Start with the horizontal grid:
    3183         is_finite = (!isFinite(start) || !isFinite(end) || Math.abs(end) > Math.abs(gridY * max_lines)) ? false : true;
    3184         for (i = start; is_finite && i > end - gridY; i -= gridY) {
    3185             c.dataX.push(topLeft.usrCoords[1], bottomRight.usrCoords[1], NaN);
    3186             c.dataY.push(i, i, NaN);
    3187         }
    3188
    3189         // fix_grid: adding integer function to calculation of start and end values
    3190         start = Math.ceil(topLeft.usrCoords[1] / gridX) * gridX;
    3191         end = Math.floor(bottomRight.usrCoords[1] / gridX) * gridX;
    3192
    3193         if (topLeft.usrCoords[1] > bottomRight.usrCoords[1]) {
    3194             start = Math.floor(bottomRight.usrCoords[1] / gridX) * gridX;
    3195             end = Math.ceil(topLeft.usrCoords[1] / gridX) * gridX;
    3196         }
    3197
    3198         // Build vertical grid
    3199         is_finite = (!isFinite(start) || !isFinite(end) || Math.abs(end) > Math.abs(gridX * max_lines)) ? false : true;
    3200         for (i = start; is_finite && i < end + gridX; i += gridX) {
    3201             c.dataX.push(i, i, NaN);
    3202             c.dataY.push(topLeft.usrCoords[2], bottomRight.usrCoords[2], NaN);
    3203         }
    3204     };
    3205
    3206     // We don't care about highlighting so we turn it off completely to save a lot of
    3207     // time on every mouse move
    3208     c.hasPoint = function () {
    3209         return false;
    3210     };
    3211
    3212     board.grids.push(c);
    3213
    3214     return c;
    3215 };
    3216
    3217 /**
    3218  * @class Creates an area indicating the solution of a linear inequality or an inequality
    3219  * of a function graph, i.e. an inequality of type y <= f(x).
    3220  * @pseudo
    3221  * @description Display the solution set of a linear inequality (less than or equal to).
    3222  * To be precise, the solution set of the inequality <i>y <= b/a * x + c/a</i> is shown.
    3223  * In case <i>a = 0</i>, that is if the equation of the line is <i>bx + c = 0</i>,
    3224  * the area of the inequality <i>bx + c <= 0</i> is shown.
    3225  * <p>
    3226  * For function graphs the area below the function graph is filled, i.e. the
    3227  * area of the inequality y <= f(x).
    3228  * With the attribute inverse:true the area of the inequality y >= f(x) is filled.
    3229  *
    3230  * @param {JXG.Line} l The area drawn will be the area below this line. With the attribute
    3231  * inverse:true, the inequality 'greater than or equal to' is shown.
    3232  * @constructor
    3233  * @name Inequality
    3234  * @type JXG.Curve
    3235  * @augments JXG.Curve
    3236  * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown.
    3237  * @example
    3238  * var p = board.create('point', [1, 3]),
    3239  *     q = board.create('point', [-2, -4]),
    3240  *     l = board.create('line', [p, q]),
    3241  *     ineq = board.create('inequality', [l]);
    3242  * ineq = board.create('inequality', [l]);
    3243  * </pre><div class="jxgbox" id="JXG2b703006-fd98-11e1-b79e-ef9e591c002e" style="width: 400px; height: 400px;"></div>
    3244  * <script type="text/javascript">
    3245  * (function () {
    3246  *  var board = JXG.JSXGraph.initBoard('JXG2b703006-fd98-11e1-b79e-ef9e591c002e', {boundingbox:[-4, 6, 10, -6], axis: false, grid: false, keepaspectratio: true}),
    3247  *      p = board.create('point', [1, 3]),
    3248  *      q = board.create('point', [-2, -4]),
    3249  *      l = board.create('line', [p, q]),
    3250  *      ineq = board.create('inequality', [l]);
    3251  * })();
    3252  * </script><pre>
    3253  *
    3254  * @example
    3255  * // Plot the inequality
    3256  * //     y >= 2/3 x + 1
    3257  * // or
    3258  * //     0 >= -3y + 2x +1
    3259  * var l = board.create('line', [1, 2, -3]),
    3260  *     ineq = board.create('inequality', [l], {inverse:true});
    3261  * </pre><div class="jxgbox" id="JXG1ded3812-2da4-4323-abaf-1db4bad1bfbd" style="width: 400px; height: 400px;"></div>
    3262  * <script type="text/javascript">
    3263  * (function () {
    3264  *  var board = JXG.JSXGraph.initBoard('JXG1ded3812-2da4-4323-abaf-1db4bad1bfbd', {boundingbox:[-4, 6, 10, -6], axis: false, grid: false, keepaspectratio: true}),
    3265  *      l = board.create('line', [1, 2, -3]),
    3266  *      ineq = board.create('inequality', [l], {inverse:true});
    3267  * })();
    3268  * </script><pre>
    3269  *
    3270  * @example
    3271  * var f = board.create('functiongraph', ['sin(x)', -2*Math.PI, 2*Math.PI]);
    3272  *
    3273  * var ineq_lower = board.create('inequality', [f]);
    3274  * var ineq_greater = board.create('inequality', [f], {inverse: true, fillColor: 'yellow'});
    3275  *
    3276  *
    3277  * </pre><div id="JXGdb68c574-414c-11e8-839a-901b0e1b8723" class="jxgbox" style="width: 300px; height: 300px;"></div>
    3278  * <script type="text/javascript">
    3279  *     (function() {
    3280  *         var board = JXG.JSXGraph.initBoard('JXGdb68c574-414c-11e8-839a-901b0e1b8723',
    3281  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
    3282  *     var f = board.create('functiongraph', ['sin(x)', -2*Math.PI, 2*Math.PI]);
    3283  *
    3284  *     var ineq_lower = board.create('inequality', [f]);
    3285  *     var ineq_greater = board.create('inequality', [f], {inverse: true, fillColor: 'yellow'});
    3286  *
    3287  *
    3288  *     })();
    3289  *
    3290  * </script><pre>
    3291  *
    3292  */
    3293 JXG.createInequality = function (board, parents, attributes) {
    3294     var f, a, attr;
    3295
    3296     attr = Type.copyAttributes(attributes, board.options, "inequality");
    3297     if (parents[0].elementClass === Const.OBJECT_CLASS_LINE) {
    3298         a = board.create("curve", [[], []], attr);
    3299         a.hasPoint = function () {
    3300             return false;
    3301         };
    3302
    3303         /**
    3304          * @class
    3305          * @ignore
    3306          */
    3307         a.updateDataArray = function () {
    3308             var i1,
    3309                 i2,
    3310                 // This will be the height of the area. We mustn't rely upon the board height because if we pan the view
    3311                 // such that the line is not visible anymore, the borders of the area will get visible in some cases.
    3312                 h,
    3313                 bb = board.getBoundingBox(),
    3314                 inverse = Type.evaluate(this.visProp.inverse),
    3315                 factor = inverse ? -1 : 1,
    3316                 expansion = 1.5,
    3317                 w = expansion * Math.max(bb[2] - bb[0], bb[1] - bb[3]),
    3318                 // Fake a point (for Math.Geometry.perpendicular)
    3319                 // contains centroid of the board
    3320                 dp = {
    3321                     coords: {
    3322                         usrCoords: [1, (bb[0] + bb[2]) * 0.5, inverse ? bb[1] : bb[3]]
    3323                     }
    3324                 },
    3325                 slope1 = parents[0].stdform.slice(1),
    3326                 slope2 = slope1;
    3327
    3328             // Calculate the area height as
    3329             //  expansion times the distance of the line to the
    3330             // point in the middle of the top/bottom border.
    3331             h =
    3332                 expansion *
    3333                 Math.max(
    3334                     Geometry.perpendicular(parents[0], dp, board)[0].distance(
    3335                         Const.COORDS_BY_USER,
    3336                         dp.coords
    3337                     ),
    3338                     w
    3339                 );
    3340             h *= factor;
    3341
    3342             // reuse dp
    3343             dp = {
    3344                 coords: {
    3345                     usrCoords: [1, (bb[0] + bb[2]) * 0.5, (bb[1] + bb[3]) * 0.5]
    3346                 }
    3347             };
    3348
    3349             // If dp is on the line, Geometry.perpendicular will return a point not on the line.
    3350             // Since this somewhat odd behavior of Geometry.perpendicular is needed in GEONExT,
    3351             // it is circumvented here.
    3352             if (
    3353                 Math.abs(Mat.innerProduct(dp.coords.usrCoords, parents[0].stdform, 3)) >=
    3354                 Mat.eps
    3355             ) {
    3356                 dp = Geometry.perpendicular(parents[0], dp, board)[0].usrCoords;
    3357             } else {
    3358                 dp = dp.coords.usrCoords;
    3359             }
    3360             i1 = [1, dp[1] + slope1[1] * w, dp[2] - slope1[0] * w];
    3361             i2 = [1, dp[1] - slope2[1] * w, dp[2] + slope2[0] * w];
    3362
    3363             // One of the vectors based in i1 and orthogonal to the parent line has the direction d1 = (slope1, -1)
    3364             // We will go from i1 to i1 + h*d1, from there to i2 + h*d2 (with d2 calculated equivalent to d1) and
    3365             // end up in i2.
    3366             this.dataX = [i1[1], i1[1] + slope1[0] * h, i2[1] + slope2[0] * h, i2[1], i1[1]];
    3367             this.dataY = [i1[2], i1[2] + slope1[1] * h, i2[2] + slope2[1] * h, i2[2], i1[2]];
    3368         };
    3369     } else if (
    3370         parents[0].elementClass === Const.OBJECT_CLASS_CURVE &&
    3371         parents[0].visProp.curvetype === "functiongraph"
    3372     ) {
    3373         a = board.create("curve", [[], []], attr);
    3374         /**
    3375          * @class
    3376          * @ignore
    3377          */
    3378         a.updateDataArray = function () {
    3379             var bbox = this.board.getBoundingBox(),
    3380                 points = [],
    3381                 infty,
    3382                 first,
    3383                 last,
    3384                 len,
    3385                 i,
    3386                 mi = parents[0].minX(),
    3387                 ma = parents[0].maxX(),
    3388                 curve_mi,
    3389                 curve_ma,
    3390                 firstx,
    3391                 lastx,
    3392                 enlarge = (bbox[1] - bbox[3]) * 0.3, // enlarge the bbox vertically by this amount
    3393                 inverse = Type.evaluate(this.visProp.inverse);
    3394
    3395             // inverse == true <=> Fill area with y >= f(x)
    3396             infty = inverse ? 1 : 3; // we will use either bbox[1] or bbox[3] below
    3397
    3398             this.dataX = [];
    3399             this.dataY = [];
    3400             len = parents[0].points.length;
    3401             if (len === 0) {
    3402                 return;
    3403             }
    3404
    3405             bbox[1] += enlarge;
    3406             bbox[3] -= enlarge;
    3407
    3408             last = -1;
    3409             while (last < len - 1) {
    3410                 // Find the first point with real coordinates on this curve segment
    3411                 for (i = last + 1, first = len; i < len; i++) {
    3412                     if (parents[0].points[i].isReal()) {
    3413                         first = i;
    3414                         break;
    3415                     }
    3416                 }
    3417                 // No real points found -> exit
    3418                 if (first >= len) {
    3419                     break;
    3420                 }
    3421
    3422                 // Find the last point with real coordinates on this curve segment
    3423                 for (i = first, last = len - 1; i < len - 1; i++) {
    3424                     if (!parents[0].points[i + 1].isReal()) {
    3425                         last = i;
    3426                         break;
    3427                     }
    3428                 }
    3429
    3430                 firstx = parents[0].points[first].usrCoords[1];
    3431                 lastx = parents[0].points[last].usrCoords[1];
    3432
    3433                 // Restrict the plot interval if the function ends inside of the board
    3434                 curve_mi = bbox[0] < mi ? mi : bbox[0];
    3435                 curve_ma = bbox[2] > ma ? ma : bbox[2];
    3436
    3437                 // Found NaNs
    3438                 curve_mi = first === 0 ? curve_mi : Math.max(curve_mi, firstx);
    3439                 curve_ma = last === len - 1 ? curve_ma : Math.min(curve_ma, lastx);
    3440
    3441                 // First and last relevant x-coordinate of the curve
    3442                 curve_mi = first === 0 ? mi : firstx;
    3443                 curve_ma = last === len - 1 ? ma : lastx;
    3444
    3445                 // Copy the curve points
    3446                 points = [];
    3447
    3448                 points.push([1, curve_mi, bbox[infty]]);
    3449                 points.push([1, curve_mi, parents[0].points[first].usrCoords[2]]);
    3450                 for (i = first; i <= last; i++) {
    3451                     points.push(parents[0].points[i].usrCoords);
    3452                 }
    3453                 points.push([1, curve_ma, parents[0].points[last].usrCoords[2]]);
    3454                 points.push([1, curve_ma, bbox[infty]]);
    3455                 points.push(points[0]);
    3456
    3457                 for (i = 0; i < points.length; i++) {
    3458                     this.dataX.push(points[i][1]);
    3459                     this.dataY.push(points[i][2]);
    3460                 }
    3461
    3462                 if (last < len - 1) {
    3463                     this.dataX.push(NaN);
    3464                     this.dataY.push(NaN);
    3465                 }
    3466             }
    3467         };
    3468
    3469         // Previous code:
    3470         /**
    3471          * @class
    3472          * @ignore
    3473          */
    3474         a.hasPoint = function () {
    3475             return false;
    3476         };
    3477     } else {
    3478         // Not yet practical?
    3479         f = Type.createFunction(parents[0]);
    3480         a.addParentsFromJCFunctions([f]);
    3481
    3482         if (!Type.exists(f)) {
    3483             throw new Error(
    3484                 "JSXGraph: Can't create area with the given parents." +
    3485                     "\nPossible parent types: [line], [function]"
    3486             );
    3487         }
    3488     }
    3489
    3490     a.addParents(parents[0]);
    3491     return a;
    3492 };
    3493
    3494 JXG.registerElement("arrowparallel", JXG.createArrowParallel);
    3495 JXG.registerElement("bisector", JXG.createBisector);
    3496 JXG.registerElement("bisectorlines", JXG.createAngularBisectorsOfTwoLines);
    3497 JXG.registerElement("msector", JXG.createMsector);
    3498 JXG.registerElement("circumcircle", JXG.createCircumcircle);
    3499 JXG.registerElement("circumcirclemidpoint", JXG.createCircumcenter);
    3500 JXG.registerElement("circumcenter", JXG.createCircumcenter);
    3501 JXG.registerElement("incenter", JXG.createIncenter);
    3502 JXG.registerElement("incircle", JXG.createIncircle);
    3503 JXG.registerElement("integral", JXG.createIntegral);
    3504 JXG.registerElement("midpoint", JXG.createMidpoint);
    3505 JXG.registerElement("mirrorelement", JXG.createMirrorElement);
    3506 JXG.registerElement("mirrorpoint", JXG.createMirrorPoint);
    3507 JXG.registerElement("normal", JXG.createNormal);
    3508 JXG.registerElement("orthogonalprojection", JXG.createOrthogonalProjection);
    3509 JXG.registerElement("parallel", JXG.createParallel);
    3510 JXG.registerElement("parallelpoint", JXG.createParallelPoint);
    3511 JXG.registerElement("perpendicular", JXG.createPerpendicular);
    3512 JXG.registerElement("perpendicularpoint", JXG.createPerpendicularPoint);
    3513 JXG.registerElement("perpendicularsegment", JXG.createPerpendicularSegment);
    3514 JXG.registerElement("reflection", JXG.createReflection);
    3515 JXG.registerElement("grid", JXG.createGrid);
    3516 JXG.registerElement("inequality", JXG.createInequality);
    3517
    3518 // export default {
    3519 //     createArrowParallel: JXG.createArrowParallel,
    3520 //     createBisector: JXG.createBisector,
    3521 //     createAngularBisectorOfTwoLines: JXG.createAngularBisectorsOfTwoLines,
    3522 //     createCircumcircle: JXG.createCircumcircle,
    3523 //     createCircumcenter: JXG.createCircumcenter,
    3524 //     createIncenter: JXG.createIncenter,
    3525 //     createIncircle: JXG.createIncircle,
    3526 //     createIntegral: JXG.createIntegral,
    3527 //     createMidpoint: JXG.createMidpoint,
    3528 //     createMirrorElement: JXG.createMirrorElement,
    3529 //     createMirrorPoint: JXG.createMirrorPoint,
    3530 //     createNormal: JXG.createNormal,
    3531 //     createOrthogonalProjection: JXG.createOrthogonalProjection,
    3532 //     createParallel: JXG.createParallel,
    3533 //     createParallelPoint: JXG.createParallelPoint,
    3534 //     createPerpendicular: JXG.createPerpendicular,
    3535 //     createPerpendicularPoint: JXG.createPerpendicularPoint,
    3536 //     createPerpendicularSegmen: JXG.createPerpendicularSegment,
    3537 //     createReflection: JXG.createReflection,
    3538 //     createGrid: JXG.createGrid,
    3539 //     createInequality: JXG.createInequality
    3540 // };
    3541