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