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