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