      1 /*
      2     Copyright 2008-2025
      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 A point that is the orthogonal projection of a point onto 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 A perpendicular is a line orthogonal to a given line, through a given point not on the line,
    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 Orthogonal projection of a point onto a line.
    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 A line segment orthogonal to a given line, through a given point not on the line,
    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 Midpoint of two 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, el, 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     el = 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         el.addChild(a);
    656         delete a._is_new;
    657     } else {
    658         a.addChild(el);
    659     }
    660     if (Type.exists(b._is_new)) {
    661         el.addChild(b);
    662         delete b._is_new;
    663     } else {
    664         b.addChild(el);
    665     }
    666
    667     el.elType = "midpoint";
    668     el.setParents([a.id, b.id]);
    669
    670     el.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     el.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 = el.symbolic.x,
    712             t2 = el.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 el;
    722 };
    723
    724 /**
    725  * @class Given three point, a parallel point is the point such that the four points form a parallelogram.
    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, parallel to a given line.
    901  * <p>
    902  * If original line is given as a JSXGraph line object, the resulting parallel line will be defined by the given point and an
    903  * infinitely far away point (an ideal point). That means, the line can not be shortened to a segment.
    904  * <p>
    905  * If the original line is given as two points, the resulting parallel line can be shortened to a a segment.
    906  * @pseudo
    907  * @name Parallel
    908  * @augments Line
    909  * @constructor
    910  * @type JXG.Line
    911  * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown.
    912  * @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
    913  * constructed line contains p and has the same slope as the line through p1 and p2.
    914  * @example
    915  * // Create a parallel
    916  * var p1 = board.create('point', [0.0, 2.0]);
    917  * var p2 = board.create('point', [2.0, 1.0]);
    918  * var l1 = board.create('line', [p1, p2]);
    919  *
    920  * var p3 = board.create('point', [3.0, 3.0]);
    921  * var pl1 = board.create('parallel', [l1, p3]);
    922  * </pre><div class="jxgbox" id="JXG24e54f9e-5c4e-4afb-9228-0ef27a59d627" style="width: 400px; height: 400px;"></div>
    923  * <script type="text/javascript">
    924  *   var plex1_board = JXG.JSXGraph.initBoard('JXG24e54f9e-5c4e-4afb-9228-0ef27a59d627', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false});
    925  *   var plex1_p1 = plex1_board.create('point', [0.0, 2.0]);
    926  *   var plex1_p2 = plex1_board.create('point', [2.0, 1.0]);
    927  *   var plex1_l1 = plex1_board.create('line', [plex1_p1, plex1_p2]);
    928  *   var plex1_p3 = plex1_board.create('point', [3.0, 3.0]);
    929  *   var plex1_pl1 = plex1_board.create('parallel', [plex1_l1, plex1_p3]);
    930  * </script><pre>
    931  * @example
    932  * var p1, p2, p3, l1, pl1;
    933  *
    934  * p1 = board.create('point', [0.0, 2.0]);
    935  * p2 = board.create('point', [2.0, 1.0]);
    936  * l1 = board.create('line', [p1, p2]);
    937  *
    938  * p3 = board.create('point', [1.0, 3.0]);
    939  * pl1 = board.create('parallel', [p1, p2, p3], {straightFirst: false, straightLast: false});
    940  *
    941  * </pre><div id="JXGd643305d-20c3-4a88-91f9-8d0c4448594f" class="jxgbox" style="width: 300px; height: 300px;"></div>
    942  * <script type="text/javascript">
    943  *     (function() {
    944  *         var board = JXG.JSXGraph.initBoard('JXGd643305d-20c3-4a88-91f9-8d0c4448594f',
    945  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
    946  *     var p1, p2, p3, l1, pl1;
    947  *
    948  *     p1 = board.create('point', [0.0, 2.0]);
    949  *     p2 = board.create('point', [2.0, 1.0]);
    950  *     l1 = board.create('line', [p1, p2]);
    951  *
    952  *     p3 = board.create('point', [1.0, 3.0]);
    953  *     pl1 = board.create('parallel', [p1, p2, p3], {straightFirst: false, straightLast: false});
    954  *
    955  *     })();
    956  *
    957  * </script><pre>
    958  *
    959  */
    960 JXG.createParallel = function (board, parents, attributes) {
    961     var p,
    962         pp,
    963         pl,
    964         li,
    965         i,
    966         attr,
    967         ty = 1;
    968
    969     for (i = 0; i < parents.length; ++i) {
    970         parents[i] = board.select(parents[i]);
    971     }
    972     p = null;
    973     if (parents.length === 3) {
    974         // Line / segment through point parents[2] which is parallel to line through parents[0] and parents[1]
    975         parents = Type.providePoints(board, parents, attributes, "point");
    976         p = parents[2];
    977         ty = 0;
    978     } else if (Type.isPointType(board, parents[0])) {
    979         // Parallel to line parents[1] through point parents[0]
    980         p = Type.providePoints(board, [parents[0]], attributes, "point")[0];
    981         /** @ignore */
    982         li = function () {
    983             return parents[1].stdform;
    984         };
    985     } else if (Type.isPointType(board, parents[1])) {
    986         // Parallel to line parents[0] through point parents[1]
    987         p = Type.providePoints(board, [parents[1]], attributes, "point")[0];
    988         /** @ignore */
    989         li = function () {
    990             return parents[0].stdform;
    991         };
    992     }
    993
    994     if (!Type.exists(attributes.layer)) {
    995         attributes.layer = board.options.layer.line;
    996     }
    997
    998     attr = Type.copyAttributes(attributes, board.options, "parallel", "point");
    999     if (ty === 1) {
    1000         // Line is given by line element. The parallel line is
    1001         // constructed as line through an ideal point.
    1002         pp = board.create(
    1003             "point",
    1004             [
    1005                 function () {
    1006                     return Mat.crossProduct([1, 0, 0], li());
    1007                 }
    1008             ],
    1009             attr
    1010         );
    1011     } else {
    1012         // Line is given by two points. The parallel line is
    1013         // constructed as line through two finite point.
    1014         pp = board.create("parallelpoint", parents, attr);
    1015     }
    1016     pp.isDraggable = true;
    1017
    1018     attr = Type.copyAttributes(attributes, board.options, "parallel");
    1019     // line creator also calls addChild
    1020     pl = board.create("line", [p, pp], attr);
    1021
    1022     pl.elType = "parallel";
    1023     pl.subs = {
    1024         point: pp
    1025     };
    1026
    1027     pl.inherits.push(pp);
    1028     pl.setParents([parents[0].id, parents[1].id]);
    1029     if (parents.length === 3) {
    1030         pl.addParents(parents[2].id);
    1031     }
    1032
    1033     // p.addChild(pl);
    1034
    1035     /**
    1036      * Helper point used to create the parallel line. This point lies on the line at infinity, hence it's not visible,
    1037      * not even with visible set to <tt>true</tt>. Creating another line through this point would make that other line
    1038      * parallel to the create parallel.
    1039      * @memberOf Parallel.prototype
    1040      * @name point
    1041      * @type JXG.Point
    1042      */
    1043     pl.point = pp;
    1044
    1045     return pl;
    1046 };
    1047
    1048 /**
    1049  * @class A segment with an arrow head attached thath is parallel to a given segment.
    1050  * The segment is given by its defining two points, the arrow starts at a given point.
    1051  * <p>
    1052  * @pseudo
    1053  * @constructor
    1054  * @name Arrowparallel
    1055  * @type Parallel
    1056  * @augments Parallel
    1057  * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown.
    1058  * @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.
    1059  * @example
    1060  * // Create a parallel
    1061  * var p1 = board.create('point', [0.0, 2.0]);
    1062  * var p2 = board.create('point', [2.0, 1.0]);
    1063  * var l1 = board.create('segment', [p1, p2]);
    1064  *
    1065  * var p3 = board.create('point', [3.0, 3.0]);
    1066  * var pl1 = board.create('arrowparallel', [p1, p2, p3]);
    1067  * </pre><div class="jxgbox" id="JXGeeacdf99-036f-4e83-aeb6-f7388423e369" style="width: 400px; height: 400px;"></div>
    1068  * <script type="text/javascript">
    1069  * (function () {
    1070  *   var plex1_board = JXG.JSXGraph.initBoard('JXGeeacdf99-036f-4e83-aeb6-f7388423e369', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false});
    1071  *   var plex1_p1 = plex1_board.create('point', [0.0, 2.0]);
    1072  *   var plex1_p2 = plex1_board.create('point', [2.0, 1.0]);
    1073  *   var plex1_l1 = plex1_board.create('segment', [plex1_p1, plex1_p2]);
    1074  *   var plex1_p3 = plex1_board.create('point', [3.0, 3.0]);
    1075  *   var plex1_pl1 = plex1_board.create('arrowparallel', [plex1_p1, plex1_p2, plex1_p3]);
    1076  * })();
    1077  * </script><pre>
    1078  */
    1079 JXG.createArrowParallel = function (board, parents, attributes) {
    1080     var p, attr;
    1081
    1082     /* parallel arrow point polynomials are done in createParallelPoint */
    1083     try {
    1084         attr = Type.copyAttributes(attributes, board.options, 'arrowparallel');
    1085
    1086         if (attr.lastArrow === false) {
    1087             // An arrow has to have an arrow head.
    1088             attr.lastArrow = true;
    1089         }
    1090         p = JXG.createParallel(board, parents, attr).setAttribute({
    1091             straightFirst: false,
    1092             straightLast: false
    1093         });
    1094         p.type = Const.OBJECT_TYPE_VECTOR;
    1095         p.elType = "arrowparallel";
    1096
    1097         // parents are set in createParallel
    1098
    1099         return p;
    1100     } catch (e) {
    1101         throw new Error(
    1102             "JSXGraph: Can't create arrowparallel with parent types '" +
    1103                 typeof parents[0] +
    1104                 "' and '" +
    1105                 typeof parents[1] +
    1106                 "'." +
    1107                 "\nPossible parent types: [line,point], [point,point,point]"
    1108         );
    1109     }
    1110 };
    1111
    1112 /**
    1113  * @class A bisector is a line which divides an angle into two equal angles. It is given by three points A, B, and
    1114  * C and divides the angle ABC into two equal sized parts.
    1115  * @pseudo
    1116  * @constructor
    1117  * @name Bisector
    1118  * @type JXG.Line
    1119  * @augments JXG.Line
    1120  * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown.
    1121  * @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
    1122  * be divided into two equal angles.
    1123  * @example
    1124  * var p1 = board.create('point', [6.0, 4.0]);
    1125  * var p2 = board.create('point', [3.0, 2.0]);
    1126  * var p3 = board.create('point', [1.0, 7.0]);
    1127  *
    1128  * var bi1 = board.create('bisector', [p1, p2, p3]);
    1129  * </pre><div class="jxgbox" id="JXG0d58cea8-b06a-407c-b27c-0908f508f5a4" style="width: 400px; height: 400px;"></div>
    1130  * <script type="text/javascript">
    1131  * (function () {
    1132  *   var board = JXG.JSXGraph.initBoard('JXG0d58cea8-b06a-407c-b27c-0908f508f5a4', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false});
    1133  *   var p1 = board.create('point', [6.0, 4.0]);
    1134  *   var p2 = board.create('point', [3.0, 2.0]);
    1135  *   var p3 = board.create('point', [1.0, 7.0]);
    1136  *   var bi1 = board.create('bisector', [p1, p2, p3]);
    1137  * })();
    1138  * </script><pre>
    1139  */
    1140 JXG.createBisector = function (board, parents, attributes) {
    1141     var p, l, i, attr;
    1142
    1143     parents = Type.providePoints(board, parents, attributes, "point");
    1144     if (Type.isPoint(parents[0]) && Type.isPoint(parents[1]) && Type.isPoint(parents[2])) {
    1145         // hidden and fixed helper
    1146         attr = Type.copyAttributes(attributes, board.options, "bisector", "point");
    1147         attr.snapToGrid = false;
    1148
    1149         p = board.create(
    1150             "point",
    1151             [
    1152                 function () {
    1153                     return Geometry.angleBisector(parents[0], parents[1], parents[2], board);
    1154                 }
    1155             ],
    1156             attr
    1157         );
    1158         p.dump = false;
    1159
    1160         for (i = 0; i < 3; i++) {
    1161             // required for algorithm requiring dependencies between elements
    1162             if (Type.exists(parents[i]._is_new)) {
    1163                 p.addChild(parents[i]);
    1164                 delete parents[i]._is_new;
    1165             } else {
    1166                 parents[i].addChild(p);
    1167             }
    1168         }
    1169
    1170         if (!Type.exists(attributes.layer)) {
    1171             attributes.layer = board.options.layer.line;
    1172         }
    1173
    1174         attr = Type.copyAttributes(attributes, board.options, "bisector");
    1175         l = JXG.createLine(board, [parents[1], p], attr);
    1176
    1177         /**
    1178          * Helper point
    1179          * @memberOf Bisector.prototype
    1180          * @type Point
    1181          * @name point
    1182          */
    1183         l.point = p;
    1184
    1185         l.elType = "bisector";
    1186         l.setParents(parents);
    1187         l.subs = {
    1188             point: p
    1189         };
    1190         l.inherits.push(p);
    1191
    1192         return l;
    1193     }
    1194
    1195     throw new Error(
    1196         "JSXGraph: Can't create angle bisector with parent types '" +
    1197             typeof parents[0] +
    1198             "' and '" +
    1199             typeof parents[1] +
    1200             "'." +
    1201             "\nPossible parent types: [point,point,point]"
    1202     );
    1203 };
    1204
    1205 /**
    1206  * @class Bisector lines are similar to {@link Bisector} but take two lines as parent elements. The resulting element is
    1207  * a composition of two lines.
    1208  * @pseudo
    1209  * @constructor
    1210  * @name Bisectorlines
    1211  * @type JXG.Composition
    1212  * @augments JXG.Composition
    1213  * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown.
    1214  * @param {JXG.Line_JXG.Line} l1,l2 The four angles described by the lines <tt>l1</tt> and <tt>l2</tt> will each
    1215  * be divided into two equal angles.
    1216  * @example
    1217  * var p1 = board.create('point', [6.0, 4.0]);
    1218  * var p2 = board.create('point', [3.0, 2.0]);
    1219  * var p3 = board.create('point', [1.0, 7.0]);
    1220  * var p4 = board.create('point', [3.0, 0.0]);
    1221  * var l1 = board.create('line', [p1, p2]);
    1222  * var l2 = board.create('line', [p3, p4]);
    1223  *
    1224  * var bi1 = board.create('bisectorlines', [l1, l2]);
    1225  * </pre><div class="jxgbox" id="JXG3121ff67-44f0-4dda-bb10-9cda0b80bf18" style="width: 400px; height: 400px;"></div>
    1226  * <script type="text/javascript">
    1227  * (function () {
    1228  *   var board = JXG.JSXGraph.initBoard('JXG3121ff67-44f0-4dda-bb10-9cda0b80bf18', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false});
    1229  *   var p1 = board.create('point', [6.0, 4.0]);
    1230  *   var p2 = board.create('point', [3.0, 2.0]);
    1231  *   var p3 = board.create('point', [1.0, 7.0]);
    1232  *   var p4 = board.create('point', [3.0, 0.0]);
    1233  *   var l1 = board.create('line', [p1, p2]);
    1234  *   var l2 = board.create('line', [p3, p4]);
    1235  *   var bi1 = board.create('bisectorlines', [l1, l2]);
    1236  * })();
    1237  * </script><pre>
    1238  */
    1239 JXG.createAngularBisectorsOfTwoLines = function (board, parents, attributes) {
    1240     // The angular bisectors of two line [c1,a1,b1] and [c2,a2,b2] are determined by the equation:
    1241     // (a1*x+b1*y+c1*z)/sqrt(a1^2+b1^2) = +/- (a2*x+b2*y+c2*z)/sqrt(a2^2+b2^2)
    1242
    1243     var g1,
    1244         g2,
    1245         attr,
    1246         ret,
    1247         l1 = board.select(parents[0]),
    1248         l2 = board.select(parents[1]);
    1249
    1250     if (
    1251         l1.elementClass !== Const.OBJECT_CLASS_LINE ||
    1252         l2.elementClass !== Const.OBJECT_CLASS_LINE
    1253     ) {
    1254         throw new Error(
    1255             "JSXGraph: Can't create angle bisectors of two lines with parent types '" +
    1256                 typeof parents[0] +
    1257                 "' and '" +
    1258                 typeof parents[1] +
    1259                 "'." +
    1260                 "\nPossible parent types: [line,line]"
    1261         );
    1262     }
    1263
    1264     if (!Type.exists(attributes.layer)) {
    1265         attributes.layer = board.options.layer.line;
    1266     }
    1267
    1268     attr = Type.copyAttributes(attributes, board.options, "bisectorlines", "line1");
    1269     g1 = board.create(
    1270         "line",
    1271         [
    1272             function () {
    1273                 var d1 = Mat.hypot(l1.stdform[1], l1.stdform[2]),
    1274                     d2 = Mat.hypot(l2.stdform[1], l2.stdform[2]);
    1275
    1276                 return l1.stdform[0] / d1 - l2.stdform[0] / d2;
    1277             },
    1278             function () {
    1279                 var d1 = Mat.hypot(l1.stdform[1], l1.stdform[2]),
    1280                     d2 = Mat.hypot(l2.stdform[1], l2.stdform[2]);
    1281
    1282                 return l1.stdform[1] / d1 - l2.stdform[1] / d2;
    1283             },
    1284             function () {
    1285                 var d1 = Mat.hypot(l1.stdform[1], l1.stdform[2]),
    1286                     d2 = Mat.hypot(l2.stdform[1], l2.stdform[2]);
    1287
    1288                 return l1.stdform[2] / d1 - l2.stdform[2] / d2;
    1289             }
    1290         ],
    1291         attr
    1292     );
    1293
    1294     if (!Type.exists(attributes.layer)) {
    1295         attributes.layer = board.options.layer.line;
    1296     }
    1297     attr = Type.copyAttributes(attributes, board.options, "bisectorlines", "line2");
    1298     g2 = board.create(
    1299         "line",
    1300         [
    1301             function () {
    1302                 var d1 = Mat.hypot(l1.stdform[1], l1.stdform[2]),
    1303                     d2 = Mat.hypot(l2.stdform[1], l2.stdform[2]);
    1304
    1305                 return l1.stdform[0] / d1 + l2.stdform[0] / d2;
    1306             },
    1307             function () {
    1308                 var d1 = Mat.hypot(l1.stdform[1], l1.stdform[2]),
    1309                     d2 = Mat.hypot(l2.stdform[1], l2.stdform[2]);
    1310
    1311                 return l1.stdform[1] / d1 + l2.stdform[1] / d2;
    1312             },
    1313             function () {
    1314                 var d1 = Mat.hypot(l1.stdform[1], l1.stdform[2]),
    1315                     d2 = Mat.hypot(l2.stdform[1], l2.stdform[2]);
    1316
    1317                 return l1.stdform[2] / d1 + l2.stdform[2] / d2;
    1318             }
    1319         ],
    1320         attr
    1321     );
    1322
    1323     // documentation
    1324     /**
    1325      * First line.
    1326      * @memberOf Bisectorlines.prototype
    1327      * @name line1
    1328      * @type Line
    1329      */
    1330
    1331     /**
    1332      * Second line.
    1333      * @memberOf Bisectorlines.prototype
    1334      * @name line2
    1335      * @type Line
    1336      */
    1337
    1338     ret = new Composition({ line1: g1, line2: g2 });
    1339
    1340     g1.dump = false;
    1341     g2.dump = false;
    1342
    1343     ret.elType = "bisectorlines";
    1344     ret.setParents([l1.id, l2.id]);
    1345     ret.subs = {
    1346         line1: g1,
    1347         line2: g2
    1348     };
    1349     // ret.inherits.push(g1, g2);
    1350
    1351     return ret;
    1352 };
    1353
    1354 // /**
    1355 //  * @class An m-sector is a line which divides an angle into two angles. It is given by three points A, B, and
    1356 //  * C and a real number m, and divides an angle into two angles, an angle with amplitude m and an angle with
    1357 //  * amplitude (1-m)
    1358 //  * @pseudo
    1359 //  * @constructor
    1360 //  * @name Msector
    1361 //  * @type JXG.Line
    1362 //  * @augments JXG.Line
    1363 //  * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown.
    1364 //  * @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
    1365 //  * be divided into two angles according to the value of <tt>m</tt>.
    1366 //  * @example
    1367 //  * var p1 = board.create('point', [6.0, 4.0]);
    1368 //  * var p2 = board.create('point', [3.0, 2.0]);
    1369 //  * var p3 = board.create('point', [1.0, 7.0]);
    1370 //  *
    1371 //  * var bi1 = board.create('msector', [p1, p2, p3], 1/5);
    1372 //  * </pre><div id="JXG0d58cea8-b06a-407c-b27c-0908f508f5a4" style="width: 400px; height: 400px;"></div>
    1373 //  * <script type="text/javascript">
    1374 //  * (function () {
    1375 //  *   var board = JXG.JSXGraph.initBoard('JXG0d58cea8-b06a-407c-b27c-0908f508f5a4', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false});
    1376 //  *   var p1 = board.create('point', [6.0, 4.0]);
    1377 //  *   var p2 = board.create('point', [3.0, 2.0]);
    1378 //  *   var p3 = board.create('point', [1.0, 7.0]);
    1379 //  *   var bi1 = board.create('msector', [p1, p2, p3], 1/5);
    1380 //  * })();
    1381 //  * </script><pre>
    1382 //  */
    1383 // JXG.createMsector = function (board, parents, attributes) {
    1384 //     var p, l, i, attr;
    1385
    1386 //     if (parents[0].elementClass === Const.OBJECT_CLASS_POINT &&
    1387 //             parents[1].elementClass === Const.OBJECT_CLASS_POINT &&
    1388 //             parents[2].elementClass === Const.OBJECT_CLASS_POINT) {
    1389 //         // hidden and fixed helper
    1390 //         attr = Type.copyAttributes(attributes, board.options, 'msector', 'point');
    1391 //         p = board.create('point', [
    1392 //             function () {
    1393 //                 return Geometry.angleMsector(parents[0], parents[1], parents[2], parents[3], board);
    1394 //             }
    1395 //         ], attr);
    1396 //         p.dump = false;
    1397
    1398 //         for (i = 0; i < 3; i++) {
    1399 //             // required for algorithm requiring dependencies between elements
    1400 //             parents[i].addChild(p);
    1401 //         }
    1402
    1403 //         if (!Type.exists(attributes.layer)) {
    1404 //             attributes.layer = board.options.layer.line;
    1405 //         }
    1406
    1407 //         attr = Type.copyAttributes(attributes, board.options, 'msector');
    1408 //         l = JXG.createLine(board, [parents[1], p], attr);
    1409
    1410 //         /**
    1411 //          * Helper point
    1412 //          * @memberOf Msector.prototype
    1413 //          * @type Point
    1414 //          * @name point
    1415 //          */
    1416 //         l.point = p;
    1417
    1418 //         l.elType = 'msector';
    1419 //         l.parents = [parents[0].id, parents[1].id, parents[2].id];
    1420 //         l.subs = {
    1421 //             point: p
    1422 //         };
    1423 //         l.inherits.push(p);
    1424
    1425 //         return l;
    1426 //     }
    1427
    1428 //     throw new Error("JSXGraph: Can't create angle msector with parent types '" +
    1429 //         (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." +
    1430 //         "\nPossible parent types: [point,point,point,Number]");
    1431 // };
    1432
    1433 /**
    1434  * @class Constructs the center of a {@link Circumcircle} without creating the circle.
    1435  * Like the circumcircle the circumcenter is constructed by providing three points.
    1436  * @pseudo
    1437  * @description A circumcenter is given by three points which are all lying on the circle with the
    1438  * constructed circumcenter as the midpoint.
    1439  * @constructor
    1440  * @name Circumcenter
    1441  * @type JXG.Point
    1442  * @augments JXG.Point
    1443  * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown.
    1444  * @param {JXG.Point_JXG.Point_JXG.Point} p1,p2,p3 The constructed point is the midpoint of the circle determined
    1445  * by p1, p2, and p3.
    1446  * @example
    1447  * var p1 = board.create('point', [0.0, 2.0]);
    1448  * var p2 = board.create('point', [2.0, 1.0]);
    1449  * var p3 = board.create('point', [3.0, 3.0]);
    1450  *
    1451  * var cc1 = board.create('circumcenter', [p1, p2, p3]);
    1452  * </pre><div class="jxgbox" id="JXGe8a40f95-bf30-4eb4-88a8-f4d5495261fd" style="width: 400px; height: 400px;"></div>
    1453  * <script type="text/javascript">
    1454  *   var ccmex1_board = JXG.JSXGraph.initBoard('JXGe8a40f95-bf30-4eb4-88a8-f4d5495261fd', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false});
    1455  *   var ccmex1_p1 = ccmex1_board.create('point', [0.0, 2.0]);
    1456  *   var ccmex1_p2 = ccmex1_board.create('point', [6.0, 1.0]);
    1457  *   var ccmex1_p3 = ccmex1_board.create('point', [3.0, 7.0]);
    1458  *   var ccmex1_cc1 = ccmex1_board.create('circumcenter', [ccmex1_p1, ccmex1_p2, ccmex1_p3]);
    1459  * </script><pre>
    1460  */
    1461 JXG.createCircumcenter = function (board, parents, attributes) {
    1462     var p, i, a, b, c;
    1463
    1464     parents = Type.providePoints(board, parents, attributes, "point");
    1465     if (Type.isPoint(parents[0]) && Type.isPoint(parents[1]) && Type.isPoint(parents[2])) {
    1466         a = parents[0];
    1467         b = parents[1];
    1468         c = parents[2];
    1469
    1470         p = JXG.createPoint(
    1471             board,
    1472             [
    1473                 function () {
    1474                     return Geometry.circumcenter(a, b, c, board);
    1475                 }
    1476             ],
    1477             attributes
    1478         );
    1479
    1480         for (i = 0; i < 3; i++) {
    1481             if (Type.exists(parents[i]._is_new)) {
    1482                 p.addChild(parents[i]);
    1483                 delete parents[i]._is_new;
    1484             } else {
    1485                 parents[i].addChild(p);
    1486             }
    1487         }
    1488
    1489         p.elType = "circumcenter";
    1490         p.setParents(parents);
    1491
    1492         p.generatePolynomial = function () {
    1493             /*
    1494              *  CircumcircleMidpoint takes three points A, B and C  and creates point M, which is the circumcenter of A, B, and C.
    1495              *
    1496              *
    1497              * So we have two conditions:
    1498              *
    1499              *   (a)   CT  ==  AT           (distance condition I)
    1500              *   (b)   BT  ==  AT           (distance condition II)
    1501              *
    1502              */
    1503             var a1 = a.symbolic.x,
    1504                 a2 = a.symbolic.y,
    1505                 b1 = b.symbolic.x,
    1506                 b2 = b.symbolic.y,
    1507                 c1 = c.symbolic.x,
    1508                 c2 = c.symbolic.y,
    1509                 t1 = p.symbolic.x,
    1510                 t2 = p.symbolic.y,
    1511                 poly1 = ["((", t1, ")-(", a1, "))^2+((", t2, ")-(", a2, "))^2-((", t1, ")-(", b1, "))^2-((", t2, ")-(", b2, "))^2"].join(""),
    1512                 poly2 = ["((", t1, ")-(", a1, "))^2+((", t2, ")-(", a2, "))^2-((", t1, ")-(", c1, "))^2-((", t2, ")-(", c2, "))^2"].join("");
    1513
    1514             return [poly1, poly2];
    1515         };
    1516
    1517         return p;
    1518     }
    1519
    1520     throw new Error(
    1521         "JSXGraph: Can't create circumcircle midpoint with parent types '" +
    1522             typeof parents[0] +
    1523             "', '" +
    1524             typeof parents[1] +
    1525             "' and '" +
    1526             typeof parents[2] +
    1527             "'." +
    1528             "\nPossible parent types: [point,point,point]"
    1529     );
    1530 };
    1531
    1532 /**
    1533  * @class The center of the incircle of the triangle described by the three given points (without
    1534  * constructing the circle).
    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 the unique circle through three points.
    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 The circle which touches the three sides of a triangle 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  Reflect a point, line, circle, curve, polygon across a given line.
    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(r_c.evalVisProp('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(r.evalVisProp('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 Reflect a point, line, circle, curve, polygon across a given point.
    2048  * @pseudo
    2049  * @description A mirror element is determined by the reflection of a
    2050  * given point, line, circle, curve, polygon across another given point.
    2051  * In contrast to generic transformations, mirror elements of circles are again circles.
    2052  * @constructor
    2053  * @name MirrorElement
    2054  * @type JXG.GeometryElement
    2055  * @augments JXG.GeometryElement
    2056  * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown.
    2057  * @param {JXG.Point|JXG.Line|JXG.Curve|JXG.Ppolygon_JXG.Point} p1,p2 The constructed element is the mirror image of p2 across p1.
    2058  * @example
    2059  *         // point of reflection
    2060  *         var mirr = board.create('point', [-1,-1], {color: '#aaaaaa'});
    2061  *
    2062  *         var p1 = board.create('point', [-3,-1], {name: "A"});
    2063  *         var q1 = board.create('mirrorelement', [p1, mirr], {name: "A'"});
    2064  *
    2065  *         var l1 = board.create('line', [1, -5, 1]);
    2066  *         var l2 = board.create('mirrorelement', [l1, mirr]);
    2067  *
    2068  *         var cu1 = board.create('curve', [[-3, -3, -2.5, -3, -3, -2.5], [-3, -2, -2, -2, -2.5, -2.5]], {strokeWidth:3});
    2069  *         var cu2 = board.create('mirrorelement', [cu1, mirr], {strokeColor: 'red', strokeWidth:3});
    2070  *
    2071  *         var pol1 = board.create('polygon', [[-6,-2], [-4,-4], [-5,-0.5]]);
    2072  *         var pol2 = board.create('mirrorelement', [pol1, mirr]);
    2073  *
    2074  *         var c1 = board.create('circle', [[-6,-6], [-6, -5]]);
    2075  *         var c2 = board.create('mirrorelement', [c1, mirr]);
    2076  *
    2077  *         var a1 = board.create('arc', [[1, 1], [0, 1], [1, 0]], {strokeColor: 'red'});
    2078  *         var a2 = board.create('mirrorelement', [a1, mirr], {strokeColor: 'red'});
    2079  *
    2080  *         var s1 = board.create('sector', [[-3.5,-3], [-3.5, -2], [-3.5,-4]], {
    2081  *                           anglePoint: {visible:true}, center: {visible: true}, radiusPoint: {visible: true},
    2082  *                           fillColor: 'yellow', strokeColor: 'black'});
    2083  *         var s2 = board.create('mirrorelement', [s1, mirr], {fillColor: 'yellow', strokeColor: 'black', fillOpacity: 0.5});
    2084  *
    2085  *         var an1 = board.create('angle', [[-4,3.9], [-3, 4], [-3, 3]]);
    2086  *         var an2 = board.create('mirrorelement', [an1, mirr]);
    2087  *
    2088  *
    2089  * </pre><div id="JXG026c779c-d8d9-11e7-93b3-901b0e1b8723" class="jxgbox" style="width: 300px; height: 300px;"></div>
    2090  * <script type="text/javascript">
    2091  *     (function() {
    2092  *         var board = JXG.JSXGraph.initBoard('JXG026c779c-d8d9-11e7-93b3-901b0e1b8723',
    2093  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
    2094  *             // point of reflection
    2095  *             var mirr = board.create('point', [-1,-1], {color: '#aaaaaa'});
    2096  *
    2097  *             var p1 = board.create('point', [-3,-1], {name: "A"});
    2098  *             var q1 = board.create('mirrorelement', [p1, mirr], {name: "A'"});
    2099  *
    2100  *             var l1 = board.create('line', [1,-5, 1]);
    2101  *             var l2 = board.create('mirrorelement', [l1, mirr]);
    2102  *
    2103  *             var cu1 = board.create('curve', [[-3, -3, -2.5, -3, -3, -2.5], [-3, -2, -2, -2, -2.5, -2.5]], {strokeWidth:3});
    2104  *             var cu2 = board.create('mirrorelement', [cu1, mirr], {strokeColor: 'red', strokeWidth:3});
    2105  *
    2106  *             var pol1 = board.create('polygon', [[-6,-2], [-4,-4], [-5,-0.5]]);
    2107  *             var pol2 = board.create('mirrorelement', [pol1, mirr]);
    2108  *
    2109  *             var c1 = board.create('circle', [[-6,-6], [-6, -5]]);
    2110  *             var c2 = board.create('mirrorelement', [c1, mirr]);
    2111  *
    2112  *         var a1 = board.create('arc', [[1, 1], [0, 1], [1, 0]], {strokeColor: 'red'});
    2113  *         var a2 = board.create('mirrorelement', [a1, mirr], {strokeColor: 'red'});
    2114  *
    2115  *         var s1 = board.create('sector', [[-3.5,-3], [-3.5, -2], [-3.5,-4]], {
    2116  *                           anglePoint: {visible:true}, center: {visible: true}, radiusPoint: {visible: true},
    2117  *                           fillColor: 'yellow', strokeColor: 'black'});
    2118  *         var s2 = board.create('mirrorelement', [s1, mirr], {fillColor: 'yellow', strokeColor: 'black', fillOpacity: 0.5});
    2119  *
    2120  *         var an1 = board.create('angle', [[-4,3.9], [-3, 4], [-3, 3]]);
    2121  *         var an2 = board.create('mirrorelement', [an1, mirr]);
    2122  *
    2123  *     })();
    2124  *
    2125  * </script><pre>
    2126  */
    2127 JXG.createMirrorElement = function (board, parents, attributes) {
    2128     var org, i, m, r, r_c, t,
    2129         attr, attr2,
    2130         errStr = "\nPossible parent types: [point|line|curve|polygon|circle|arc|sector, point]";
    2131
    2132     for (i = 0; i < parents.length; ++i) {
    2133         parents[i] = board.select(parents[i]);
    2134     }
    2135
    2136     attr = Type.copyAttributes(attributes, board.options, "mirrorelement");
    2137     if (Type.isPoint(parents[0])) {
    2138         // Create point to be mirrored if supplied by coords array.
    2139         org = Type.providePoints(board, [parents[0]], attr)[0];
    2140     } else if (
    2141         parents[0].elementClass === Const.OBJECT_CLASS_CURVE ||
    2142         parents[0].elementClass === Const.OBJECT_CLASS_LINE ||
    2143         parents[0].type === Const.OBJECT_TYPE_POLYGON ||
    2144         parents[0].elementClass === Const.OBJECT_CLASS_CIRCLE
    2145     ) {
    2146         org = parents[0];
    2147     } else {
    2148         throw new Error(
    2149             "JSXGraph: Can't create mirror element with parent types '" +
    2150                 typeof parents[0] +
    2151                 "' and '" +
    2152                 typeof parents[1] +
    2153                 "'." +
    2154                 errStr
    2155         );
    2156     }
    2157
    2158     if (Type.isPoint(parents[1])) {
    2159         attr2 = Type.copyAttributes(attributes, board.options, "mirrorelement", "point");
    2160         // Create mirror point if supplied by coords array.
    2161         m = Type.providePoints(board, [parents[1]], attr2)[0];
    2162     } else {
    2163         throw new Error(
    2164             "JSXGraph: Can't create mirror element with parent types '" +
    2165                 typeof parents[0] +
    2166                 "' and '" +
    2167                 typeof parents[1] +
    2168                 "'." +
    2169                 errStr
    2170         );
    2171     }
    2172
    2173     t = JXG.createTransform(board, [Math.PI, m], { type: "rotate" });
    2174     if (Type.isPoint(org)) {
    2175         r = JXG.createPoint(board, [org, t], attr);
    2176
    2177         // Arcs and sectors are treated as curves
    2178     } else if (org.elementClass === Const.OBJECT_CLASS_CURVE) {
    2179         r = JXG.createCurve(board, [org, t], attr);
    2180     } else if (org.elementClass === Const.OBJECT_CLASS_LINE) {
    2181         r = JXG.createLine(board, [org, t], attr);
    2182     } else if (org.type === Const.OBJECT_TYPE_POLYGON) {
    2183         r = JXG.createPolygon(board, [org, t], attr);
    2184     } else if (org.elementClass === Const.OBJECT_CLASS_CIRCLE) {
    2185         if (attr.type.toLowerCase() === "euclidean") {
    2186             // Create a circle element from a circle and a Euclidean transformation
    2187             attr2 = Type.copyAttributes(attributes, board.options, "mirrorelement", "center");
    2188             r_c = JXG.createPoint(board, [org.center, t], attr2);
    2189             r_c.prepareUpdate()
    2190                 .update()
    2191                 .updateVisibility(r_c.evalVisProp('visible'))
    2192                 .updateRenderer();
    2193             r = JXG.createCircle(
    2194                 board,
    2195                 [
    2196                     r_c,
    2197                     function () {
    2198                         return org.Radius();
    2199                     }
    2200                 ],
    2201                 attr
    2202             );
    2203         } else {
    2204             // Create a conic element from a circle and a projective transformation
    2205             r = JXG.createCircle(board, [org, t], attr);
    2206         }
    2207     } else {
    2208         throw new Error(
    2209             "JSXGraph: Can't create mirror element with parent types '" +
    2210                 typeof parents[0] +
    2211                 "' and '" +
    2212                 typeof parents[1] +
    2213                 "'." +
    2214                 errStr
    2215         );
    2216     }
    2217
    2218     if (Type.exists(org._is_new)) {
    2219         r.addChild(org);
    2220         delete org._is_new;
    2221     } else {
    2222         // org.addChild(r);
    2223     }
    2224     m.addChild(r);
    2225
    2226     r.elType = "mirrorelement";
    2227     r.addParents(m);
    2228     r.prepareUpdate().update();
    2229
    2230     return r;
    2231 };
    2232
    2233 /**
    2234  * @class A MirrorPoint is a special case of a {@link MirrorElement}.
    2235  * @pseudo
    2236  * @description A mirror point is determined by the reflection of a given point against another given point.
    2237  * @constructor
    2238  * @name MirrorPoint
    2239  * @type JXG.Point
    2240  * @augments JXG.Point
    2241  * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown.
    2242  * @param {JXG.Point_JXG.Point} p1,p2 The constructed point is the reflection of p2 against p1.
    2243  *
    2244  * This method is superseeded by the more general {@link JXG.createMirrorElement}.
    2245  * @example
    2246  * var p1 = board.create('point', [3.0, 3.0]);
    2247  * var p2 = board.create('point', [6.0, 1.0]);
    2248  *
    2249  * var mp1 = board.create('mirrorpoint', [p1, p2]);
    2250  * </pre><div class="jxgbox" id="JXG7eb2a814-6c4b-4caa-8cfa-4183a948d25b" style="width: 400px; height: 400px;"></div>
    2251  * <script type="text/javascript">
    2252  *   var mpex1_board = JXG.JSXGraph.initBoard('JXG7eb2a814-6c4b-4caa-8cfa-4183a948d25b', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false});
    2253  *   var mpex1_p1 = mpex1_board.create('point', [3.0, 3.0]);
    2254  *   var mpex1_p2 = mpex1_board.create('point', [6.0, 1.0]);
    2255  *   var mpex1_mp1 = mpex1_board.create('mirrorpoint', [mpex1_p1, mpex1_p2]);
    2256  * </script><pre>
    2257  */
    2258 JXG.createMirrorPoint = function (board, parents, attributes) {
    2259     var el = JXG.createMirrorElement(board, parents, attributes);
    2260     el.elType = "mirrorpoint";
    2261     return el;
    2262 };
    2263
    2264 /**
    2265  * @class The graph of the integral function of a given function in a given interval.
    2266  * @pseudo
    2267  * @description The Integral element is used to visualize the area under a given curve over a given interval
    2268  * and to calculate the area's value. For that a polygon and gliders are used. The polygon displays the area,
    2269  * the gliders are used to change the interval dynamically.
    2270  * @constructor
    2271  * @name Integral
    2272  * @type JXG.Curve
    2273  * @augments JXG.Curve
    2274  * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown.
    2275  * @param {Array_JXG.Curve} i,c The constructed element covers the area between the curve <tt>c</tt> and the x-axis
    2276  * within the interval <tt>i</tt>.
    2277  * @example
    2278  * var c1 = board.create('functiongraph', [function (t) { return Math.cos(t)*t; }]);
    2279  * var i1 = board.create('integral', [[-2.0, 2.0], c1]);
    2280  * </pre><div class="jxgbox" id="JXGd45d7188-6624-4d6e-bebb-1efa2a305c8a" style="width: 400px; height: 400px;"></div>
    2281  * <script type="text/javascript">
    2282  *   var intex1_board = JXG.JSXGraph.initBoard('JXGd45d7188-6624-4d6e-bebb-1efa2a305c8a', {boundingbox: [-5, 5, 5, -5], axis: true, showcopyright: false, shownavigation: false});
    2283  *   var intex1_c1 = intex1_board.create('functiongraph', [function (t) { return Math.cos(t)*t; }]);
    2284  *   var intex1_i1 = intex1_board.create('integral', [[-2.0, 2.0], intex1_c1]);
    2285  * </script><pre>
    2286  */
    2287 JXG.createIntegral = function (board, parents, attributes) {
    2288     var interval, curve, attr, start, end,
    2289         startx, starty, endx, endy,
    2290         pa_on_curve, pa_on_axis, pb_on_curve, pb_on_axis,
    2291         txt_fun,
    2292         t = null, p;
    2293
    2294     if (Type.isArray(parents[0]) && parents[1].elementClass === Const.OBJECT_CLASS_CURVE) {
    2295         interval = parents[0];
    2296         curve = parents[1];
    2297     } else if (
    2298         Type.isArray(parents[1]) &&
    2299         parents[0].elementClass === Const.OBJECT_CLASS_CURVE
    2300     ) {
    2301         interval = parents[1];
    2302         curve = parents[0];
    2303     } else {
    2304         throw new Error(
    2305             "JSXGraph: Can't create integral with parent types '" +
    2306                 typeof parents[0] +
    2307                 "' and '" +
    2308                 typeof parents[1] +
    2309                 "'." +
    2310                 "\nPossible parent types: [[number|function,number|function],curve]"
    2311         );
    2312     }
    2313
    2314     attr = Type.copyAttributes(attributes, board.options, "integral");
    2315     attr.withlabel = false; // There is a custom 'label' below.
    2316     p = board.create("curve", [[0], [0]], attr);
    2317
    2318     // Dirty hack: the integral curve is removed from board.objectsList
    2319     // and inserted below again after the pa_/pb_on_axis elements.
    2320     // Otherwise, the filled area lags is updated before the
    2321     // update of the bounds.
    2322     board.objectsList.pop();
    2323
    2324     // Correct the interval if necessary - NOT ANYMORE, GGB's fault
    2325     start = interval[0];
    2326     end = interval[1];
    2327
    2328     if (Type.isFunction(start)) {
    2329         startx = start;
    2330         starty = function () {
    2331             return curve.Y(startx());
    2332         };
    2333         start = startx();
    2334     } else {
    2335         startx = start;
    2336         starty = curve.Y(start);
    2337     }
    2338
    2339     if (Type.isFunction(end)) {
    2340         endx = end;
    2341         endy = function () {
    2342             return curve.Y(endx());
    2343         };
    2344         end = endx();
    2345     } else {
    2346         endx = end;
    2347         endy = curve.Y(end);
    2348     }
    2349
    2350     attr = Type.copyAttributes(attributes, board.options, "integral", "curveleft");
    2351     pa_on_curve = board.create("glider", [startx, starty, curve], attr);
    2352     if (Type.isFunction(startx)) {
    2353         pa_on_curve.hideElement();
    2354     }
    2355
    2356     attr = Type.copyAttributes(attributes, board.options, 'integral', 'baseleft');
    2357     pa_on_axis = board.create('point', [
    2358             function () {
    2359                 if (p.evalVisProp('axis') === "y") {
    2360                     return 0;
    2361                 }
    2362                 return pa_on_curve.X();
    2363             },
    2364             function () {
    2365                 if (p.evalVisProp('axis') === "y") {
    2366                     return pa_on_curve.Y();
    2367                 }
    2368                 return 0;
    2369             }
    2370         ], attr);
    2371
    2372     attr = Type.copyAttributes(attributes, board.options, "integral", "curveright");
    2373     pb_on_curve = board.create("glider", [endx, endy, curve], attr);
    2374     if (Type.isFunction(endx)) {
    2375         pb_on_curve.hideElement();
    2376     }
    2377
    2378     attr = Type.copyAttributes(attributes, board.options, "integral", "baseright");
    2379     pb_on_axis = board.create('point', [
    2380             function () {
    2381                 if (p.evalVisProp('axis') === "y") {
    2382                     return 0;
    2383                 }
    2384                 return pb_on_curve.X();
    2385             },
    2386             function () {
    2387                 if (p.evalVisProp('axis') === "y") {
    2388                     return pb_on_curve.Y();
    2389                 }
    2390
    2391                 return 0;
    2392             }
    2393         ], attr);
    2394
    2395     // Re-insert the filled integral curve element
    2396     p._pos = board.objectsList.length;
    2397     board.objectsList.push(p);
    2398
    2399     attr = Type.copyAttributes(attributes, board.options, "integral");
    2400     if (attr.withlabel !== false && attr.axis !== "y") {
    2401         attr = Type.copyAttributes(attributes, board.options, "integral", "label");
    2402         attr = Type.copyAttributes(attr, board.options, "label");
    2403
    2404         t = board.create('text', [
    2405                 function () {
    2406                     var off = new Coords(
    2407                             Const.COORDS_BY_SCREEN,
    2408                             [
    2409                                 this.evalVisProp('offset.0') +
    2410                                     this.board.origin.scrCoords[1],
    2411                                 0
    2412                             ],
    2413                             this.board,
    2414                             false
    2415                         ),
    2416                         bb = this.board.getBoundingBox(),
    2417                         dx = (bb[2] - bb[0]) * 0.1,
    2418                         x = pb_on_curve.X();
    2419
    2420                     if (x < bb[0]) {
    2421                         x = bb[0] + dx;
    2422                     } else if (x > bb[2]) {
    2423                         x = bb[2] - dx;
    2424                     }
    2425
    2426                     return x + off.usrCoords[1];
    2427                 },
    2428                 function () {
    2429                     var off = new Coords(
    2430                             Const.COORDS_BY_SCREEN,
    2431                             [
    2432                                 0,
    2433                                 this.evalVisProp('offset.1') +
    2434                                     this.board.origin.scrCoords[2]
    2435                             ],
    2436                             this.board,
    2437                             false
    2438                         ),
    2439                         bb = this.board.getBoundingBox(),
    2440                         dy = (bb[1] - bb[3]) * 0.1,
    2441                         y = pb_on_curve.Y();
    2442
    2443                     if (y > bb[1]) {
    2444                         y = bb[1] - dy;
    2445                     } else if (y < bb[3]) {
    2446                         y = bb[3] + dy;
    2447                     }
    2448
    2449                     return y + off.usrCoords[2];
    2450                 },
    2451                 ''
    2452             ], attr);
    2453
    2454         txt_fun = function () {
    2455             var intSymbol = '∫',
    2456                 Int = Numerics.NewtonCotes([pa_on_axis.X(), pb_on_axis.X()], curve.Y),
    2457                 digits = t.evalVisProp('digits'),
    2458                 val;
    2459
    2460             if (t.useLocale()) {
    2461                 val = t.formatNumberLocale(Int, digits);
    2462             } else {
    2463                 val = Type.toFixed(Int, digits);
    2464             }
    2465             if (t.evalVisProp('usemathjax') || t.evalVisProp('usekatex')) {
    2466                 intSymbol = '\\int';
    2467             }
    2468             return intSymbol + ' = ' + val;
    2469         };
    2470         t.setText(txt_fun);
    2471         t.dump = false;
    2472
    2473         pa_on_curve.addChild(t);
    2474         pb_on_curve.addChild(t);
    2475     }
    2476
    2477     // dump stuff
    2478     pa_on_curve.dump = false;
    2479     pa_on_axis.dump = false;
    2480
    2481     pb_on_curve.dump = false;
    2482     pb_on_axis.dump = false;
    2483
    2484     p.elType = "integral";
    2485     p.setParents([curve.id, interval]);
    2486     p.subs = {
    2487         curveLeft: pa_on_curve,
    2488         baseLeft: pa_on_axis,
    2489         curveRight: pb_on_curve,
    2490         baseRight: pb_on_axis
    2491     };
    2492     p.inherits.push(pa_on_curve, pa_on_axis, pb_on_curve, pb_on_axis);
    2493
    2494     if (attr.withlabel) {
    2495         p.subs.label = t;
    2496         p.inherits.push(t);
    2497     }
    2498
    2499     /**
    2500      * Returns the current value of the integral.
    2501      * @memberOf Integral
    2502      * @name Value
    2503      * @function
    2504      * @returns {Number}
    2505      */
    2506     p.Value = function () {
    2507         return Numerics.I([pa_on_axis.X(), pb_on_axis.X()], curve.Y);
    2508     };
    2509
    2510     /**
    2511      * documented in JXG.Curve
    2512      * @class
    2513      * @ignore
    2514      */
    2515     p.updateDataArray = function () {
    2516         var x, y, i, left, right, lowx, upx, lowy, upy;
    2517
    2518         if (this.evalVisProp('axis') === "y") {
    2519             if (pa_on_curve.Y() < pb_on_curve.Y()) {
    2520                 lowx = pa_on_curve.X();
    2521                 lowy = pa_on_curve.Y();
    2522                 upx = pb_on_curve.X();
    2523                 upy = pb_on_curve.Y();
    2524             } else {
    2525                 lowx = pb_on_curve.X();
    2526                 lowy = pb_on_curve.Y();
    2527                 upx = pa_on_curve.X();
    2528                 upy = pa_on_curve.Y();
    2529             }
    2530             left = Math.min(lowx, upx);
    2531             right = Math.max(lowx, upx);
    2532
    2533             x = [0, lowx];
    2534             y = [lowy, lowy];
    2535
    2536             for (i = 0; i < curve.numberPoints; i++) {
    2537                 if (
    2538                     lowy <= curve.points[i].usrCoords[2] &&
    2539                     left <= curve.points[i].usrCoords[1] &&
    2540                     curve.points[i].usrCoords[2] <= upy &&
    2541                     curve.points[i].usrCoords[1] <= right
    2542                 ) {
    2543                     x.push(curve.points[i].usrCoords[1]);
    2544                     y.push(curve.points[i].usrCoords[2]);
    2545                 }
    2546             }
    2547             x.push(upx);
    2548             y.push(upy);
    2549             x.push(0);
    2550             y.push(upy);
    2551
    2552             // close the curve
    2553             x.push(0);
    2554             y.push(lowy);
    2555         } else {
    2556             if (pa_on_axis.X() < pb_on_axis.X()) {
    2557                 left = pa_on_axis.X();
    2558                 right = pb_on_axis.X();
    2559             } else {
    2560                 left = pb_on_axis.X();
    2561                 right = pa_on_axis.X();
    2562             }
    2563
    2564             x = [left, left];
    2565             y = [0, curve.Y(left)];
    2566
    2567             for (i = 0; i < curve.numberPoints; i++) {
    2568                 if (
    2569                     left <= curve.points[i].usrCoords[1] &&
    2570                     curve.points[i].usrCoords[1] <= right
    2571                 ) {
    2572                     x.push(curve.points[i].usrCoords[1]);
    2573                     y.push(curve.points[i].usrCoords[2]);
    2574                 }
    2575             }
    2576             x.push(right);
    2577             y.push(curve.Y(right));
    2578             x.push(right);
    2579             y.push(0);
    2580
    2581             // close the curve
    2582             x.push(left);
    2583             y.push(0);
    2584         }
    2585
    2586         this.dataX = x;
    2587         this.dataY = y;
    2588     };
    2589
    2590     pa_on_curve.addChild(p);
    2591     pb_on_curve.addChild(p);
    2592     pa_on_axis.addChild(p);
    2593     pb_on_axis.addChild(p);
    2594
    2595     /**
    2596      * The point on the axis initially corresponding to the lower value of the interval.
    2597      *
    2598      * @name baseLeft
    2599      * @memberOf Integral
    2600      * @type JXG.Point
    2601      */
    2602     p.baseLeft = pa_on_axis;
    2603
    2604     /**
    2605      * The point on the axis initially corresponding to the higher value of the interval.
    2606      *
    2607      * @name baseRight
    2608      * @memberOf Integral
    2609      * @type JXG.Point
    2610      */
    2611     p.baseRight = pb_on_axis;
    2612
    2613     /**
    2614      * The glider on the curve corresponding to the lower value of the interval.
    2615      *
    2616      * @name curveLeft
    2617      * @memberOf Integral
    2618      * @type Glider
    2619      */
    2620     p.curveLeft = pa_on_curve;
    2621
    2622     /**
    2623      * The glider on the axis corresponding to the higher value of the interval.
    2624      *
    2625      * @name curveRight
    2626      * @memberOf Integral
    2627      * @type Glider
    2628      */
    2629     p.curveRight = pb_on_curve;
    2630
    2631     p.methodMap = JXG.deepCopy(p.methodMap, {
    2632         curveLeft: "curveLeft",
    2633         baseLeft: "baseLeft",
    2634         curveRight: "curveRight",
    2635         baseRight: "baseRight",
    2636         Value: "Value"
    2637     });
    2638
    2639     /**
    2640      * documented in GeometryElement
    2641      * @ignore
    2642      */
    2643     p.label = t;
    2644
    2645     return p;
    2646 };
    2647
    2648 /**
    2649  * @class The area which is the set of solutions of a linear inequality or an inequality
    2650  * of a function graph.
    2651  * For example, an inequality of type y <= f(x).
    2652  * @pseudo
    2653  * @description Display the solution set of a linear inequality (less than or equal to).
    2654  * To be precise, the solution set of the inequality <i>y <= b/a * x + c/a</i> is shown.
    2655  * In case <i>a = 0</i>, that is if the equation of the line is <i>bx + c = 0</i>,
    2656  * the area of the inequality <i>bx + c <= 0</i> is shown.
    2657  * <p>
    2658  * For function graphs the area below the function graph is filled, i.e. the
    2659  * area of the inequality y <= f(x).
    2660  * With the attribute inverse:true the area of the inequality y >= f(x) is filled.
    2661  *
    2662  * @param {JXG.Line} l The area drawn will be the area below this line. With the attribute
    2663  * inverse:true, the inequality 'greater than or equal to' is shown.
    2664  * @constructor
    2665  * @name Inequality
    2666  * @type JXG.Curve
    2667  * @augments JXG.Curve
    2668  * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown.
    2669  * @example
    2670  * var p = board.create('point', [1, 3]),
    2671  *     q = board.create('point', [-2, -4]),
    2672  *     l = board.create('line', [p, q]),
    2673  *     ineq = board.create('inequality', [l]);
    2674  * ineq = board.create('inequality', [l]);
    2675  * </pre><div class="jxgbox" id="JXG2b703006-fd98-11e1-b79e-ef9e591c002e" style="width: 400px; height: 400px;"></div>
    2676  * <script type="text/javascript">
    2677  * (function () {
    2678  *  var board = JXG.JSXGraph.initBoard('JXG2b703006-fd98-11e1-b79e-ef9e591c002e', {boundingbox:[-4, 6, 10, -6], axis: false, grid: false, keepaspectratio: true}),
    2679  *      p = board.create('point', [1, 3]),
    2680  *      q = board.create('point', [-2, -4]),
    2681  *      l = board.create('line', [p, q]),
    2682  *      ineq = board.create('inequality', [l]);
    2683  * })();
    2684  * </script><pre>
    2685  *
    2686  * @example
    2687  * // Plot the inequality
    2688  * //     y >= 2/3 x + 1
    2689  * // or
    2690  * //     0 >= -3y + 2x +1
    2691  * var l = board.create('line', [1, 2, -3]),
    2692  *     ineq = board.create('inequality', [l], {inverse:true});
    2693  * </pre><div class="jxgbox" id="JXG1ded3812-2da4-4323-abaf-1db4bad1bfbd" style="width: 400px; height: 400px;"></div>
    2694  * <script type="text/javascript">
    2695  * (function () {
    2696  *  var board = JXG.JSXGraph.initBoard('JXG1ded3812-2da4-4323-abaf-1db4bad1bfbd', {boundingbox:[-4, 6, 10, -6], axis: false, grid: false, keepaspectratio: true}),
    2697  *      l = board.create('line', [1, 2, -3]),
    2698  *      ineq = board.create('inequality', [l], {inverse:true});
    2699  * })();
    2700  * </script><pre>
    2701  *
    2702  * @example
    2703  * var f = board.create('functiongraph', ['sin(x)', -2*Math.PI, 2*Math.PI]);
    2704  *
    2705  * var ineq_lower = board.create('inequality', [f]);
    2706  * var ineq_greater = board.create('inequality', [f], {inverse: true, fillColor: 'yellow'});
    2707  *
    2708  *
    2709  * </pre><div id="JXGdb68c574-414c-11e8-839a-901b0e1b8723" class="jxgbox" style="width: 300px; height: 300px;"></div>
    2710  * <script type="text/javascript">
    2711  *     (function() {
    2712  *         var board = JXG.JSXGraph.initBoard('JXGdb68c574-414c-11e8-839a-901b0e1b8723',
    2713  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
    2714  *     var f = board.create('functiongraph', ['sin(x)', -2*Math.PI, 2*Math.PI]);
    2715  *
    2716  *     var ineq_lower = board.create('inequality', [f]);
    2717  *     var ineq_greater = board.create('inequality', [f], {inverse: true, fillColor: 'yellow'});
    2718  *
    2719  *
    2720  *     })();
    2721  *
    2722  * </script><pre>
    2723  *
    2724  */
    2725 JXG.createInequality = function (board, parents, attributes) {
    2726     var f, a, attr;
    2727
    2728     attr = Type.copyAttributes(attributes, board.options, "inequality");
    2729     if (parents[0].elementClass === Const.OBJECT_CLASS_LINE) {
    2730         a = board.create("curve", [[], []], attr);
    2731         a.hasPoint = function () {
    2732             return false;
    2733         };
    2734
    2735         /**
    2736          * @class
    2737          * @ignore
    2738          */
    2739         a.updateDataArray = function () {
    2740             var i1,
    2741                 i2,
    2742                 // This will be the height of the area. We mustn't rely upon the board height because if we pan the view
    2743                 // such that the line is not visible anymore, the borders of the area will get visible in some cases.
    2744                 h,
    2745                 bb = board.getBoundingBox(),
    2746                 inverse = this.evalVisProp('inverse'),
    2747                 factor = inverse ? -1 : 1,
    2748                 expansion = 1.5,
    2749                 w = expansion * Math.max(bb[2] - bb[0], bb[1] - bb[3]),
    2750                 // Fake a point (for Math.Geometry.perpendicular)
    2751                 // contains centroid of the board
    2752                 dp = {
    2753                     coords: {
    2754                         usrCoords: [1, (bb[0] + bb[2]) * 0.5, inverse ? bb[1] : bb[3]]
    2755                     }
    2756                 },
    2757                 slope1 = parents[0].stdform.slice(1),
    2758                 slope2 = slope1;
    2759
    2760             // Calculate the area height as
    2761             //  expansion times the distance of the line to the
    2762             // point in the middle of the top/bottom border.
    2763             h =
    2764                 expansion *
    2765                 Math.max(
    2766                     Geometry.perpendicular(parents[0], dp, board)[0].distance(
    2767                         Const.COORDS_BY_USER,
    2768                         dp.coords
    2769                     ),
    2770                     w
    2771                 );
    2772             h *= factor;
    2773
    2774             // reuse dp
    2775             dp = {
    2776                 coords: {
    2777                     usrCoords: [1, (bb[0] + bb[2]) * 0.5, (bb[1] + bb[3]) * 0.5]
    2778                 }
    2779             };
    2780
    2781             // If dp is on the line, Geometry.perpendicular will return a point not on the line.
    2782             // Since this somewhat odd behavior of Geometry.perpendicular is needed in GEONExT,
    2783             // it is circumvented here.
    2784             if (
    2785                 Math.abs(Mat.innerProduct(dp.coords.usrCoords, parents[0].stdform, 3)) >=
    2786                 Mat.eps
    2787             ) {
    2788                 dp = Geometry.perpendicular(parents[0], dp, board)[0].usrCoords;
    2789             } else {
    2790                 dp = dp.coords.usrCoords;
    2791             }
    2792             i1 = [1, dp[1] + slope1[1] * w, dp[2] - slope1[0] * w];
    2793             i2 = [1, dp[1] - slope2[1] * w, dp[2] + slope2[0] * w];
    2794
    2795             // One of the vectors based in i1 and orthogonal to the parent line has the direction d1 = (slope1, -1)
    2796             // We will go from i1 to i1 + h*d1, from there to i2 + h*d2 (with d2 calculated equivalent to d1) and
    2797             // end up in i2.
    2798             this.dataX = [i1[1], i1[1] + slope1[0] * h, i2[1] + slope2[0] * h, i2[1], i1[1]];
    2799             this.dataY = [i1[2], i1[2] + slope1[1] * h, i2[2] + slope2[1] * h, i2[2], i1[2]];
    2800         };
    2801     } else if (
    2802         parents[0].elementClass === Const.OBJECT_CLASS_CURVE &&
    2803         parents[0].visProp.curvetype === "functiongraph"
    2804     ) {
    2805         a = board.create("curve", [[], []], attr);
    2806         /**
    2807          * @class
    2808          * @ignore
    2809          */
    2810         a.updateDataArray = function () {
    2811             var bbox = this.board.getBoundingBox(),
    2812                 points = [],
    2813                 infty,
    2814                 first,
    2815                 last,
    2816                 len,
    2817                 i,
    2818                 mi = parents[0].minX(),
    2819                 ma = parents[0].maxX(),
    2820                 curve_mi,
    2821                 curve_ma,
    2822                 firstx,
    2823                 lastx,
    2824                 enlarge = (bbox[1] - bbox[3]) * 0.3, // enlarge the bbox vertically by this amount
    2825                 inverse = this.evalVisProp('inverse');
    2826
    2827             // inverse == true <=> Fill area with y >= f(x)
    2828             infty = inverse ? 1 : 3; // we will use either bbox[1] or bbox[3] below
    2829
    2830             this.dataX = [];
    2831             this.dataY = [];
    2832             len = parents[0].points.length;
    2833             if (len === 0) {
    2834                 return;
    2835             }
    2836
    2837             bbox[1] += enlarge;
    2838             bbox[3] -= enlarge;
    2839
    2840             last = -1;
    2841             while (last < len - 1) {
    2842                 // Find the first point with real coordinates on this curve segment
    2843                 for (i = last + 1, first = len; i < len; i++) {
    2844                     if (parents[0].points[i].isReal()) {
    2845                         first = i;
    2846                         break;
    2847                     }
    2848                 }
    2849                 // No real points found -> exit
    2850                 if (first >= len) {
    2851                     break;
    2852                 }
    2853
    2854                 // Find the last point with real coordinates on this curve segment
    2855                 for (i = first, last = len - 1; i < len - 1; i++) {
    2856                     if (!parents[0].points[i + 1].isReal()) {
    2857                         last = i;
    2858                         break;
    2859                     }
    2860                 }
    2861
    2862                 firstx = parents[0].points[first].usrCoords[1];
    2863                 lastx = parents[0].points[last].usrCoords[1];
    2864
    2865                 // Restrict the plot interval if the function ends inside of the board
    2866                 curve_mi = bbox[0] < mi ? mi : bbox[0];
    2867                 curve_ma = bbox[2] > ma ? ma : bbox[2];
    2868
    2869                 // Found NaNs
    2870                 curve_mi = first === 0 ? curve_mi : Math.max(curve_mi, firstx);
    2871                 curve_ma = last === len - 1 ? curve_ma : Math.min(curve_ma, lastx);
    2872
    2873                 // First and last relevant x-coordinate of the curve
    2874                 curve_mi = first === 0 ? mi : firstx;
    2875                 curve_ma = last === len - 1 ? ma : lastx;
    2876
    2877                 // Copy the curve points
    2878                 points = [];
    2879
    2880                 points.push([1, curve_mi, bbox[infty]]);
    2881                 points.push([1, curve_mi, parents[0].points[first].usrCoords[2]]);
    2882                 for (i = first; i <= last; i++) {
    2883                     points.push(parents[0].points[i].usrCoords);
    2884                 }
    2885                 points.push([1, curve_ma, parents[0].points[last].usrCoords[2]]);
    2886                 points.push([1, curve_ma, bbox[infty]]);
    2887                 points.push(points[0]);
    2888
    2889                 for (i = 0; i < points.length; i++) {
    2890                     this.dataX.push(points[i][1]);
    2891                     this.dataY.push(points[i][2]);
    2892                 }
    2893
    2894                 if (last < len - 1) {
    2895                     this.dataX.push(NaN);
    2896                     this.dataY.push(NaN);
    2897                 }
    2898             }
    2899         };
    2900
    2901         // Previous code:
    2902         /**
    2903          * @class
    2904          * @ignore
    2905          */
    2906         a.hasPoint = function () {
    2907             return false;
    2908         };
    2909     } else {
    2910         // Not yet practical?
    2911         f = Type.createFunction(parents[0]);
    2912         a.addParentsFromJCFunctions([f]);
    2913
    2914         if (!Type.exists(f)) {
    2915             throw new Error(
    2916                 "JSXGraph: Can't create area with the given parents." +
    2917                     "\nPossible parent types: [line], [function]"
    2918             );
    2919         }
    2920     }
    2921
    2922     a.addParents(parents[0]);
    2923     return a;
    2924 };
    2925
    2926 JXG.registerElement("arrowparallel", JXG.createArrowParallel);
    2927 JXG.registerElement("bisector", JXG.createBisector);
    2928 JXG.registerElement("bisectorlines", JXG.createAngularBisectorsOfTwoLines);
    2929 JXG.registerElement("msector", JXG.createMsector);
    2930 JXG.registerElement("circumcircle", JXG.createCircumcircle);
    2931 JXG.registerElement("circumcirclemidpoint", JXG.createCircumcenter);
    2932 JXG.registerElement("circumcenter", JXG.createCircumcenter);
    2933 JXG.registerElement("incenter", JXG.createIncenter);
    2934 JXG.registerElement("incircle", JXG.createIncircle);
    2935 JXG.registerElement("integral", JXG.createIntegral);
    2936 JXG.registerElement("midpoint", JXG.createMidpoint);
    2937 JXG.registerElement("mirrorelement", JXG.createMirrorElement);
    2938 JXG.registerElement("mirrorpoint", JXG.createMirrorPoint);
    2939 JXG.registerElement("orthogonalprojection", JXG.createOrthogonalProjection);
    2940 JXG.registerElement("parallel", JXG.createParallel);
    2941 JXG.registerElement("parallelpoint", JXG.createParallelPoint);
    2942 JXG.registerElement("perpendicular", JXG.createPerpendicular);
    2943 JXG.registerElement("perpendicularpoint", JXG.createPerpendicularPoint);
    2944 JXG.registerElement("perpendicularsegment", JXG.createPerpendicularSegment);
    2945 JXG.registerElement("reflection", JXG.createReflection);
    2946 JXG.registerElement("inequality", JXG.createInequality);
    2947
    2948 // export default {
    2949 //     createArrowParallel: JXG.createArrowParallel,
    2950 //     createBisector: JXG.createBisector,
    2951 //     createAngularBisectorOfTwoLines: JXG.createAngularBisectorsOfTwoLines,
    2952 //     createCircumcircle: JXG.createCircumcircle,
    2953 //     createCircumcenter: JXG.createCircumcenter,
    2954 //     createIncenter: JXG.createIncenter,
    2955 //     createIncircle: JXG.createIncircle,
    2956 //     createIntegral: JXG.createIntegral,
    2957 //     createMidpoint: JXG.createMidpoint,
    2958 //     createMirrorElement: JXG.createMirrorElement,
    2959 //     createMirrorPoint: JXG.createMirrorPoint,
    2960 //     createNormal: JXG.createNormal,
    2961 //     createOrthogonalProjection: JXG.createOrthogonalProjection,
    2962 //     createParallel: JXG.createParallel,
    2963 //     createParallelPoint: JXG.createParallelPoint,
    2964 //     createPerpendicular: JXG.createPerpendicular,
    2965 //     createPerpendicularPoint: JXG.createPerpendicularPoint,
    2966 //     createPerpendicularSegmen: JXG.createPerpendicularSegment,
    2967 //     createReflection: JXG.createReflection,
    2968 //     createInequality: JXG.createInequality
    2969 // };
    2970