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