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