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