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