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