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