      1 /*
      2     Copyright 2010
      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: you can redistribute it and/or modify
     13     it under the terms of the GNU Lesser General Public License as published by
     14     the Free Software Foundation, either version 3 of the License, or
     15     (at your option) any later version.
     16
     17     JSXGraph is distributed in the hope that it will be useful,
     18     but WITHOUT ANY WARRANTY; without even the implied warranty of
     19     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     20     GNU Lesser General Public License for more details.
     21
     22     You should have received a copy of the GNU Lesser General Public License
     23     along with JSXGraph. If not, see <http://www.gnu.org/licenses/>.
     24 */
     25
     26 /**
     27  * @fileoverview In this file the conic sections defined.
     28  */
     29
     30 /**
     31  * @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
     32  * the length of the major axis.
     33  * @pseudo
     34  * @description
     35  * @name Ellipse
     36  * @augments JXG.Curve
     37  * @constructor
     38  * @type JXG.Curve
     39  * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
     40  * @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
     41  * numbers describing the coordinates of a point. In the latter case the point will be constructed automatically as a fixed invisible point.
     42  * @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
     43  * numbers describing the coordinates of a point. The third parameter is a number/function which defines the length of the major axis
     44  * Optional parameters four and five are numbers which define the curve length (e.g. start/end). Default values are -pi and pi.
     45  * @example
     46  * // Create an Ellipse by three points
     47  * var A = board.create('point', [-1,4]);
     48  * var B = board.create('point', [-1,-4]);
     49  * var C = board.create('point', [1,1]);
     50  * var el = board.create('ellipse',[A,B,C]);
     51  * </pre><div id="a4d7fb6f-8708-4e45-87f2-2379ae2bd2c0" style="width: 300px; height: 300px;"></div>
     52  * <script type="text/javascript">
     53  *   var glex1_board = JXG.JSXGraph.initBoard('a4d7fb6f-8708-4e45-87f2-2379ae2bd2c0', {boundingbox:[-6,6,6,-6], keepaspectratio:true, showcopyright: false, shownavigation: false});
     54  *   var A = glex1_board.create('point', [-1,4]);
     55  *   var B = glex1_board.create('point', [-1,-4]);
     56  *   var C = glex1_board.create('point', [1,1]);
     57  *   var el = glex1_board.create('ellipse',[A,B,C]);
     58  * </script><pre>
     59  */
     60 JXG.createEllipse = function(board, parents, attributes) {
     61     var F = [],  // focus 1 and focus 2
     62         C, majorAxis, i,
     63         rotationMatrix,
     64         attr_foci = JXG.copyAttributes(attributes, board.options, 'conic', 'foci'),
     65         attr_curve = JXG.copyAttributes(attributes, board.options, 'conic');
     66
     67     // The foci and the third point are either points or coordinate arrays.
     68     for (i = 0; i < 2; i++) {
     69         if (parents[i].length > 1) { // focus i given by coordinates
     70             F[i] = board.create('point', parents[i], attr_foci);
     71         } else if (JXG.isPoint(parents[i])) { // focus i given by point
     72             F[i] = JXG.getReference(board,parents[i]);
     73         } else if ((typeof parents[i] == 'function') && (parents[i]().elementClass === JXG.OBJECT_CLASS_POINT)) {  // given by function
     74             F[i] = parents[i]();
     75         } else if (JXG.isString(parents[i])) { // focus i given by point name
     76             F[i] = JXG.getReference(board,parents[i]);
     77         } else
     78             throw new Error("JSXGraph: Can't create Ellipse with parent types '" +
     79                             (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." +
     80                             "\nPossible parent types: [point,point,point], [point,point,number|function]");
     81     }
     82     if (JXG.isNumber(parents[2])) { // length of major axis
     83         majorAxis = JXG.createFunction(parents[2],board);
     84     } else if ((typeof parents[2] == 'function') && (JXG.isNumber(parents[2]()))){
     85         majorAxis = parents[2];
     86     } else {
     87         if (JXG.isPoint(parents[2])) {                                               // point on ellipse
     88             C = JXG.getReference(board,parents[2]);
     89         } else if (parents[2].length>1) {                                            // point on ellipse given by coordinates
     90             C = board.create('point', parents[2], attr_foci);
     91         } else if ((typeof parents[2] == 'function') && (parents[2]().elementClass == JXG.OBJECT_CLASS_POINT)) {  // given by function
     92             C = parents[2]();
     93         } else if (JXG.isString(parents[2])) {                                      // focus i given by point name
     94             C = JXG.getReference(board,parents[2]);
     95         } else {
     96             throw new Error("JSXGraph: Can't create Ellipse with parent types '" +
     97                             (typeof parents[0]) + "' and '" + (typeof parents[1]) + "' and '" + (typeof parents[2]) +"'." +
     98                             "\nPossible parent types: [point,point,point], [point,point,number|function]");
     99         }
    100         majorAxis = function(){ return C.Dist(F[0])+C.Dist(F[1]);};
    101     }
    102
    103     if (typeof parents[4]=='undefined') parents[4] = 1.0001*Math.PI;   // to
    104     if (typeof parents[3]=='undefined') parents[3] = -1.0001*Math.PI;  // from
    105
    106     var M = board.create('point', [
    107                 function(){return (F[0].X()+F[1].X())*0.5;},
    108                 function(){return (F[0].Y()+F[1].Y())*0.5;}
    109             ], attr_foci);
    110
    111     var transformFunc = function() {
    112             var ax = F[0].X(),
    113                 ay = F[0].Y(),
    114                 bx = F[1].X(),
    115                 by = F[1].Y(),
    116                 beta, co, si;
    117
    118             // Rotate by the slope of the line [F[0],F[1]]
    119             var sgn = (bx-ax>0)?1:-1;
    120             if (Math.abs(bx-ax)>0.0000001) {
    121                 beta = Math.atan2(by-ay,bx-ax)+ ((sgn<0)?Math.PI:0);
    122             } else {
    123                 beta = ((by-ay>0)?0.5:-0.5)*Math.PI;
    124             }
    125             co = Math.cos(beta);
    126             si = Math.sin(beta);
    127             var m = [
    128                         [1,     0,  0],
    129                         [M.X(),co,-si],
    130                         [M.Y(),si, co]
    131                     ];
    132             return m;
    133         };
    134
    135     var curve = board.create('curve', [function(x) {return 0;}, function(x) {return 0;}, parents[3], parents[4]], attr_curve);
    136
    137     var polarForm = function(phi,suspendUpdate) {
    138                 var a = majorAxis()*0.5,
    139                     aa = a*a,
    140                     e = F[1].Dist(F[0])*0.5,
    141                     bb = aa-e*e,
    142                     b = Math.sqrt(bb),
    143                     transformMat = [[1,0,0],[0,1,0],[0,0,1]],
    144                     mx, my;
    145
    146                 if (!suspendUpdate) {
    147                     rotationMatrix = transformFunc();
    148                     mx = M.X();
    149                     my = M.Y();
    150                     transformMat[0][0] = rotationMatrix[0][0];
    151                     transformMat[0][1] = 0.0;
    152                     transformMat[0][2] = 0.0;
    153                     transformMat[1][0] = mx*(1-rotationMatrix[1][1])+my*rotationMatrix[1][2];
    154                     transformMat[1][1] = rotationMatrix[1][1];
    155                     transformMat[1][2] = rotationMatrix[2][1];
    156                     transformMat[2][0] = my*(1-rotationMatrix[1][1])-mx*rotationMatrix[1][2];
    157                     transformMat[2][1] = rotationMatrix[1][2];
    158                     transformMat[2][2] = rotationMatrix[2][2];
    159                     curve.quadraticform =
    160                         JXG.Math.matMatMult(JXG.Math.transpose(transformMat),
    161                         JXG.Math.matMatMult(
    162                             [
    163                                 [-1+mx*mx/(a*a)+my*my/bb, -mx/aa , -mx/bb],
    164                                 [-mx/aa                 ,   1/aa ,  0    ],
    165                                 [-my/bb                 ,      0 ,  1/bb ]
    166                             ],
    167                         transformMat));
    168                 }
    169                 return JXG.Math.matVecMult(rotationMatrix,[1,a*Math.cos(phi),b*Math.sin(phi)]);
    170         };
    171
    172     curve.X = function(phi,suspendUpdate) {return polarForm(phi,suspendUpdate)[1];};
    173     curve.Y = function(phi,suspendUpdate) {return polarForm(phi,suspendUpdate)[2];};
    174     curve.midpoint = M;
    175     curve.type = JXG.OBJECT_TYPE_CONIC;
    176     return curve;
    177 };
    178
    179 /**
    180  * @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
    181  * the length of the major axis.
    182  * @pseudo
    183  * @description
    184  * @name Hyperbola
    185  * @augments JXG.Curve
    186  * @constructor
    187  * @type JXG.Curve
    188  * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
    189  * @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
    190  * numbers describing the coordinates of a point. In the latter case the point will be constructed automatically as a fixed invisible point.
    191  * @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
    192  * numbers describing the coordinates of a point. The third parameter is a number/function which defines the length of the major axis
    193  * Optional parameters four and five are numbers which define the curve length (e.g. start/end). Default values are -pi and pi.
    194  * @example
    195  * // Create an Hyperbola by three points
    196  * var A = board.create('point', [-1,4]);
    197  * var B = board.create('point', [-1,-4]);
    198  * var C = board.create('point', [1,1]);
    199  * var el = board.create('hyperbola',[A,B,C]);
    200  * </pre><div id="cf99049d-a3fe-407f-b936-27d76550f8c4" style="width: 300px; height: 300px;"></div>
    201  * <script type="text/javascript">
    202  *   var glex1_board = JXG.JSXGraph.initBoard('cf99049d-a3fe-407f-b936-27d76550f8c4', {boundingbox:[-6,6,6,-6], keepaspectratio:true, showcopyright: false, shownavigation: false});
    203  *   var A = glex1_board.create('point', [-1,4]);
    204  *   var B = glex1_board.create('point', [-1,-4]);
    205  *   var C = glex1_board.create('point', [1,1]);
    206  *   var el = glex1_board.create('hyperbola',[A,B,C]);
    207  * </script><pre>
    208  */
    209 JXG.createHyperbola = function(board, parents, attributes) {
    210     var F = [],  // focus 1 and focus 2
    211         C,
    212         majorAxis,
    213         i,
    214         rotationMatrix,
    215         attr_foci = JXG.copyAttributes(attributes, board.options, 'conic', 'foci'),
    216         attr_curve = JXG.copyAttributes(attributes, board.options, 'conic');
    217
    218     // The foci and the third point are either points or coordinate arrays.
    219     for (i=0;i<2;i++) {
    220         if (parents[i].length>1) { // focus i given by coordinates
    221             F[i] = board.create('point', parents[i], attr_focu);
    222         } else if (JXG.isPoint(parents[i])) { // focus i given by point
    223             F[i] = JXG.getReference(board,parents[i]);
    224         } else if ((typeof parents[i] == 'function') && (parents[i]().elementClass == JXG.OBJECT_CLASS_POINT)) {  // given by function
    225             F[i] = parents[i]();
    226         } else if (JXG.isString(parents[i])) { // focus i given by point name
    227             F[i] = JXG.getReference(board,parents[i]);
    228         } else
    229             throw new Error("JSXGraph: Can't create Hyperbola with parent types '" +
    230                             (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." +
    231                             "\nPossible parent types: [point,point,point], [point,point,number|function]");
    232     }
    233     if (JXG.isNumber(parents[2])) { // length of major axis
    234         majorAxis = JXG.createFunction(parents[2],board);
    235     } else if ((typeof parents[2] == 'function') && (JXG.isNumber(parents[2]()))){
    236         majorAxis = parents[2];
    237     } else {
    238         if (JXG.isPoint(parents[2])) {                                               // point on ellipse
    239             C = JXG.getReference(board,parents[2]);
    240         } else if (parents[2].length>1) {                                            // point on ellipse given by coordinates
    241             C = board.create('point', parents[2], attr_foci);
    242         } else if ((typeof parents[2] == 'function') && (parents[2]().elementClass == JXG.OBJECT_CLASS_POINT)) {  // given by function
    243             C = parents[2]();
    244         } else if (JXG.isString(parents[2])) {                                      // focus i given by point name
    245             C = JXG.getReference(board,parents[2]);
    246         } else {
    247             throw new Error("JSXGraph: Can't create Hyperbola with parent types '" +
    248                             (typeof parents[0]) + "' and '" + (typeof parents[1]) + "' and '" + (typeof parents[2]) +"'." +
    249                             "\nPossible parent types: [point,point,point], [point,point,number|function]");
    250         }
    251         majorAxis = function(){ return C.Dist(F[0])-C.Dist(F[1]);};
    252     }
    253
    254     if (typeof parents[4]=='undefined') parents[4] = 1.0001*Math.PI;   // to
    255     if (typeof parents[3]=='undefined') parents[3] = -1.0001*Math.PI;  // from
    256
    257     var M = board.create('point', [
    258                 function(){return (F[0].X()+F[1].X())*0.5;},
    259                 function(){return (F[0].Y()+F[1].Y())*0.5;}
    260             ], attr_foci);
    261
    262     var transformFunc = function() {
    263             var ax = F[0].X(),
    264                 ay = F[0].Y(),
    265                 bx = F[1].X(),
    266                 by = F[1].Y(),
    267                 beta;
    268
    269             // Rotate by the slope of the line [F[0],F[1]]
    270             var sgn = (bx-ax>0)?1:-1;
    271             if (Math.abs(bx-ax)>0.0000001) {
    272                 beta = Math.atan2(by-ay,bx-ax)+ ((sgn<0)?Math.PI:0);
    273             } else {
    274                 beta = ((by-ay>0)?0.5:-0.5)*Math.PI;
    275             }
    276             var m = [
    277                         [1,    0,             0],
    278                         [M.X(),Math.cos(beta),-Math.sin(beta)],
    279                         [M.Y(),Math.sin(beta), Math.cos(beta)]
    280                     ];
    281             return m;
    282         };
    283
    284     var curve = board.create('curve',[function(x) {return 0;},function(x) {return 0;},parents[3],parents[4]], attr_curve);
    285     /*
    286     * Hyperbola is defined by (a*sec(t),b*tan(t)) and sec(t) = 1/cos(t)
    287     */
    288     var polarForm = function(phi,suspendUpdate) {
    289                 var a = majorAxis()*0.5,
    290                     aa = a*a,
    291                     e = F[1].Dist(F[0])*0.5,
    292                     b = Math.sqrt(-a*a+e*e),
    293                     bb = b*b,
    294                     transformMat = [[1,0,0],[0,1,0],[0,0,1]],
    295                     mx, my;
    296
    297                 if (!suspendUpdate) {
    298                     rotationMatrix = transformFunc();
    299                     mx = M.X();
    300                     my = M.Y();
    301                     transformMat[0][0] = rotationMatrix[0][0];
    302                     transformMat[0][1] = 0.0;
    303                     transformMat[0][2] = 0.0;
    304                     transformMat[1][0] = mx*(1-rotationMatrix[1][1])+my*rotationMatrix[1][2];
    305                     transformMat[1][1] = rotationMatrix[1][1];
    306                     transformMat[1][2] = rotationMatrix[2][1];
    307                     transformMat[2][0] = my*(1-rotationMatrix[1][1])-mx*rotationMatrix[1][2];
    308                     transformMat[2][1] = rotationMatrix[1][2];
    309                     transformMat[2][2] = rotationMatrix[2][2];
    310                     curve.quadraticform =
    311                         JXG.Math.matMatMult(JXG.Math.transpose(transformMat),
    312                         JXG.Math.matMatMult(
    313                             [
    314                                 [-1+mx*mx/aa+my*my/bb, -mx/aa , my/bb],
    315                                 [-mx/aa              ,    1/aa,  0   ],
    316                                 [my/bb               ,      0 , -1/bb]
    317                             ],
    318                         transformMat));
    319                 }
    320                 return JXG.Math.matVecMult(rotationMatrix,[1,a/Math.cos(phi),b*Math.tan(phi)]);
    321         };
    322
    323     curve.X = function(phi,suspendUpdate) {return polarForm(phi,suspendUpdate)[1];};
    324     curve.Y = function(phi,suspendUpdate) {return polarForm(phi,suspendUpdate)[2];};
    325     curve.midpoint = M;
    326     curve.type = JXG.OBJECT_TYPE_CONIC;
    327     return curve;
    328 };
    329
    330 /**
    331  * @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).
    332  * @pseudo
    333  * @description
    334  * @name Parabola
    335  * @augments JXG.Curve
    336  * @constructor
    337  * @type JXG.Curve
    338  * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
    339  * @param {JXG.Point,array_JXG.Line} point,line Parent elements are a point and a line.
    340  * Optional parameters three and four are numbers which define the curve length (e.g. start/end). Default values are -pi and pi.
    341  * @example
    342  * // Create a parabola by a point C and a line l.
    343  * var A = board.create('point', [-1,4]);
    344  * var B = board.create('point', [-1,-4]);
    345  * var l = board.create('line', [A,B]);
    346  * var C = board.create('point', [1,1]);
    347  * var el = board.create('parabola',[C,l]);
    348  * </pre><div id="524d1aae-217d-44d4-ac58-a19c7ab1de36" style="width: 300px; height: 300px;"></div>
    349  * <script type="text/javascript">
    350  *   var glex1_board = JXG.JSXGraph.initBoard('524d1aae-217d-44d4-ac58-a19c7ab1de36', {boundingbox:[-6,6,6,-6], keepaspectratio:true, showcopyright: false, shownavigation: false});
    351  *   var A = glex1_board.create('point', [-1,4]);
    352  *   var B = glex1_board.create('point', [-1,-4]);
    353  *   var l = glex1_board.create('line', [A,B]);
    354  *   var C = glex1_board.create('point', [1,1]);
    355  *   var el = glex1_board.create('parabola',[C,l]);
    356  * </script><pre>
    357  */
    358 JXG.createParabola = function(board, parents, attributes) {
    359     var F1 = parents[0], // focus
    360         l = parents[1],  // directrix
    361         rotationMatrix,
    362         attr_foci = JXG.copyAttributes(attributes, board.options, 'conic', 'foci'),
    363         attr_curve = JXG.copyAttributes(attributes, board.options, 'conic');
    364
    365     if (parents[0].length>1) { // focus 1 given by coordinates
    366         F1 = board.create('point', parents[0], attr_foci);
    367     } else if (JXG.isPoint(parents[0])) { // focus i given by point
    368         F1 = JXG.getReference(board,parents[0]);
    369     } else if ((typeof parents[0] == 'function') && (parents[0]().elementClass == JXG.OBJECT_CLASS_POINT)) {  // given by function
    370         F1 = parents[0]();
    371     } else if (JXG.isString(parents[0])) { // focus i given by point name
    372         F1 = JXG.getReference(board,parents[0]);
    373     } else
    374         throw new Error("JSXGraph: Can't create Parabola with parent types '" +
    375                         (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." +
    376                         "\nPossible parent types: [point,line]");
    377
    378     if (typeof parents[3]=='undefined') parents[3] = 10.0;   // to
    379     if (typeof parents[2]=='undefined') parents[2] = -10.0;  // from
    380
    381     var M = board.create('point', [
    382                 function() {
    383                     var v = [0,l.stdform[1],l.stdform[2]];
    384                     v = JXG.Math.crossProduct(v,F1.coords.usrCoords);
    385                     return JXG.Math.Geometry.meetLineLine(v,l.stdform,0,board).usrCoords;
    386                 }
    387             ], attr_foci);
    388
    389     var transformFunc = function() {
    390             var beta = Math.atan(l.getSlope()),
    391                 x = (M.X()+F1.X())*0.5,
    392                 y = (M.Y()+F1.Y())*0.5;
    393             beta += (F1.Y()-M.Y()<0 || (F1.Y()==M.Y() && F1.X()>M.X()) ) ? Math.PI : 0;
    394
    395             // Rotate by the slope of the line l (Leitlinie = directrix)
    396             var m = [
    397                         [1,    0,             0],
    398                         [x*(1-Math.cos(beta))+y*Math.sin(beta),Math.cos(beta),-Math.sin(beta)],
    399                         [y*(1-Math.cos(beta))-x*Math.sin(beta),Math.sin(beta), Math.cos(beta)]
    400                     ];
    401             return m;
    402         };
    403
    404     var curve = board.create('curve',[function(x) {return 0;},function(x) {return 0;},parents[2],parents[3]], attr_curve);
    405
    406     var polarForm = function(t,suspendUpdate) {
    407                 var e = M.Dist(F1)*0.5,
    408                     transformMat = [[1,0,0],[0,1,0],[0,0,1]],
    409                     a = (M.X()+F1.X())*0.5,
    410                     b = (M.Y()+F1.Y())*0.5;
    411
    412                 if (!suspendUpdate) {
    413                     rotationMatrix = transformFunc();
    414                     transformMat[0][0] = rotationMatrix[0][0];
    415                     transformMat[0][1] = 0.0;
    416                     transformMat[0][2] = 0.0;
    417                     transformMat[1][0] = a*(1-rotationMatrix[1][1])+b*rotationMatrix[1][2];
    418                     transformMat[1][1] = rotationMatrix[1][1];
    419                     transformMat[1][2] = rotationMatrix[2][1];
    420                     transformMat[2][0] = b*(1-rotationMatrix[1][1])-a*rotationMatrix[1][2];
    421                     transformMat[2][1] = rotationMatrix[1][2];
    422                     transformMat[2][2] = rotationMatrix[2][2];
    423                     curve.quadraticform =
    424                         JXG.Math.matMatMult(JXG.Math.transpose(transformMat),
    425                         JXG.Math.matMatMult(
    426                             [
    427                                 [-b*4*e-a*a, a, 2*e],
    428                                 [a,       -1, 0],
    429                                 [2*e,      0, 0]
    430                             ],
    431                         transformMat));
    432                 }
    433                 return JXG.Math.matVecMult(rotationMatrix,[1,t+a,t*t/(e*4)+b]);
    434         };
    435     curve.X = function(phi,suspendUpdate) {return polarForm(phi,suspendUpdate)[1];};
    436     curve.Y = function(phi,suspendUpdate) {return polarForm(phi,suspendUpdate)[2];};
    437     curve.type = JXG.OBJECT_TYPE_CONIC;
    438     return curve;
    439 };
    440
    441 /**
    442  *
    443  * @class This element is used to provide a constructor for a generic conic section uniquely defined by five points.
    444  * @pseudo
    445  * @description
    446  * @name Conic
    447  * @augments JXG.Curve
    448  * @constructor
    449  * @type JXG.Conic
    450  * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
    451  * @param {JXG.Point,array_JXG.Point,array_JXG.Point,array_JXG.Point,array_JXG.Point,array_} point,point,point,point,point Parent elements are five points.
    452  * @param {number_number_number_number_number_number} 6 numbers (a_00,a_11,a_22,a_01,a_12,a_22)
    453  * @example
    454  * // Create a conic section through the points A, B, C, D, and E.
    455  *  var A = board.create('point', [1,5]);
    456  *  var B = board.create('point', [1,2]);
    457  *  var C = board.create('point', [2,0]);
    458  *  var D = board.create('point', [0,0]);
    459  *  var E = board.create('point', [-1,5]);
    460  *  var conic = board.create('conic',[A,B,C,D,E]);
    461  * </pre><div id="2d79bd6a-db9b-423c-9cba-2497f0b06320" style="width: 300px; height: 300px;"></div>
    462  * <script type="text/javascript">
    463  *   var glex1_board = JXG.JSXGraph.initBoard('2d79bd6a-db9b-423c-9cba-2497f0b06320', {boundingbox:[-6,6,6,-6], keepaspectratio:true, showcopyright: false, shownavigation: false});
    464  *   var A = glex1_board.create('point', [1,5]);
    465  *   var B = glex1_board.create('point', [1,2]);
    466  *   var C = glex1_board.create('point', [2,0]);
    467  *   var D = glex1_board.create('point', [0,0]);
    468  *   var E = glex1_board.create('point', [-1,5]);
    469  *   var conic = glex1_board.create('conic',[A,B,C,D,E]);
    470  * </script><pre>
    471  */
    472 JXG.createConic = function(board, parents, attributes) {
    473     var rotationMatrix = [[1,0,0],[0,1,0],[0,0,1]],
    474         eigen, a, b, c, M = [[1,0,0],[0,1,0],[0,0,1]],
    475         c1, c2, points = [], i, definingMat,
    476         givenByPoints,
    477         p = [],
    478         attr_foci = JXG.copyAttributes(attributes, board.options, 'conic', 'foci'),
    479         attr_curve = JXG.copyAttributes(attributes, board.options, 'conic');
    480
    481     if (parents.length==5) {
    482         givenByPoints = true;
    483     } else if (parents.length==6) {
    484         givenByPoints = false;
    485     } else
    486         throw new Error("JSXGraph: Can't create generic Conic with " + parent.length + " parameters.");
    487
    488     if (givenByPoints) {
    489         for (i=0;i<5;i++) {
    490             if (parents[i].length>1) { // point i given by coordinates
    491                 points[i] = board.create('point', parents[i], attr_foci);
    492             } else if (JXG.isPoint(parents[i])) { // point i given by point
    493                 points[i] = JXG.getReference(board,parents[i]);
    494             } else if ((typeof parents[i] == 'function') && (parents[i]().elementClass == JXG.OBJECT_CLASS_POINT)) {  // given by function
    495                 points[i] = parents[i]();
    496             } else if (JXG.isString(parents[i])) { // point i given by point name
    497                 points[i] = JXG.getReference(board,parents[i]);
    498             } else
    499                 throw new Error("JSXGraph: Can't create Conic section with parent types '" + (typeof parents[i]) + "'." +
    500                                 "\nPossible parent types: [point,point,point,point,point], [a00,a11,a22,a01,a02,a12]");
    501         }
    502     } else {
    503         /* Usual notation (x,y,z):
    504          *  [[A0,A3,A4],
    505          *   [A3,A1,A5],
    506          *   [A4,A5,A2]].
    507          * Our notation (z,x,y):
    508          *  [[-A2   , A4*2.0, A5*0.5],
    509          *   [A4*2.0,    -A0, A3*0.5],
    510          *   [A5*0.5, A3*0.5,    -A1]]
    511          * New: (z,x,y):
    512          *  [[A2, A4, A5],
    513          *   [A4, A0, A3],
    514          *   [A5, A3, A1]]
    515         */
    516         definingMat = [[0,0,0],[0,0,0],[0,0,0]];
    517         definingMat[0][0] = (JXG.isFunction(parents[2])) ? function(){ return    parents[2]();} : function(){ return    parents[2];};
    518         definingMat[0][1] = (JXG.isFunction(parents[4])) ? function(){ return    parents[4]();} : function(){ return    parents[4];};
    519         definingMat[0][2] = (JXG.isFunction(parents[5])) ? function(){ return    parents[5]();} : function(){ return    parents[5];};
    520         definingMat[1][1] = (JXG.isFunction(parents[0])) ? function(){ return    parents[0]();} : function(){ return    parents[0];};
    521         definingMat[1][2] = (JXG.isFunction(parents[3])) ? function(){ return    parents[3]();} : function(){ return    parents[3];};
    522         definingMat[2][2] = (JXG.isFunction(parents[1])) ? function(){ return    parents[1]();} : function(){ return    parents[1];};
    523     }
    524
    525     // sym(A) = A + A^t . Manipulates A in place.
    526     var sym = function(A) {
    527         var i, j;
    528         for (i=0;i<3;i++) {
    529             for (j=i;j<3;j++) {
    530                 A[i][j] += A[j][i];
    531             }
    532         }
    533         for (i=0;i<3;i++) {
    534             for (j=0;j<i;j++) {
    535                 A[i][j] = A[j][i];
    536             }
    537         }
    538         return A;
    539     };
    540
    541     // degconic(v,w) = sym(v*w^t)
    542     var degconic = function(v,w) {
    543         var i, j, mat = [[0,0,0],[0,0,0],[0,0,0]];
    544         for (i=0;i<3;i++) {
    545             for (j=0;j<3;j++) {
    546                 mat[i][j] = v[i]*w[j];
    547             }
    548         }
    549         return sym(mat);
    550     };
    551
    552     // (p^t*B*p)*A-(p^t*A*p)*B
    553     var fitConic = function(A,B,p)  {
    554         var pBp, pAp, Mv, mat = [[0,0,0],[0,0,0],[0,0,0]], i, j;
    555         Mv = JXG.Math.matVecMult(B,p);
    556         pBp = JXG.Math.innerProduct(p,Mv);
    557         Mv = JXG.Math.matVecMult(A,p);
    558         pAp = JXG.Math.innerProduct(p,Mv);
    559         for (i=0;i<3;i++) {
    560             for (j=0;j<3;j++) {
    561                 mat[i][j] = pBp*A[i][j]-pAp*B[i][j];
    562             }
    563         }
    564         return mat;
    565     };
    566
    567     // Here, the defining functions for the curve are just dummy functions.
    568     // In polarForm there is a reference to curve.quadraticform.
    569     var curve = board.create('curve',[function(x) {return 0;},function(x) {return 0;},0,2*Math.PI], attr_curve);
    570
    571     var polarForm = function(phi,suspendUpdate) {
    572         var i, j, len, v;
    573         if (!suspendUpdate) {
    574             if (givenByPoints) {
    575                 // Copy the point coordinate vectors
    576                 for (i=0;i<5;i++) {
    577                     p[i] = points[i].coords.usrCoords;
    578                 }
    579                 // Compute the quadratic form
    580                 c1 = degconic(JXG.Math.crossProduct(p[0],p[1]),JXG.Math.crossProduct(p[2],p[3]));
    581                 c2 = degconic(JXG.Math.crossProduct(p[0],p[2]),JXG.Math.crossProduct(p[1],p[3]));
    582                 M = fitConic(c1,c2,p[4]);
    583             } else {
    584                 for (i=0;i<3;i++) {
    585                     for (j=i;j<3;j++) {
    586                         M[i][j] = definingMat[i][j]();
    587                         if (j>i) M[j][i] = M[i][j];
    588                     }
    589                 }
    590             }
    591             curve.quadraticform = M;    // Here is the reference back to the curve.
    592
    593             // Compute Eigenvalues and Eigenvectors
    594             eigen = JXG.Math.Numerics.Jacobi(M);
    595             // Scale the Eigenvalues such that the first Eigenvalue is positive
    596             if (eigen[0][0][0]<0) {
    597                 eigen[0][0][0] *= (-1);
    598                 eigen[0][1][1] *= (-1);
    599                 eigen[0][2][2] *= (-1);
    600             }
    601             // Normalize the Eigenvectors
    602             for (i=0;i<3;i++) {
    603                 len = 0.0;
    604                 for (j=0;j<3;j++) {
    605                     len += eigen[1][j][i]*eigen[1][j][i];
    606                 }
    607                 len = Math.sqrt(len);
    608                 for (j=0;j<3;j++) {
    609                     //eigen[1][j][i] /= len;
    610                 }
    611             }
    612             rotationMatrix = eigen[1];
    613             c = Math.sqrt(Math.abs(eigen[0][0][0]));
    614             a = Math.sqrt(Math.abs(eigen[0][1][1]));
    615             b = Math.sqrt(Math.abs(eigen[0][2][2]));
    616
    617         }
    618         // The degenerate cases with eigen[0][i][i]==0 are not handled correct yet.
    619         if (eigen[0][1][1]<=0.0 && eigen[0][2][2]<=0.0) {
    620             v = JXG.Math.matVecMult(rotationMatrix,[1/c,Math.cos(phi)/a,Math.sin(phi)/b]);
    621         } else if (eigen[0][1][1]<=0.0 && eigen[0][2][2]>0.0) {
    622             v = JXG.Math.matVecMult(rotationMatrix,[Math.cos(phi)/c,1/a,Math.sin(phi)/b]);
    623         } else if (eigen[0][2][2]<0.0) {
    624             v = JXG.Math.matVecMult(rotationMatrix,[Math.sin(phi)/c,Math.cos(phi)/a,1/b]);
    625         }
    626         // Normalize
    627         v[1] /= v[0];
    628         v[2] /= v[0];
    629         v[0] = 1.0;
    630         return v;
    631     };
    632
    633     curve.X = function(phi,suspendUpdate) {return polarForm(phi,suspendUpdate)[1];};
    634     curve.Y = function(phi,suspendUpdate) {return polarForm(phi,suspendUpdate)[2];};
    635
    636
    637     // Center coordinates see http://en.wikipedia.org/wiki/Matrix_representation_of_conic_sections
    638     curve.midpoint = board.create('point',
    639         [
    640         function(){
    641             var m = curve.quadraticform;
    642             return [
    643                 m[1][1]*m[2][2]-m[1][2]*m[1][2],
    644                 m[1][2]*m[0][2]-m[2][2]*m[0][1],
    645                 m[0][1]*m[1][2]-m[1][1]*m[0][2]
    646             ];
    647         }
    648         ], attr_foci);
    649
    650     curve.type = JXG.OBJECT_TYPE_CONIC;
    651     return curve;
    652 };
    653
    654 JXG.JSXGraph.registerElement('ellipse', JXG.createEllipse);
    655 JXG.JSXGraph.registerElement('hyperbola', JXG.createHyperbola);
    656 JXG.JSXGraph.registerElement('parabola', JXG.createParabola);
    657 JXG.JSXGraph.registerElement('conic', JXG.createConic);
    658
    659