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