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