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