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