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, console: true, window: true*/
 34 /*jslint nomen: true, plusplus: true*/
 35 
 36 /* depends:
 37  jxg
 38  options
 39  math/math
 40  math/geometry
 41  math/numerics
 42  base/coords
 43  base/constants
 44  base/element
 45  parser/geonext
 46  utils/type
 47   elements:
 48    transform
 49  */
 50 
 51 /**
 52  * @fileoverview The geometry object Point is defined in this file. Point stores all
 53  * style and functional properties that are required to draw and move a point on
 54  * a board.
 55  */
 56 
 57 define([
 58     'jxg', 'options', 'math/math', 'math/geometry', 'math/numerics', 'base/coords', 'base/constants', 'base/element',
 59     'parser/geonext', 'utils/type', 'base/transformation', 'base/coordselement'
 60 ], function (JXG, Options, Mat, Geometry, Numerics, Coords, Const, GeometryElement, GeonextParser, Type, Transform, CoordsElement) {
 61 
 62     "use strict";
 63 
 64     /**
 65      * A point is the basic geometric element. Based on points lines and circles can be constructed which can be intersected
 66      * which in turn are points again which can be used to construct new lines, circles, polygons, etc. This class holds methods for
 67      * all kind of points like free points, gliders, and intersection points.
 68      * @class Creates a new point object. Do not use this constructor to create a point. Use {@link JXG.Board#create} with
 69      * type {@link Point}, {@link Glider}, or {@link Intersection} instead.
 70      * @augments JXG.GeometryElement
 71      * @augments JXG.CoordsElement
 72      * @param {string|JXG.Board} board The board the new point is drawn on.
 73      * @param {Array} coordinates An array with the user coordinates of the point.
 74      * @param {Object} attributes An object containing visual properties like in {@link JXG.Options#point} and
 75      * {@link JXG.Options#elements}, and optional a name and an id.
 76      * @see JXG.Board#generateName
 77      */
 78     JXG.Point = function (board, coordinates, attributes) {
 79         this.constructor(board, attributes, Const.OBJECT_TYPE_POINT, Const.OBJECT_CLASS_POINT);
 80         this.element = this.board.select(attributes.anchor);
 81         this.coordsConstructor(coordinates);
 82 
 83         this.elType = 'point';
 84 
 85         /* Register point at board. */
 86         this.id = this.board.setId(this, 'P');
 87         this.board.renderer.drawPoint(this);
 88         this.board.finalizeAdding(this);
 89 
 90         this.createLabel();
 91 
 92     };
 93 
 94     /**
 95      * Inherits here from {@link JXG.GeometryElement}.
 96      */
 97     JXG.Point.prototype = new GeometryElement();
 98     Type.copyPrototypeMethods(JXG.Point, CoordsElement, 'coordsConstructor');
 99 
100     JXG.extend(JXG.Point.prototype, /** @lends JXG.Point.prototype */ {
101         /**
102          * Checks whether (x,y) is near the point.
103          * @param {Number} x Coordinate in x direction, screen coordinates.
104          * @param {Number} y Coordinate in y direction, screen coordinates.
105          * @returns {Boolean} True if (x,y) is near the point, False otherwise.
106          * @private
107          */
108         hasPoint: function (x, y) {
109             var coordsScr = this.coords.scrCoords, r,
110                 prec, type,
111                 unit = Type.evaluate(this.visProp.sizeunit);
112 
113             if (Type.isObject(Type.evaluate(this.visProp.precision))) {
114                 type = this.board._inputDevice;
115                 prec = Type.evaluate(this.visProp.precision[type]);
116             } else {
117                 // 'inherit'
118                 prec = this.board.options.precision.hasPoint;
119             }
120             r = parseFloat(Type.evaluate(this.visProp.size));
121             if (unit === 'user') {
122                 r *= Math.sqrt(this.board.unitX * this.board.unitY);
123             }
124 
125             r += parseFloat(Type.evaluate(this.visProp.strokewidth)) * 0.5;
126             if (r < prec) {
127                 r = prec;
128             }
129 
130             return ((Math.abs(coordsScr[1] - x) < r + 2) && (Math.abs(coordsScr[2] - y) < r + 2));
131         },
132 
133         /**
134          * Updates the position of the point.
135          */
136         update: function (fromParent) {
137             if (!this.needsUpdate) {
138                 return this;
139             }
140 
141             this.updateCoords(fromParent);
142 
143             if (Type.evaluate(this.visProp.trace)) {
144                 this.cloneToBackground(true);
145             }
146 
147             return this;
148         },
149 
150         /**
151          * Applies the transformations of the element to {@link JXG.Point#baseElement}.
152          * Point transformations are relative to a base element.
153          * @returns {JXG.CoordsElement} Reference to this object.
154          */
155         updateTransform: function () {
156             var c, i;
157 
158             if (this.transformations.length === 0 || this.baseElement === null) {
159                 return this;
160             }
161 
162             // case of bindTo
163             if (this === this.baseElement) {
164                 c = this.transformations[0].apply(this.baseElement, 'self');
165             // case of board.create('point',[baseElement,transform]);
166             } else {
167                 c = this.transformations[0].apply(this.baseElement);
168             }
169 
170             this.coords.setCoordinates(Const.COORDS_BY_USER, c);
171 
172             for (i = 1; i < this.transformations.length; i++) {
173                 this.coords.setCoordinates(Const.COORDS_BY_USER, this.transformations[i].apply(this));
174             }
175             return this;
176         },
177 
178         /**
179          * Calls the renderer to update the drawing.
180          * @private
181          */
182         updateRenderer: function () {
183             this.updateRendererGeneric('updatePoint');
184             return this;
185         },
186 
187         // documented in JXG.GeometryElement
188         bounds: function () {
189             return this.coords.usrCoords.slice(1).concat(this.coords.usrCoords.slice(1));
190         },
191 
192         /**
193          * Convert the point to intersection point and update the construction.
194          * To move the point visual onto the intersection, a call of board update is necessary.
195          *
196          * @param {String|Object} el1, el2, i, j The intersecting objects and the numbers.
197          **/
198         makeIntersection: function (el1, el2, i, j) {
199             var func;
200 
201             el1 = this.board.select(el1);
202             el2 = this.board.select(el2);
203 
204             func = Geometry.intersectionFunction(this.board, el1, el2, i, j,
205                     Type.evaluate(this.visProp.alwaysintersect));
206             this.addConstraint([func]);
207 
208             try {
209                 el1.addChild(this);
210                 el2.addChild(this);
211             } catch (e) {
212                 throw new Error("JSXGraph: Can't create 'intersection' with parent types '" +
213                     (typeof el1) + "' and '" + (typeof el2) + "'.");
214             }
215 
216             this.type = Const.OBJECT_TYPE_INTERSECTION;
217             this.elType = 'intersection';
218             this.parents = [el1.id, el2.id, i, j];
219 
220             this.generatePolynomial = function () {
221                 var poly1 = el1.generatePolynomial(this),
222                     poly2 = el2.generatePolynomial(this);
223 
224                 if ((poly1.length === 0) || (poly2.length === 0)) {
225                     return [];
226                 }
227 
228                 return [poly1[0], poly2[0]];
229             };
230 
231             this.prepareUpdate().update();
232         },
233 
234         /**
235          * Set the style of a point.
236          * Used for GEONExT import and should not be used to set the point's face and size.
237          * @param {Number} i Integer to determine the style.
238          * @private
239          */
240         setStyle: function (i) {
241             var facemap = [
242                 // 0-2
243                 'cross', 'cross', 'cross',
244                 // 3-6
245                 'circle', 'circle', 'circle', 'circle',
246                 // 7-9
247                 'square', 'square', 'square',
248                 // 10-12
249                 'plus', 'plus', 'plus'
250             ], sizemap = [
251                 // 0-2
252                 2, 3, 4,
253                 // 3-6
254                 1, 2, 3, 4,
255                 // 7-9
256                 2, 3, 4,
257                 // 10-12
258                 2, 3, 4
259             ];
260 
261             this.visProp.face = facemap[i];
262             this.visProp.size = sizemap[i];
263 
264             this.board.renderer.changePointStyle(this);
265             return this;
266         },
267 
268         /**
269          * @deprecated Use JXG#normalizePointFace instead
270          * @param s
271          * @returns {*}
272          */
273         normalizeFace: function (s) {
274             JXG.deprecated('Point.normalizeFace()', 'JXG.normalizePointFace()');
275             return Options.normalizePointFace(s);
276         },
277 
278         /**
279          * Set the face of a point element.
280          * @param {String} f String which determines the face of the point. See {@link JXG.GeometryElement#face} for a list of available faces.
281          * @see JXG.GeometryElement#face
282          * @deprecated Use setAttribute()
283          */
284         face: function (f) {
285             JXG.deprecated('Point.face()', 'Point.setAttribute()');
286             this.setAttribute({face: f});
287         },
288 
289         /**
290          * Set the size of a point element
291          * @param {Number} s Integer which determines the size of the point.
292          * @see JXG.GeometryElement#size
293          * @deprecated Use setAttribute()
294          */
295         size: function (s) {
296             JXG.deprecated('Point.size()', 'Point.setAttribute()');
297             this.setAttribute({size: s});
298         },
299 
300         // already documented in GeometryElement
301         cloneToBackground: function () {
302             var copy = {};
303 
304             copy.id = this.id + 'T' + this.numTraces;
305             this.numTraces += 1;
306 
307             copy.coords = this.coords;
308             copy.visProp = Type.deepCopy(this.visProp, this.visProp.traceattributes, true);
309             copy.visProp.layer = this.board.options.layer.trace;
310             copy.elementClass = Const.OBJECT_CLASS_POINT;
311             copy.board = this.board;
312             Type.clearVisPropOld(copy);
313 
314             copy.visPropCalc = {
315                 visible: Type.evaluate(copy.visProp.visible)
316             };
317 
318             this.board.renderer.drawPoint(copy);
319             this.traces[copy.id] = copy.rendNode;
320 
321             return this;
322         }
323 
324     });
325 
326     /**
327      * @class This element is used to provide a constructor for a general point. A free point is created if the given parent elements are all numbers
328      * and the property fixed is not set or set to false. If one or more parent elements is not a number but a string containing a GEONE<sub>x</sub>T
329      * constraint or a function the point will be considered as constrained). That means that the user won't be able to change the point's
330      * position directly.
331      * @pseudo
332      * @description
333      * @name Point
334      * @augments JXG.Point
335      * @constructor
336      * @type JXG.Point
337      * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
338      * @param {Number,string,function_Number,string,function_Number,string,function} z_,x,y Parent elements can be two or three elements of type number, a string containing a GEONE<sub>x</sub>T
339      * constraint, or a function which takes no parameter and returns a number. Every parent element determines one coordinate. If a coordinate is
340      * given by a number, the number determines the initial position of a free point. If given by a string or a function that coordinate will be constrained
341      * that means the user won't be able to change the point's position directly by mouse because it will be calculated automatically depending on the string
342      * or the function's return value. If two parent elements are given the coordinates will be interpreted as 2D affine Euclidean coordinates, if three such
343      * parent elements are given they will be interpreted as homogeneous coordinates.
344      * @param {JXG.Point_JXG.Transformation_Array} Point,Transformation A point can also be created providing a transformation or an array of transformations.
345      * The resulting point is a clone of the base point transformed by the given Transformation. {@see JXG.Transformation}.
346      *
347      * @example
348      * // Create a free point using affine Euclidean coordinates
349      * var p1 = board.create('point', [3.5, 2.0]);
350      * </pre><div class="jxgbox" id="JXG672f1764-7dfa-4abc-a2c6-81fbbf83e44b" style="width: 200px; height: 200px;"></div>
351      * <script type="text/javascript">
352      *   var board = JXG.JSXGraph.initBoard('JXG672f1764-7dfa-4abc-a2c6-81fbbf83e44b', {boundingbox: [-1, 5, 5, -1], axis: true, showcopyright: false, shownavigation: false});
353      *   var p1 = board.create('point', [3.5, 2.0]);
354      * </script><pre>
355      * @example
356      * // Create a constrained point using anonymous function
357      * var p2 = board.create('point', [3.5, function () { return p1.X(); }]);
358      * </pre><div class="jxgbox" id="JXG4fd4410c-3383-4e80-b1bb-961f5eeef224" style="width: 200px; height: 200px;"></div>
359      * <script type="text/javascript">
360      *   var fpex1_board = JXG.JSXGraph.initBoard('JXG4fd4410c-3383-4e80-b1bb-961f5eeef224', {boundingbox: [-1, 5, 5, -1], axis: true, showcopyright: false, shownavigation: false});
361      *   var fpex1_p1 = fpex1_board.create('point', [3.5, 2.0]);
362      *   var fpex1_p2 = fpex1_board.create('point', [3.5, function () { return fpex1_p1.X(); }]);
363      * </script><pre>
364      * @example
365      * // Create a point using transformations
366      * var trans = board.create('transform', [2, 0.5], {type:'scale'});
367      * var p3 = board.create('point', [p2, trans]);
368      * </pre><div class="jxgbox" id="JXG630afdf3-0a64-46e0-8a44-f51bd197bb8d" style="width: 400px; height: 400px;"></div>
369      * <script type="text/javascript">
370      *   var fpex2_board = JXG.JSXGraph.initBoard('JXG630afdf3-0a64-46e0-8a44-f51bd197bb8d', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false});
371      *   var fpex2_trans = fpex2_board.create('transform', [2, 0.5], {type:'scale'});
372      *   var fpex2_p2 = fpex2_board.create('point', [3.5, 2.0]);
373      *   var fpex2_p3 = fpex2_board.create('point', [fpex2_p2, fpex2_trans]);
374      * </script><pre>
375      */
376     JXG.createPoint = function (board, parents, attributes) {
377         var el, attr;
378 
379         attr = Type.copyAttributes(attributes, board.options, 'point');
380         el = CoordsElement.create(JXG.Point, board, parents, attr);
381         if (!el) {
382             throw new Error("JSXGraph: Can't create point with parent types '" +
383                     (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." +
384                     "\nPossible parent types: [x,y], [z,x,y], [element,transformation]");
385         }
386 
387         return el;
388     };
389 
390     /**
391      * @class This element is used to provide a constructor for a glider point.
392      * @pseudo
393      * @description A glider is a point which lives on another geometric element like a line, circle, curve, turtle.
394      * @name Glider
395      * @augments JXG.Point
396      * @constructor
397      * @type JXG.Point
398      * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
399      * @param {Number_Number_Number_JXG.GeometryElement} z_,x_,y_,GlideObject Parent elements can be two or three elements of type number and the object the glider lives on.
400      * The coordinates are completely optional. If not given the origin is used. If you provide two numbers for coordinates they will be interpreted as affine Euclidean
401      * coordinates, otherwise they will be interpreted as homogeneous coordinates. In any case the point will be projected on the glide object.
402      * @example
403      * // Create a glider with user defined coordinates. If the coordinates are not on
404      * // the circle (like in this case) the point will be projected onto the circle.
405      * var p1 = board.create('point', [2.0, 2.0]);
406      * var c1 = board.create('circle', [p1, 2.0]);
407      * var p2 = board.create('glider', [2.0, 1.5, c1]);
408      * </pre><div class="jxgbox" id="JXG4f65f32f-e50a-4b50-9b7c-f6ec41652930" style="width: 300px; height: 300px;"></div>
409      * <script type="text/javascript">
410      *   var gpex1_board = JXG.JSXGraph.initBoard('JXG4f65f32f-e50a-4b50-9b7c-f6ec41652930', {boundingbox: [-1, 5, 5, -1], axis: true, showcopyright: false, shownavigation: false});
411      *   var gpex1_p1 = gpex1_board.create('point', [2.0, 2.0]);
412      *   var gpex1_c1 = gpex1_board.create('circle', [gpex1_p1, 2.0]);
413      *   var gpex1_p2 = gpex1_board.create('glider', [2.0, 1.5, gpex1_c1]);
414      * </script><pre>
415      * @example
416      * // Create a glider with default coordinates (1,0,0). Same premises as above.
417      * var p1 = board.create('point', [2.0, 2.0]);
418      * var c1 = board.create('circle', [p1, 2.0]);
419      * var p2 = board.create('glider', [c1]);
420      * </pre><div class="jxgbox" id="JXG4de7f181-631a-44b1-a12f-bc4d995609e8" style="width: 200px; height: 200px;"></div>
421      * <script type="text/javascript">
422      *   var gpex2_board = JXG.JSXGraph.initBoard('JXG4de7f181-631a-44b1-a12f-bc4d995609e8', {boundingbox: [-1, 5, 5, -1], axis: true, showcopyright: false, shownavigation: false});
423      *   var gpex2_p1 = gpex2_board.create('point', [2.0, 2.0]);
424      *   var gpex2_c1 = gpex2_board.create('circle', [gpex2_p1, 2.0]);
425      *   var gpex2_p2 = gpex2_board.create('glider', [gpex2_c1]);
426      * </script><pre>
427      *@example
428      * //animate example 2
429      * var p1 = board.create('point', [2.0, 2.0]);
430      * var c1 = board.create('circle', [p1, 2.0]);
431      * var p2 = board.create('glider', [c1]);
432      * var button1 = board.create('button', [1, 7, 'start animation',function(){p2.startAnimation(1,4)}]);
433      * var button2 = board.create('button', [1, 5, 'stop animation',function(){p2.stopAnimation()}]);
434      * </pre><div class="jxgbox" id="JXG4de7f181-631a-44b1-a12f-bc4d133709e8" style="width: 200px; height: 200px;"></div>
435      * <script type="text/javascript">
436      *   var gpex3_board = JXG.JSXGraph.initBoard('JXG4de7f181-631a-44b1-a12f-bc4d133709e8', {boundingbox: [-1, 10, 10, -1], axis: true, showcopyright: false, shownavigation: false});
437      *   var gpex3_p1 = gpex3_board.create('point', [2.0, 2.0]);
438      *   var gpex3_c1 = gpex3_board.create('circle', [gpex3_p1, 2.0]);
439      *   var gpex3_p2 = gpex3_board.create('glider', [gpex3_c1]);
440      *   gpex3_board.create('button', [1, 7, 'start animation',function(){gpex3_p2.startAnimation(1,4)}]);
441      *   gpex3_board.create('button', [1, 5, 'stop animation',function(){gpex3_p2.stopAnimation()}]);
442      * </script><pre>
443      */
444     JXG.createGlider = function (board, parents, attributes) {
445         var el, coords,
446             attr = Type.copyAttributes(attributes, board.options, 'glider');
447 
448         if (parents.length === 1) {
449             coords = [0, 0];
450         } else {
451             coords = parents.slice(0, 2);
452         }
453         el = board.create('point', coords, attr);
454 
455         // eltype is set in here
456         el.makeGlider(parents[parents.length - 1]);
457 
458         return el;
459     };
460 
461 
462     /**
463      * @class An intersection point is a point which lives on two JSXGraph elements, i.e. it is one point of the the set
464      * consisting of the intersection points of the two elements. The following element types can be (mutually) intersected: line, circle,
465      * curve, polygon, polygonal chain.
466      *
467      * @pseudo
468      * @name Intersection
469      * @augments JXG.Point
470      * @constructor
471      * @type JXG.Point
472      * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
473      * @param {JXG.Line,JXG.Circle_JXG.Line,JXG.Circle_Number} el1,el2,i The result will be a intersection point on el1 and el2. i determines the
474      * intersection point if two points are available: <ul>
475      *   <li>i==0: use the positive square root,</li>
476      *   <li>i==1: use the negative square root.</li></ul>
477      * @example
478      * // Create an intersection point of circle and line
479      * var p1 = board.create('point', [2.0, 2.0]);
480      * var c1 = board.create('circle', [p1, 2.0]);
481      *
482      * var p2 = board.create('point', [2.0, 2.0]);
483      * var p3 = board.create('point', [2.0, 2.0]);
484      * var l1 = board.create('line', [p2, p3]);
485      *
486      * var i = board.create('intersection', [c1, l1, 0]);
487      * </pre><div class="jxgbox" id="JXGe5b0e190-5200-4bc3-b995-b6cc53dc5dc0" style="width: 300px; height: 300px;"></div>
488      * <script type="text/javascript">
489      *   var ipex1_board = JXG.JSXGraph.initBoard('JXGe5b0e190-5200-4bc3-b995-b6cc53dc5dc0', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false});
490      *   var ipex1_p1 = ipex1_board.create('point', [4.0, 4.0]);
491      *   var ipex1_c1 = ipex1_board.create('circle', [ipex1_p1, 2.0]);
492      *   var ipex1_p2 = ipex1_board.create('point', [1.0, 1.0]);
493      *   var ipex1_p3 = ipex1_board.create('point', [5.0, 3.0]);
494      *   var ipex1_l1 = ipex1_board.create('line', [ipex1_p2, ipex1_p3]);
495      *   var ipex1_i = ipex1_board.create('intersection', [ipex1_c1, ipex1_l1, 0]);
496      * </script><pre>
497      */
498     JXG.createIntersectionPoint = function (board, parents, attributes) {
499         var el, el1, el2, func, i, j,
500             attr = Type.copyAttributes(attributes, board.options, 'intersection');
501 
502         // make sure we definitely have the indices
503         parents.push(0, 0);
504 
505         el1 = board.select(parents[0]);
506         el2 = board.select(parents[1]);
507 
508         i = parents[2] || 0;
509         j = parents[3] || 0;
510 
511         el = board.create('point', [0, 0, 0], attr);
512 
513         // el.visProp.alwaysintersect is evaluated as late as in the returned function
514         func = Geometry.intersectionFunction(board, el1, el2, i, j, el.visProp.alwaysintersect);
515         el.addConstraint([func]);
516 
517         try {
518             el1.addChild(el);
519             el2.addChild(el);
520         } catch (e) {
521             throw new Error("JSXGraph: Can't create 'intersection' with parent types '" +
522                 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'.");
523         }
524 
525         el.type = Const.OBJECT_TYPE_INTERSECTION;
526         el.elType = 'intersection';
527         el.setParents([el1.id, el2.id]);
528 
529         /**
530          * Array of length 2 containing the numbers i and j.
531          * The intersection point is i-th intersection point.
532          * j is unused.
533          * @type Array
534          * @private
535          */
536         el.intersectionNumbers = [i, j];
537         el.getParents = function() {
538             return this.parents.concat(this.intersectionNumbers);
539         };
540 
541         el.generatePolynomial = function () {
542             var poly1 = el1.generatePolynomial(el),
543                 poly2 = el2.generatePolynomial(el);
544 
545             if ((poly1.length === 0) || (poly2.length === 0)) {
546                 return [];
547             }
548 
549             return [poly1[0], poly2[0]];
550         };
551 
552         return el;
553     };
554 
555     /**
556      * @class This element is used to provide a constructor for the "other" intersection point.
557      * @pseudo
558      * @description An intersection point is a point which lives on two Lines or Circles or one Line and one Circle at the same time, i.e.
559      * an intersection point of the two elements. Additionally, one intersection point is provided. The function returns the other intersection point.
560      * @name OtherIntersection
561      * @augments JXG.Point
562      * @constructor
563      * @type JXG.Point
564      * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
565      * @param {JXG.Line,JXG.Circle_JXG.Line,JXG.Circle_JXG.Point} el1,el2,p The result will be a intersection point on el1 and el2. i determines the
566      * intersection point different from p:
567      * @example
568      * // Create an intersection point of circle and line
569      * var p1 = board.create('point', [2.0, 2.0]);
570      * var c1 = board.create('circle', [p1, 2.0]);
571      *
572      * var p2 = board.create('point', [2.0, 2.0]);
573      * var p3 = board.create('point', [2.0, 2.0]);
574      * var l1 = board.create('line', [p2, p3]);
575      *
576      * var i = board.create('intersection', [c1, l1, 0]);
577      * var j = board.create('otherintersection', [c1, l1, i]);
578      * </pre><div class="jxgbox" id="JXG45e25f12-a1de-4257-a466-27a2ae73614c" style="width: 300px; height: 300px;"></div>
579      * <script type="text/javascript">
580      *   var ipex2_board = JXG.JSXGraph.initBoard('JXG45e25f12-a1de-4257-a466-27a2ae73614c', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false});
581      *   var ipex2_p1 = ipex2_board.create('point', [4.0, 4.0]);
582      *   var ipex2_c1 = ipex2_board.create('circle', [ipex2_p1, 2.0]);
583      *   var ipex2_p2 = ipex2_board.create('point', [1.0, 1.0]);
584      *   var ipex2_p3 = ipex2_board.create('point', [5.0, 3.0]);
585      *   var ipex2_l1 = ipex2_board.create('line', [ipex2_p2, ipex2_p3]);
586      *   var ipex2_i = ipex2_board.create('intersection', [ipex2_c1, ipex2_l1, 0], {name:'D'});
587      *   var ipex2_j = ipex2_board.create('otherintersection', [ipex2_c1, ipex2_l1, ipex2_i], {name:'E'});
588      * </script><pre>
589      */
590     JXG.createOtherIntersectionPoint = function (board, parents, attributes) {
591         var el, el1, el2, other;
592 
593         if (parents.length !== 3 ||
594                 !Type.isPoint(parents[2]) ||
595                 (parents[0].elementClass !== Const.OBJECT_CLASS_LINE && parents[0].elementClass !== Const.OBJECT_CLASS_CIRCLE) ||
596                 (parents[1].elementClass !== Const.OBJECT_CLASS_LINE && parents[1].elementClass !== Const.OBJECT_CLASS_CIRCLE)) {
597             // Failure
598             throw new Error("JSXGraph: Can't create 'other intersection point' with parent types '" +
599                 (typeof parents[0]) + "',  '" + (typeof parents[1]) + "'and  '" + (typeof parents[2]) + "'." +
600                 "\nPossible parent types: [circle|line,circle|line,point]");
601         }
602 
603         el1 = board.select(parents[0]);
604         el2 = board.select(parents[1]);
605         other = board.select(parents[2]);
606 
607         el = board.create('point', [function () {
608             var c = Geometry.meet(el1.stdform, el2.stdform, 0, el1.board);
609 
610             if (Math.abs(other.X() - c.usrCoords[1]) > Mat.eps ||
611                     Math.abs(other.Y() - c.usrCoords[2]) > Mat.eps ||
612                     Math.abs(other.Z() - c.usrCoords[0]) > Mat.eps) {
613                 return c;
614             }
615 
616             return Geometry.meet(el1.stdform, el2.stdform, 1, el1.board);
617         }], attributes);
618 
619         el.type = Const.OBJECT_TYPE_INTERSECTION;
620         el.elType = 'otherintersection';
621         el.setParents([el1.id, el2.id, other]);
622 
623         el1.addChild(el);
624         el2.addChild(el);
625 
626         el.generatePolynomial = function () {
627             var poly1 = el1.generatePolynomial(el),
628                 poly2 = el2.generatePolynomial(el);
629 
630             if ((poly1.length === 0) || (poly2.length === 0)) {
631                 return [];
632             }
633 
634             return [poly1[0], poly2[0]];
635         };
636 
637         return el;
638     };
639 
640     /**
641      * @class This element is used to provide a constructor for the pole point of a line with respect to a conic or a circle.
642      * @pseudo
643      * @description The pole point is the unique reciprocal relationship of a line with respect to a conic.
644      * The lines tangent to the intersections of a conic and a line intersect at the pole point of that line with respect to that conic.
645      * A line tangent to a conic has the pole point of that line with respect to that conic as the tangent point.
646      * See {@link http://en.wikipedia.org/wiki/Pole_and_polar} for more information on pole and polar.
647      * @name PolePoint
648      * @augments JXG.Point
649      * @constructor
650      * @type JXG.Point
651      * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
652      * @param {JXG.Conic,JXG.Circle_JXG.Point} el1,el2 or
653      * @param {JXG.Point_JXG.Conic,JXG.Circle} el1,el2 The result will be the pole point of the line with respect to the conic or the circle.
654      * @example
655      * // Create the pole point of a line with respect to a conic
656      * var p1 = board.create('point', [-1, 2]);
657      * var p2 = board.create('point', [ 1, 4]);
658      * var p3 = board.create('point', [-1,-2]);
659      * var p4 = board.create('point', [ 0, 0]);
660      * var p5 = board.create('point', [ 4,-2]);
661      * var c1 = board.create('conic',[p1,p2,p3,p4,p5]);
662      * var p6 = board.create('point', [-1, 4]);
663      * var p7 = board.create('point', [2, -2]);
664      * var l1 = board.create('line', [p6, p7]);
665      * var p8 = board.create('polepoint', [c1, l1]);
666      * </pre><div class="jxgbox" id="JXG7b7233a0-f363-47dd-9df5-8018d0d17a98" class="jxgbox" style="width:400px; height:400px;"></div>
667      * <script type='text/javascript'>
668      * var ppex1_board = JXG.JSXGraph.initBoard('JXG7b7233a0-f363-47dd-9df5-8018d0d17a98', {boundingbox: [-3, 5, 5, -3], axis: true, showcopyright: false, shownavigation: false});
669      * var ppex1_p1 = ppex1_board.create('point', [-1, 2]);
670      * var ppex1_p2 = ppex1_board.create('point', [ 1, 4]);
671      * var ppex1_p3 = ppex1_board.create('point', [-1,-2]);
672      * var ppex1_p4 = ppex1_board.create('point', [ 0, 0]);
673      * var ppex1_p5 = ppex1_board.create('point', [ 4,-2]);
674      * var ppex1_c1 = ppex1_board.create('conic',[ppex1_p1,ppex1_p2,ppex1_p3,ppex1_p4,ppex1_p5]);
675      * var ppex1_p6 = ppex1_board.create('point', [-1, 4]);
676      * var ppex1_p7 = ppex1_board.create('point', [2, -2]);
677      * var ppex1_l1 = ppex1_board.create('line', [ppex1_p6, ppex1_p7]);
678      * var ppex1_p8 = ppex1_board.create('polepoint', [ppex1_c1, ppex1_l1]);
679      * </script><pre>
680      * @example
681      * // Create the pole point of a line with respect to a circle
682      * var p1 = board.create('point', [1, 1]);
683      * var p2 = board.create('point', [2, 3]);
684      * var c1 = board.create('circle',[p1,p2]);
685      * var p3 = board.create('point', [-1, 4]);
686      * var p4 = board.create('point', [4, -1]);
687      * var l1 = board.create('line', [p3, p4]);
688      * var p5 = board.create('polepoint', [c1, l1]);
689      * </pre><div class="jxgbox" id="JXG7b7233a0-f363-47dd-9df5-9018d0d17a98" class="jxgbox" style="width:400px; height:400px;"></div>
690      * <script type='text/javascript'>
691      * var ppex2_board = JXG.JSXGraph.initBoard('JXG7b7233a0-f363-47dd-9df5-9018d0d17a98', {boundingbox: [-3, 7, 7, -3], axis: true, showcopyright: false, shownavigation: false});
692      * var ppex2_p1 = ppex2_board.create('point', [1, 1]);
693      * var ppex2_p2 = ppex2_board.create('point', [2, 3]);
694      * var ppex2_c1 = ppex2_board.create('circle',[ppex2_p1,ppex2_p2]);
695      * var ppex2_p3 = ppex2_board.create('point', [-1, 4]);
696      * var ppex2_p4 = ppex2_board.create('point', [4, -1]);
697      * var ppex2_l1 = ppex2_board.create('line', [ppex2_p3, ppex2_p4]);
698      * var ppex2_p5 = ppex2_board.create('polepoint', [ppex2_c1, ppex2_l1]);
699      * </script><pre>
700      */
701     JXG.createPolePoint = function (board, parents, attributes) {
702         var el, el1, el2,
703             firstParentIsConic, secondParentIsConic,
704             firstParentIsLine, secondParentIsLine;
705 
706         if (parents.length > 1) {
707             firstParentIsConic = (parents[0].type === Const.OBJECT_TYPE_CONIC ||
708                 parents[0].elementClass === Const.OBJECT_CLASS_CIRCLE);
709             secondParentIsConic = (parents[1].type === Const.OBJECT_TYPE_CONIC ||
710                 parents[1].elementClass === Const.OBJECT_CLASS_CIRCLE);
711 
712             firstParentIsLine = (parents[0].elementClass === Const.OBJECT_CLASS_LINE);
713             secondParentIsLine = (parents[1].elementClass === Const.OBJECT_CLASS_LINE);
714         }
715 
716 /*        if (parents.length !== 2 || !((
717                 parents[0].type === Const.OBJECT_TYPE_CONIC ||
718                 parents[0].elementClass === Const.OBJECT_CLASS_CIRCLE) &&
719                 parents[1].elementClass === Const.OBJECT_CLASS_LINE ||
720                 parents[0].elementClass === Const.OBJECT_CLASS_LINE && (
721                 parents[1].type === Const.OBJECT_TYPE_CONIC ||
722                 parents[1].elementClass === Const.OBJECT_CLASS_CIRCLE))) {*/
723         if (parents.length !== 2 ||
724                 !((firstParentIsConic && secondParentIsLine) ||
725                     (firstParentIsLine && secondParentIsConic))) {
726             // Failure
727             throw new Error("JSXGraph: Can't create 'pole point' with parent types '" +
728                 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." +
729                 "\nPossible parent type: [conic|circle,line], [line,conic|circle]");
730         }
731 
732         if (secondParentIsLine) {
733             el1 = board.select(parents[0]);
734             el2 = board.select(parents[1]);
735         } else {
736             el1 = board.select(parents[1]);
737             el2 = board.select(parents[0]);
738         }
739 
740         el = board.create('point',
741             [function () {
742                 var q = el1.quadraticform,
743                     s = el2.stdform.slice(0, 3);
744 
745                 return [JXG.Math.Numerics.det([s, q[1], q[2]]),
746                         JXG.Math.Numerics.det([q[0], s, q[2]]),
747                         JXG.Math.Numerics.det([q[0], q[1], s])];
748             }], attributes);
749 
750         el.elType = 'polepoint';
751         el.setParents([el1.id, el2.id]);
752 
753         el1.addChild(el);
754         el2.addChild(el);
755 
756         return el;
757     };
758 
759     JXG.registerElement('point', JXG.createPoint);
760     JXG.registerElement('glider', JXG.createGlider);
761     JXG.registerElement('intersection', JXG.createIntersectionPoint);
762     JXG.registerElement('otherintersection', JXG.createOtherIntersectionPoint);
763     JXG.registerElement('polepoint', JXG.createPolePoint);
764 
765     return {
766         Point: JXG.Point,
767         createPoint: JXG.createPoint,
768         createGlider: JXG.createGlider,
769         createIntersection: JXG.createIntersectionPoint,
770         createOtherIntersection: JXG.createOtherIntersectionPoint,
771         createPolePoint: JXG.createPolePoint
772     };
773 });
774