      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 (Type.evaluate(this.visProp.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