      1 /*
      2     Copyright 2008-2024
      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.js";
     58 import Mat from "../math/math.js";
     59 import Geometry from "../math/geometry.js";
     60 import Numerics from "../math/numerics.js";
     61 import Coords from "../base/coords.js";
     62 import Type from "../utils/type.js";
     63 import Const from "../base/constants.js";
     64 // import Point from "../base/point.js";
     65 // import Line from "../base/line.js";
     66 // import Circle from "../base/circle.js";
     67 // import Transform from "../base/transformation.js";
     68 import Composition from "../base/composition.js";
     69 // import Curve from "../base/curve.js";
     70 // import Polygon from "../base/polygon.js";
     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
    1087         if (attr.lastArrow === false) {
    1088             // An arrow has to have an arrow head.
    1089             attr.lastArrow = true;
    1090         }
    1091         p = JXG.createParallel(board, parents, attr).setAttribute({
    1092             straightFirst: false,
    1093             straightLast: false
    1094         });
    1095         p.type = Const.OBJECT_TYPE_VECTOR;
    1096         p.elType = "arrowparallel";
    1097
    1098         // parents are set in createParallel
    1099
    1100         return p;
    1101     } catch (e) {
    1102         throw new Error(
    1103             "JSXGraph: Can't create arrowparallel with parent types '" +
    1104                 typeof parents[0] +
    1105                 "' and '" +
    1106                 typeof parents[1] +
    1107                 "'." +
    1108                 "\nPossible parent types: [line,point], [point,point,point]"
    1109         );
    1110     }
    1111 };
    1112
    1113 /**
    1114  * @class A bisector is a line which divides an angle into two equal angles. It is given by three points A, B, and
    1115  * C and divides the angle ABC into two equal sized parts.
    1116  * @pseudo
    1117  * @constructor
    1118  * @name Bisector
    1119  * @type JXG.Line
    1120  * @augments JXG.Line
    1121  * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown.
    1122  * @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
    1123  * be divided into two equal angles.
    1124  * @example
    1125  * var p1 = board.create('point', [6.0, 4.0]);
    1126  * var p2 = board.create('point', [3.0, 2.0]);
    1127  * var p3 = board.create('point', [1.0, 7.0]);
    1128  *
    1129  * var bi1 = board.create('bisector', [p1, p2, p3]);
    1130  * </pre><div class="jxgbox" id="JXG0d58cea8-b06a-407c-b27c-0908f508f5a4" style="width: 400px; height: 400px;"></div>
    1131  * <script type="text/javascript">
    1132  * (function () {
    1133  *   var board = JXG.JSXGraph.initBoard('JXG0d58cea8-b06a-407c-b27c-0908f508f5a4', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false});
    1134  *   var p1 = board.create('point', [6.0, 4.0]);
    1135  *   var p2 = board.create('point', [3.0, 2.0]);
    1136  *   var p3 = board.create('point', [1.0, 7.0]);
    1137  *   var bi1 = board.create('bisector', [p1, p2, p3]);
    1138  * })();
    1139  * </script><pre>
    1140  */
    1141 JXG.createBisector = function (board, parents, attributes) {
    1142     var p, l, i, attr;
    1143
    1144     parents = Type.providePoints(board, parents, attributes, "point");
    1145     if (Type.isPoint(parents[0]) && Type.isPoint(parents[1]) && Type.isPoint(parents[2])) {
    1146         // hidden and fixed helper
    1147         attr = Type.copyAttributes(attributes, board.options, "bisector", "point");
    1148         attr.snapToGrid = false;
    1149
    1150         p = board.create(
    1151             "point",
    1152             [
    1153                 function () {
    1154                     return Geometry.angleBisector(parents[0], parents[1], parents[2], board);
    1155                 }
    1156             ],
    1157             attr
    1158         );
    1159         p.dump = false;
    1160
    1161         for (i = 0; i < 3; i++) {
    1162             // required for algorithm requiring dependencies between elements
    1163             if (Type.exists(parents[i]._is_new)) {
    1164                 p.addChild(parents[i]);
    1165                 delete parents[i]._is_new;
    1166             } else {
    1167                 parents[i].addChild(p);
    1168             }
    1169         }
    1170
    1171         if (!Type.exists(attributes.layer)) {
    1172             attributes.layer = board.options.layer.line;
    1173         }
    1174
    1175         attr = Type.copyAttributes(attributes, board.options, "bisector");
    1176         l = JXG.createLine(board, [parents[1], p], attr);
    1177
    1178         /**
    1179          * Helper point
    1180          * @memberOf Bisector.prototype
    1181          * @type Point
    1182          * @name point
    1183          */
    1184         l.point = p;
    1185
    1186         l.elType = "bisector";
    1187         l.setParents(parents);
    1188         l.subs = {
    1189             point: p
    1190         };
    1191         l.inherits.push(p);
    1192
    1193         return l;
    1194     }
    1195
    1196     throw new Error(
    1197         "JSXGraph: Can't create angle bisector with parent types '" +
    1198             typeof parents[0] +
    1199             "' and '" +
    1200             typeof parents[1] +
    1201             "'." +
    1202             "\nPossible parent types: [point,point,point]"
    1203     );
    1204 };
    1205
    1206 /**
    1207  * @class Bisector lines are similar to {@link Bisector} but take two lines as parent elements. The resulting element is
    1208  * a composition of two lines.
    1209  * @pseudo
    1210  * @constructor
    1211  * @name Bisectorlines
    1212  * @type JXG.Composition
    1213  * @augments JXG.Composition
    1214  * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown.
    1215  * @param {JXG.Line_JXG.Line} l1,l2 The four angles described by the lines <tt>l1</tt> and <tt>l2</tt> will each
    1216  * be divided into two equal angles.
    1217  * @example
    1218  * var p1 = board.create('point', [6.0, 4.0]);
    1219  * var p2 = board.create('point', [3.0, 2.0]);
    1220  * var p3 = board.create('point', [1.0, 7.0]);
    1221  * var p4 = board.create('point', [3.0, 0.0]);
    1222  * var l1 = board.create('line', [p1, p2]);
    1223  * var l2 = board.create('line', [p3, p4]);
    1224  *
    1225  * var bi1 = board.create('bisectorlines', [l1, l2]);
    1226  * </pre><div class="jxgbox" id="JXG3121ff67-44f0-4dda-bb10-9cda0b80bf18" style="width: 400px; height: 400px;"></div>
    1227  * <script type="text/javascript">
    1228  * (function () {
    1229  *   var board = JXG.JSXGraph.initBoard('JXG3121ff67-44f0-4dda-bb10-9cda0b80bf18', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false});
    1230  *   var p1 = board.create('point', [6.0, 4.0]);
    1231  *   var p2 = board.create('point', [3.0, 2.0]);
    1232  *   var p3 = board.create('point', [1.0, 7.0]);
    1233  *   var p4 = board.create('point', [3.0, 0.0]);
    1234  *   var l1 = board.create('line', [p1, p2]);
    1235  *   var l2 = board.create('line', [p3, p4]);
    1236  *   var bi1 = board.create('bisectorlines', [l1, l2]);
    1237  * })();
    1238  * </script><pre>
    1239  */
    1240 JXG.createAngularBisectorsOfTwoLines = function (board, parents, attributes) {
    1241     // The angular bisectors of two line [c1,a1,b1] and [c2,a2,b2] are determined by the equation:
    1242     // (a1*x+b1*y+c1*z)/sqrt(a1^2+b1^2) = +/- (a2*x+b2*y+c2*z)/sqrt(a2^2+b2^2)
    1243
    1244     var g1,
    1245         g2,
    1246         attr,
    1247         ret,
    1248         l1 = board.select(parents[0]),
    1249         l2 = board.select(parents[1]);
    1250
    1251     if (
    1252         l1.elementClass !== Const.OBJECT_CLASS_LINE ||
    1253         l2.elementClass !== Const.OBJECT_CLASS_LINE
    1254     ) {
    1255         throw new Error(
    1256             "JSXGraph: Can't create angle bisectors of two lines with parent types '" +
    1257                 typeof parents[0] +
    1258                 "' and '" +
    1259                 typeof parents[1] +
    1260                 "'." +
    1261                 "\nPossible parent types: [line,line]"
    1262         );
    1263     }
    1264
    1265     if (!Type.exists(attributes.layer)) {
    1266         attributes.layer = board.options.layer.line;
    1267     }
    1268
    1269     attr = Type.copyAttributes(attributes, board.options, "bisectorlines", "line1");
    1270     g1 = board.create(
    1271         "line",
    1272         [
    1273             function () {
    1274                 var d1 = Mat.hypot(l1.stdform[1], l1.stdform[2]),
    1275                     d2 = Mat.hypot(l2.stdform[1], l2.stdform[2]);
    1276
    1277                 return l1.stdform[0] / d1 - l2.stdform[0] / d2;
    1278             },
    1279             function () {
    1280                 var d1 = Mat.hypot(l1.stdform[1], l1.stdform[2]),
    1281                     d2 = Mat.hypot(l2.stdform[1], l2.stdform[2]);
    1282
    1283                 return l1.stdform[1] / d1 - l2.stdform[1] / d2;
    1284             },
    1285             function () {
    1286                 var d1 = Mat.hypot(l1.stdform[1], l1.stdform[2]),
    1287                     d2 = Mat.hypot(l2.stdform[1], l2.stdform[2]);
    1288
    1289                 return l1.stdform[2] / d1 - l2.stdform[2] / d2;
    1290             }
    1291         ],
    1292         attr
    1293     );
    1294
    1295     if (!Type.exists(attributes.layer)) {
    1296         attributes.layer = board.options.layer.line;
    1297     }
    1298     attr = Type.copyAttributes(attributes, board.options, "bisectorlines", "line2");
    1299     g2 = board.create(
    1300         "line",
    1301         [
    1302             function () {
    1303                 var d1 = Mat.hypot(l1.stdform[1], l1.stdform[2]),
    1304                     d2 = Mat.hypot(l2.stdform[1], l2.stdform[2]);
    1305
    1306                 return l1.stdform[0] / d1 + l2.stdform[0] / d2;
    1307             },
    1308             function () {
    1309                 var d1 = Mat.hypot(l1.stdform[1], l1.stdform[2]),
    1310                     d2 = Mat.hypot(l2.stdform[1], l2.stdform[2]);
    1311
    1312                 return l1.stdform[1] / d1 + l2.stdform[1] / d2;
    1313             },
    1314             function () {
    1315                 var d1 = Mat.hypot(l1.stdform[1], l1.stdform[2]),
    1316                     d2 = Mat.hypot(l2.stdform[1], l2.stdform[2]);
    1317
    1318                 return l1.stdform[2] / d1 + l2.stdform[2] / d2;
    1319             }
    1320         ],
    1321         attr
    1322     );
    1323
    1324     // documentation
    1325     /**
    1326      * First line.
    1327      * @memberOf Bisectorlines.prototype
    1328      * @name line1
    1329      * @type Line
    1330      */
    1331
    1332     /**
    1333      * Second line.
    1334      * @memberOf Bisectorlines.prototype
    1335      * @name line2
    1336      * @type Line
    1337      */
    1338
    1339     ret = new Composition({ line1: g1, line2: g2 });
    1340
    1341     g1.dump = false;
    1342     g2.dump = false;
    1343
    1344     ret.elType = "bisectorlines";
    1345     ret.setParents([l1.id, l2.id]);
    1346     ret.subs = {
    1347         line1: g1,
    1348         line2: g2
    1349     };
    1350     // ret.inherits.push(g1, g2);
    1351
    1352     return ret;
    1353 };
    1354
    1355 // /**
    1356 //  * @class An m-sector is a line which divides an angle into two angles. It is given by three points A, B, and
    1357 //  * C and a real number m, and divides an angle into two angles, an angle with amplitude m and an angle with
    1358 //  * amplitude (1-m)
    1359 //  * @pseudo
    1360 //  * @constructor
    1361 //  * @name Msector
    1362 //  * @type JXG.Line
    1363 //  * @augments JXG.Line
    1364 //  * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown.
    1365 //  * @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
    1366 //  * be divided into two angles according to the value of <tt>m</tt>.
    1367 //  * @example
    1368 //  * var p1 = board.create('point', [6.0, 4.0]);
    1369 //  * var p2 = board.create('point', [3.0, 2.0]);
    1370 //  * var p3 = board.create('point', [1.0, 7.0]);
    1371 //  *
    1372 //  * var bi1 = board.create('msector', [p1, p2, p3], 1/5);
    1373 //  * </pre><div id="JXG0d58cea8-b06a-407c-b27c-0908f508f5a4" style="width: 400px; height: 400px;"></div>
    1374 //  * <script type="text/javascript">
    1375 //  * (function () {
    1376 //  *   var board = JXG.JSXGraph.initBoard('JXG0d58cea8-b06a-407c-b27c-0908f508f5a4', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false});
    1377 //  *   var p1 = board.create('point', [6.0, 4.0]);
    1378 //  *   var p2 = board.create('point', [3.0, 2.0]);
    1379 //  *   var p3 = board.create('point', [1.0, 7.0]);
    1380 //  *   var bi1 = board.create('msector', [p1, p2, p3], 1/5);
    1381 //  * })();
    1382 //  * </script><pre>
    1383 //  */
    1384 // JXG.createMsector = function (board, parents, attributes) {
    1385 //     var p, l, i, attr;
    1386
    1387 //     if (parents[0].elementClass === Const.OBJECT_CLASS_POINT &&
    1388 //             parents[1].elementClass === Const.OBJECT_CLASS_POINT &&
    1389 //             parents[2].elementClass === Const.OBJECT_CLASS_POINT) {
    1390 //         // hidden and fixed helper
    1391 //         attr = Type.copyAttributes(attributes, board.options, 'msector', 'point');
    1392 //         p = board.create('point', [
    1393 //             function () {
    1394 //                 return Geometry.angleMsector(parents[0], parents[1], parents[2], parents[3], board);
    1395 //             }
    1396 //         ], attr);
    1397 //         p.dump = false;
    1398
    1399 //         for (i = 0; i < 3; i++) {
    1400 //             // required for algorithm requiring dependencies between elements
    1401 //             parents[i].addChild(p);
    1402 //         }
    1403
    1404 //         if (!Type.exists(attributes.layer)) {
    1405 //             attributes.layer = board.options.layer.line;
    1406 //         }
    1407
    1408 //         attr = Type.copyAttributes(attributes, board.options, 'msector');
    1409 //         l = JXG.createLine(board, [parents[1], p], attr);
    1410
    1411 //         /**
    1412 //          * Helper point
    1413 //          * @memberOf Msector.prototype
    1414 //          * @type Point
    1415 //          * @name point
    1416 //          */
    1417 //         l.point = p;
    1418
    1419 //         l.elType = 'msector';
    1420 //         l.parents = [parents[0].id, parents[1].id, parents[2].id];
    1421 //         l.subs = {
    1422 //             point: p
    1423 //         };
    1424 //         l.inherits.push(p);
    1425
    1426 //         return l;
    1427 //     }
    1428
    1429 //     throw new Error("JSXGraph: Can't create angle msector with parent types '" +
    1430 //         (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." +
    1431 //         "\nPossible parent types: [point,point,point,Number]");
    1432 // };
    1433
    1434 /**
    1435  * @class Constructs the midpoint of a {@link Circumcircle}. Like the circumcircle the circumcenter
    1436  * is constructed by providing three points.
    1437  * @pseudo
    1438  * @description A circumcenter is given by three points which are all lying on the circle with the
    1439  * constructed circumcenter as the midpoint.
    1440  * @constructor
    1441  * @name Circumcenter
    1442  * @type JXG.Point
    1443  * @augments JXG.Point
    1444  * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown.
    1445  * @param {JXG.Point_JXG.Point_JXG.Point} p1,p2,p3 The constructed point is the midpoint of the circle determined
    1446  * by p1, p2, and p3.
    1447  * @example
    1448  * var p1 = board.create('point', [0.0, 2.0]);
    1449  * var p2 = board.create('point', [2.0, 1.0]);
    1450  * var p3 = board.create('point', [3.0, 3.0]);
    1451  *
    1452  * var cc1 = board.create('circumcenter', [p1, p2, p3]);
    1453  * </pre><div class="jxgbox" id="JXGe8a40f95-bf30-4eb4-88a8-f4d5495261fd" style="width: 400px; height: 400px;"></div>
    1454  * <script type="text/javascript">
    1455  *   var ccmex1_board = JXG.JSXGraph.initBoard('JXGe8a40f95-bf30-4eb4-88a8-f4d5495261fd', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false});
    1456  *   var ccmex1_p1 = ccmex1_board.create('point', [0.0, 2.0]);
    1457  *   var ccmex1_p2 = ccmex1_board.create('point', [6.0, 1.0]);
    1458  *   var ccmex1_p3 = ccmex1_board.create('point', [3.0, 7.0]);
    1459  *   var ccmex1_cc1 = ccmex1_board.create('circumcenter', [ccmex1_p1, ccmex1_p2, ccmex1_p3]);
    1460  * </script><pre>
    1461  */
    1462 JXG.createCircumcenter = function (board, parents, attributes) {
    1463     var p, i, a, b, c;
    1464
    1465     parents = Type.providePoints(board, parents, attributes, "point");
    1466     if (Type.isPoint(parents[0]) && Type.isPoint(parents[1]) && Type.isPoint(parents[2])) {
    1467         a = parents[0];
    1468         b = parents[1];
    1469         c = parents[2];
    1470
    1471         p = JXG.createPoint(
    1472             board,
    1473             [
    1474                 function () {
    1475                     return Geometry.circumcenter(a, b, c, board);
    1476                 }
    1477             ],
    1478             attributes
    1479         );
    1480
    1481         for (i = 0; i < 3; i++) {
    1482             if (Type.exists(parents[i]._is_new)) {
    1483                 p.addChild(parents[i]);
    1484                 delete parents[i]._is_new;
    1485             } else {
    1486                 parents[i].addChild(p);
    1487             }
    1488         }
    1489
    1490         p.elType = "circumcenter";
    1491         p.setParents(parents);
    1492
    1493         p.generatePolynomial = function () {
    1494             /*
    1495              *  CircumcircleMidpoint takes three points A, B and C  and creates point M, which is the circumcenter of A, B, and C.
    1496              *
    1497              *
    1498              * So we have two conditions:
    1499              *
    1500              *   (a)   CT  ==  AT           (distance condition I)
    1501              *   (b)   BT  ==  AT           (distance condition II)
    1502              *
    1503              */
    1504             var a1 = a.symbolic.x,
    1505                 a2 = a.symbolic.y,
    1506                 b1 = b.symbolic.x,
    1507                 b2 = b.symbolic.y,
    1508                 c1 = c.symbolic.x,
    1509                 c2 = c.symbolic.y,
    1510                 t1 = p.symbolic.x,
    1511                 t2 = p.symbolic.y,
    1512                 poly1 = ["((", t1, ")-(", a1, "))^2+((", t2, ")-(", a2, "))^2-((", t1, ")-(", b1, "))^2-((", t2, ")-(", b2, "))^2"].join(""),
    1513                 poly2 = ["((", t1, ")-(", a1, "))^2+((", t2, ")-(", a2, "))^2-((", t1, ")-(", c1, "))^2-((", t2, ")-(", c2, "))^2"].join("");
    1514
    1515             return [poly1, poly2];
    1516         };
    1517
    1518         return p;
    1519     }
    1520
    1521     throw new Error(
    1522         "JSXGraph: Can't create circumcircle midpoint with parent types '" +
    1523             typeof parents[0] +
    1524             "', '" +
    1525             typeof parents[1] +
    1526             "' and '" +
    1527             typeof parents[2] +
    1528             "'." +
    1529             "\nPossible parent types: [point,point,point]"
    1530     );
    1531 };
    1532
    1533 /**
    1534  * @class Constructs the incenter of the triangle described by the three given points.
    1535  * {@link https://mathworld.wolfram.com/Incenter.html}
    1536  * @pseudo
    1537  * @constructor
    1538  * @name Incenter
    1539  * @type JXG.Point
    1540  * @augments JXG.Point
    1541  * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown.
    1542  * @param {JXG.Point_JXG.Point_JXG.Point} p1,p2,p3 The constructed point is the incenter of the triangle described
    1543  * by p1, p2, and p3.
    1544  * @example
    1545  * var p1 = board.create('point', [0.0, 2.0]);
    1546  * var p2 = board.create('point', [2.0, 1.0]);
    1547  * var p3 = board.create('point', [3.0, 3.0]);
    1548  *
    1549  * var ic1 = board.create('incenter', [p1, p2, p3]);
    1550  * </pre><div class="jxgbox" id="JXGe8a40f95-bf30-4eb4-88a8-a2d5495261fd" style="width: 400px; height: 400px;"></div>
    1551  * <script type="text/javascript">
    1552  *   var icmex1_board = JXG.JSXGraph.initBoard('JXGe8a40f95-bf30-4eb4-88a8-a2d5495261fd', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false});
    1553  *   var icmex1_p1 = icmex1_board.create('point', [0.0, 2.0]);
    1554  *   var icmex1_p2 = icmex1_board.create('point', [6.0, 1.0]);
    1555  *   var icmex1_p3 = icmex1_board.create('point', [3.0, 7.0]);
    1556  *   var icmex1_ic1 = icmex1_board.create('incenter', [icmex1_p1, icmex1_p2, icmex1_p3]);
    1557  * </script><pre>
    1558  */
    1559 JXG.createIncenter = function (board, parents, attributes) {
    1560     var p, A, B, C, i;
    1561
    1562     parents = Type.providePoints(board, parents, attributes, "point");
    1563     if (
    1564         parents.length >= 3 &&
    1565         Type.isPoint(parents[0]) &&
    1566         Type.isPoint(parents[1]) &&
    1567         Type.isPoint(parents[2])
    1568     ) {
    1569         A = parents[0];
    1570         B = parents[1];
    1571         C = parents[2];
    1572
    1573         p = board.create(
    1574             "point",
    1575             [
    1576                 function () {
    1577                     var a, b, c;
    1578
    1579                     a = Mat.hypot(B.X() - C.X(), B.Y() - C.Y());
    1580                     b = Mat.hypot(A.X() - C.X(), A.Y() - C.Y());
    1581                     c = Mat.hypot(B.X() - A.X(), B.Y() - A.Y());
    1582
    1583                     return new Coords(
    1584                         Const.COORDS_BY_USER,
    1585                         [
    1586                             (a * A.X() + b * B.X() + c * C.X()) / (a + b + c),
    1587                             (a * A.Y() + b * B.Y() + c * C.Y()) / (a + b + c)
    1588                         ],
    1589                         board
    1590                     );
    1591                 }
    1592             ],
    1593             attributes
    1594         );
    1595
    1596         for (i = 0; i < 3; i++) {
    1597             if (Type.exists(parents[i]._is_new)) {
    1598                 p.addChild(parents[i]);
    1599                 delete parents[i]._is_new;
    1600             } else {
    1601                 parents[i].addChild(p);
    1602             }
    1603         }
    1604
    1605         p.elType = "incenter";
    1606         p.setParents(parents);
    1607     } else {
    1608         throw new Error(
    1609             "JSXGraph: Can't create incenter with parent types '" +
    1610                 typeof parents[0] +
    1611                 "', '" +
    1612                 typeof parents[1] +
    1613                 "' and '" +
    1614                 typeof parents[2] +
    1615                 "'." +
    1616                 "\nPossible parent types: [point,point,point]"
    1617         );
    1618     }
    1619
    1620     return p;
    1621 };
    1622
    1623 /**
    1624  * @class A circumcircle is given by three points which are all lying on the circle.
    1625  * @pseudo
    1626  * @constructor
    1627  * @name Circumcircle
    1628  * @type JXG.Circle
    1629  * @augments JXG.Circle
    1630  * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown.
    1631  * @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>.
    1632  * @example
    1633  * var p1 = board.create('point', [0.0, 2.0]);
    1634  * var p2 = board.create('point', [2.0, 1.0]);
    1635  * var p3 = board.create('point', [3.0, 3.0]);
    1636  *
    1637  * var cc1 = board.create('circumcircle', [p1, p2, p3]);
    1638  * </pre><div class="jxgbox" id="JXGe65c9861-0bf0-402d-af57-3ab11962f5ac" style="width: 400px; height: 400px;"></div>
    1639  * <script type="text/javascript">
    1640  *   var ccex1_board = JXG.JSXGraph.initBoard('JXGe65c9861-0bf0-402d-af57-3ab11962f5ac', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false});
    1641  *   var ccex1_p1 = ccex1_board.create('point', [0.0, 2.0]);
    1642  *   var ccex1_p2 = ccex1_board.create('point', [6.0, 1.0]);
    1643  *   var ccex1_p3 = ccex1_board.create('point', [3.0, 7.0]);
    1644  *   var ccex1_cc1 = ccex1_board.create('circumcircle', [ccex1_p1, ccex1_p2, ccex1_p3]);
    1645  * </script><pre>
    1646  */
    1647 JXG.createCircumcircle = function (board, parents, attributes) {
    1648     var p, c, attr, i;
    1649
    1650     parents = Type.providePoints(board, parents, attributes, "point");
    1651     if (parents === false) {
    1652         throw new Error(
    1653             "JSXGraph: Can't create circumcircle with parent types '" +
    1654                 typeof parents[0] +
    1655                 "', '" +
    1656                 typeof parents[1] +
    1657                 "' and '" +
    1658                 typeof parents[2] +
    1659                 "'." +
    1660                 "\nPossible parent types: [point,point,point]"
    1661         );
    1662     }
    1663
    1664     try {
    1665         attr = Type.copyAttributes(attributes, board.options, "circumcircle", "center");
    1666         p = JXG.createCircumcenter(board, parents, attr);
    1667
    1668         p.dump = false;
    1669
    1670         if (!Type.exists(attributes.layer)) {
    1671             attributes.layer = board.options.layer.circle;
    1672         }
    1673         attr = Type.copyAttributes(attributes, board.options, "circumcircle");
    1674         c = JXG.createCircle(board, [p, parents[0]], attr);
    1675
    1676         c.elType = "circumcircle";
    1677         c.setParents(parents);
    1678         c.subs = {
    1679             center: p
    1680         };
    1681         c.inherits.push(c);
    1682         for (i = 0; i < 3; i++) {
    1683             if (Type.exists(parents[i]._is_new)) {
    1684                 c.addChild(parents[i]);
    1685                 delete parents[i]._is_new;
    1686             } else {
    1687                 parents[i].addChild(c);
    1688             }
    1689         }
    1690     } catch (e) {
    1691         throw new Error(
    1692             "JSXGraph: Can't create circumcircle with parent types '" +
    1693                 typeof parents[0] +
    1694                 "', '" +
    1695                 typeof parents[1] +
    1696                 "' and '" +
    1697                 typeof parents[2] +
    1698                 "'." +
    1699                 "\nPossible parent types: [point,point,point]"
    1700         );
    1701     }
    1702
    1703     // p is already stored as midpoint in c so there's no need to store it explicitly.
    1704
    1705     return c;
    1706 };
    1707
    1708 /**
    1709  * @class An incircle is given by three points.
    1710  * @pseudo
    1711  * @constructor
    1712  * @name Incircle
    1713  * @type JXG.Circle
    1714  * @augments JXG.Circle
    1715  * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown.
    1716  * @param {JXG.Point_JXG.Point_JXG.Point} p1,p2,p3 The constructed point is the midpoint of the incircle of
    1717  * <tt>p1</tt>, <tt>p2</tt>, and <tt>p3</tt>.
    1718  * @example
    1719  * var p1 = board.create('point', [0.0, 2.0]);
    1720  * var p2 = board.create('point', [2.0, 1.0]);
    1721  * var p3 = board.create('point', [3.0, 3.0]);
    1722  *
    1723  * var ic1 = board.create('incircle', [p1, p2, p3]);
    1724  * </pre><div class="jxgbox" id="JXGe65c9861-0bf0-402d-af57-2ab12962f8ac" style="width: 400px; height: 400px;"></div>
    1725  * <script type="text/javascript">
    1726  *   var icex1_board = JXG.JSXGraph.initBoard('JXGe65c9861-0bf0-402d-af57-2ab12962f8ac', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false});
    1727  *   var icex1_p1 = icex1_board.create('point', [0.0, 2.0]);
    1728  *   var icex1_p2 = icex1_board.create('point', [6.0, 1.0]);
    1729  *   var icex1_p3 = icex1_board.create('point', [3.0, 7.0]);
    1730  *   var icex1_ic1 = icex1_board.create('incircle', [icex1_p1, icex1_p2, icex1_p3]);
    1731  * </script><pre>
    1732  */
    1733 JXG.createIncircle = function (board, parents, attributes) {
    1734     var i, p, c, attr;
    1735
    1736     parents = Type.providePoints(board, parents, attributes, "point");
    1737     if (parents === false) {
    1738         throw new Error(
    1739             "JSXGraph: Can't create circumcircle with parent types '" +
    1740                 typeof parents[0] +
    1741                 "', '" +
    1742                 typeof parents[1] +
    1743                 "' and '" +
    1744                 typeof parents[2] +
    1745                 "'." +
    1746                 "\nPossible parent types: [point,point,point]"
    1747         );
    1748     }
    1749     try {
    1750         attr = Type.copyAttributes(attributes, board.options, "incircle", "center");
    1751         p = JXG.createIncenter(board, parents, attr);
    1752
    1753         p.dump = false;
    1754
    1755         if (!Type.exists(attributes.layer)) {
    1756             attributes.layer = board.options.layer.circle;
    1757         }
    1758         attr = Type.copyAttributes(attributes, board.options, "incircle");
    1759         c = JXG.createCircle(
    1760             board,
    1761             [
    1762                 p,
    1763                 function () {
    1764                     var a = Mat.hypot(parents[1].X() - parents[2].X(), parents[1].Y() - parents[2].Y()),
    1765                         b = Mat.hypot(parents[0].X() - parents[2].X(), parents[0].Y() - parents[2].Y()),
    1766                         c = Mat.hypot(parents[1].X() - parents[0].X(), parents[1].Y() - parents[0].Y()),
    1767                         s = (a + b + c) / 2;
    1768
    1769                     return Math.sqrt(((s - a) * (s - b) * (s - c)) / s);
    1770                 }
    1771             ],
    1772             attr
    1773         );
    1774
    1775         c.elType = "incircle";
    1776         c.setParents(parents);
    1777         for (i = 0; i < 3; i++) {
    1778             if (Type.exists(parents[i]._is_new)) {
    1779                 c.addChild(parents[i]);
    1780                 delete parents[i]._is_new;
    1781             } else {
    1782                 parents[i].addChild(c);
    1783             }
    1784         }
    1785
    1786         /**
    1787          * The center of the incircle
    1788          * @memberOf Incircle.prototype
    1789          * @type Incenter
    1790          * @name center
    1791          */
    1792         c.center = p;
    1793
    1794         c.subs = {
    1795             center: c.center
    1796         };
    1797         c.inherits.push(p);
    1798     } catch (e) {
    1799         throw new Error(
    1800             "JSXGraph: Can't create circumcircle with parent types '" +
    1801                 typeof parents[0] +
    1802                 "', '" +
    1803                 typeof parents[1] +
    1804                 "' and '" +
    1805                 typeof parents[2] +
    1806                 "'." +
    1807                 "\nPossible parent types: [point,point,point]"
    1808         );
    1809     }
    1810
    1811     // p is already stored as midpoint in c so there's no need to store it explicitly.
    1812
    1813     return c;
    1814 };
    1815
    1816 /**
    1817  * @class This element is used to construct reflected elements (points, lines, circles, curves, polygons).
    1818  * @pseudo
    1819  * @description A reflected element (point, polygon, line or curve) is given by a given
    1820  * object of the same type and a line of reflection.
    1821  * It is determined by the reflection of the given element
    1822  * across the given line.
    1823  * @constructor
    1824  * @name Reflection
    1825  * @type JXG.GeometryElement
    1826  * @augments JXG.GeometryElement
    1827  * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown.
    1828  * @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.
    1829  * @example
    1830  * var p1 = board.create('point', [0.0, 4.0]);
    1831  * var p2 = board.create('point', [6.0, 1.0]);
    1832  * var l1 = board.create('line', [p1, p2]);
    1833  * var p3 = board.create('point', [3.0, 3.0]);
    1834  *
    1835  * var rp1 = board.create('reflection', [p3, l1]);
    1836  * </pre><div class="jxgbox" id="JXG087a798e-a36a-4f52-a2b4-29a23a69393b" style="width: 400px; height: 400px;"></div>
    1837  * <script type="text/javascript">
    1838  *   var rpex1_board = JXG.JSXGraph.initBoard('JXG087a798e-a36a-4f52-a2b4-29a23a69393b', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false});
    1839  *   var rpex1_p1 = rpex1_board.create('point', [0.0, 4.0]);
    1840  *   var rpex1_p2 = rpex1_board.create('point', [6.0, 1.0]);
    1841  *   var rpex1_l1 = rpex1_board.create('line', [rpex1_p1, rpex1_p2]);
    1842  *   var rpex1_p3 = rpex1_board.create('point', [3.0, 3.0]);
    1843  *   var rpex1_rp1 = rpex1_board.create('reflection', [rpex1_p3, rpex1_l1]);
    1844  * </script><pre>
    1845  * @example
    1846  *         // Reflection of more elements
    1847  *         // reflection line
    1848  *         var li = board.create('line', [1,1,1], {strokeColor: '#aaaaaa'});
    1849  *
    1850  *         var p1 = board.create('point', [-3,-1], {name: "A"});
    1851  *         var q1 = board.create('reflection', [p1, li], {name: "A'"});
    1852  *
    1853  *         var l1 = board.create('line', [1,-5,1]);
    1854  *         var l2 = board.create('reflection', [l1, li]);
    1855  *
    1856  *         var cu1 = board.create('curve', [[-3, -3, -2.5, -3, -3, -2.5], [-3, -2, -2, -2, -2.5, -2.5]], {strokeWidth:3});
    1857  *         var cu2 = board.create('reflection', [cu1, li], {strokeColor: 'red', strokeWidth:3});
    1858  *
    1859  *         var pol1 = board.create('polygon', [[-6,-3], [-4,-5], [-5,-1.5]]);
    1860  *         var pol2 = board.create('reflection', [pol1, li]);
    1861  *
    1862  *         var c1 = board.create('circle', [[-2,-2], [-2, -1]]);
    1863  *         var c2 = board.create('reflection', [c1, li]);
    1864  *
    1865  *         var a1 = board.create('arc', [[1, 1], [0, 1], [1, 0]], {strokeColor: 'red'});
    1866  *         var a2 = board.create('reflection', [a1, li], {strokeColor: 'red'});
    1867  *
    1868  *         var s1 = board.create('sector', [[-3.5,-3], [-3.5, -2], [-3.5,-4]], {
    1869  *                           anglePoint: {visible:true}, center: {visible: true}, radiusPoint: {visible: true},
    1870  *                           fillColor: 'yellow', strokeColor: 'black'});
    1871  *         var s2 = board.create('reflection', [s1, li], {fillColor: 'yellow', strokeColor: 'black', fillOpacity: 0.5});
    1872  *
    1873  *         var an1 = board.create('angle', [[-4,3.9], [-3, 4], [-3, 3]]);
    1874  *         var an2 = board.create('reflection', [an1, li]);
    1875  *
    1876  * </pre><div id="JXG8f763af4-d449-11e7-93b3-901b0e1b8723" class="jxgbox" style="width: 300px; height: 300px;"></div>
    1877  * <script type="text/javascript">
    1878  *     (function() {
    1879  *         var board = JXG.JSXGraph.initBoard('JXG8f763af4-d449-11e7-93b3-901b0e1b8723',
    1880  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
    1881  *             // reflection line
    1882  *             var li = board.create('line', [1,1,1], {strokeColor: '#aaaaaa'});
    1883  *
    1884  *             var p1 = board.create('point', [-3,-1], {name: "A"});
    1885  *             var q1 = board.create('reflection', [p1, li], {name: "A'"});
    1886  *
    1887  *             var l1 = board.create('line', [1,-5,1]);
    1888  *             var l2 = board.create('reflection', [l1, li]);
    1889  *
    1890  *             var cu1 = board.create('curve', [[-3, -3, -2.5, -3, -3, -2.5], [-3, -2, -2, -2, -2.5, -2.5]], {strokeWidth:3});
    1891  *             var cu2 = board.create('reflection', [cu1, li], {strokeColor: 'red', strokeWidth:3});
    1892  *
    1893  *             var pol1 = board.create('polygon', [[-6,-3], [-4,-5], [-5,-1.5]]);
    1894  *             var pol2 = board.create('reflection', [pol1, li]);
    1895  *
    1896  *             var c1 = board.create('circle', [[-2,-2], [-2, -1]]);
    1897  *             var c2 = board.create('reflection', [c1, li]);
    1898  *
    1899  *         var a1 = board.create('arc', [[1, 1], [0, 1], [1, 0]], {strokeColor: 'red'});
    1900  *         var a2 = board.create('reflection', [a1, li], {strokeColor: 'red'});
    1901  *
    1902  *         var s1 = board.create('sector', [[-3.5,-3], [-3.5, -2], [-3.5,-4]], {
    1903  *                           anglePoint: {visible:true}, center: {visible: true}, radiusPoint: {visible: true},
    1904  *                           fillColor: 'yellow', strokeColor: 'black'});
    1905  *         var s2 = board.create('reflection', [s1, li], {fillColor: 'yellow', strokeColor: 'black', fillOpacity: 0.5});
    1906  *
    1907  *         var an1 = board.create('angle', [[-4,3.9], [-3, 4], [-3, 3]]);
    1908  *         var an2 = board.create('reflection', [an1, li]);
    1909  *
    1910  *     })();
    1911  *
    1912  * </script><pre>
    1913  *
    1914  */
    1915 JXG.createReflection = function (board, parents, attributes) {
    1916     var l, org, r, r_c,
    1917         t, i, attr, attr2,
    1918         errStr = "\nPossible parent types: [point|line|curve|polygon|circle|arc|sector, line]";
    1919
    1920     for (i = 0; i < parents.length; ++i) {
    1921         parents[i] = board.select(parents[i]);
    1922     }
    1923
    1924     attr = Type.copyAttributes(attributes, board.options, "reflection");
    1925
    1926     if (Type.isPoint(parents[0])) {
    1927         org = Type.providePoints(board, [parents[0]], attr2)[0];
    1928     } else if (
    1929         parents[0].elementClass === Const.OBJECT_CLASS_CURVE ||
    1930         parents[0].elementClass === Const.OBJECT_CLASS_LINE ||
    1931         parents[0].type === Const.OBJECT_TYPE_POLYGON ||
    1932         parents[0].elementClass === Const.OBJECT_CLASS_CIRCLE
    1933     ) {
    1934         org = parents[0];
    1935     } else {
    1936         throw new Error(
    1937             "JSXGraph: Can't create reflection element with parent types '" +
    1938                 typeof parents[0] +
    1939                 "' and '" +
    1940                 typeof parents[1] +
    1941                 "'." +
    1942                 errStr
    1943         );
    1944     }
    1945
    1946     if (parents[1].elementClass === Const.OBJECT_CLASS_LINE) {
    1947         l = parents[1];
    1948     } else {
    1949         throw new Error(
    1950             "JSXGraph: Can't create reflected element with parent types '" +
    1951                 typeof parents[0] +
    1952                 "' and '" +
    1953                 typeof parents[1] +
    1954                 "'." +
    1955                 errStr
    1956         );
    1957     }
    1958     t = JXG.createTransform(board, [l], { type: "reflect" });
    1959
    1960     if (Type.isPoint(org)) {
    1961         r = JXG.createPoint(board, [org, t], attr);
    1962
    1963         // Arcs and sectors are treated as curves
    1964     } else if (org.elementClass === Const.OBJECT_CLASS_CURVE) {
    1965         r = JXG.createCurve(board, [org, t], attr);
    1966     } else if (org.elementClass === Const.OBJECT_CLASS_LINE) {
    1967         r = JXG.createLine(board, [org, t], attr);
    1968     } else if (org.type === Const.OBJECT_TYPE_POLYGON) {
    1969         r = JXG.createPolygon(board, [org, t], attr);
    1970     } else if (org.elementClass === Const.OBJECT_CLASS_CIRCLE) {
    1971         if (attr.type.toLowerCase() === "euclidean") {
    1972             // Create a circle element from a circle and a Euclidean transformation
    1973             attr2 = Type.copyAttributes(attributes, board.options, "reflection", "center");
    1974             r_c = JXG.createPoint(board, [org.center, t], attr2);
    1975             r_c.prepareUpdate()
    1976                 .update()
    1977                 .updateVisibility(Type.evaluate(r_c.visProp.visible))
    1978                 .updateRenderer();
    1979             r = JXG.createCircle(
    1980                 board,
    1981                 [
    1982                     r_c,
    1983                     function () {
    1984                         return org.Radius();
    1985                     }
    1986                 ],
    1987                 attr
    1988             );
    1989         } else {
    1990             // Create a conic element from a circle and a projective transformation
    1991             r = JXG.createCircle(board, [org, t], attr);
    1992         }
    1993     } else {
    1994         throw new Error(
    1995             "JSXGraph: Can't create reflected element with parent types '" +
    1996                 typeof parents[0] +
    1997                 "' and '" +
    1998                 typeof parents[1] +
    1999                 "'." +
    2000                 errStr
    2001         );
    2002     }
    2003
    2004     if (Type.exists(org._is_new)) {
    2005         r.addChild(org);
    2006         delete org._is_new;
    2007     } else {
    2008         // org.addChild(r);
    2009     }
    2010     l.addChild(r);
    2011
    2012     r.elType = "reflection";
    2013     r.addParents(l);
    2014     r.prepareUpdate().update(); //.updateVisibility(Type.evaluate(r.visProp.visible)).updateRenderer();
    2015
    2016     if (Type.isPoint(r)) {
    2017         r.generatePolynomial = function () {
    2018             /*
    2019              *  Reflection takes a point R and a line L and creates point P, which is the reflection of R on L.
    2020              *  L is defined by two points A and B.
    2021              *
    2022              * So we have two conditions:
    2023              *
    2024              *   (a)   RP  _|_  AB            (orthogonality condition)
    2025              *   (b)   AR  ==   AP            (distance condition)
    2026              *
    2027              */
    2028             var a1 = l.point1.symbolic.x,
    2029                 a2 = l.point1.symbolic.y,
    2030                 b1 = l.point2.symbolic.x,
    2031                 b2 = l.point2.symbolic.y,
    2032                 p1 = org.symbolic.x,
    2033                 p2 = org.symbolic.y,
    2034                 r1 = r.symbolic.x,
    2035                 r2 = r.symbolic.y,
    2036                 poly1 = ["((", r2, ")-(", p2, "))*((", a2, ")-(", b2, "))+((", a1, ")-(", b1, "))*((", r1, ")-(", p1, "))"].join(""),
    2037                 poly2 = ["((", r1, ")-(", a1, "))^2+((", r2, ")-(", a2, "))^2-((", p1, ")-(", a1, "))^2-((", p2, ")-(", a2, "))^2"].join("");
    2038
    2039             return [poly1, poly2];
    2040         };
    2041     }
    2042
    2043     return r;
    2044 };
    2045
    2046 /**
    2047  * @class A mirror element of a point, line, circle, curve, polygon will be constructed.
    2048  * @pseudo
    2049  * @description A mirror element is determined by the reflection of a given point, line, circle, curve, polygon across another given point.
    2050  * @constructor
    2051  * @name mirrorelement
    2052  * @type JXG.GeometryElement
    2053  * @augments JXG.GeometryElement
    2054  * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown.
    2055  * @param {JXG.Point|JXG.Line|JXG.Curve|JXG.Ppolygon_JXG.Point} p1,p2 The constructed element is the mirror image of p2 across p1.
    2056  * @example
    2057  *         // point of reflection
    2058  *         var mirr = board.create('point', [-1,-1], {color: '#aaaaaa'});
    2059  *
    2060  *         var p1 = board.create('point', [-3,-1], {name: "A"});
    2061  *         var q1 = board.create('mirrorelement', [p1, mirr], {name: "A'"});
    2062  *
    2063  *         var l1 = board.create('line', [1, -5, 1]);
    2064  *         var l2 = board.create('mirrorelement', [l1, mirr]);
    2065  *
    2066  *         var cu1 = board.create('curve', [[-3, -3, -2.5, -3, -3, -2.5], [-3, -2, -2, -2, -2.5, -2.5]], {strokeWidth:3});
    2067  *         var cu2 = board.create('mirrorelement', [cu1, mirr], {strokeColor: 'red', strokeWidth:3});
    2068  *
    2069  *         var pol1 = board.create('polygon', [[-6,-2], [-4,-4], [-5,-0.5]]);
    2070  *         var pol2 = board.create('mirrorelement', [pol1, mirr]);
    2071  *
    2072  *         var c1 = board.create('circle', [[-6,-6], [-6, -5]]);
    2073  *         var c2 = board.create('mirrorelement', [c1, mirr]);
    2074  *
    2075  *         var a1 = board.create('arc', [[1, 1], [0, 1], [1, 0]], {strokeColor: 'red'});
    2076  *         var a2 = board.create('mirrorelement', [a1, mirr], {strokeColor: 'red'});
    2077  *
    2078  *         var s1 = board.create('sector', [[-3.5,-3], [-3.5, -2], [-3.5,-4]], {
    2079  *                           anglePoint: {visible:true}, center: {visible: true}, radiusPoint: {visible: true},
    2080  *                           fillColor: 'yellow', strokeColor: 'black'});
    2081  *         var s2 = board.create('mirrorelement', [s1, mirr], {fillColor: 'yellow', strokeColor: 'black', fillOpacity: 0.5});
    2082  *
    2083  *         var an1 = board.create('angle', [[-4,3.9], [-3, 4], [-3, 3]]);
    2084  *         var an2 = board.create('mirrorelement', [an1, mirr]);
    2085  *
    2086  *
    2087  * </pre><div id="JXG026c779c-d8d9-11e7-93b3-901b0e1b8723" class="jxgbox" style="width: 300px; height: 300px;"></div>
    2088  * <script type="text/javascript">
    2089  *     (function() {
    2090  *         var board = JXG.JSXGraph.initBoard('JXG026c779c-d8d9-11e7-93b3-901b0e1b8723',
    2091  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
    2092  *             // point of reflection
    2093  *             var mirr = board.create('point', [-1,-1], {color: '#aaaaaa'});
    2094  *
    2095  *             var p1 = board.create('point', [-3,-1], {name: "A"});
    2096  *             var q1 = board.create('mirrorelement', [p1, mirr], {name: "A'"});
    2097  *
    2098  *             var l1 = board.create('line', [1,-5, 1]);
    2099  *             var l2 = board.create('mirrorelement', [l1, mirr]);
    2100  *
    2101  *             var cu1 = board.create('curve', [[-3, -3, -2.5, -3, -3, -2.5], [-3, -2, -2, -2, -2.5, -2.5]], {strokeWidth:3});
    2102  *             var cu2 = board.create('mirrorelement', [cu1, mirr], {strokeColor: 'red', strokeWidth:3});
    2103  *
    2104  *             var pol1 = board.create('polygon', [[-6,-2], [-4,-4], [-5,-0.5]]);
    2105  *             var pol2 = board.create('mirrorelement', [pol1, mirr]);
    2106  *
    2107  *             var c1 = board.create('circle', [[-6,-6], [-6, -5]]);
    2108  *             var c2 = board.create('mirrorelement', [c1, mirr]);
    2109  *
    2110  *         var a1 = board.create('arc', [[1, 1], [0, 1], [1, 0]], {strokeColor: 'red'});
    2111  *         var a2 = board.create('mirrorelement', [a1, mirr], {strokeColor: 'red'});
    2112  *
    2113  *         var s1 = board.create('sector', [[-3.5,-3], [-3.5, -2], [-3.5,-4]], {
    2114  *                           anglePoint: {visible:true}, center: {visible: true}, radiusPoint: {visible: true},
    2115  *                           fillColor: 'yellow', strokeColor: 'black'});
    2116  *         var s2 = board.create('mirrorelement', [s1, mirr], {fillColor: 'yellow', strokeColor: 'black', fillOpacity: 0.5});
    2117  *
    2118  *         var an1 = board.create('angle', [[-4,3.9], [-3, 4], [-3, 3]]);
    2119  *         var an2 = board.create('mirrorelement', [an1, mirr]);
    2120  *
    2121  *     })();
    2122  *
    2123  * </script><pre>
    2124  */
    2125 JXG.createMirrorElement = function (board, parents, attributes) {
    2126     var org, i, m, r, r_c, t,
    2127         attr, attr2,
    2128         errStr = "\nPossible parent types: [point|line|curve|polygon|circle|arc|sector, point]";
    2129
    2130     for (i = 0; i < parents.length; ++i) {
    2131         parents[i] = board.select(parents[i]);
    2132     }
    2133
    2134     attr = Type.copyAttributes(attributes, board.options, "mirrorelement");
    2135     if (Type.isPoint(parents[0])) {
    2136         // Create point to be mirrored if supplied by coords array.
    2137         org = Type.providePoints(board, [parents[0]], attr)[0];
    2138     } else if (
    2139         parents[0].elementClass === Const.OBJECT_CLASS_CURVE ||
    2140         parents[0].elementClass === Const.OBJECT_CLASS_LINE ||
    2141         parents[0].type === Const.OBJECT_TYPE_POLYGON ||
    2142         parents[0].elementClass === Const.OBJECT_CLASS_CIRCLE
    2143     ) {
    2144         org = parents[0];
    2145     } else {
    2146         throw new Error(
    2147             "JSXGraph: Can't create mirror element with parent types '" +
    2148                 typeof parents[0] +
    2149                 "' and '" +
    2150                 typeof parents[1] +
    2151                 "'." +
    2152                 errStr
    2153         );
    2154     }
    2155
    2156     if (Type.isPoint(parents[1])) {
    2157         attr2 = Type.copyAttributes(attributes, board.options, "mirrorelement", "point");
    2158         // Create mirror point if supplied by coords array.
    2159         m = Type.providePoints(board, [parents[1]], attr2)[0];
    2160     } else {
    2161         throw new Error(
    2162             "JSXGraph: Can't create mirror element with parent types '" +
    2163                 typeof parents[0] +
    2164                 "' and '" +
    2165                 typeof parents[1] +
    2166                 "'." +
    2167                 errStr
    2168         );
    2169     }
    2170
    2171     t = JXG.createTransform(board, [Math.PI, m], { type: "rotate" });
    2172     if (Type.isPoint(org)) {
    2173         r = JXG.createPoint(board, [org, t], attr);
    2174
    2175         // Arcs and sectors are treated as curves
    2176     } else if (org.elementClass === Const.OBJECT_CLASS_CURVE) {
    2177         r = JXG.createCurve(board, [org, t], attr);
    2178     } else if (org.elementClass === Const.OBJECT_CLASS_LINE) {
    2179         r = JXG.createLine(board, [org, t], attr);
    2180     } else if (org.type === Const.OBJECT_TYPE_POLYGON) {
    2181         r = JXG.createPolygon(board, [org, t], attr);
    2182     } else if (org.elementClass === Const.OBJECT_CLASS_CIRCLE) {
    2183         if (attr.type.toLowerCase() === "euclidean") {
    2184             // Create a circle element from a circle and a Euclidean transformation
    2185             attr2 = Type.copyAttributes(attributes, board.options, "mirrorelement", "center");
    2186             r_c = JXG.createPoint(board, [org.center, t], attr2);
    2187             r_c.prepareUpdate()
    2188                 .update()
    2189                 .updateVisibility(Type.evaluate(r_c.visProp.visible))
    2190                 .updateRenderer();
    2191             r = JXG.createCircle(
    2192                 board,
    2193                 [
    2194                     r_c,
    2195                     function () {
    2196                         return org.Radius();
    2197                     }
    2198                 ],
    2199                 attr
    2200             );
    2201         } else {
    2202             // Create a conic element from a circle and a projective transformation
    2203             r = JXG.createCircle(board, [org, t], attr);
    2204         }
    2205     } else {
    2206         throw new Error(
    2207             "JSXGraph: Can't create mirror element with parent types '" +
    2208                 typeof parents[0] +
    2209                 "' and '" +
    2210                 typeof parents[1] +
    2211                 "'." +
    2212                 errStr
    2213         );
    2214     }
    2215
    2216     if (Type.exists(org._is_new)) {
    2217         r.addChild(org);
    2218         delete org._is_new;
    2219     } else {
    2220         // org.addChild(r);
    2221     }
    2222     m.addChild(r);
    2223
    2224     r.elType = "mirrorelement";
    2225     r.addParents(m);
    2226     r.prepareUpdate().update();
    2227
    2228     return r;
    2229 };
    2230
    2231 /**
    2232  * @class A mirror point will be constructed.
    2233  * @pseudo
    2234  * @description A mirror point is determined by the reflection of a given point against another given point.
    2235  * @constructor
    2236  * @name Mirrorpoint
    2237  * @type JXG.Point
    2238  * @augments JXG.Point
    2239  * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown.
    2240  * @param {JXG.Point_JXG.Point} p1,p2 The constructed point is the reflection of p2 against p1.
    2241  *
    2242  * This method is superseeded by the more general {@link JXG.createMirrorElement}.
    2243  * @example
    2244  * var p1 = board.create('point', [3.0, 3.0]);
    2245  * var p2 = board.create('point', [6.0, 1.0]);
    2246  *
    2247  * var mp1 = board.create('mirrorpoint', [p1, p2]);
    2248  * </pre><div class="jxgbox" id="JXG7eb2a814-6c4b-4caa-8cfa-4183a948d25b" style="width: 400px; height: 400px;"></div>
    2249  * <script type="text/javascript">
    2250  *   var mpex1_board = JXG.JSXGraph.initBoard('JXG7eb2a814-6c4b-4caa-8cfa-4183a948d25b', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false});
    2251  *   var mpex1_p1 = mpex1_board.create('point', [3.0, 3.0]);
    2252  *   var mpex1_p2 = mpex1_board.create('point', [6.0, 1.0]);
    2253  *   var mpex1_mp1 = mpex1_board.create('mirrorpoint', [mpex1_p1, mpex1_p2]);
    2254  * </script><pre>
    2255  */
    2256 JXG.createMirrorPoint = function (board, parents, attributes) {
    2257     var el = JXG.createMirrorElement(board, parents, attributes);
    2258     el.elType = "mirrorpoint";
    2259     return el;
    2260 };
    2261
    2262 /**
    2263  * @class This element is used to visualize the integral of a given curve over a given interval.
    2264  * @pseudo
    2265  * @description The Integral element is used to visualize the area under a given curve over a given interval
    2266  * and to calculate the area's value. For that a polygon and gliders are used. The polygon displays the area,
    2267  * the gliders are used to change the interval dynamically.
    2268  * @constructor
    2269  * @name Integral
    2270  * @type JXG.Curve
    2271  * @augments JXG.Curve
    2272  * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown.
    2273  * @param {Array_JXG.Curve} i,c The constructed element covers the area between the curve <tt>c</tt> and the x-axis
    2274  * within the interval <tt>i</tt>.
    2275  * @example
    2276  * var c1 = board.create('functiongraph', [function (t) { return Math.cos(t)*t; }]);
    2277  * var i1 = board.create('integral', [[-2.0, 2.0], c1]);
    2278  * </pre><div class="jxgbox" id="JXGd45d7188-6624-4d6e-bebb-1efa2a305c8a" style="width: 400px; height: 400px;"></div>
    2279  * <script type="text/javascript">
    2280  *   var intex1_board = JXG.JSXGraph.initBoard('JXGd45d7188-6624-4d6e-bebb-1efa2a305c8a', {boundingbox: [-5, 5, 5, -5], axis: true, showcopyright: false, shownavigation: false});
    2281  *   var intex1_c1 = intex1_board.create('functiongraph', [function (t) { return Math.cos(t)*t; }]);
    2282  *   var intex1_i1 = intex1_board.create('integral', [[-2.0, 2.0], intex1_c1]);
    2283  * </script><pre>
    2284  */
    2285 JXG.createIntegral = function (board, parents, attributes) {
    2286     var interval, curve, attr, start, end,
    2287         startx, starty, endx, endy,
    2288         pa_on_curve, pa_on_axis, pb_on_curve, pb_on_axis,
    2289         txt_fun,
    2290         t = null, p;
    2291
    2292     if (Type.isArray(parents[0]) && parents[1].elementClass === Const.OBJECT_CLASS_CURVE) {
    2293         interval = parents[0];
    2294         curve = parents[1];
    2295     } else if (
    2296         Type.isArray(parents[1]) &&
    2297         parents[0].elementClass === Const.OBJECT_CLASS_CURVE
    2298     ) {
    2299         interval = parents[1];
    2300         curve = parents[0];
    2301     } else {
    2302         throw new Error(
    2303             "JSXGraph: Can't create integral with parent types '" +
    2304                 typeof parents[0] +
    2305                 "' and '" +
    2306                 typeof parents[1] +
    2307                 "'." +
    2308                 "\nPossible parent types: [[number|function,number|function],curve]"
    2309         );
    2310     }
    2311
    2312     attr = Type.copyAttributes(attributes, board.options, "integral");
    2313     attr.withLabel = false; // There is a custom 'label' below.
    2314     p = board.create("curve", [[0], [0]], attr);
    2315
    2316     // Correct the interval if necessary - NOT ANYMORE, GGB's fault
    2317     start = interval[0];
    2318     end = interval[1];
    2319
    2320     if (Type.isFunction(start)) {
    2321         startx = start;
    2322         starty = function () {
    2323             return curve.Y(startx());
    2324         };
    2325         start = startx();
    2326     } else {
    2327         startx = start;
    2328         starty = curve.Y(start);
    2329     }
    2330
    2331     if (Type.isFunction(end)) {
    2332         endx = end;
    2333         endy = function () {
    2334             return curve.Y(endx());
    2335         };
    2336         end = endx();
    2337     } else {
    2338         endx = end;
    2339         endy = curve.Y(end);
    2340     }
    2341
    2342     attr = Type.copyAttributes(attributes, board.options, "integral", "curveleft");
    2343     pa_on_curve = board.create("glider", [startx, starty, curve], attr);
    2344     if (Type.isFunction(startx)) {
    2345         pa_on_curve.hideElement();
    2346     }
    2347
    2348     attr = Type.copyAttributes(attributes, board.options, 'integral', 'baseleft');
    2349     pa_on_axis = board.create('point', [
    2350             function () {
    2351                 if (Type.evaluate(p.visProp.axis) === "y") {
    2352                     return 0;
    2353                 }
    2354                 return pa_on_curve.X();
    2355             },
    2356             function () {
    2357                 if (Type.evaluate(p.visProp.axis) === "y") {
    2358                     return pa_on_curve.Y();
    2359                 }
    2360                 return 0;
    2361             }
    2362         ], attr);
    2363
    2364     attr = Type.copyAttributes(attributes, board.options, "integral", "curveright");
    2365     pb_on_curve = board.create("glider", [endx, endy, curve], attr);
    2366     if (Type.isFunction(endx)) {
    2367         pb_on_curve.hideElement();
    2368     }
    2369
    2370     attr = Type.copyAttributes(attributes, board.options, "integral", "baseright");
    2371     pb_on_axis = board.create('point', [
    2372             function () {
    2373                 if (Type.evaluate(p.visProp.axis) === "y") {
    2374                     return 0;
    2375                 }
    2376                 return pb_on_curve.X();
    2377             },
    2378             function () {
    2379                 if (Type.evaluate(p.visProp.axis) === "y") {
    2380                     return pb_on_curve.Y();
    2381                 }
    2382
    2383                 return 0;
    2384             }
    2385         ], attr);
    2386
    2387     attr = Type.copyAttributes(attributes, board.options, "integral");
    2388     if (attr.withlabel !== false && attr.axis !== "y") {
    2389         attr = Type.copyAttributes(attributes, board.options, "integral", "label");
    2390         attr = Type.copyAttributes(attr, board.options, "label");
    2391
    2392         t = board.create('text', [
    2393                 function () {
    2394                     var off = new Coords(
    2395                             Const.COORDS_BY_SCREEN,
    2396                             [
    2397                                 Type.evaluate(this.visProp.offset[0]) +
    2398                                     this.board.origin.scrCoords[1],
    2399                                 0
    2400                             ],
    2401                             this.board,
    2402                             false
    2403                         ),
    2404                         bb = this.board.getBoundingBox(),
    2405                         dx = (bb[2] - bb[0]) * 0.1,
    2406                         x = pb_on_curve.X();
    2407
    2408                     if (x < bb[0]) {
    2409                         x = bb[0] + dx;
    2410                     } else if (x > bb[2]) {
    2411                         x = bb[2] - dx;
    2412                     }
    2413
    2414                     return x + off.usrCoords[1];
    2415                 },
    2416                 function () {
    2417                     var off = new Coords(
    2418                             Const.COORDS_BY_SCREEN,
    2419                             [
    2420                                 0,
    2421                                 Type.evaluate(this.visProp.offset[1]) +
    2422                                     this.board.origin.scrCoords[2]
    2423                             ],
    2424                             this.board,
    2425                             false
    2426                         ),
    2427                         bb = this.board.getBoundingBox(),
    2428                         dy = (bb[1] - bb[3]) * 0.1,
    2429                         y = pb_on_curve.Y();
    2430
    2431                     if (y > bb[1]) {
    2432                         y = bb[1] - dy;
    2433                     } else if (y < bb[3]) {
    2434                         y = bb[3] + dy;
    2435                     }
    2436
    2437                     return y + off.usrCoords[2];
    2438                 },
    2439                 ''
    2440             ], attr);
    2441
    2442         txt_fun = function () {
    2443             var Int = Numerics.NewtonCotes([pa_on_axis.X(), pb_on_axis.X()], curve.Y),
    2444                 digits = Type.evaluate(t.visProp.digits),
    2445                 val;
    2446
    2447             if (t.useLocale()) {
    2448                 val = t.formatNumberLocale(Int, digits);
    2449             } else {
    2450                 val = Type.toFixed(Int, digits);
    2451             }
    2452             return '∫ = ' + val;
    2453         };
    2454         t.setText(txt_fun);
    2455         t.dump = false;
    2456
    2457         pa_on_curve.addChild(t);
    2458         pb_on_curve.addChild(t);
    2459     }
    2460
    2461     // dump stuff
    2462     pa_on_curve.dump = false;
    2463     pa_on_axis.dump = false;
    2464
    2465     pb_on_curve.dump = false;
    2466     pb_on_axis.dump = false;
    2467
    2468     p.elType = "integral";
    2469     p.setParents([curve.id, interval]);
    2470     p.subs = {
    2471         curveLeft: pa_on_curve,
    2472         baseLeft: pa_on_axis,
    2473         curveRight: pb_on_curve,
    2474         baseRight: pb_on_axis
    2475     };
    2476     p.inherits.push(pa_on_curve, pa_on_axis, pb_on_curve, pb_on_axis);
    2477
    2478     if (attr.withLabel) {
    2479         p.subs.label = t;
    2480         p.inherits.push(t);
    2481     }
    2482
    2483     /**
    2484      * Returns the current value of the integral.
    2485      * @memberOf Integral
    2486      * @name Value
    2487      * @function
    2488      * @returns {Number}
    2489      */
    2490     p.Value = function () {
    2491         return Numerics.I([pa_on_axis.X(), pb_on_axis.X()], curve.Y);
    2492     };
    2493
    2494     /**
    2495      * documented in JXG.Curve
    2496      * @class
    2497      * @ignore
    2498      */
    2499     p.updateDataArray = function () {
    2500         var x, y, i, left, right, lowx, upx, lowy, upy;
    2501
    2502         if (Type.evaluate(this.visProp.axis) === "y") {
    2503             if (pa_on_curve.Y() < pb_on_curve.Y()) {
    2504                 lowx = pa_on_curve.X();
    2505                 lowy = pa_on_curve.Y();
    2506                 upx = pb_on_curve.X();
    2507                 upy = pb_on_curve.Y();
    2508             } else {
    2509                 lowx = pb_on_curve.X();
    2510                 lowy = pb_on_curve.Y();
    2511                 upx = pa_on_curve.X();
    2512                 upy = pa_on_curve.Y();
    2513             }
    2514             left = Math.min(lowx, upx);
    2515             right = Math.max(lowx, upx);
    2516
    2517             x = [0, lowx];
    2518             y = [lowy, lowy];
    2519
    2520             for (i = 0; i < curve.numberPoints; i++) {
    2521                 if (
    2522                     lowy <= curve.points[i].usrCoords[2] &&
    2523                     left <= curve.points[i].usrCoords[1] &&
    2524                     curve.points[i].usrCoords[2] <= upy &&
    2525                     curve.points[i].usrCoords[1] <= right
    2526                 ) {
    2527                     x.push(curve.points[i].usrCoords[1]);
    2528                     y.push(curve.points[i].usrCoords[2]);
    2529                 }
    2530             }
    2531             x.push(upx);
    2532             y.push(upy);
    2533             x.push(0);
    2534             y.push(upy);
    2535
    2536             // close the curve
    2537             x.push(0);
    2538             y.push(lowy);
    2539         } else {
    2540             if (pa_on_axis.X() < pb_on_axis.X()) {
    2541                 left = pa_on_axis.X();
    2542                 right = pb_on_axis.X();
    2543             } else {
    2544                 left = pb_on_axis.X();
    2545                 right = pa_on_axis.X();
    2546             }
    2547
    2548             x = [left, left];
    2549             y = [0, curve.Y(left)];
    2550
    2551             for (i = 0; i < curve.numberPoints; i++) {
    2552                 if (
    2553                     left <= curve.points[i].usrCoords[1] &&
    2554                     curve.points[i].usrCoords[1] <= right
    2555                 ) {
    2556                     x.push(curve.points[i].usrCoords[1]);
    2557                     y.push(curve.points[i].usrCoords[2]);
    2558                 }
    2559             }
    2560             x.push(right);
    2561             y.push(curve.Y(right));
    2562             x.push(right);
    2563             y.push(0);
    2564
    2565             // close the curve
    2566             x.push(left);
    2567             y.push(0);
    2568         }
    2569
    2570         this.dataX = x;
    2571         this.dataY = y;
    2572     };
    2573
    2574     pa_on_curve.addChild(p);
    2575     pb_on_curve.addChild(p);
    2576     pa_on_axis.addChild(p);
    2577     pb_on_axis.addChild(p);
    2578
    2579     /**
    2580      * The point on the axis initially corresponding to the lower value of the interval.
    2581      *
    2582      * @name baseLeft
    2583      * @memberOf Integral
    2584      * @type JXG.Point
    2585      */
    2586     p.baseLeft = pa_on_axis;
    2587
    2588     /**
    2589      * The point on the axis initially corresponding to the higher value of the interval.
    2590      *
    2591      * @name baseRight
    2592      * @memberOf Integral
    2593      * @type JXG.Point
    2594      */
    2595     p.baseRight = pb_on_axis;
    2596
    2597     /**
    2598      * The glider on the curve corresponding to the lower value of the interval.
    2599      *
    2600      * @name curveLeft
    2601      * @memberOf Integral
    2602      * @type Glider
    2603      */
    2604     p.curveLeft = pa_on_curve;
    2605
    2606     /**
    2607      * The glider on the axis corresponding to the higher value of the interval.
    2608      *
    2609      * @name curveRight
    2610      * @memberOf Integral
    2611      * @type Glider
    2612      */
    2613     p.curveRight = pb_on_curve;
    2614
    2615     p.methodMap = JXG.deepCopy(p.methodMap, {
    2616         curveLeft: "curveLeft",
    2617         baseLeft: "baseLeft",
    2618         curveRight: "curveRight",
    2619         baseRight: "baseRight",
    2620         Value: "Value"
    2621     });
    2622
    2623     /**
    2624      * documented in GeometryElement
    2625      * @ignore
    2626      */
    2627     p.label = t;
    2628
    2629     return p;
    2630 };
    2631
    2632 /**
    2633  * @class Creates an area indicating the solution of a linear inequality or an inequality
    2634  * of a function graph, i.e. an inequality of type y <= f(x).
    2635  * @pseudo
    2636  * @description Display the solution set of a linear inequality (less than or equal to).
    2637  * To be precise, the solution set of the inequality <i>y <= b/a * x + c/a</i> is shown.
    2638  * In case <i>a = 0</i>, that is if the equation of the line is <i>bx + c = 0</i>,
    2639  * the area of the inequality <i>bx + c <= 0</i> is shown.
    2640  * <p>
    2641  * For function graphs the area below the function graph is filled, i.e. the
    2642  * area of the inequality y <= f(x).
    2643  * With the attribute inverse:true the area of the inequality y >= f(x) is filled.
    2644  *
    2645  * @param {JXG.Line} l The area drawn will be the area below this line. With the attribute
    2646  * inverse:true, the inequality 'greater than or equal to' is shown.
    2647  * @constructor
    2648  * @name Inequality
    2649  * @type JXG.Curve
    2650  * @augments JXG.Curve
    2651  * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown.
    2652  * @example
    2653  * var p = board.create('point', [1, 3]),
    2654  *     q = board.create('point', [-2, -4]),
    2655  *     l = board.create('line', [p, q]),
    2656  *     ineq = board.create('inequality', [l]);
    2657  * ineq = board.create('inequality', [l]);
    2658  * </pre><div class="jxgbox" id="JXG2b703006-fd98-11e1-b79e-ef9e591c002e" style="width: 400px; height: 400px;"></div>
    2659  * <script type="text/javascript">
    2660  * (function () {
    2661  *  var board = JXG.JSXGraph.initBoard('JXG2b703006-fd98-11e1-b79e-ef9e591c002e', {boundingbox:[-4, 6, 10, -6], axis: false, grid: false, keepaspectratio: true}),
    2662  *      p = board.create('point', [1, 3]),
    2663  *      q = board.create('point', [-2, -4]),
    2664  *      l = board.create('line', [p, q]),
    2665  *      ineq = board.create('inequality', [l]);
    2666  * })();
    2667  * </script><pre>
    2668  *
    2669  * @example
    2670  * // Plot the inequality
    2671  * //     y >= 2/3 x + 1
    2672  * // or
    2673  * //     0 >= -3y + 2x +1
    2674  * var l = board.create('line', [1, 2, -3]),
    2675  *     ineq = board.create('inequality', [l], {inverse:true});
    2676  * </pre><div class="jxgbox" id="JXG1ded3812-2da4-4323-abaf-1db4bad1bfbd" style="width: 400px; height: 400px;"></div>
    2677  * <script type="text/javascript">
    2678  * (function () {
    2679  *  var board = JXG.JSXGraph.initBoard('JXG1ded3812-2da4-4323-abaf-1db4bad1bfbd', {boundingbox:[-4, 6, 10, -6], axis: false, grid: false, keepaspectratio: true}),
    2680  *      l = board.create('line', [1, 2, -3]),
    2681  *      ineq = board.create('inequality', [l], {inverse:true});
    2682  * })();
    2683  * </script><pre>
    2684  *
    2685  * @example
    2686  * var f = board.create('functiongraph', ['sin(x)', -2*Math.PI, 2*Math.PI]);
    2687  *
    2688  * var ineq_lower = board.create('inequality', [f]);
    2689  * var ineq_greater = board.create('inequality', [f], {inverse: true, fillColor: 'yellow'});
    2690  *
    2691  *
    2692  * </pre><div id="JXGdb68c574-414c-11e8-839a-901b0e1b8723" class="jxgbox" style="width: 300px; height: 300px;"></div>
    2693  * <script type="text/javascript">
    2694  *     (function() {
    2695  *         var board = JXG.JSXGraph.initBoard('JXGdb68c574-414c-11e8-839a-901b0e1b8723',
    2696  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
    2697  *     var f = board.create('functiongraph', ['sin(x)', -2*Math.PI, 2*Math.PI]);
    2698  *
    2699  *     var ineq_lower = board.create('inequality', [f]);
    2700  *     var ineq_greater = board.create('inequality', [f], {inverse: true, fillColor: 'yellow'});
    2701  *
    2702  *
    2703  *     })();
    2704  *
    2705  * </script><pre>
    2706  *
    2707  */
    2708 JXG.createInequality = function (board, parents, attributes) {
    2709     var f, a, attr;
    2710
    2711     attr = Type.copyAttributes(attributes, board.options, "inequality");
    2712     if (parents[0].elementClass === Const.OBJECT_CLASS_LINE) {
    2713         a = board.create("curve", [[], []], attr);
    2714         a.hasPoint = function () {
    2715             return false;
    2716         };
    2717
    2718         /**
    2719          * @class
    2720          * @ignore
    2721          */
    2722         a.updateDataArray = function () {
    2723             var i1,
    2724                 i2,
    2725                 // This will be the height of the area. We mustn't rely upon the board height because if we pan the view
    2726                 // such that the line is not visible anymore, the borders of the area will get visible in some cases.
    2727                 h,
    2728                 bb = board.getBoundingBox(),
    2729                 inverse = Type.evaluate(this.visProp.inverse),
    2730                 factor = inverse ? -1 : 1,
    2731                 expansion = 1.5,
    2732                 w = expansion * Math.max(bb[2] - bb[0], bb[1] - bb[3]),
    2733                 // Fake a point (for Math.Geometry.perpendicular)
    2734                 // contains centroid of the board
    2735                 dp = {
    2736                     coords: {
    2737                         usrCoords: [1, (bb[0] + bb[2]) * 0.5, inverse ? bb[1] : bb[3]]
    2738                     }
    2739                 },
    2740                 slope1 = parents[0].stdform.slice(1),
    2741                 slope2 = slope1;
    2742
    2743             // Calculate the area height as
    2744             //  expansion times the distance of the line to the
    2745             // point in the middle of the top/bottom border.
    2746             h =
    2747                 expansion *
    2748                 Math.max(
    2749                     Geometry.perpendicular(parents[0], dp, board)[0].distance(
    2750                         Const.COORDS_BY_USER,
    2751                         dp.coords
    2752                     ),
    2753                     w
    2754                 );
    2755             h *= factor;
    2756
    2757             // reuse dp
    2758             dp = {
    2759                 coords: {
    2760                     usrCoords: [1, (bb[0] + bb[2]) * 0.5, (bb[1] + bb[3]) * 0.5]
    2761                 }
    2762             };
    2763
    2764             // If dp is on the line, Geometry.perpendicular will return a point not on the line.
    2765             // Since this somewhat odd behavior of Geometry.perpendicular is needed in GEONExT,
    2766             // it is circumvented here.
    2767             if (
    2768                 Math.abs(Mat.innerProduct(dp.coords.usrCoords, parents[0].stdform, 3)) >=
    2769                 Mat.eps
    2770             ) {
    2771                 dp = Geometry.perpendicular(parents[0], dp, board)[0].usrCoords;
    2772             } else {
    2773                 dp = dp.coords.usrCoords;
    2774             }
    2775             i1 = [1, dp[1] + slope1[1] * w, dp[2] - slope1[0] * w];
    2776             i2 = [1, dp[1] - slope2[1] * w, dp[2] + slope2[0] * w];
    2777
    2778             // One of the vectors based in i1 and orthogonal to the parent line has the direction d1 = (slope1, -1)
    2779             // We will go from i1 to i1 + h*d1, from there to i2 + h*d2 (with d2 calculated equivalent to d1) and
    2780             // end up in i2.
    2781             this.dataX = [i1[1], i1[1] + slope1[0] * h, i2[1] + slope2[0] * h, i2[1], i1[1]];
    2782             this.dataY = [i1[2], i1[2] + slope1[1] * h, i2[2] + slope2[1] * h, i2[2], i1[2]];
    2783         };
    2784     } else if (
    2785         parents[0].elementClass === Const.OBJECT_CLASS_CURVE &&
    2786         parents[0].visProp.curvetype === "functiongraph"
    2787     ) {
    2788         a = board.create("curve", [[], []], attr);
    2789         /**
    2790          * @class
    2791          * @ignore
    2792          */
    2793         a.updateDataArray = function () {
    2794             var bbox = this.board.getBoundingBox(),
    2795                 points = [],
    2796                 infty,
    2797                 first,
    2798                 last,
    2799                 len,
    2800                 i,
    2801                 mi = parents[0].minX(),
    2802                 ma = parents[0].maxX(),
    2803                 curve_mi,
    2804                 curve_ma,
    2805                 firstx,
    2806                 lastx,
    2807                 enlarge = (bbox[1] - bbox[3]) * 0.3, // enlarge the bbox vertically by this amount
    2808                 inverse = Type.evaluate(this.visProp.inverse);
    2809
    2810             // inverse == true <=> Fill area with y >= f(x)
    2811             infty = inverse ? 1 : 3; // we will use either bbox[1] or bbox[3] below
    2812
    2813             this.dataX = [];
    2814             this.dataY = [];
    2815             len = parents[0].points.length;
    2816             if (len === 0) {
    2817                 return;
    2818             }
    2819
    2820             bbox[1] += enlarge;
    2821             bbox[3] -= enlarge;
    2822
    2823             last = -1;
    2824             while (last < len - 1) {
    2825                 // Find the first point with real coordinates on this curve segment
    2826                 for (i = last + 1, first = len; i < len; i++) {
    2827                     if (parents[0].points[i].isReal()) {
    2828                         first = i;
    2829                         break;
    2830                     }
    2831                 }
    2832                 // No real points found -> exit
    2833                 if (first >= len) {
    2834                     break;
    2835                 }
    2836
    2837                 // Find the last point with real coordinates on this curve segment
    2838                 for (i = first, last = len - 1; i < len - 1; i++) {
    2839                     if (!parents[0].points[i + 1].isReal()) {
    2840                         last = i;
    2841                         break;
    2842                     }
    2843                 }
    2844
    2845                 firstx = parents[0].points[first].usrCoords[1];
    2846                 lastx = parents[0].points[last].usrCoords[1];
    2847
    2848                 // Restrict the plot interval if the function ends inside of the board
    2849                 curve_mi = bbox[0] < mi ? mi : bbox[0];
    2850                 curve_ma = bbox[2] > ma ? ma : bbox[2];
    2851
    2852                 // Found NaNs
    2853                 curve_mi = first === 0 ? curve_mi : Math.max(curve_mi, firstx);
    2854                 curve_ma = last === len - 1 ? curve_ma : Math.min(curve_ma, lastx);
    2855
    2856                 // First and last relevant x-coordinate of the curve
    2857                 curve_mi = first === 0 ? mi : firstx;
    2858                 curve_ma = last === len - 1 ? ma : lastx;
    2859
    2860                 // Copy the curve points
    2861                 points = [];
    2862
    2863                 points.push([1, curve_mi, bbox[infty]]);
    2864                 points.push([1, curve_mi, parents[0].points[first].usrCoords[2]]);
    2865                 for (i = first; i <= last; i++) {
    2866                     points.push(parents[0].points[i].usrCoords);
    2867                 }
    2868                 points.push([1, curve_ma, parents[0].points[last].usrCoords[2]]);
    2869                 points.push([1, curve_ma, bbox[infty]]);
    2870                 points.push(points[0]);
    2871
    2872                 for (i = 0; i < points.length; i++) {
    2873                     this.dataX.push(points[i][1]);
    2874                     this.dataY.push(points[i][2]);
    2875                 }
    2876
    2877                 if (last < len - 1) {
    2878                     this.dataX.push(NaN);
    2879                     this.dataY.push(NaN);
    2880                 }
    2881             }
    2882         };
    2883
    2884         // Previous code:
    2885         /**
    2886          * @class
    2887          * @ignore
    2888          */
    2889         a.hasPoint = function () {
    2890             return false;
    2891         };
    2892     } else {
    2893         // Not yet practical?
    2894         f = Type.createFunction(parents[0]);
    2895         a.addParentsFromJCFunctions([f]);
    2896
    2897         if (!Type.exists(f)) {
    2898             throw new Error(
    2899                 "JSXGraph: Can't create area with the given parents." +
    2900                     "\nPossible parent types: [line], [function]"
    2901             );
    2902         }
    2903     }
    2904
    2905     a.addParents(parents[0]);
    2906     return a;
    2907 };
    2908
    2909 JXG.registerElement("arrowparallel", JXG.createArrowParallel);
    2910 JXG.registerElement("bisector", JXG.createBisector);
    2911 JXG.registerElement("bisectorlines", JXG.createAngularBisectorsOfTwoLines);
    2912 JXG.registerElement("msector", JXG.createMsector);
    2913 JXG.registerElement("circumcircle", JXG.createCircumcircle);
    2914 JXG.registerElement("circumcirclemidpoint", JXG.createCircumcenter);
    2915 JXG.registerElement("circumcenter", JXG.createCircumcenter);
    2916 JXG.registerElement("incenter", JXG.createIncenter);
    2917 JXG.registerElement("incircle", JXG.createIncircle);
    2918 JXG.registerElement("integral", JXG.createIntegral);
    2919 JXG.registerElement("midpoint", JXG.createMidpoint);
    2920 JXG.registerElement("mirrorelement", JXG.createMirrorElement);
    2921 JXG.registerElement("mirrorpoint", JXG.createMirrorPoint);
    2922 JXG.registerElement("orthogonalprojection", JXG.createOrthogonalProjection);
    2923 JXG.registerElement("parallel", JXG.createParallel);
    2924 JXG.registerElement("parallelpoint", JXG.createParallelPoint);
    2925 JXG.registerElement("perpendicular", JXG.createPerpendicular);
    2926 JXG.registerElement("perpendicularpoint", JXG.createPerpendicularPoint);
    2927 JXG.registerElement("perpendicularsegment", JXG.createPerpendicularSegment);
    2928 JXG.registerElement("reflection", JXG.createReflection);
    2929 JXG.registerElement("inequality", JXG.createInequality);
    2930
    2931 // export default {
    2932 //     createArrowParallel: JXG.createArrowParallel,
    2933 //     createBisector: JXG.createBisector,
    2934 //     createAngularBisectorOfTwoLines: JXG.createAngularBisectorsOfTwoLines,
    2935 //     createCircumcircle: JXG.createCircumcircle,
    2936 //     createCircumcenter: JXG.createCircumcenter,
    2937 //     createIncenter: JXG.createIncenter,
    2938 //     createIncircle: JXG.createIncircle,
    2939 //     createIntegral: JXG.createIntegral,
    2940 //     createMidpoint: JXG.createMidpoint,
    2941 //     createMirrorElement: JXG.createMirrorElement,
    2942 //     createMirrorPoint: JXG.createMirrorPoint,
    2943 //     createNormal: JXG.createNormal,
    2944 //     createOrthogonalProjection: JXG.createOrthogonalProjection,
    2945 //     createParallel: JXG.createParallel,
    2946 //     createParallelPoint: JXG.createParallelPoint,
    2947 //     createPerpendicular: JXG.createPerpendicular,
    2948 //     createPerpendicularPoint: JXG.createPerpendicularPoint,
    2949 //     createPerpendicularSegmen: JXG.createPerpendicularSegment,
    2950 //     createReflection: JXG.createReflection,
    2951 //     createGrid: JXG.createGrid,
    2952 //     createInequality: JXG.createInequality
    2953 // };
    2954