1 /*
  2     Copyright 2008-2024
  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 <https://www.gnu.org/licenses/>
 29     and <https://opensource.org/licenses/MIT/>.
 30  */
 31 
 32 /*global JXG: true, define: true, console: true, window: true*/
 33 /*jslint nomen: true, plusplus: true*/
 34 
 35 /**
 36  * @fileoverview The geometry object Point is defined in this file. Point stores all
 37  * style and functional properties that are required to draw and move a point on
 38  * a board.
 39  */
 40 
 41 import JXG from "../jxg.js";
 42 import Options from "../options.js";
 43 import Mat from "../math/math.js";
 44 import Geometry from "../math/geometry.js";
 45 import Const from "./constants.js";
 46 import GeometryElement from "./element.js";
 47 import Type from "../utils/type.js";
 48 import CoordsElement from "./coordselement.js";
 49 
 50 /**
 51  * A point is the basic geometric element. Based on points lines and circles can be constructed which can be intersected
 52  * which in turn are points again which can be used to construct new lines, circles, polygons, etc. This class holds methods for
 53  * all kind of points like free points, gliders, and intersection points.
 54  * @class Creates a new point object. Do not use this constructor to create a point. Use {@link JXG.Board#create} with
 55  * type {@link Point}, {@link Glider}, or {@link Intersection} instead.
 56  * @augments JXG.GeometryElement
 57  * @augments JXG.CoordsElement
 58  * @param {string|JXG.Board} board The board the new point is drawn on.
 59  * @param {Array} coordinates An array with the user coordinates of the point.
 60  * @param {Object} attributes An object containing visual properties like in {@link JXG.Options#point} and
 61  * {@link JXG.Options#elements}, and optional a name and an id.
 62  * @see JXG.Board#generateName
 63  */
 64 JXG.Point = function (board, coordinates, attributes) {
 65     this.constructor(board, attributes, Const.OBJECT_TYPE_POINT, Const.OBJECT_CLASS_POINT);
 66     this.element = this.board.select(attributes.anchor);
 67     this.coordsConstructor(coordinates);
 68 
 69     this.elType = "point";
 70 
 71     /* Register point at board. */
 72     this.id = this.board.setId(this, "P");
 73     this.board.renderer.drawPoint(this);
 74     this.board.finalizeAdding(this);
 75 
 76     this.createGradient();
 77     this.createLabel();
 78 };
 79 
 80 /**
 81  * Inherits here from {@link JXG.GeometryElement}.
 82  */
 83 JXG.Point.prototype = new GeometryElement();
 84 Type.copyPrototypeMethods(JXG.Point, CoordsElement, "coordsConstructor");
 85 
 86 JXG.extend(
 87     JXG.Point.prototype,
 88     /** @lends JXG.Point.prototype */ {
 89         /**
 90          * Checks whether (x,y) is near the point.
 91          * @param {Number} x Coordinate in x direction, screen coordinates.
 92          * @param {Number} y Coordinate in y direction, screen coordinates.
 93          * @returns {Boolean} True if (x,y) is near the point, False otherwise.
 94          * @private
 95          */
 96         hasPoint: function (x, y) {
 97             var coordsScr = this.coords.scrCoords,
 98                 r,
 99                 prec,
100                 type,
101                 unit = this.evalVisProp('sizeunit');
102 
103             if (Type.isObject(this.evalVisProp('precision'))) {
104                 type = this.board._inputDevice;
105                 prec = this.evalVisProp('precision.' + type);
106             } else {
107                 // 'inherit'
108                 prec = this.board.options.precision.hasPoint;
109             }
110             r = parseFloat(this.evalVisProp('size'));
111             if (unit === "user") {
112                 r *= Math.sqrt(Math.abs(this.board.unitX * this.board.unitY));
113             }
114 
115             r += parseFloat(this.evalVisProp('strokewidth')) * 0.5;
116             if (r < prec) {
117                 r = prec;
118             }
119 
120             return Math.abs(coordsScr[1] - x) < r + 2 && Math.abs(coordsScr[2] - y) < r + 2;
121         },
122 
123         /**
124          * Updates the position of the point.
125          */
126         update: function (fromParent) {
127             if (!this.needsUpdate) {
128                 return this;
129             }
130 
131             this.updateCoords(fromParent);
132 
133             if (this.evalVisProp('trace')) {
134                 this.cloneToBackground(true);
135             }
136 
137             return this;
138         },
139 
140         /**
141          * Applies the transformations of the element to {@link JXG.Point#baseElement}.
142          * Point transformations are relative to a base element.
143          * @param {Boolean} fromParent True if the drag comes from a child element. This is the case if a line
144          *    through two points is dragged. Otherwise, the element is the drag element and we apply the
145          *    the inverse transformation to the baseElement if is different from the element.
146          * @returns {JXG.CoordsElement} Reference to this object.
147          */
148         updateTransform: function (fromParent) {
149             var c, i;
150 
151             if (this.transformations.length === 0 || this.baseElement === null) {
152                 return this;
153             }
154 
155             this.transformations[0].update();
156             if (this === this.baseElement) {
157                 // Case of bindTo
158                 c = this.transformations[0].apply(this, "self");
159             } else {
160                 c = this.transformations[0].apply(this.baseElement);
161             }
162             for (i = 1; i < this.transformations.length; i++) {
163                 this.transformations[i].update();
164                 c = Mat.matVecMult(this.transformations[i].matrix, c);
165             }
166             this.coords.setCoordinates(Const.COORDS_BY_USER, c);
167 
168             return this;
169         },
170 
171         /**
172          * Calls the renderer to update the drawing.
173          * @private
174          */
175         updateRenderer: function () {
176             this.updateRendererGeneric("updatePoint");
177             return this;
178         },
179 
180         // documented in JXG.GeometryElement
181         bounds: function () {
182             return this.coords.usrCoords.slice(1).concat(this.coords.usrCoords.slice(1));
183         },
184 
185         /**
186          * Convert the point to intersection point and update the construction.
187          * To move the point visual onto the intersection, a call of board update is necessary.
188          *
189          * @param {String|Object} el1, el2, i, j The intersecting objects and the numbers.
190          **/
191         makeIntersection: function (el1, el2, i, j) {
192             var func;
193 
194             el1 = this.board.select(el1);
195             el2 = this.board.select(el2);
196 
197             func = Geometry.intersectionFunction(
198                 this.board,
199                 el1,
200                 el2,
201                 i,
202                 j,
203                 this.visProp.alwaysintersect
204             );
205             this.addConstraint([func]);
206 
207             try {
208                 el1.addChild(this);
209                 el2.addChild(this);
210             } catch (e) {
211                 throw new Error(
212                     "JSXGraph: Can't create 'intersection' with parent types '" +
213                         typeof el1 +
214                         "' and '" +
215                         typeof el2 +
216                         "'."
217                 );
218             }
219 
220             this.type = Const.OBJECT_TYPE_INTERSECTION;
221             this.elType = "intersection";
222             this.parents = [el1.id, el2.id, i, j];
223 
224             this.generatePolynomial = function () {
225                 var poly1 = el1.generatePolynomial(this),
226                     poly2 = el2.generatePolynomial(this);
227 
228                 if (poly1.length === 0 || poly2.length === 0) {
229                     return [];
230                 }
231 
232                 return [poly1[0], poly2[0]];
233             };
234 
235             this.prepareUpdate().update();
236         },
237 
238         /**
239          * Set the style of a point.
240          * Used for GEONExT import and should not be used to set the point's face and size.
241          * @param {Number} i Integer to determine the style.
242          * @private
243          */
244         setStyle: function (i) {
245             var facemap = [
246                     // 0-2
247                     "cross",
248                     "cross",
249                     "cross",
250                     // 3-6
251                     "circle",
252                     "circle",
253                     "circle",
254                     "circle",
255                     // 7-9
256                     "square",
257                     "square",
258                     "square",
259                     // 10-12
260                     "plus",
261                     "plus",
262                     "plus"
263                 ],
264                 sizemap = [
265                     // 0-2
266                     2, 3, 4,
267                     // 3-6
268                     1, 2, 3, 4,
269                     // 7-9
270                     2, 3, 4,
271                     // 10-12
272                     2, 3, 4
273                 ];
274 
275             this.visProp.face = facemap[i];
276             this.visProp.size = sizemap[i];
277 
278             this.board.renderer.changePointStyle(this);
279             return this;
280         },
281 
282         /**
283          * @deprecated Use JXG#normalizePointFace instead
284          * @param s
285          * @returns {*}
286          */
287         normalizeFace: function (s) {
288             JXG.deprecated("Point.normalizeFace()", "JXG.normalizePointFace()");
289             return Options.normalizePointFace(s);
290         },
291 
292         /**
293          * Set the face of a point element.
294          * @param {String} f String which determines the face of the point. See {@link JXG.GeometryElement#face} for a list of available faces.
295          * @see JXG.GeometryElement#face
296          * @deprecated Use setAttribute()
297          */
298         face: function (f) {
299             JXG.deprecated("Point.face()", "Point.setAttribute()");
300             this.setAttribute({ face: f });
301         },
302 
303         /**
304          * Set the size of a point element
305          * @param {Number} s Integer which determines the size of the point.
306          * @see JXG.GeometryElement#size
307          * @deprecated Use setAttribute()
308          */
309         size: function (s) {
310             JXG.deprecated("Point.size()", "Point.setAttribute()");
311             this.setAttribute({ size: s });
312         },
313 
314         /**
315          * Test if the point is on (is incident with) element "el".
316          *
317          * @param {JXG.GeometryElement} el
318          * @param {Number} tol
319          * @returns {Boolean}
320          *
321          * @example
322          * var circ = board.create('circle', [[-2, -2], 1]);
323          * var seg = board.create('segment', [[-1, -3], [0,0]]);
324          * var line = board.create('line', [[1, 3], [2, -2]]);
325          * var po = board.create('point', [-1, 0], {color: 'blue'});
326          * var curve = board.create('functiongraph', ['sin(x)'], {strokeColor: 'blue'});
327          * var pol = board.create('polygon', [[2,2], [4,2], [4,3]], {strokeColor: 'blue'});
328          *
329          * var point = board.create('point', [-1, 1], {
330          *               attractors: [line, seg, circ, po, curve, pol],
331          *               attractorDistance: 0.2
332          *             });
333          *
334          * var txt = board.create('text', [-4, 3, function() {
335          *              return 'point on line: ' + point.isOn(line) + '<br>' +
336          *                 'point on seg: ' + point.isOn(seg) + '<br>' +
337          *                 'point on circ = ' + point.isOn(circ) + '<br>' +
338          *                 'point on point = ' + point.isOn(po) + '<br>' +
339          *                 'point on curve = ' + point.isOn(curve) + '<br>' +
340          *                 'point on polygon = ' + point.isOn(pol) + '<br>';
341          * }]);
342          *
343          * </pre><div id="JXG6c7d7404-758a-44eb-802c-e9644b9fab71" class="jxgbox" style="width: 300px; height: 300px;"></div>
344          * <script type="text/javascript">
345          *     (function() {
346          *         var board = JXG.JSXGraph.initBoard('JXG6c7d7404-758a-44eb-802c-e9644b9fab71',
347          *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
348          *     var circ = board.create('circle', [[-2, -2], 1]);
349          *     var seg = board.create('segment', [[-1, -3], [0,0]]);
350          *     var line = board.create('line', [[1, 3], [2, -2]]);
351          *     var po = board.create('point', [-1, 0], {color: 'blue'});
352          *     var curve = board.create('functiongraph', ['sin(x)'], {strokeColor: 'blue'});
353          *     var pol = board.create('polygon', [[2,2], [4,2], [4,3]], {strokeColor: 'blue'});
354          *
355          *     var point = board.create('point', [-1, 1], {
356          *                   attractors: [line, seg, circ, po, curve, pol],
357          *                   attractorDistance: 0.2
358          *                 });
359          *
360          *     var txt = board.create('text', [-4, 3, function() {
361          *             return 'point on line: ' + point.isOn(line) + '<br>' +
362          *                     'point on seg: ' + point.isOn(seg) + '<br>' +
363          *                     'point on circ = ' + point.isOn(circ) + '<br>' +
364          *                     'point on point = ' + point.isOn(po) + '<br>' +
365          *                     'point on curve = ' + point.isOn(curve) + '<br>' +
366          *                     'point on polygon = ' + point.isOn(pol) + '<br>';
367          *     }]);
368          *
369          *     })();
370          *
371          * </script><pre>
372          *
373          */
374         isOn: function (el, tol) {
375             var arr, crds;
376 
377             tol = tol || Mat.eps;
378 
379             if (Type.isPoint(el)) {
380                 return this.Dist(el) < tol;
381             } else if (el.elementClass === Const.OBJECT_CLASS_LINE) {
382                 if (el.elType === "segment" && !this.evalVisProp('alwaysintersect')) {
383                     arr = JXG.Math.Geometry.projectCoordsToSegment(
384                         this.coords.usrCoords,
385                         el.point1.coords.usrCoords,
386                         el.point2.coords.usrCoords
387                     );
388                     if (
389                         arr[1] >= 0 &&
390                         arr[1] <= 1 &&
391                         Geometry.distPointLine(this.coords.usrCoords, el.stdform) < tol
392                     ) {
393                         return true;
394                     } else {
395                         return false;
396                     }
397                 } else {
398                     return Geometry.distPointLine(this.coords.usrCoords, el.stdform) < tol;
399                 }
400             } else if (el.elementClass === Const.OBJECT_CLASS_CIRCLE) {
401                 if (el.evalVisProp('hasinnerpoints')) {
402                     return this.Dist(el.center) < el.Radius() + tol;
403                 }
404                 return Math.abs(this.Dist(el.center) - el.Radius()) < tol;
405             } else if (el.elementClass === Const.OBJECT_CLASS_CURVE) {
406                 crds = Geometry.projectPointToCurve(this, el, this.board)[0];
407                 return Geometry.distance(this.coords.usrCoords, crds.usrCoords, 3) < tol;
408             } else if (el.type === Const.OBJECT_TYPE_POLYGON) {
409                 if (el.evalVisProp('hasinnerpoints')) {
410                     if (
411                         el.pnpoly(
412                             this.coords.usrCoords[1],
413                             this.coords.usrCoords[2],
414                             JXG.COORDS_BY_USER
415                         )
416                     ) {
417                         return true;
418                     }
419                 }
420                 arr = Geometry.projectCoordsToPolygon(this.coords.usrCoords, el);
421                 return Geometry.distance(this.coords.usrCoords, arr, 3) < tol;
422             } else if (el.type === Const.OBJECT_TYPE_TURTLE) {
423                 crds = Geometry.projectPointToTurtle(this, el, this.board);
424                 return Geometry.distance(this.coords.usrCoords, crds.usrCoords, 3) < tol;
425             }
426 
427             // TODO: Arc, Sector
428             return false;
429         },
430 
431         // Already documented in GeometryElement
432         cloneToBackground: function () {
433             var copy = Type.getCloneObject(this);
434 
435             this.board.renderer.drawPoint(copy);
436             this.traces[copy.id] = copy.rendNode;
437 
438             return this;
439         }
440     }
441 );
442 
443 /**
444  * @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
445  * 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
446  * 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
447  * position directly.
448  * @see Glider for a non-free point that is attached to another geometric element.
449  * @pseudo
450  * @name Point
451  * @augments JXG.Point
452  * @constructor
453  * @type JXG.Point
454  * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
455  * @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
456  * constraint, or a function which takes no parameter and returns a number. Every parent element determines one coordinate. If a coordinate is
457  * 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
458  * 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
459  * 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
460  * parent elements are given they will be interpreted as homogeneous coordinates.
461  * @param {JXG.Point_JXG.Transformation_Array} Point,Transformation A point can also be created providing a transformation or an array of transformations.
462  * The resulting point is a clone of the base point transformed by the given Transformation. {@see JXG.Transformation}.
463  *
464  * @example
465  * // Create a free point using affine Euclidean coordinates
466  * var p1 = board.create('point', [3.5, 2.0]);
467  * </pre><div class="jxgbox" id="JXG672f1764-7dfa-4abc-a2c6-81fbbf83e44b" style="width: 200px; height: 200px;"></div>
468  * <script type="text/javascript">
469  *   var board = JXG.JSXGraph.initBoard('JXG672f1764-7dfa-4abc-a2c6-81fbbf83e44b', {boundingbox: [-1, 5, 5, -1], axis: true, showcopyright: false, shownavigation: false});
470  *   var p1 = board.create('point', [3.5, 2.0]);
471  * </script><pre>
472  * @example
473  * // Create a constrained point using anonymous function
474  * var p2 = board.create('point', [3.5, function () { return p1.X(); }]);
475  * </pre><div class="jxgbox" id="JXG4fd4410c-3383-4e80-b1bb-961f5eeef224" style="width: 200px; height: 200px;"></div>
476  * <script type="text/javascript">
477  *   var fpex1_board = JXG.JSXGraph.initBoard('JXG4fd4410c-3383-4e80-b1bb-961f5eeef224', {boundingbox: [-1, 5, 5, -1], axis: true, showcopyright: false, shownavigation: false});
478  *   var fpex1_p1 = fpex1_board.create('point', [3.5, 2.0]);
479  *   var fpex1_p2 = fpex1_board.create('point', [3.5, function () { return fpex1_p1.X(); }]);
480  * </script><pre>
481  * @example
482  * // Create a point using transformations
483  * var trans = board.create('transform', [2, 0.5], {type:'scale'});
484  * var p3 = board.create('point', [p2, trans]);
485  * </pre><div class="jxgbox" id="JXG630afdf3-0a64-46e0-8a44-f51bd197bb8d" style="width: 400px; height: 400px;"></div>
486  * <script type="text/javascript">
487  *   var fpex2_board = JXG.JSXGraph.initBoard('JXG630afdf3-0a64-46e0-8a44-f51bd197bb8d', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false});
488  *   var fpex2_trans = fpex2_board.create('transform', [2, 0.5], {type:'scale'});
489  *   var fpex2_p2 = fpex2_board.create('point', [3.5, 2.0]);
490  *   var fpex2_p3 = fpex2_board.create('point', [fpex2_p2, fpex2_trans]);
491  * </script><pre>
492  */
493 JXG.createPoint = function (board, parents, attributes) {
494     var el, attr;
495 
496     attr = Type.copyAttributes(attributes, board.options, "point");
497     el = CoordsElement.create(JXG.Point, board, parents, attr);
498     if (!el) {
499         throw new Error(
500             "JSXGraph: Can't create point with parent types '" +
501                 typeof parents[0] +
502                 "' and '" +
503                 typeof parents[1] +
504                 "'." +
505                 "\nPossible parent types: [x,y], [z,x,y], [element,transformation]"
506         );
507     }
508 
509     return el;
510 };
511 
512 /**
513  * @class This element is used to provide a constructor for a glider point.
514  * @pseudo
515  * @description A glider is a point which lives on another geometric element like a line, circle, curve, turtle.
516  * @name Glider
517  * @augments JXG.Point
518  * @constructor
519  * @type JXG.Point
520  * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
521  * @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.
522  * 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
523  * coordinates, otherwise they will be interpreted as homogeneous coordinates. In any case the point will be projected on the glide object.
524  * @example
525  * // Create a glider with user defined coordinates. If the coordinates are not on
526  * // the circle (like in this case) the point will be projected onto the circle.
527  * var p1 = board.create('point', [2.0, 2.0]);
528  * var c1 = board.create('circle', [p1, 2.0]);
529  * var p2 = board.create('glider', [2.0, 1.5, c1]);
530  * </pre><div class="jxgbox" id="JXG4f65f32f-e50a-4b50-9b7c-f6ec41652930" style="width: 300px; height: 300px;"></div>
531  * <script type="text/javascript">
532  *   var gpex1_board = JXG.JSXGraph.initBoard('JXG4f65f32f-e50a-4b50-9b7c-f6ec41652930', {boundingbox: [-1, 5, 5, -1], axis: true, showcopyright: false, shownavigation: false});
533  *   var gpex1_p1 = gpex1_board.create('point', [2.0, 2.0]);
534  *   var gpex1_c1 = gpex1_board.create('circle', [gpex1_p1, 2.0]);
535  *   var gpex1_p2 = gpex1_board.create('glider', [2.0, 1.5, gpex1_c1]);
536  * </script><pre>
537  * @example
538  * // Create a glider with default coordinates (1,0,0). Same premises as above.
539  * var p1 = board.create('point', [2.0, 2.0]);
540  * var c1 = board.create('circle', [p1, 2.0]);
541  * var p2 = board.create('glider', [c1]);
542  * </pre><div class="jxgbox" id="JXG4de7f181-631a-44b1-a12f-bc4d995609e8" style="width: 200px; height: 200px;"></div>
543  * <script type="text/javascript">
544  *   var gpex2_board = JXG.JSXGraph.initBoard('JXG4de7f181-631a-44b1-a12f-bc4d995609e8', {boundingbox: [-1, 5, 5, -1], axis: true, showcopyright: false, shownavigation: false});
545  *   var gpex2_p1 = gpex2_board.create('point', [2.0, 2.0]);
546  *   var gpex2_c1 = gpex2_board.create('circle', [gpex2_p1, 2.0]);
547  *   var gpex2_p2 = gpex2_board.create('glider', [gpex2_c1]);
548  * </script><pre>
549  *@example
550  * //animate example 2
551  * var p1 = board.create('point', [2.0, 2.0]);
552  * var c1 = board.create('circle', [p1, 2.0]);
553  * var p2 = board.create('glider', [c1]);
554  * var button1 = board.create('button', [1, 7, 'start animation',function(){p2.startAnimation(1,4)}]);
555  * var button2 = board.create('button', [1, 5, 'stop animation',function(){p2.stopAnimation()}]);
556  * </pre><div class="jxgbox" id="JXG4de7f181-631a-44b1-a12f-bc4d133709e8" style="width: 200px; height: 200px;"></div>
557  * <script type="text/javascript">
558  *   var gpex3_board = JXG.JSXGraph.initBoard('JXG4de7f181-631a-44b1-a12f-bc4d133709e8', {boundingbox: [-1, 10, 10, -1], axis: true, showcopyright: false, shownavigation: false});
559  *   var gpex3_p1 = gpex3_board.create('point', [2.0, 2.0]);
560  *   var gpex3_c1 = gpex3_board.create('circle', [gpex3_p1, 2.0]);
561  *   var gpex3_p2 = gpex3_board.create('glider', [gpex3_c1]);
562  *   gpex3_board.create('button', [1, 7, 'start animation',function(){gpex3_p2.startAnimation(1,4)}]);
563  *   gpex3_board.create('button', [1, 5, 'stop animation',function(){gpex3_p2.stopAnimation()}]);
564  * </script><pre>
565  */
566 JXG.createGlider = function (board, parents, attributes) {
567     var el,
568         coords,
569         attr = Type.copyAttributes(attributes, board.options, "glider");
570 
571     if (parents.length === 1) {
572         coords = [0, 0];
573     } else {
574         coords = parents.slice(0, 2);
575     }
576     el = board.create("point", coords, attr);
577 
578     // eltype is set in here
579     el.makeGlider(parents[parents.length - 1]);
580 
581     return el;
582 };
583 
584 /**
585  * @class An intersection point is a point which lives on two JSXGraph elements, i.e. it is one point of the set
586  * consisting of the intersection points of the two elements. The following element types can be (mutually) intersected: line, circle,
587  * curve, polygon, polygonal chain.
588  *
589  * @pseudo
590  * @name Intersection
591  * @augments JXG.Point
592  * @constructor
593  * @type JXG.Point
594  * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
595  * @param {JXG.Line,JXG.Circle_JXG.Line,JXG.Circle_Number|Function} el1,el2,i The result will be a intersection point on el1 and el2. i determines the
596  * intersection point if two points are available: <ul>
597  *   <li>i==0: use the positive square root,</li>
598  *   <li>i==1: use the negative square root.</li></ul>
599  * @example
600  * // Create an intersection point of circle and line
601  * var p1 = board.create('point', [4.0, 4.0]);
602  * var c1 = board.create('circle', [p1, 2.0]);
603  *
604  * var p2 = board.create('point', [1.0, 1.0]);
605  * var p3 = board.create('point', [5.0, 3.0]);
606  * var l1 = board.create('line', [p2, p3]);
607  *
608  * var i = board.create('intersection', [c1, l1, 0]);
609  * </pre><div class="jxgbox" id="JXGe5b0e190-5200-4bc3-b995-b6cc53dc5dc0" style="width: 300px; height: 300px;"></div>
610  * <script type="text/javascript">
611  *   var ipex1_board = JXG.JSXGraph.initBoard('JXGe5b0e190-5200-4bc3-b995-b6cc53dc5dc0', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false});
612  *   var ipex1_p1 = ipex1_board.create('point', [4.0, 4.0]);
613  *   var ipex1_c1 = ipex1_board.create('circle', [ipex1_p1, 2.0]);
614  *   var ipex1_p2 = ipex1_board.create('point', [1.0, 1.0]);
615  *   var ipex1_p3 = ipex1_board.create('point', [5.0, 3.0]);
616  *   var ipex1_l1 = ipex1_board.create('line', [ipex1_p2, ipex1_p3]);
617  *   var ipex1_i = ipex1_board.create('intersection', [ipex1_c1, ipex1_l1, 0]);
618  * </script><pre>
619  */
620 JXG.createIntersectionPoint = function (board, parents, attributes) {
621     var el, el1, el2, func,
622         i, j,
623         attr = Type.copyAttributes(attributes, board.options, "intersection");
624 
625     // make sure we definitely have the indices
626     parents.push(0, 0);
627 
628     el1 = board.select(parents[0]);
629     el2 = board.select(parents[1]);
630 
631     i = parents[2] || 0;
632     j = parents[3] || 0;
633 
634     el = board.create("point", [0, 0, 0], attr);
635 
636     // el.visProp.alwaysintersect is evaluated as late as in the returned function
637     func = Geometry.intersectionFunction(board, el1, el2, i, j, el.visProp.alwaysintersect);
638     el.addConstraint([func]);
639 
640     try {
641         el1.addChild(el);
642         el2.addChild(el);
643     } catch (e) {
644         throw new Error(
645             "JSXGraph: Can't create 'intersection' with parent types '" +
646                 typeof parents[0] +
647                 "' and '" +
648                 typeof parents[1] +
649                 "'."
650         );
651     }
652 
653     el.type = Const.OBJECT_TYPE_INTERSECTION;
654     el.elType = "intersection";
655     el.setParents([el1.id, el2.id]);
656 
657     /**
658      * Array of length 2 containing the numbers i and j.
659      * The intersection point is i-th intersection point.
660      * j is unused.
661      * @type Array
662      * @name intersectionNumbers
663      * @memberOf Intersection
664      * @private
665      */
666     el.intersectionNumbers = [i, j];
667     el.getParents = function () {
668         return this.parents.concat(this.intersectionNumbers);
669     };
670 
671     el.generatePolynomial = function () {
672         var poly1 = el1.generatePolynomial(el),
673             poly2 = el2.generatePolynomial(el);
674 
675         if (poly1.length === 0 || poly2.length === 0) {
676             return [];
677         }
678 
679         return [poly1[0], poly2[0]];
680     };
681 
682     return el;
683 };
684 
685 /**
686  * @class This element is used to provide a constructor for the "other" intersection point.
687  * @pseudo
688  * @description If two elements of type curve, circle or line intersect in more than one point, with this element it is possible
689  * to construct the "other" intersection. This is a an intersection which is different from a supplied point or different from any
690  * point in an array of supplied points. This might be helpful in situtations where one intersection point is already part of the construction
691  * or in situtation where the order of the intersection points changes while interacting with the construction.
692  *
693  * @name OtherIntersection
694  * @augments JXG.Point
695  * @constructor
696  * @type JXG.Point
697  * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
698  * @param {JXG.Line,JXG.Circle_JXG.Line,JXG.Circle_JXG.Point,Array} el1,el2,p Two elements which are intersected and a point or an array of points
699  * which have to be different from the new intersection point.
700  *
701  * @example
702  * // Create an intersection point of circle and line
703  * var p1 = board.create('point', [2.0, 2.0]);
704  * var c1 = board.create('circle', [p1, 2.0]);
705  *
706  * var p2 = board.create('point', [2.0, 2.0]);
707  * var p3 = board.create('point', [2.0, 2.0]);
708  * var l1 = board.create('line', [p2, p3]);
709  *
710  * var p1 = board.create('intersection', [c1, l1, 0]);
711  * var p2 = board.create('otherintersection', [c1, l1, p1]);
712  * </pre><div class="jxgbox" id="JXG45e25f12-a1de-4257-a466-27a2ae73614c" style="width: 300px; height: 300px;"></div>
713  * <script type="text/javascript">
714  *   var ipex2_board = JXG.JSXGraph.initBoard('JXG45e25f12-a1de-4257-a466-27a2ae73614c', {boundingbox: [-1, 7, 7, -1], axis: false, showcopyright: false, shownavigation: false});
715  *   var ipex2_p1 = ipex2_board.create('point', [4.0, 4.0]);
716  *   var ipex2_c1 = ipex2_board.create('circle', [ipex2_p1, 2.0]);
717  *   var ipex2_p2 = ipex2_board.create('point', [1.0, 1.0]);
718  *   var ipex2_p3 = ipex2_board.create('point', [5.0, 3.0]);
719  *   var ipex2_l1 = ipex2_board.create('line', [ipex2_p2, ipex2_p3]);
720  *   var ipex2_i = ipex2_board.create('intersection', [ipex2_c1, ipex2_l1, 0], {name:'D'});
721  *   var ipex2_j = ipex2_board.create('otherintersection', [ipex2_c1, ipex2_l1, ipex2_i], {name:'E'});
722  * </script><pre>
723  *
724  * @example
725  *  // circle / circle
726  *  var c1 = board.create('circle', [[0, 0], 3]);
727  *  var c2 = board.create('circle', [[2, 2], 3]);
728  *
729  *  var p1 = board.create('intersection', [c1, c2, 0]);
730  *  var p2 = board.create('otherintersection', [c1, c2, p1]);
731  *
732  * </pre><div id="JXGdb5c974c-3092-4cdf-b5ef-d0af4a912581" class="jxgbox" style="width: 300px; height: 300px;"></div>
733  * <script type="text/javascript">
734  *     (function() {
735  *         var board = JXG.JSXGraph.initBoard('JXGdb5c974c-3092-4cdf-b5ef-d0af4a912581',
736  *             {boundingbox: [-8, 8, 8,-8], axis: false, showcopyright: false, shownavigation: false});
737  *           var c1 = board.create('circle', [[0, 0], 3]);
738  *           var c2 = board.create('circle', [[2, 2], 3]);
739  *
740  *           var p1 = board.create('intersection', [c1, c2, 0]);
741  *           var p2 = board.create('otherintersection', [c1, c2, p1]);
742  *     })();
743  * </script><pre>
744  *
745  * @example
746  *  // curve / line
747  *  var curve = board.create('implicitcurve', ['-(y**2) + x**3 - 2 * x + 1'], { strokeWidth: 2 });
748  *  var A = board.create('glider', [-1.5, 1, curve]);
749  *  var B = board.create('glider', [0.5, 0.5, curve]);
750  *  var line = board.create('line', [A, B], { color: 'black', strokeWidth: 1 });
751  *  var C = board.create('otherintersection', [curve, line, [A, B]], {precision: 0.01});
752  *  var D = board.create('point', [() => C.X(), () => -C.Y()], { name: '-C = A + B' });
753  *
754  * </pre><div id="JXG033f15b0-f5f1-4003-ab6a-b7e13e867fbd" class="jxgbox" style="width: 300px; height: 300px;"></div>
755  * <script type="text/javascript">
756  *     (function() {
757  *         var board = JXG.JSXGraph.initBoard('JXG033f15b0-f5f1-4003-ab6a-b7e13e867fbd',
758  *             {boundingbox: [-2, 2, 2, -2], axis: false, showcopyright: false, shownavigation: false});
759  *           var curve = board.create('implicitcurve', ['-(y**2) + x**3 - 2 * x + 1'], { strokeWidth: 2 });
760  *           var A = board.create('glider', [-1.5, 1, curve]);
761  *           var B = board.create('glider', [0.5, 0.5, curve]);
762  *           var line = board.create('line', [A, B], { color: 'black', strokeWidth: 1 });
763  *           var C = board.create('otherintersection', [curve, line, [A, B]], {precision: 0.01});
764  *           var D = board.create('point', [() => C.X(), () => -C.Y()], { name: '-C = A + B' });
765  *     })();
766  * </script><pre>
767  *
768  * @example
769  *  // curve / curve
770  *  var c1 = board.create('functiongraph', ['x**2 - 3'], { strokeWidth: 2 });
771  *  var A = board.create('point', [0, 2]);
772  *  var c2 = board.create('functiongraph', [(x) => -(x**2) + 2 * A.X() * x + A.Y() - A.X()**2], { strokeWidth: 2 });
773  *  var p1 = board.create('intersection', [c1, c2]);
774  *  var p2 = board.create('otherintersection', [c1, c2, [p1]]);
775  *
776  * </pre><div id="JXG29359aa9-3066-4f45-9e5d-d74201b991d3" class="jxgbox" style="width: 300px; height: 300px;"></div>
777  * <script type="text/javascript">
778  *     (function() {
779  *         var board = JXG.JSXGraph.initBoard('JXG29359aa9-3066-4f45-9e5d-d74201b991d3',
780  *             {boundingbox: [-5, 5, 5, -5], axis: true, showcopyright: false, shownavigation: false});
781  *           var c1 = board.create('functiongraph', ['x**2 - 3'], { strokeWidth: 2 });
782  *           var A = board.create('point', [0, 2]);
783  *           var c2 = board.create('functiongraph', [(x) => -(x**2) + 2 * A.X() * x + A.Y() - A.X()**2], { strokeWidth: 2 });
784  *           var p1 = board.create('intersection', [c1, c2]);
785  *           var p2 = board.create('otherintersection', [c1, c2, [p1]]);
786  *     })();
787  * </script><pre>
788  *
789  */
790 JXG.createOtherIntersectionPoint = function (board, parents, attributes) {
791     var el, el1, el2, i,
792     others, func, input,
793     isGood = true,
794     attr = Type.copyAttributes(attributes, board.options, 'otherintersection');
795 
796     if (parents.length !== 3) {
797         isGood = false;
798     } else {
799         el1 = board.select(parents[0]);
800         el2 = board.select(parents[1]);
801         if (Type.isArray(parents[2])) {
802             others = parents[2];
803         } else {
804             others = [parents[2]];
805         }
806 
807         for (i = 0; i < others.length; i++) {
808             others[i] = board.select(others[i]);
809             if (!Type.isPoint(others[i])) {
810                 isGood = false;
811                 break;
812             }
813         }
814         if (isGood) {
815             input = [el1, el2];
816             // Sort parent elements in order: curve, circle, line
817             input.sort(function (a, b) { return b.elementClass - a.elementClass; });
818 
819             // Two lines are forbidden:
820             if ([Const.OBJECT_CLASS_CIRCLE, Const.OBJECT_CLASS_CURVE].indexOf(input[0].elementClass) < 0) {
821                 isGood = false;
822             } else if ([Const.OBJECT_CLASS_CIRCLE, Const.OBJECT_CLASS_CURVE, Const.OBJECT_CLASS_LINE].indexOf(input[1].elementClass) < 0) {
823                 isGood = false;
824             }
825         }
826     }
827 
828     if (!isGood) {
829         throw new Error(
830             "JSXGraph: Can't create 'other intersection point' with parent types '" +
831                 typeof parents[0] + "',  '" + typeof parents[1] + "'and  '" + typeof parents[2] + "'." +
832                 "\nPossible parent types: [circle|curve|line,circle|curve|line, point], not two lines"
833         );
834     }
835 
836     el = board.create('point', [0, 0, 0], attr);
837     // el.visProp.alwaysintersect is evaluated as late as in the returned function
838     func = Geometry.otherIntersectionFunction(input, others, el.visProp.alwaysintersect, el.visProp.precision);
839     el.addConstraint([func]);
840 
841     el.type = Const.OBJECT_TYPE_INTERSECTION;
842     el.elType = "otherintersection";
843     el.setParents([el1.id, el2.id]);
844     el.addParents(others);
845 
846     el1.addChild(el);
847     el2.addChild(el);
848 
849     if (el1.elementClass === Const.OBJECT_CLASS_CIRCLE) {
850         // circle, circle|line
851         el.generatePolynomial = function () {
852             var poly1 = el1.generatePolynomial(el),
853                 poly2 = el2.generatePolynomial(el);
854 
855             if (poly1.length === 0 || poly2.length === 0) {
856                 return [];
857             }
858 
859             return [poly1[0], poly2[0]];
860         };
861     }
862 
863     return el;
864 };
865 
866 /**
867  * @class This element is used to provide a constructor for the pole point of a line with respect to a conic or a circle.
868  * @pseudo
869  * @description The pole point is the unique reciprocal relationship of a line with respect to a conic.
870  * 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.
871  * A line tangent to a conic has the pole point of that line with respect to that conic as the tangent point.
872  * See {@link https://en.wikipedia.org/wiki/Pole_and_polar} for more information on pole and polar.
873  * @name PolePoint
874  * @augments JXG.Point
875  * @constructor
876  * @type JXG.Point
877  * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
878  * @param {JXG.Conic,JXG.Circle_JXG.Point} el1,el2 or
879  * @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.
880  * @example
881  * // Create the pole point of a line with respect to a conic
882  * var p1 = board.create('point', [-1, 2]);
883  * var p2 = board.create('point', [ 1, 4]);
884  * var p3 = board.create('point', [-1,-2]);
885  * var p4 = board.create('point', [ 0, 0]);
886  * var p5 = board.create('point', [ 4,-2]);
887  * var c1 = board.create('conic',[p1,p2,p3,p4,p5]);
888  * var p6 = board.create('point', [-1, 4]);
889  * var p7 = board.create('point', [2, -2]);
890  * var l1 = board.create('line', [p6, p7]);
891  * var p8 = board.create('polepoint', [c1, l1]);
892  * </pre><div class="jxgbox" id="JXG7b7233a0-f363-47dd-9df5-8018d0d17a98" class="jxgbox" style="width:400px; height:400px;"></div>
893  * <script type='text/javascript'>
894  * var ppex1_board = JXG.JSXGraph.initBoard('JXG7b7233a0-f363-47dd-9df5-8018d0d17a98', {boundingbox: [-3, 5, 5, -3], axis: true, showcopyright: false, shownavigation: false});
895  * var ppex1_p1 = ppex1_board.create('point', [-1, 2]);
896  * var ppex1_p2 = ppex1_board.create('point', [ 1, 4]);
897  * var ppex1_p3 = ppex1_board.create('point', [-1,-2]);
898  * var ppex1_p4 = ppex1_board.create('point', [ 0, 0]);
899  * var ppex1_p5 = ppex1_board.create('point', [ 4,-2]);
900  * var ppex1_c1 = ppex1_board.create('conic',[ppex1_p1,ppex1_p2,ppex1_p3,ppex1_p4,ppex1_p5]);
901  * var ppex1_p6 = ppex1_board.create('point', [-1, 4]);
902  * var ppex1_p7 = ppex1_board.create('point', [2, -2]);
903  * var ppex1_l1 = ppex1_board.create('line', [ppex1_p6, ppex1_p7]);
904  * var ppex1_p8 = ppex1_board.create('polepoint', [ppex1_c1, ppex1_l1]);
905  * </script><pre>
906  * @example
907  * // Create the pole point of a line with respect to a circle
908  * var p1 = board.create('point', [1, 1]);
909  * var p2 = board.create('point', [2, 3]);
910  * var c1 = board.create('circle',[p1,p2]);
911  * var p3 = board.create('point', [-1, 4]);
912  * var p4 = board.create('point', [4, -1]);
913  * var l1 = board.create('line', [p3, p4]);
914  * var p5 = board.create('polepoint', [c1, l1]);
915  * </pre><div class="jxgbox" id="JXG7b7233a0-f363-47dd-9df5-9018d0d17a98" class="jxgbox" style="width:400px; height:400px;"></div>
916  * <script type='text/javascript'>
917  * var ppex2_board = JXG.JSXGraph.initBoard('JXG7b7233a0-f363-47dd-9df5-9018d0d17a98', {boundingbox: [-3, 7, 7, -3], axis: true, showcopyright: false, shownavigation: false});
918  * var ppex2_p1 = ppex2_board.create('point', [1, 1]);
919  * var ppex2_p2 = ppex2_board.create('point', [2, 3]);
920  * var ppex2_c1 = ppex2_board.create('circle',[ppex2_p1,ppex2_p2]);
921  * var ppex2_p3 = ppex2_board.create('point', [-1, 4]);
922  * var ppex2_p4 = ppex2_board.create('point', [4, -1]);
923  * var ppex2_l1 = ppex2_board.create('line', [ppex2_p3, ppex2_p4]);
924  * var ppex2_p5 = ppex2_board.create('polepoint', [ppex2_c1, ppex2_l1]);
925  * </script><pre>
926  */
927 JXG.createPolePoint = function (board, parents, attributes) {
928     var el,
929         el1,
930         el2,
931         firstParentIsConic,
932         secondParentIsConic,
933         firstParentIsLine,
934         secondParentIsLine;
935 
936     if (parents.length > 1) {
937         firstParentIsConic =
938             parents[0].type === Const.OBJECT_TYPE_CONIC ||
939             parents[0].elementClass === Const.OBJECT_CLASS_CIRCLE;
940         secondParentIsConic =
941             parents[1].type === Const.OBJECT_TYPE_CONIC ||
942             parents[1].elementClass === Const.OBJECT_CLASS_CIRCLE;
943 
944         firstParentIsLine = parents[0].elementClass === Const.OBJECT_CLASS_LINE;
945         secondParentIsLine = parents[1].elementClass === Const.OBJECT_CLASS_LINE;
946     }
947 
948     /*        if (parents.length !== 2 || !((
949                 parents[0].type === Const.OBJECT_TYPE_CONIC ||
950                 parents[0].elementClass === Const.OBJECT_CLASS_CIRCLE) &&
951                 parents[1].elementClass === Const.OBJECT_CLASS_LINE ||
952                 parents[0].elementClass === Const.OBJECT_CLASS_LINE && (
953                 parents[1].type === Const.OBJECT_TYPE_CONIC ||
954                 parents[1].elementClass === Const.OBJECT_CLASS_CIRCLE))) {*/
955     if (
956         parents.length !== 2 ||
957         !(
958             (firstParentIsConic && secondParentIsLine) ||
959             (firstParentIsLine && secondParentIsConic)
960         )
961     ) {
962         // Failure
963         throw new Error(
964             "JSXGraph: Can't create 'pole point' with parent types '" +
965                 typeof parents[0] +
966                 "' and '" +
967                 typeof parents[1] +
968                 "'." +
969                 "\nPossible parent type: [conic|circle,line], [line,conic|circle]"
970         );
971     }
972 
973     if (secondParentIsLine) {
974         el1 = board.select(parents[0]);
975         el2 = board.select(parents[1]);
976     } else {
977         el1 = board.select(parents[1]);
978         el2 = board.select(parents[0]);
979     }
980 
981     el = board.create(
982         "point",
983         [
984             function () {
985                 var q = el1.quadraticform,
986                     s = el2.stdform.slice(0, 3);
987 
988                 return [
989                     JXG.Math.Numerics.det([s, q[1], q[2]]),
990                     JXG.Math.Numerics.det([q[0], s, q[2]]),
991                     JXG.Math.Numerics.det([q[0], q[1], s])
992                 ];
993             }
994         ],
995         attributes
996     );
997 
998     el.elType = "polepoint";
999     el.setParents([el1.id, el2.id]);
1000 
1001     el1.addChild(el);
1002     el2.addChild(el);
1003 
1004     return el;
1005 };
1006 
1007 JXG.registerElement("point", JXG.createPoint);
1008 JXG.registerElement("glider", JXG.createGlider);
1009 JXG.registerElement("intersection", JXG.createIntersectionPoint);
1010 JXG.registerElement("otherintersection", JXG.createOtherIntersectionPoint);
1011 JXG.registerElement("polepoint", JXG.createPolePoint);
1012 
1013 export default JXG.Point;
1014 // export default {
1015 //     Point: JXG.Point,
1016 //     createPoint: JXG.createPoint,
1017 //     createGlider: JXG.createGlider,
1018 //     createIntersection: JXG.createIntersectionPoint,
1019 //     createOtherIntersection: JXG.createOtherIntersectionPoint,
1020 //     createPolePoint: JXG.createPolePoint
1021 // };
1022