1 /*
  2     Copyright 2008-2024
  3         Matthias Ehmann,
  4         Michael Gerhaeuser,
  5         Carsten Miller,
  6         Bianca Valentin,
  7         Alfred Wassermann,
  8         Peter Wilfahrt
  9 
 10     This file is part of JSXGraph.
 11 
 12     JSXGraph is free software dual licensed under the GNU LGPL or MIT License.
 13 
 14     You can redistribute it and/or modify it under the terms of the
 15 
 16       * GNU Lesser General Public License as published by
 17         the Free Software Foundation, either version 3 of the License, or
 18         (at your option) any later version
 19       OR
 20       * MIT License: https://github.com/jsxgraph/jsxgraph/blob/master/LICENSE.MIT
 21 
 22     JSXGraph is distributed in the hope that it will be useful,
 23     but WITHOUT ANY WARRANTY; without even the implied warranty of
 24     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 25     GNU Lesser General Public License for more details.
 26 
 27     You should have received a copy of the GNU Lesser General Public License and
 28     the MIT License along with JSXGraph. If not, see <https://www.gnu.org/licenses/>
 29     and <https://opensource.org/licenses/MIT/>.
 30  */
 31 
 32 /*global JXG: true, define: true*/
 33 /*jslint nomen: true, plusplus: true*/
 34 
 35 /**
 36  * @fileoverview In this file the conic sections defined.
 37  */
 38 
 39 import JXG from "../jxg.js";
 40 import Const from "../base/constants.js";
 41 import Coords from "../base/coords.js";
 42 import Mat from "../math/math.js";
 43 import Numerics from "../math/numerics.js";
 44 import Geometry from "../math/geometry.js";
 45 import Type from "../utils/type.js";
 46 
 47 /**
 48  * @class This element is used to provide a constructor for an ellipse. An ellipse is given by two points (the foci) and a third point on the ellipse or
 49  * the length of the major axis.
 50  * @pseudo
 51  * @name Ellipse
 52  * @augments Conic
 53  * @constructor
 54  * @type JXG.Curve
 55  * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
 56  * @param {JXG.Point,array_JXG.Point,array_JXG.Point,array} point1,point2,point3 Parent elements can be three elements either of type {@link JXG.Point} or array of
 57  * numbers describing the coordinates of a point. In the latter case the point will be constructed automatically as a fixed invisible point.
 58  * @param {JXG.Point,array_JXG.Point,array_number,function} point1,point2,number Parent elements can be two elements either of type {@link JXG.Point} or array of
 59  * numbers describing the coordinates of a point. The third parameter is a number/function which defines the length of the major axis
 60  * @param {Number} start (Optional) parameter of the curve start, default: 0.
 61  * @param {Number} end (Optional) parameter for the curve end, default: 2π.
 62  * @example
 63  * // Create an Ellipse by three points
 64  * var A = board.create('point', [-1,4]);
 65  * var B = board.create('point', [-1,-4]);
 66  * var C = board.create('point', [1,1]);
 67  * var el = board.create('ellipse',[A,B,C]);
 68  * </pre><div class="jxgbox" id="JXGa4d7fb6f-8708-4e45-87f2-2379ae2bd2c0" style="width: 300px; height: 300px;"></div>
 69  * <script type="text/javascript">
 70  *   (function() {
 71  *   var glex1_board = JXG.JSXGraph.initBoard('JXGa4d7fb6f-8708-4e45-87f2-2379ae2bd2c0', {boundingbox:[-6,6,6,-6], keepaspectratio:true, showcopyright: false, shownavigation: false});
 72  *   var A = glex1_board.create('point', [-1,4]);
 73  *   var B = glex1_board.create('point', [-1,-4]);
 74  *   var C = glex1_board.create('point', [1,1]);
 75  *   var el = glex1_board.create('ellipse',[A,B,C]);
 76  * })();
 77  * </script><pre>
 78  *
 79  * @example
 80  * // Create an elliptical arc
 81  * var p1 = board.create('point', [-1, 2]);
 82  * var p2 = board.create('point', [ 1, 2]);
 83  * var p3 = board.create('point', [0, 3]);
 84  *
 85  * var ell = board.create('ellipse', [
 86  *   p1, p2, p3, 0, Math.PI], {
 87  *   lastArrow: {type: 7}
 88  * });
 89  *
 90  * </pre><div id="JXG950f7c07-27a4-4c67-9505-c73c22ce9345" class="jxgbox" style="width: 300px; height: 300px;"></div>
 91  * <script type="text/javascript">
 92  *     (function() {
 93  *         var board = JXG.JSXGraph.initBoard('JXG950f7c07-27a4-4c67-9505-c73c22ce9345',
 94  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
 95  *     var p1 = board.create('point', [-1, 2]);
 96  *     var p2 = board.create('point', [ 1, 2]);
 97  *     var p3 = board.create('point', [0, 3]);
 98  *
 99  *     var ell = board.create('ellipse', [
100  *       p1, p2, p3, 0, Math.PI], {
101  *       lastArrow: {type: 7}
102  *     });
103  *
104  *     })();
105  *
106  * </script><pre>
107  *
108  *
109  */
110 JXG.createEllipse = function (board, parents, attributes) {
111     var polarForm,
112         curve,
113         M,
114         C,
115         majorAxis,
116         i,
117         hasPointOrg,
118         // focus 1 and focus 2
119         F = [],
120         attr_foci = Type.copyAttributes(attributes, board.options, "conic", "foci"),
121         attr_center = Type.copyAttributes(attributes, board.options, "conic", "center"),
122         attr_curve = Type.copyAttributes(attributes, board.options, "conic");
123 
124     // The foci and the third point are either points or coordinate arrays.
125     for (i = 0; i < 2; i++) {
126         // focus i given by coordinates
127         if (parents[i].length > 1) {
128             F[i] = board.create("point", parents[i], attr_foci);
129             // focus i given by point
130         } else if (Type.isPoint(parents[i])) {
131             F[i] = board.select(parents[i]);
132             // given by function
133         } else if (Type.isFunction(parents[i]) && Type.isPoint(parents[i]())) {
134             F[i] = parents[i]();
135             // focus i given by point name
136         } else if (Type.isString(parents[i])) {
137             F[i] = board.select(parents[i]);
138         } else {
139             throw new Error(
140                 "JSXGraph: Can't create Ellipse with parent types '" +
141                     typeof parents[0] +
142                     "' and '" +
143                     typeof parents[1] +
144                     "'." +
145                     "\nPossible parent types: [point,point,point], [point,point,number|function]"
146             );
147         }
148     }
149 
150     // length of major axis
151     if (Type.isNumber(parents[2])) {
152         majorAxis = Type.createFunction(parents[2], board);
153     } else if (Type.isFunction(parents[2]) && Type.isNumber(parents[2]())) {
154         majorAxis = parents[2];
155     } else {
156         // point on ellipse
157         if (Type.isPoint(parents[2])) {
158             C = board.select(parents[2]);
159             // point on ellipse given by coordinates
160         } else if (parents[2].length > 1) {
161             C = board.create("point", parents[2], attr_foci);
162             // given by function
163         } else if (Type.isFunction(parents[2]) && Type.isPoint(parents[2]())) {
164             C = parents[2]();
165             // focus i given by point name
166         } else if (Type.isString(parents[2])) {
167             C = board.select(parents[2]);
168         } else {
169             throw new Error(
170                 "JSXGraph: Can't create Ellipse with parent types '" +
171                     typeof parents[0] +
172                     "' and '" +
173                     typeof parents[1] +
174                     "' and '" +
175                     typeof parents[2] +
176                     "'." +
177                     "\nPossible parent types: [point,point,point], [point,point,number|function]"
178             );
179         }
180         /** @ignore */
181         majorAxis = function () {
182             return C.Dist(F[0]) + C.Dist(F[1]);
183         };
184     }
185 
186     // to
187     if (!Type.exists(parents[4])) {
188         parents[4] = 2 * Math.PI;
189     }
190 
191     // from
192     if (!Type.exists(parents[3])) {
193         parents[3] = 0.0;
194     }
195 
196     M = board.create(
197         "point",
198         [
199             function () {
200                 return (F[0].X() + F[1].X()) * 0.5;
201             },
202             function () {
203                 return (F[0].Y() + F[1].Y()) * 0.5;
204             }
205         ],
206         attr_center
207     );
208 
209     /**
210      * @class
211      * @ignore
212      */
213     curve = board.create(
214         "curve",
215         [
216             function (x) {
217                 return 0;
218             },
219             function (x) {
220                 return 0;
221             },
222             parents[3],
223             parents[4]
224         ],
225         attr_curve
226     );
227 
228     curve.majorAxis = majorAxis;
229 
230     // Save the original hasPoint method. It will be called inside of the new hasPoint method.
231     hasPointOrg = curve.hasPoint;
232 
233     /** @ignore */
234     polarForm = function (phi, suspendUpdate) {
235         var r, rr, ax, ay, bx, by, axbx, ayby, f;
236 
237         if (!suspendUpdate) {
238             r = majorAxis();
239             rr = r * r;
240             ax = F[0].X();
241             ay = F[0].Y();
242             bx = F[1].X();
243             by = F[1].Y();
244             axbx = ax - bx;
245             ayby = ay - by;
246             f = (rr - ax * ax - ay * ay + bx * bx + by * by) / (2 * r);
247 
248             curve.quadraticform = [
249                 [f * f - bx * bx - by * by, (f * axbx) / r + bx, (f * ayby) / r + by],
250                 [(f * axbx) / r + bx, (axbx * axbx) / rr - 1, (axbx * ayby) / rr],
251                 [(f * ayby) / r + by, (axbx * ayby) / rr, (ayby * ayby) / rr - 1]
252             ];
253         }
254     };
255 
256     /** @ignore */
257     curve.X = function (phi, suspendUpdate) {
258         var r = majorAxis(),
259             c = F[1].Dist(F[0]),
260             b = (0.5 * (c * c - r * r)) / (c * Math.cos(phi) - r),
261             beta = Math.atan2(F[1].Y() - F[0].Y(), F[1].X() - F[0].X());
262 
263         if (!suspendUpdate) {
264             polarForm(phi, suspendUpdate);
265         }
266 
267         return F[0].X() + Math.cos(beta + phi) * b;
268     };
269 
270     /** @ignore */
271     curve.Y = function (phi, suspendUpdate) {
272         var r = majorAxis(),
273             c = F[1].Dist(F[0]),
274             b = (0.5 * (c * c - r * r)) / (c * Math.cos(phi) - r),
275             beta = Math.atan2(F[1].Y() - F[0].Y(), F[1].X() - F[0].X());
276 
277         return F[0].Y() + Math.sin(beta + phi) * b;
278     };
279 
280     curve.midpoint = curve.center = M;
281     curve.type = Const.OBJECT_TYPE_CONIC;
282     curve.subs = {
283         center: curve.center
284     };
285     curve.inherits.push(curve.center, F[0], F[1]);
286     if (Type.isPoint(C)) {
287         curve.inherits.push(C);
288     }
289 
290     /**
291      * Checks whether (x,y) is near the ellipse line or inside of the ellipse
292      * (in case JXG.Options.conic#hasInnerPoints is true).
293      * @param {Number} x Coordinate in x direction, screen coordinates.
294      * @param {Number} y Coordinate in y direction, screen coordinates.
295      * @returns {Boolean} True if (x,y) is near the ellipse, False otherwise.
296      * @private
297      * @ignore
298      */
299     curve.hasPoint = function (x, y) {
300         var ac, bc, r, p, dist;
301 
302         if (this.evalVisProp('hasinnerpoints')) {
303             ac = F[0].coords;
304             bc = F[1].coords;
305             r = this.majorAxis();
306             p = new Coords(Const.COORDS_BY_SCREEN, [x, y], this.board);
307             dist = p.distance(Const.COORDS_BY_USER, ac) + p.distance(Const.COORDS_BY_USER, bc);
308 
309             return dist <= r;
310         }
311 
312         return hasPointOrg.apply(this, arguments);
313     };
314 
315     M.addChild(curve);
316     for (i = 0; i < 2; i++) {
317         if (Type.isPoint(F[i])) {
318             F[i].addChild(curve);
319         }
320     }
321     if (Type.isPoint(C)) {
322         C.addChild(curve);
323     }
324     curve.setParents(parents);
325 
326     return curve;
327 };
328 
329 /**
330  * @class This element is used to provide a constructor for an hyperbola. An hyperbola is given by two points (the foci) and a third point on the hyperbola or
331  * the length of the major axis.
332  * @pseudo
333  * @name Hyperbola
334  * @augments Conic
335  * @constructor
336  * @type JXG.Curve
337  * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
338  * @param {JXG.Point,array_JXG.Point,array_JXG.Point,array} point1,point2,point3 Parent elements can be three elements either of type {@link JXG.Point} or array of
339  * numbers describing the coordinates of a point. In the latter case the point will be constructed automatically as a fixed invisible point.
340  * @param {JXG.Point,array_JXG.Point,array_number,function} point1,point2,number Parent elements can be two elements either of type {@link JXG.Point} or array of
341  * numbers describing the coordinates of a point. The third parameter is a number/function which defines the length of the major axis
342  * @param {Number} start (Optional) parameter of the curve start, default: -π.
343  * @param {Number} end (Optional) parameter for the curve end, default: π.
344  * @example
345  * // Create an Hyperbola by three points
346  * var A = board.create('point', [-1,4]);
347  * var B = board.create('point', [-1,-4]);
348  * var C = board.create('point', [1,1]);
349  * var el = board.create('hyperbola',[A,B,C]);
350  * </pre><div class="jxgbox" id="JXGcf99049d-a3fe-407f-b936-27d76550f8c4" style="width: 300px; height: 300px;"></div>
351  * <script type="text/javascript">
352  *   (function(){
353  *   var glex1_board = JXG.JSXGraph.initBoard('JXGcf99049d-a3fe-407f-b936-27d76550f8c4', {boundingbox:[-6,6,6,-6], keepaspectratio:true, showcopyright: false, shownavigation: false});
354  *   var A = glex1_board.create('point', [-1,4]);
355  *   var B = glex1_board.create('point', [-1,-4]);
356  *   var C = glex1_board.create('point', [1,1]);
357  *   var el = glex1_board.create('hyperbola',[A,B,C]);
358  * })();
359  * </script><pre>
360  */
361 JXG.createHyperbola = function (board, parents, attributes) {
362     var polarForm,
363         curve,
364         M,
365         C,
366         majorAxis,
367         i,
368         // focus 1 and focus 2
369         F = [],
370         attr_foci = Type.copyAttributes(attributes, board.options, "conic", "foci"),
371         attr_center = Type.copyAttributes(attributes, board.options, "conic", "center"),
372         attr_curve = Type.copyAttributes(attributes, board.options, "conic");
373 
374     // The foci and the third point are either points or coordinate arrays.
375     for (i = 0; i < 2; i++) {
376         // focus i given by coordinates
377         if (parents[i].length > 1) {
378             F[i] = board.create("point", parents[i], attr_foci);
379             // focus i given by point
380         } else if (Type.isPoint(parents[i])) {
381             F[i] = board.select(parents[i]);
382             // given by function
383         } else if (Type.isFunction(parents[i]) && Type.isPoint(parents[i]())) {
384             F[i] = parents[i]();
385             // focus i given by point name
386         } else if (Type.isString(parents[i])) {
387             F[i] = board.select(parents[i]);
388         } else {
389             throw new Error(
390                 "JSXGraph: Can't create Hyperbola with parent types '" +
391                     typeof parents[0] +
392                     "' and '" +
393                     typeof parents[1] +
394                     "'." +
395                     "\nPossible parent types: [point,point,point], [point,point,number|function]"
396             );
397         }
398     }
399 
400     // length of major axis
401     if (Type.isNumber(parents[2])) {
402         majorAxis = Type.createFunction(parents[2], board);
403     } else if (Type.isFunction(parents[2]) && Type.isNumber(parents[2]())) {
404         majorAxis = parents[2];
405     } else {
406         // point on ellipse
407         if (Type.isPoint(parents[2])) {
408             C = board.select(parents[2]);
409             // point on ellipse given by coordinates
410         } else if (parents[2].length > 1) {
411             C = board.create("point", parents[2], attr_foci);
412             // given by function
413         } else if (Type.isFunction(parents[2]) && Type.isPoint(parents[2]())) {
414             C = parents[2]();
415             // focus i given by point name
416         } else if (Type.isString(parents[2])) {
417             C = board.select(parents[2]);
418         } else {
419             throw new Error(
420                 "JSXGraph: Can't create Hyperbola with parent types '" +
421                     typeof parents[0] +
422                     "' and '" +
423                     typeof parents[1] +
424                     "' and '" +
425                     typeof parents[2] +
426                     "'." +
427                     "\nPossible parent types: [point,point,point], [point,point,number|function]"
428             );
429         }
430         /** @ignore */
431         majorAxis = function () {
432             return C.Dist(F[0]) - C.Dist(F[1]);
433         };
434     }
435 
436     // to
437     if (!Type.exists(parents[4])) {
438         parents[4] = 1.0001 * Math.PI;
439     }
440 
441     // from
442     if (!Type.exists(parents[3])) {
443         parents[3] = -1.0001 * Math.PI;
444     }
445 
446     M = board.create(
447         "point",
448         [
449             function () {
450                 return (F[0].X() + F[1].X()) * 0.5;
451             },
452             function () {
453                 return (F[0].Y() + F[1].Y()) * 0.5;
454             }
455         ],
456         attr_center
457     );
458 
459     /**
460      * @class
461      * @ignore
462      */
463     curve = board.create(
464         "curve",
465         [
466             function (x) {
467                 return 0;
468             },
469             function (x) {
470                 return 0;
471             },
472             parents[3],
473             parents[4]
474         ],
475         attr_curve
476     );
477 
478     curve.majorAxis = majorAxis;
479 
480     // Hyperbola is defined by (a*sec(t),b*tan(t)) and sec(t) = 1/cos(t)
481     /** @ignore */
482     polarForm = function (phi, suspendUpdate) {
483         var r, rr, ax, ay, bx, by, axbx, ayby, f;
484 
485         if (!suspendUpdate) {
486             r = majorAxis();
487             rr = r * r;
488             ax = F[0].X();
489             ay = F[0].Y();
490             bx = F[1].X();
491             by = F[1].Y();
492             axbx = ax - bx;
493             ayby = ay - by;
494             f = (rr - ax * ax - ay * ay + bx * bx + by * by) / (2 * r);
495 
496             curve.quadraticform = [
497                 [f * f - bx * bx - by * by, (f * axbx) / r + bx, (f * ayby) / r + by],
498                 [(f * axbx) / r + bx, (axbx * axbx) / rr - 1, (axbx * ayby) / rr],
499                 [(f * ayby) / r + by, (axbx * ayby) / rr, (ayby * ayby) / rr - 1]
500             ];
501         }
502     };
503 
504     /** @ignore */
505     curve.X = function (phi, suspendUpdate) {
506         var r = majorAxis(),
507             c = F[1].Dist(F[0]),
508             b = (0.5 * (c * c - r * r)) / (c * Math.cos(phi) + r),
509             beta = Math.atan2(F[1].Y() - F[0].Y(), F[1].X() - F[0].X());
510 
511         if (!suspendUpdate) {
512             polarForm(phi, suspendUpdate);
513         }
514 
515         return F[0].X() + Math.cos(beta + phi) * b;
516     };
517 
518     /** @ignore */
519     curve.Y = function (phi, suspendUpdate) {
520         var r = majorAxis(),
521             c = F[1].Dist(F[0]),
522             b = (0.5 * (c * c - r * r)) / (c * Math.cos(phi) + r),
523             beta = Math.atan2(F[1].Y() - F[0].Y(), F[1].X() - F[0].X());
524 
525         return F[0].Y() + Math.sin(beta + phi) * b;
526     };
527 
528     curve.midpoint = curve.center = M;
529     curve.subs = {
530         center: curve.center
531     };
532     curve.inherits.push(curve.center, F[0], F[1]);
533     if (Type.isPoint(C)) {
534         curve.inherits.push(C);
535     }
536     curve.type = Const.OBJECT_TYPE_CONIC;
537 
538     M.addChild(curve);
539     for (i = 0; i < 2; i++) {
540         if (Type.isPoint(F[i])) {
541             F[i].addChild(curve);
542         }
543     }
544     if (Type.isPoint(C)) {
545         C.addChild(curve);
546     }
547     curve.setParents(parents);
548 
549     return curve;
550 };
551 
552 /**
553  * @class This element is used to provide a constructor for a parabola. A parabola is given by one point (the focus) and a line (the directrix).
554  * @pseudo
555  * @name Parabola
556  * @augments Conic
557  * @constructor
558  * @type Object
559  * @description JXG.Curve
560  * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
561  * @param {JXG.Point,array_JXG.Line} point,line Parent elements are a point and a line or a pair of coordinates.
562  * Optional parameters three and four are numbers which define the curve length (e.g. start/end). Default values are -pi and pi.
563  * @example
564  * // Create a parabola by a point C and a line l.
565  * var A = board.create('point', [-1,4]);
566  * var B = board.create('point', [-1,-4]);
567  * var l = board.create('line', [A,B]);
568  * var C = board.create('point', [1,1]);
569  * var el = board.create('parabola',[C,l]);
570  * </pre><div class="jxgbox" id="JXG524d1aae-217d-44d4-ac58-a19c7ab1de36" style="width: 300px; height: 300px;"></div>
571  * <script type="text/javascript">
572  * (function() {
573  *   var glex1_board = JXG.JSXGraph.initBoard('JXG524d1aae-217d-44d4-ac58-a19c7ab1de36', {boundingbox:[-6,6,6,-6], keepaspectratio:true, showcopyright: false, shownavigation: false});
574  *   var A = glex1_board.create('point', [-1,4]);
575  *   var B = glex1_board.create('point', [-1,-4]);
576  *   var l = glex1_board.create('line', [A,B]);
577  *   var C = glex1_board.create('point', [1,1]);
578  *   var el = glex1_board.create('parabola',[C,l]);
579  * })();
580  * </script><pre>
581  *
582  * @example
583  * var par = board.create('parabola',[[3.25, 0], [[0.25, 1],[0.25, 0]]]);
584  *
585  * </pre><div id="JXG09252542-b77a-4990-a109-66ffb649a472" class="jxgbox" style="width: 300px; height: 300px;"></div>
586  * <script type="text/javascript">
587  *     (function() {
588  *         var board = JXG.JSXGraph.initBoard('JXG09252542-b77a-4990-a109-66ffb649a472',
589  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
590  *     var par = board.create('parabola',[[3.25, 0], [[0.25, 1],[0.25, 0]]]);
591  *
592  *     })();
593  *
594  * </script><pre>
595  *
596  */
597 JXG.createParabola = function (board, parents, attributes) {
598     var polarForm,
599         curve,
600         M,
601         // focus
602         F1 = parents[0],
603         // directrix
604         l = parents[1],
605         attr_foci = Type.copyAttributes(attributes, board.options, "conic", "foci"),
606         attr_center = Type.copyAttributes(attributes, board.options, "conic", "center"),
607         attr_curve = Type.copyAttributes(attributes, board.options, "conic"),
608         attr_line;
609 
610     // focus 1 given by coordinates
611     if (parents[0].length > 1) {
612         F1 = board.create("point", parents[0], attr_foci);
613         // focus 1 given by point
614     } else if (Type.isPoint(parents[0])) {
615         F1 = board.select(parents[0]);
616         // given by function
617     } else if (Type.isFunction(parents[0]) && Type.isPoint(parents[0]())) {
618         F1 = parents[0]();
619         // focus 1 given by point name
620     } else if (Type.isString(parents[0])) {
621         F1 = board.select(parents[0]);
622     } else {
623         throw new Error(
624             "JSXGraph: Can't create Parabola with parent types '" +
625                 typeof parents[0] +
626                 "' and '" +
627                 typeof parents[1] +
628                 "'." +
629                 "\nPossible parent types: [point,line]"
630         );
631     }
632 
633     // Create line if given as array of two points.
634     if (Type.isArray(l) && l.length === 2) {
635         attr_line = Type.copyAttributes(attributes, board.options, "conic", "line");
636         l = board.create("line", l, attr_line);
637     }
638 
639     // to
640     if (!Type.exists(parents[3])) {
641         parents[3] = 2 * Math.PI;
642     }
643 
644     // from
645     if (!Type.exists(parents[2])) {
646         parents[2] = 0;
647     }
648 
649     M = board.create(
650         "point",
651         [
652             function () {
653                 /*
654                 var v = [0, l.stdform[1], l.stdform[2]];
655                 v = Mat.crossProduct(v, F1.coords.usrCoords);
656                 return Geometry.meetLineLine(v, l.stdform, 0, board).usrCoords;
657                 */
658                 return Geometry.projectPointToLine(F1, l, board).usrCoords;
659             }
660         ],
661         attr_center
662     );
663 
664     /**
665      * @class
666      * @ignore
667      */
668     curve = board.create(
669         "curve",
670         [
671             function (x) {
672                 return 0;
673             },
674             function (x) {
675                 return 0;
676             },
677             parents[2],
678             parents[3]
679         ],
680         attr_curve
681     );
682 
683     curve.midpoint = curve.center = M;
684     curve.subs = {
685         center: curve.center
686     };
687     curve.inherits.push(curve.center);
688 
689     /** @ignore */
690     polarForm = function (t, suspendUpdate) {
691         var a, b, c, ab, px, py;
692 
693         if (!suspendUpdate) {
694             a = l.stdform[1];
695             b = l.stdform[2];
696             c = l.stdform[0];
697             ab = a * a + b * b;
698             px = F1.X();
699             py = F1.Y();
700 
701             curve.quadraticform = [
702                 [c * c - ab * (px * px + py * py), c * a + ab * px, c * b + ab * py],
703                 [c * a + ab * px, -b * b, a * b],
704                 [c * b + ab * py, a * b, -a * a]
705             ];
706         }
707     };
708 
709     /** @ignore */
710     curve.X = function (phi, suspendUpdate) {
711         var a,
712             det,
713             beta = l.getAngle(),
714             d = Geometry.distPointLine(F1.coords.usrCoords, l.stdform),
715             A = l.point1.coords.usrCoords,
716             B = l.point2.coords.usrCoords,
717             M = F1.coords.usrCoords;
718 
719         // Handle the case if one of the two defining points of the line is an ideal point
720         if (A[0] === 0) {
721             A = [1, B[1] + l.stdform[2], B[2] - l.stdform[1]];
722         } else if (B[0] === 0) {
723             B = [1, A[1] + l.stdform[2], A[2] - l.stdform[1]];
724         }
725         det = (B[1] - A[1]) * (M[2] - A[2]) - (B[2] - A[2]) * (M[1] - A[1]) >= 0 ? 1 : -1;
726         a = (det * d) / (1 - Math.sin(phi));
727 
728         if (!suspendUpdate) {
729             polarForm(phi, suspendUpdate);
730         }
731 
732         return F1.X() + Math.cos(phi + beta) * a;
733     };
734 
735     /** @ignore */
736     curve.Y = function (phi, suspendUpdate) {
737         var a,
738             det,
739             beta = l.getAngle(),
740             d = Geometry.distPointLine(F1.coords.usrCoords, l.stdform),
741             A = l.point1.coords.usrCoords,
742             B = l.point2.coords.usrCoords,
743             M = F1.coords.usrCoords;
744 
745         // Handle the case if one of the two defining points of the line is an ideal point
746         if (A[0] === 0) {
747             A = [1, B[1] + l.stdform[2], B[2] - l.stdform[1]];
748         } else if (B[0] === 0) {
749             B = [1, A[1] + l.stdform[2], A[2] - l.stdform[1]];
750         }
751         det = (B[1] - A[1]) * (M[2] - A[2]) - (B[2] - A[2]) * (M[1] - A[1]) >= 0 ? 1 : -1;
752         a = (det * d) / (1 - Math.sin(phi));
753 
754         return F1.Y() + Math.sin(phi + beta) * a;
755     };
756 
757     curve.type = Const.OBJECT_TYPE_CONIC;
758     M.addChild(curve);
759 
760     if (Type.isPoint(F1)) {
761         F1.addChild(curve);
762         curve.inherits.push(F1);
763     }
764 
765     l.addChild(curve);
766     curve.setParents(parents);
767 
768     return curve;
769 };
770 
771 /**
772  *
773  * @class This element is used to provide a constructor for a generic conic section uniquely defined by five points or
774  * a conic defined by the coefficients of the equation
775  * <p><i>Ax<sup>2</sup>+ Bxy+Cy<sup>2</sup> + Dx + Ey + F = 0</i></p>.
776  * Then the parameters are as follows:
777  * <pre>
778  *     board.create('conic', [A, C, F, B/2, D/2, E/2]);
779  * </pre>
780  * @pseudo
781  * @name Conic
782  * @augments JXG.Curve
783  * @constructor
784  * @type JXG.Conic
785  * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
786  * @param {JXG.Point,Array_JXG.Point,Array_JXG.Point,Array_JXG.Point,Array_JXG.Point,Array} a,b,c,d,e Parent elements are five points.
787  * @param {Number_Number_Number_Number_Number_Number} a_00,a_11,a_22,a_01,a_02,a_12 6 numbers, i.e. A, C, F, B/2, D/2, E/2
788  * @example
789  * // Create a conic section through the points A, B, C, D, and E.
790  *  var A = board.create('point', [1,5]);
791  *  var B = board.create('point', [1,2]);
792  *  var C = board.create('point', [2,0]);
793  *  var D = board.create('point', [0,0]);
794  *  var E = board.create('point', [-1,5]);
795  *  var conic = board.create('conic',[A,B,C,D,E]);
796  * </pre><div class="jxgbox" id="JXG2d79bd6a-db9b-423c-9cba-2497f0b06320" style="width: 300px; height: 300px;"></div>
797  * <script type="text/javascript">
798  * (function(){
799  *   var glex1_board = JXG.JSXGraph.initBoard('JXG2d79bd6a-db9b-423c-9cba-2497f0b06320', {boundingbox:[-6,6,6,-6], keepaspectratio:true, showcopyright: false, shownavigation: false});
800  *   var A = glex1_board.create('point', [1,5]);
801  *   var B = glex1_board.create('point', [1,2]);
802  *   var C = glex1_board.create('point', [2,0]);
803  *   var D = glex1_board.create('point', [0,0]);
804  *   var E = glex1_board.create('point', [-1,5]);
805  *   var conic = glex1_board.create('conic',[A,B,C,D,E]);
806  * })();
807  * </script><pre>
808  *
809  * @example
810  * // Parameters: A, C, F, B/2, D/2, E/2
811  * var conic = board.create('conic', [1, 2, -4, 0, 0, 0]);
812  *
813  * </pre><div id="JXG8576a04a-52d8-4a7e-8d54-e32443910b97" class="jxgbox" style="width: 300px; height: 300px;"></div>
814  * <script type="text/javascript">
815  *     (function() {
816  *         var board = JXG.JSXGraph.initBoard('JXG8576a04a-52d8-4a7e-8d54-e32443910b97',
817  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
818  *     // Parameters: A, C, F, B/2, D/2, E/2
819  *     var conic = board.create('conic', [1, 2, -4, 0, 0, 0]);
820  *     })();
821  *
822  * </script><pre>
823  *
824  */
825 JXG.createConic = function (board, parents, attributes) {
826     var polarForm,
827         curve,
828         fitConic,
829         degconic,
830         sym,
831         eigen,
832         a,
833         b,
834         c,
835         c1,
836         c2,
837         i,
838         definingMat,
839         givenByPoints,
840         rotationMatrix = [
841             [1, 0, 0],
842             [0, 1, 0],
843             [0, 0, 1]
844         ],
845         M = [
846             [1, 0, 0],
847             [0, 1, 0],
848             [0, 0, 1]
849         ],
850         points = [],
851         p = [],
852         attr_point = Type.copyAttributes(attributes, board.options, "conic", "point"),
853         attr_center = Type.copyAttributes(attributes, board.options, "conic", "center"),
854         attr_curve = Type.copyAttributes(attributes, board.options, "conic");
855 
856     if (parents.length === 5) {
857         givenByPoints = true;
858     } else if (parents.length === 6) {
859         givenByPoints = false;
860     } else {
861         throw new Error(
862             "JSXGraph: Can't create generic Conic with " + parents.length + " parameters."
863         );
864     }
865 
866     if (givenByPoints) {
867         for (i = 0; i < 5; i++) {
868             // point i given by coordinates
869             if (parents[i].length > 1) {
870                 points[i] = board.create("point", parents[i], attr_point);
871                 // point i given by point
872             } else if (Type.isPoint(parents[i])) {
873                 points[i] = board.select(parents[i]);
874                 // given by function
875             } else if (Type.isFunction(parents[i]) && Type.isPoint(parents[i]())) {
876                 points[i] = parents[i]();
877                 // point i given by point name
878             } else if (Type.isString(parents[i])) {
879                 points[i] = board.select(parents[i]);
880             } else {
881                 throw new Error(
882                     "JSXGraph: Can't create Conic section with parent types '" +
883                         typeof parents[i] +
884                         "'." +
885                         "\nPossible parent types: [point,point,point,point,point], [a00,a11,a22,a01,a02,a12]"
886                 );
887             }
888         }
889     } else {
890         /* Usual notation (x,y,z):
891          *  [[A0,A3,A4],
892          *   [A3,A1,A5],
893          *   [A4,A5,A2]].
894          * Our notation (z,x,y):
895          *  [[A2, A4, A5],
896          *   [A4, A0, A3],
897          *   [A5, A3, A1]]
898          */
899         definingMat = [
900             [0, 0, 0],
901             [0, 0, 0],
902             [0, 0, 0]
903         ];
904         definingMat[0][0] = Type.isFunction(parents[2])
905             ? function () {
906                   return parents[2]();
907               }
908             : function () {
909                   return parents[2];
910               };
911         definingMat[0][1] = Type.isFunction(parents[4])
912             ? function () {
913                   return parents[4]();
914               }
915             : function () {
916                   return parents[4];
917               };
918         definingMat[0][2] = Type.isFunction(parents[5])
919             ? function () {
920                   return parents[5]();
921               }
922             : function () {
923                   return parents[5];
924               };
925         definingMat[1][1] = Type.isFunction(parents[0])
926             ? function () {
927                   return parents[0]();
928               }
929             : function () {
930                   return parents[0];
931               };
932         definingMat[1][2] = Type.isFunction(parents[3])
933             ? function () {
934                   return parents[3]();
935               }
936             : function () {
937                   return parents[3];
938               };
939         definingMat[2][2] = Type.isFunction(parents[1])
940             ? function () {
941                   return parents[1]();
942               }
943             : function () {
944                   return parents[1];
945               };
946     }
947 
948     // sym(A) = A + A^t . Manipulates A in place.
949     sym = function (A) {
950         var i, j;
951         for (i = 0; i < 3; i++) {
952             for (j = i; j < 3; j++) {
953                 A[i][j] += A[j][i];
954             }
955         }
956         for (i = 0; i < 3; i++) {
957             for (j = 0; j < i; j++) {
958                 A[i][j] = A[j][i];
959             }
960         }
961         return A;
962     };
963 
964     // degconic(v,w) = sym(v*w^t)
965     degconic = function (v, w) {
966         var i,
967             j,
968             mat = [
969                 [0, 0, 0],
970                 [0, 0, 0],
971                 [0, 0, 0]
972             ];
973 
974         for (i = 0; i < 3; i++) {
975             for (j = 0; j < 3; j++) {
976                 mat[i][j] = v[i] * w[j];
977             }
978         }
979 
980         return sym(mat);
981     };
982 
983     // (p^t*B*p)*A-(p^t*A*p)*B
984     fitConic = function (A, B, p) {
985         var i,
986             j,
987             pBp,
988             pAp,
989             Mv,
990             mat = [
991                 [0, 0, 0],
992                 [0, 0, 0],
993                 [0, 0, 0]
994             ];
995 
996         Mv = Mat.matVecMult(B, p);
997         pBp = Mat.innerProduct(p, Mv);
998         Mv = Mat.matVecMult(A, p);
999         pAp = Mat.innerProduct(p, Mv);
1000 
1001         for (i = 0; i < 3; i++) {
1002             for (j = 0; j < 3; j++) {
1003                 mat[i][j] = pBp * A[i][j] - pAp * B[i][j];
1004             }
1005         }
1006         return mat;
1007     };
1008 
1009     // Here, the defining functions for the curve are just dummy functions.
1010     // In polarForm there is a reference to curve.quadraticform.
1011     /**
1012      * @class
1013      * @ignore
1014      */
1015     curve = board.create(
1016         "curve",
1017         [
1018             function (x) {
1019                 return 0;
1020             },
1021             function (x) {
1022                 return 0;
1023             },
1024             0,
1025             2 * Math.PI
1026         ],
1027         attr_curve
1028     );
1029 
1030     /** @ignore */
1031     polarForm = function (phi, suspendUpdate) {
1032         var i, j, v; // len,;
1033 
1034         if (!suspendUpdate) {
1035             if (givenByPoints) {
1036                 // Copy the point coordinate vectors
1037                 for (i = 0; i < 5; i++) {
1038                     p[i] = points[i].coords.usrCoords;
1039                 }
1040 
1041                 // Compute the quadratic form
1042                 c1 = degconic(Mat.crossProduct(p[0], p[1]), Mat.crossProduct(p[2], p[3]));
1043                 c2 = degconic(Mat.crossProduct(p[0], p[2]), Mat.crossProduct(p[1], p[3]));
1044                 M = fitConic(c1, c2, p[4]);
1045             } else {
1046                 for (i = 0; i < 3; i++) {
1047                     for (j = i; j < 3; j++) {
1048                         M[i][j] = definingMat[i][j]();
1049                         if (j > i) {
1050                             M[j][i] = M[i][j];
1051                         }
1052                     }
1053                 }
1054             }
1055 
1056             // Here is the reference back to the curve.
1057             curve.quadraticform = M;
1058 
1059             // Compute Eigenvalues and Eigenvectors
1060             eigen = Numerics.Jacobi(M);
1061 
1062             // Scale the Eigenvalues such that the first Eigenvalue is positive
1063             if (eigen[0][0][0] < 0) {
1064                 eigen[0][0][0] *= -1;
1065                 eigen[0][1][1] *= -1;
1066                 eigen[0][2][2] *= -1;
1067             }
1068 
1069             // Normalize the Eigenvectors
1070             // for (i = 0; i < 3; i++) {
1071             //     // len = Mat.hypot(eigen[1][0][i], eigen[1][1][i], eigen[1][2][i])
1072             //     for (j = 0; j < 3; j++) {
1073             //         len += eigen[1][j][i] * eigen[1][j][i];
1074             //     }
1075             //     len = Math.sqrt(len);
1076             //     /*for (j = 0; j < 3; j++) {
1077             //             //eigen[1][j][i] /= len;
1078             //         }*/
1079             // }
1080             rotationMatrix = eigen[1];
1081             c = Math.sqrt(Math.abs(eigen[0][0][0]));
1082             a = Math.sqrt(Math.abs(eigen[0][1][1]));
1083             b = Math.sqrt(Math.abs(eigen[0][2][2]));
1084         }
1085 
1086         // The degenerate cases with eigen[0][i][i]==0 are not handled correct yet.
1087         if (eigen[0][1][1] <= 0.0 && eigen[0][2][2] <= 0.0) {
1088             v = Mat.matVecMult(rotationMatrix, [1 / c, Math.cos(phi) / a, Math.sin(phi) / b]);
1089         } else if (eigen[0][1][1] <= 0.0 && eigen[0][2][2] > 0.0) {
1090             v = Mat.matVecMult(rotationMatrix, [Math.cos(phi) / c, 1 / a, Math.sin(phi) / b]);
1091         } else if (eigen[0][2][2] < 0.0) {
1092             v = Mat.matVecMult(rotationMatrix, [Math.sin(phi) / c, Math.cos(phi) / a, 1 / b]);
1093         }
1094 
1095         if (Type.exists(v)) {
1096             // Normalize
1097             v[1] /= v[0];
1098             v[2] /= v[0];
1099             v[0] = 1.0;
1100         } else {
1101             v = [1, NaN, NaN];
1102         }
1103 
1104         return v;
1105     };
1106 
1107     /** @ignore */
1108     curve.X = function (phi, suspendUpdate) {
1109         return polarForm(phi, suspendUpdate)[1];
1110     };
1111 
1112     /** @ignore */
1113     curve.Y = function (phi, suspendUpdate) {
1114         return polarForm(phi, suspendUpdate)[2];
1115     };
1116 
1117     // Center coordinates see https://en.wikipedia.org/wiki/Matrix_representation_of_conic_sections
1118     curve.midpoint = board.create(
1119         "point",
1120         [
1121             function () {
1122                 var m = curve.quadraticform;
1123 
1124                 return [
1125                     m[1][1] * m[2][2] - m[1][2] * m[1][2],
1126                     m[1][2] * m[0][2] - m[2][2] * m[0][1],
1127                     m[0][1] * m[1][2] - m[1][1] * m[0][2]
1128                 ];
1129             }
1130         ],
1131         attr_center
1132     );
1133 
1134     curve.type = Const.OBJECT_TYPE_CONIC;
1135     curve.center = curve.midpoint;
1136     curve.subs = {
1137         center: curve.center
1138     };
1139     curve.inherits.push(curve.center);
1140     curve.inherits = curve.inherits.concat(points);
1141 
1142     if (givenByPoints) {
1143         for (i = 0; i < 5; i++) {
1144             if (Type.isPoint(points[i])) {
1145                 points[i].addChild(curve);
1146             }
1147         }
1148         curve.setParents(parents);
1149     }
1150     curve.addChild(curve.center);
1151 
1152     return curve;
1153 };
1154 
1155 JXG.registerElement("ellipse", JXG.createEllipse);
1156 JXG.registerElement("hyperbola", JXG.createHyperbola);
1157 JXG.registerElement("parabola", JXG.createParabola);
1158 JXG.registerElement("conic", JXG.createConic);
1159 
1160 // export default {
1161 //     createEllipse: JXG.createEllipse,
1162 //     createHyperbola: JXG.createHyperbola,
1163 //     createParabola: JXG.createParabola,
1164 //     createConic: JXG.createConic
1165 // };
1166