1 /*
  2     Copyright 2008-2021
  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="JXGa4d7fb6f-8708-4e45-87f2-2379ae2bd2c0" style="width: 300px; height: 300px;"></div>
 80      * <script type="text/javascript">
 81      *   var glex1_board = JXG.JSXGraph.initBoard('JXGa4d7fb6f-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="JXGcf99049d-a3fe-407f-b936-27d76550f8c4" style="width: 300px; height: 300px;"></div>
298      * <script type="text/javascript">
299      *   var glex1_board = JXG.JSXGraph.initBoard('JXGcf99049d-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 or a pair of coordinates.
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="JXG524d1aae-217d-44d4-ac58-a19c7ab1de36" style="width: 300px; height: 300px;"></div>
484      * <script type="text/javascript">
485      *   var glex1_board = JXG.JSXGraph.initBoard('JXG524d1aae-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      * @example
494      * var par = board.create('parabola',[[3.25, 0], [[0.25, 1],[0.25, 0]]]);
495      *
496      * </pre><div id="JXG09252542-b77a-4990-a109-66ffb649a472" class="jxgbox" style="width: 300px; height: 300px;"></div>
497      * <script type="text/javascript">
498      *     (function() {
499      *         var board = JXG.JSXGraph.initBoard('JXG09252542-b77a-4990-a109-66ffb649a472',
500      *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
501      *     var par = board.create('parabola',[[3.25, 0], [[0.25, 1],[0.25, 0]]]);
502      *
503      *     })();
504      *
505      * </script><pre>
506      *
507      */
508     JXG.createParabola = function (board, parents, attributes) {
509         var polarForm, curve, M, i,
510             // focus
511             F1 = parents[0],
512             // directrix
513             l = parents[1],
514             attr_foci = Type.copyAttributes(attributes, board.options, 'conic', 'foci'),
515             attr_center = Type.copyAttributes(attributes, board.options, 'conic', 'center'),
516             attr_curve = Type.copyAttributes(attributes, board.options, 'conic'),
517             attr_line;
518 
519         // focus 1 given by coordinates
520         if (parents[0].length > 1) {
521             F1 = board.create('point', parents[0], attr_foci);
522         // focus 1 given by point
523         } else if (Type.isPoint(parents[0])) {
524             F1 = board.select(parents[0]);
525         // given by function
526         } else if (Type.isFunction(parents[0]) && Type.isPoint(parents[0]()) ) {
527             F1 = parents[0]();
528         // focus 1 given by point name
529         } else if (Type.isString(parents[0])) {
530             F1 = board.select(parents[0]);
531         } else {
532             throw new Error("JSXGraph: Can't create Parabola with parent types '" +
533                 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." +
534                 "\nPossible parent types: [point,line]");
535         }
536 
537         // Create line if given as array of two points.
538         if (Type.isArray(l) && l.length == 2) {
539             attr_line = Type.copyAttributes(attributes, board.options, 'conic', 'line');
540             l = board.create('line', l, attr_line);
541         }
542 
543         // to
544         if (!Type.exists(parents[3])) {
545             parents[3] = 2 * Math.PI;
546         }
547 
548         // from
549         if (!Type.exists(parents[2])) {
550             parents[2] = 0;
551         }
552 
553         M = board.create('point', [
554             function () {
555                 /*
556                 var v = [0, l.stdform[1], l.stdform[2]];
557                 v = Mat.crossProduct(v, F1.coords.usrCoords);
558                 return Geometry.meetLineLine(v, l.stdform, 0, board).usrCoords;
559                 */
560                 return Geometry.projectPointToLine(F1, l, board).usrCoords;
561             }
562         ], attr_center);
563 
564         /** @ignore */
565         curve = board.create('curve', [
566             function (x) {
567                 return 0;
568             },
569             function (x) {
570                 return 0;
571             }, parents[2], parents[3]], attr_curve);
572 
573         curve.midpoint = curve.center = M;
574         curve.subs = {
575             center: curve.center
576         };
577         curve.inherits.push(curve.center);
578 
579         /** @ignore */
580         polarForm = function (t, suspendUpdate) {
581             var a, b, c, ab, px, py;
582 
583             if (!suspendUpdate) {
584                 a = l.stdform[1];
585                 b = l.stdform[2];
586                 c = l.stdform[0];
587                 ab = a * a + b * b;
588                 px = F1.X();
589                 py = F1.Y();
590 
591                 curve.quadraticform = [
592                     [(c * c - ab * (px * px + py * py)), c * a + ab * px, c * b + ab * py],
593                     [c * a + ab * px,                  -b * b,          a * b],
594                     [c * b + ab * py,                  a * b,           -a * a]
595                 ];
596             }
597         };
598 
599         /** @ignore */
600         curve.X = function (phi, suspendUpdate) {
601             var a, det,
602                 beta = l.getAngle(),
603                 d = Geometry.distPointLine(F1.coords.usrCoords, l.stdform),
604                 A = l.point1.coords.usrCoords,
605                 B = l.point2.coords.usrCoords,
606                 M = F1.coords.usrCoords;
607 
608             // Handle the case if one of the two defining points of the line is an ideal point
609             if (A[0] === 0) {
610                 A = [1, B[1] + l.stdform[2], B[2] - l.stdform[1]];
611             } else if (B[0] === 0) {
612                 B = [1, A[1] + l.stdform[2], A[2] - l.stdform[1]];
613             }
614             det = ((B[1] - A[1]) * (M[2] - A[2]) - (B[2] - A[2]) * (M[1] - A[1]) >= 0) ? 1 : -1;
615             a = det * d / (1 - Math.sin(phi));
616 
617             if (!suspendUpdate) {
618                 polarForm(phi, suspendUpdate);
619             }
620 
621             return F1.X() + Math.cos(phi + beta) * a;
622         };
623 
624         /** @ignore */
625         curve.Y = function (phi, suspendUpdate) {
626             var a, det,
627                 beta = l.getAngle(),
628                 d = Geometry.distPointLine(F1.coords.usrCoords, l.stdform),
629                 A = l.point1.coords.usrCoords,
630                 B = l.point2.coords.usrCoords,
631                 M = F1.coords.usrCoords;
632 
633             // Handle the case if one of the two defining points of the line is an ideal point
634             if (A[0] === 0) {
635                 A = [1, B[1] + l.stdform[2], B[2] - l.stdform[1]];
636             } else if (B[0] === 0) {
637                 B = [1, A[1] + l.stdform[2], A[2] - l.stdform[1]];
638             }
639             det = ((B[1] - A[1]) * (M[2] - A[2]) - (B[2] - A[2]) * (M[1] - A[1]) >= 0) ? 1 : -1;
640             a = det * d / (1 - Math.sin(phi));
641 
642             return F1.Y() + Math.sin(phi + beta) * a;
643         };
644 
645         curve.type = Const.OBJECT_TYPE_CONIC;
646         M.addChild(curve);
647 
648         if (Type.isPoint(F1)) {
649             F1.addChild(curve);
650             curve.inherits.push(F1);
651         }
652 
653         l.addChild(curve);
654         curve.setParents(parents);
655 
656         return curve;
657     };
658 
659     /**
660      *
661      * @class This element is used to provide a constructor for a generic conic section uniquely defined by five points or
662      * a conic defined by the coefficients of the equation
663      * <p><i>Ax<sup>2</sup>+ Bxy+Cy<sup>2</sup> + Dx + Ey + F = 0</i></p>.
664      * Then the parameters are as follows:
665      * <pre>
666      *     board.create('conic', [A, C, F, B/2, D/2, E/2]);
667      * </pre>
668      * @pseudo
669      * @description
670      * @name Conic
671      * @augments JXG.Curve
672      * @constructor
673      * @type JXG.Conic
674      * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
675      * @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.
676      * @param {Number_Number_Number_Number_Number_Number} a_00,a_11,a_22,a_01,a_02,a_12 6 numbers, i.e. A, C, F, B/2, D/2, E/2
677      * @example
678      * // Create a conic section through the points A, B, C, D, and E.
679      *  var A = board.create('point', [1,5]);
680      *  var B = board.create('point', [1,2]);
681      *  var C = board.create('point', [2,0]);
682      *  var D = board.create('point', [0,0]);
683      *  var E = board.create('point', [-1,5]);
684      *  var conic = board.create('conic',[A,B,C,D,E]);
685      * </pre><div class="jxgbox" id="JXG2d79bd6a-db9b-423c-9cba-2497f0b06320" style="width: 300px; height: 300px;"></div>
686      * <script type="text/javascript">
687      *   var glex1_board = JXG.JSXGraph.initBoard('JXG2d79bd6a-db9b-423c-9cba-2497f0b06320', {boundingbox:[-6,6,6,-6], keepaspectratio:true, showcopyright: false, shownavigation: false});
688      *   var A = glex1_board.create('point', [1,5]);
689      *   var B = glex1_board.create('point', [1,2]);
690      *   var C = glex1_board.create('point', [2,0]);
691      *   var D = glex1_board.create('point', [0,0]);
692      *   var E = glex1_board.create('point', [-1,5]);
693      *   var conic = glex1_board.create('conic',[A,B,C,D,E]);
694      * </script><pre>
695      *
696      * @example
697      * // Parameters: A, C, F, B/2, D/2, E/2
698      * var conic = board.create('conic', [1, 2, -4, 0, 0, 0]s);
699      *
700      * </pre><div id="JXG8576a04a-52d8-4a7e-8d54-e32443910b97" class="jxgbox" style="width: 300px; height: 300px;"></div>
701      * <script type="text/javascript">
702      *     (function() {
703      *         var board = JXG.JSXGraph.initBoard('JXG8576a04a-52d8-4a7e-8d54-e32443910b97',
704      *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
705      *     // Parameters: A, C, F, B/2, D/2, E/2
706      *     var conic = board.create('conic', [1, 2, -4, 0, 0, 0]s);
707      *     })();
708      *
709      * </script><pre>
710      *
711      */
712     JXG.createConic = function (board, parents, attributes) {
713         var polarForm, curve, fitConic, degconic, sym,
714             eigen, a, b, c, c1, c2,
715             i, definingMat, givenByPoints,
716             rotationMatrix = [
717                 [1, 0, 0],
718                 [0, 1, 0],
719                 [0, 0, 1]
720             ],
721             M = [
722                 [1, 0, 0],
723                 [0, 1, 0],
724                 [0, 0, 1]
725             ],
726             points = [],
727             p = [],
728             attr_point = Type.copyAttributes(attributes, board.options, 'conic', 'point'),
729             attr_center = Type.copyAttributes(attributes, board.options, 'conic', 'center'),
730             attr_curve = Type.copyAttributes(attributes, board.options, 'conic');
731 
732         if (parents.length === 5) {
733             givenByPoints = true;
734         } else if (parents.length === 6) {
735             givenByPoints = false;
736         } else {
737             throw new Error("JSXGraph: Can't create generic Conic with " + parents.length + " parameters.");
738         }
739 
740         if (givenByPoints) {
741             for (i = 0; i < 5; i++) {
742                 // point i given by coordinates
743                 if (parents[i].length > 1) {
744                     points[i] = board.create('point', parents[i], attr_point);
745                 // point i given by point
746                 } else if (Type.isPoint(parents[i])) {
747                     points[i] = board.select(parents[i]);
748                 // given by function
749                 } else if (Type.isFunction(parents[i]) && Type.isPoint(parents[i]()) ) {
750                     points[i] = parents[i]();
751                 // point i given by point name
752                 } else if (Type.isString(parents[i])) {
753                     points[i] = board.select(parents[i]);
754                 } else {
755                     throw new Error("JSXGraph: Can't create Conic section with parent types '" + (typeof parents[i]) + "'." +
756                         "\nPossible parent types: [point,point,point,point,point], [a00,a11,a22,a01,a02,a12]");
757                 }
758             }
759         } else {
760             /* Usual notation (x,y,z):
761              *  [[A0,A3,A4],
762              *   [A3,A1,A5],
763              *   [A4,A5,A2]].
764              * Our notation (z,x,y):
765              *  [[A2, A4, A5],
766              *   [A4, A0, A3],
767              *   [A5, A3, A1]]
768              */
769             definingMat = [
770                 [0, 0, 0],
771                 [0, 0, 0],
772                 [0, 0, 0]
773             ];
774             definingMat[0][0] = (Type.isFunction(parents[2])) ? function () { return parents[2](); } : function () { return parents[2]; };
775             definingMat[0][1] = (Type.isFunction(parents[4])) ? function () { return parents[4](); } : function () { return parents[4]; };
776             definingMat[0][2] = (Type.isFunction(parents[5])) ? function () { return parents[5](); } : function () { return parents[5]; };
777             definingMat[1][1] = (Type.isFunction(parents[0])) ? function () { return parents[0](); } : function () { return parents[0]; };
778             definingMat[1][2] = (Type.isFunction(parents[3])) ? function () { return parents[3](); } : function () { return parents[3]; };
779             definingMat[2][2] = (Type.isFunction(parents[1])) ? function () { return parents[1](); } : function () { return parents[1]; };
780         }
781 
782         // sym(A) = A + A^t . Manipulates A in place.
783         sym = function (A) {
784             var i, j;
785             for (i = 0; i < 3; i++) {
786                 for (j = i; j < 3; j++) {
787                     A[i][j] += A[j][i];
788                 }
789             }
790             for (i = 0; i < 3; i++) {
791                 for (j = 0; j < i; j++) {
792                     A[i][j] = A[j][i];
793                 }
794             }
795             return A;
796         };
797 
798         // degconic(v,w) = sym(v*w^t)
799         degconic = function (v, w) {
800             var i, j, mat = [
801                 [0, 0, 0],
802                 [0, 0, 0],
803                 [0, 0, 0]
804             ];
805 
806             for (i = 0; i < 3; i++) {
807                 for (j = 0; j < 3; j++) {
808                     mat[i][j] = v[i] * w[j];
809                 }
810             }
811 
812             return sym(mat);
813         };
814 
815         // (p^t*B*p)*A-(p^t*A*p)*B
816         fitConic = function (A, B, p) {
817             var i, j, pBp, pAp, Mv,
818                 mat = [
819                     [0, 0, 0],
820                     [0, 0, 0],
821                     [0, 0, 0]
822                 ];
823 
824             Mv = Mat.matVecMult(B, p);
825             pBp = Mat.innerProduct(p, Mv);
826             Mv = Mat.matVecMult(A, p);
827             pAp = Mat.innerProduct(p, Mv);
828 
829             for (i = 0; i < 3; i++) {
830                 for (j = 0; j < 3; j++) {
831                     mat[i][j] = pBp * A[i][j] - pAp * B[i][j];
832                 }
833             }
834             return mat;
835         };
836 
837         // Here, the defining functions for the curve are just dummy functions.
838         // In polarForm there is a reference to curve.quadraticform.
839         curve = board.create('curve', [
840             function (x) {
841                 return 0;
842             },
843             function (x) {
844                 return 0;
845             }, 0, 2 * Math.PI], attr_curve);
846 
847         /** @ignore */
848         polarForm = function (phi, suspendUpdate) {
849             var i, j, len, v;
850 
851             if (!suspendUpdate) {
852                 if (givenByPoints) {
853                     // Copy the point coordinate vectors
854                     for (i = 0; i < 5; i++) {
855                         p[i] = points[i].coords.usrCoords;
856                     }
857 
858                     // Compute the quadratic form
859                     c1 = degconic(Mat.crossProduct(p[0], p[1]), Mat.crossProduct(p[2], p[3]));
860                     c2 = degconic(Mat.crossProduct(p[0], p[2]), Mat.crossProduct(p[1], p[3]));
861                     M = fitConic(c1, c2, p[4]);
862                 } else {
863                     for (i = 0; i < 3; i++) {
864                         for (j = i; j < 3; j++) {
865                             M[i][j] = definingMat[i][j]();
866                             if (j > i) {
867                                 M[j][i] = M[i][j];
868                             }
869                         }
870                     }
871                 }
872 
873                 // Here is the reference back to the curve.
874                 curve.quadraticform = M;
875 
876                 // Compute Eigenvalues and Eigenvectors
877                 eigen = Numerics.Jacobi(M);
878 
879                 // Scale the Eigenvalues such that the first Eigenvalue is positive
880                 if (eigen[0][0][0] < 0) {
881                     eigen[0][0][0] *= (-1);
882                     eigen[0][1][1] *= (-1);
883                     eigen[0][2][2] *= (-1);
884                 }
885 
886                 // Normalize the Eigenvectors
887                 for (i = 0; i < 3; i++) {
888                     len = 0.0;
889                     for (j = 0; j < 3; j++) {
890                         len += eigen[1][j][i] * eigen[1][j][i];
891                     }
892                     len = Math.sqrt(len);
893                     /*for (j = 0; j < 3; j++) {
894                         //eigen[1][j][i] /= len;
895                     }*/
896                 }
897                 rotationMatrix = eigen[1];
898                 c = Math.sqrt(Math.abs(eigen[0][0][0]));
899                 a = Math.sqrt(Math.abs(eigen[0][1][1]));
900                 b = Math.sqrt(Math.abs(eigen[0][2][2]));
901 
902             }
903 
904             // The degenerate cases with eigen[0][i][i]==0 are not handled correct yet.
905             if (eigen[0][1][1] <= 0.0 && eigen[0][2][2] <= 0.0) {
906                 v = Mat.matVecMult(rotationMatrix, [1 / c, Math.cos(phi) / a, Math.sin(phi) / b]);
907             } else if (eigen[0][1][1] <= 0.0 && eigen[0][2][2] > 0.0) {
908                 v = Mat.matVecMult(rotationMatrix, [Math.cos(phi) / c, 1 / a, Math.sin(phi) / b]);
909             } else if (eigen[0][2][2] < 0.0) {
910                 v = Mat.matVecMult(rotationMatrix, [Math.sin(phi) / c, Math.cos(phi) / a, 1 / b]);
911             }
912 
913             if (Type.exists(v)) {
914                 // Normalize
915                 v[1] /= v[0];
916                 v[2] /= v[0];
917                 v[0] = 1.0;
918             } else {
919                 v = [1, NaN, NaN];
920             }
921 
922             return v;
923         };
924 
925         /** @ignore */
926         curve.X = function (phi, suspendUpdate) {
927             return polarForm(phi, suspendUpdate)[1];
928         };
929 
930         /** @ignore */
931         curve.Y = function (phi, suspendUpdate) {
932             return polarForm(phi, suspendUpdate)[2];
933         };
934 
935         // Center coordinates see http://en.wikipedia.org/wiki/Matrix_representation_of_conic_sections
936         curve.midpoint = board.create('point', [
937             function () {
938                 var m = curve.quadraticform;
939 
940                 return [
941                     m[1][1] * m[2][2] - m[1][2] * m[1][2],
942                     m[1][2] * m[0][2] - m[2][2] * m[0][1],
943                     m[0][1] * m[1][2] - m[1][1] * m[0][2]
944                 ];
945             }
946         ], attr_center);
947 
948         curve.type = Const.OBJECT_TYPE_CONIC;
949         curve.center = curve.midpoint;
950         curve.subs = {
951             center: curve.center
952         };
953         curve.inherits.push(curve.center);
954         curve.inherits = curve.inherits.concat(points);
955 
956         if (givenByPoints) {
957             for (i = 0; i < 5; i++) {
958                 if (Type.isPoint(points[i])) {
959                     points[i].addChild(curve);
960                 }
961             }
962             curve.setParents(parents);
963         }
964         curve.addChild(curve.center);
965 
966         return curve;
967     };
968 
969     JXG.registerElement('ellipse', JXG.createEllipse);
970     JXG.registerElement('hyperbola', JXG.createHyperbola);
971     JXG.registerElement('parabola', JXG.createParabola);
972     JXG.registerElement('conic', JXG.createConic);
973 
974     return {
975         createEllipse: JXG.createEllipse,
976         createHyperbola: JXG.createHyperbola,
977         createParabola: JXG.createParabola,
978         createConic: JXG.createConic
979     };
980 });
981