      1 /*
      2     Copyright 2008-2025
      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 Construct a free or a fixed 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 A glider is a point bound to a line, circle or curve or even another 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 A point intersecting two 1-dimensional elements.
    586  * It is one point of the set  * consisting of the intersection points of the two elements.
    587  * The following element types can be (mutually) intersected: line, circle,
    588  * curve, polygon, polygonal chain.
    589  *
    590  * @pseudo
    591  * @name Intersection
    592  * @augments JXG.Point
    593  * @constructor
    594  * @type JXG.Point
    595  * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
    596  * @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
    597  * intersection point if two points are available: <ul>
    598  *   <li>i==0: use the positive square root,</li>
    599  *   <li>i==1: use the negative square root.</li></ul>
    600  * @example
    601  * // Create an intersection point of circle and line
    602  * var p1 = board.create('point', [4.0, 4.0]);
    603  * var c1 = board.create('circle', [p1, 2.0]);
    604  *
    605  * var p2 = board.create('point', [1.0, 1.0]);
    606  * var p3 = board.create('point', [5.0, 3.0]);
    607  * var l1 = board.create('line', [p2, p3]);
    608  *
    609  * var i = board.create('intersection', [c1, l1, 0]);
    610  * </pre><div class="jxgbox" id="JXGe5b0e190-5200-4bc3-b995-b6cc53dc5dc0" style="width: 300px; height: 300px;"></div>
    611  * <script type="text/javascript">
    612  *   var ipex1_board = JXG.JSXGraph.initBoard('JXGe5b0e190-5200-4bc3-b995-b6cc53dc5dc0', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false});
    613  *   var ipex1_p1 = ipex1_board.create('point', [4.0, 4.0]);
    614  *   var ipex1_c1 = ipex1_board.create('circle', [ipex1_p1, 2.0]);
    615  *   var ipex1_p2 = ipex1_board.create('point', [1.0, 1.0]);
    616  *   var ipex1_p3 = ipex1_board.create('point', [5.0, 3.0]);
    617  *   var ipex1_l1 = ipex1_board.create('line', [ipex1_p2, ipex1_p3]);
    618  *   var ipex1_i = ipex1_board.create('intersection', [ipex1_c1, ipex1_l1, 0]);
    619  * </script><pre>
    620  */
    621 JXG.createIntersectionPoint = function (board, parents, attributes) {
    622     var el, el1, el2, func,
    623         i, j,
    624         attr = Type.copyAttributes(attributes, board.options, "intersection");
    625
    626     // make sure we definitely have the indices
    627     parents.push(0, 0);
    628
    629     el1 = board.select(parents[0]);
    630     el2 = board.select(parents[1]);
    631
    632     i = parents[2] || 0;
    633     j = parents[3] || 0;
    634
    635     el = board.create("point", [0, 0, 0], attr);
    636
    637     // el.visProp.alwaysintersect is evaluated as late as in the returned function
    638     func = Geometry.intersectionFunction(board, el1, el2, i, j, el.visProp.alwaysintersect);
    639     el.addConstraint([func]);
    640
    641     try {
    642         el1.addChild(el);
    643         el2.addChild(el);
    644     } catch (e) {
    645         throw new Error(
    646             "JSXGraph: Can't create 'intersection' with parent types '" +
    647                 typeof parents[0] +
    648                 "' and '" +
    649                 typeof parents[1] +
    650                 "'."
    651         );
    652     }
    653
    654     el.type = Const.OBJECT_TYPE_INTERSECTION;
    655     el.elType = "intersection";
    656     el.setParents([el1.id, el2.id]);
    657
    658     /**
    659      * Array of length 2 containing the numbers i and j.
    660      * The intersection point is i-th intersection point.
    661      * j is unused.
    662      * @type Array
    663      * @name intersectionNumbers
    664      * @memberOf Intersection
    665      * @private
    666      */
    667     el.intersectionNumbers = [i, j];
    668     el.getParents = function () {
    669         return this.parents.concat(this.intersectionNumbers);
    670     };
    671
    672     el.generatePolynomial = function () {
    673         var poly1 = el1.generatePolynomial(el),
    674             poly2 = el2.generatePolynomial(el);
    675
    676         if (poly1.length === 0 || poly2.length === 0) {
    677             return [];
    678         }
    679
    680         return [poly1[0], poly2[0]];
    681     };
    682
    683     return el;
    684 };
    685
    686 /**
    687  * @class Given a set of intersection points, this is another ("other") intersection point,
    688  * @pseudo
    689  * @description If two elements of type curve, circle or line intersect in more than one point, with this element it is possible
    690  * to construct the "other" intersection. This is a an intersection which is different from a supplied point or different from any
    691  * point in an array of supplied points. This might be helpful in situtations where one intersection point is already part of the construction
    692  * or in situtation where the order of the intersection points changes while interacting with the construction.
    693  *
    694  * @name OtherIntersection
    695  * @augments JXG.Point
    696  * @constructor
    697  * @type JXG.Point
    698  * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
    699  * @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
    700  * which have to be different from the new intersection point.
    701  *
    702  * @example
    703  * // Create an intersection point of circle and line
    704  * var p1 = board.create('point', [2.0, 2.0]);
    705  * var c1 = board.create('circle', [p1, 2.0]);
    706  *
    707  * var p2 = board.create('point', [2.0, 2.0]);
    708  * var p3 = board.create('point', [2.0, 2.0]);
    709  * var l1 = board.create('line', [p2, p3]);
    710  *
    711  * var p1 = board.create('intersection', [c1, l1, 0]);
    712  * var p2 = board.create('otherintersection', [c1, l1, p1]);
    713  * </pre><div class="jxgbox" id="JXG45e25f12-a1de-4257-a466-27a2ae73614c" style="width: 300px; height: 300px;"></div>
    714  * <script type="text/javascript">
    715  *   var ipex2_board = JXG.JSXGraph.initBoard('JXG45e25f12-a1de-4257-a466-27a2ae73614c', {boundingbox: [-1, 7, 7, -1], axis: false, showcopyright: false, shownavigation: false});
    716  *   var ipex2_p1 = ipex2_board.create('point', [4.0, 4.0]);
    717  *   var ipex2_c1 = ipex2_board.create('circle', [ipex2_p1, 2.0]);
    718  *   var ipex2_p2 = ipex2_board.create('point', [1.0, 1.0]);
    719  *   var ipex2_p3 = ipex2_board.create('point', [5.0, 3.0]);
    720  *   var ipex2_l1 = ipex2_board.create('line', [ipex2_p2, ipex2_p3]);
    721  *   var ipex2_i = ipex2_board.create('intersection', [ipex2_c1, ipex2_l1, 0], {name:'D'});
    722  *   var ipex2_j = ipex2_board.create('otherintersection', [ipex2_c1, ipex2_l1, ipex2_i], {name:'E'});
    723  * </script><pre>
    724  *
    725  * @example
    726  *  // circle / circle
    727  *  var c1 = board.create('circle', [[0, 0], 3]);
    728  *  var c2 = board.create('circle', [[2, 2], 3]);
    729  *
    730  *  var p1 = board.create('intersection', [c1, c2, 0]);
    731  *  var p2 = board.create('otherintersection', [c1, c2, p1]);
    732  *
    733  * </pre><div id="JXGdb5c974c-3092-4cdf-b5ef-d0af4a912581" class="jxgbox" style="width: 300px; height: 300px;"></div>
    734  * <script type="text/javascript">
    735  *     (function() {
    736  *         var board = JXG.JSXGraph.initBoard('JXGdb5c974c-3092-4cdf-b5ef-d0af4a912581',
    737  *             {boundingbox: [-8, 8, 8,-8], axis: false, showcopyright: false, shownavigation: false});
    738  *           var c1 = board.create('circle', [[0, 0], 3]);
    739  *           var c2 = board.create('circle', [[2, 2], 3]);
    740  *
    741  *           var p1 = board.create('intersection', [c1, c2, 0]);
    742  *           var p2 = board.create('otherintersection', [c1, c2, p1]);
    743  *     })();
    744  * </script><pre>
    745  *
    746  * @example
    747  *  // curve / line
    748  *  var curve = board.create('implicitcurve', ['-(y**2) + x**3 - 2 * x + 1'], { strokeWidth: 2 });
    749  *  var A = board.create('glider', [-1.5, 1, curve]);
    750  *  var B = board.create('glider', [0.5, 0.5, curve]);
    751  *  var line = board.create('line', [A, B], { color: 'black', strokeWidth: 1 });
    752  *  var C = board.create('otherintersection', [curve, line, [A, B]], {precision: 0.01});
    753  *  var D = board.create('point', [() => C.X(), () => -C.Y()], { name: '-C = A + B' });
    754  *
    755  * </pre><div id="JXG033f15b0-f5f1-4003-ab6a-b7e13e867fbd" class="jxgbox" style="width: 300px; height: 300px;"></div>
    756  * <script type="text/javascript">
    757  *     (function() {
    758  *         var board = JXG.JSXGraph.initBoard('JXG033f15b0-f5f1-4003-ab6a-b7e13e867fbd',
    759  *             {boundingbox: [-2, 2, 2, -2], axis: false, showcopyright: false, shownavigation: false});
    760  *           var curve = board.create('implicitcurve', ['-(y**2) + x**3 - 2 * x + 1'], { strokeWidth: 2 });
    761  *           var A = board.create('glider', [-1.5, 1, curve]);
    762  *           var B = board.create('glider', [0.5, 0.5, curve]);
    763  *           var line = board.create('line', [A, B], { color: 'black', strokeWidth: 1 });
    764  *           var C = board.create('otherintersection', [curve, line, [A, B]], {precision: 0.01});
    765  *           var D = board.create('point', [() => C.X(), () => -C.Y()], { name: '-C = A + B' });
    766  *     })();
    767  * </script><pre>
    768  *
    769  * @example
    770  *  // curve / curve
    771  *  var c1 = board.create('functiongraph', ['x**2 - 3'], { strokeWidth: 2 });
    772  *  var A = board.create('point', [0, 2]);
    773  *  var c2 = board.create('functiongraph', [(x) => -(x**2) + 2 * A.X() * x + A.Y() - A.X()**2], { strokeWidth: 2 });
    774  *  var p1 = board.create('intersection', [c1, c2]);
    775  *  var p2 = board.create('otherintersection', [c1, c2, [p1]]);
    776  *
    777  * </pre><div id="JXG29359aa9-3066-4f45-9e5d-d74201b991d3" class="jxgbox" style="width: 300px; height: 300px;"></div>
    778  * <script type="text/javascript">
    779  *     (function() {
    780  *         var board = JXG.JSXGraph.initBoard('JXG29359aa9-3066-4f45-9e5d-d74201b991d3',
    781  *             {boundingbox: [-5, 5, 5, -5], axis: true, showcopyright: false, shownavigation: false});
    782  *           var c1 = board.create('functiongraph', ['x**2 - 3'], { strokeWidth: 2 });
    783  *           var A = board.create('point', [0, 2]);
    784  *           var c2 = board.create('functiongraph', [(x) => -(x**2) + 2 * A.X() * x + A.Y() - A.X()**2], { strokeWidth: 2 });
    785  *           var p1 = board.create('intersection', [c1, c2]);
    786  *           var p2 = board.create('otherintersection', [c1, c2, [p1]]);
    787  *     })();
    788  * </script><pre>
    789  *
    790  */
    791 JXG.createOtherIntersectionPoint = function (board, parents, attributes) {
    792     var el, el1, el2, i,
    793     others, func, input,
    794     isGood = true,
    795     attr = Type.copyAttributes(attributes, board.options, 'otherintersection');
    796
    797     if (parents.length !== 3) {
    798         isGood = false;
    799     } else {
    800         el1 = board.select(parents[0]);
    801         el2 = board.select(parents[1]);
    802         if (Type.isArray(parents[2])) {
    803             others = parents[2];
    804         } else {
    805             others = [parents[2]];
    806         }
    807
    808         for (i = 0; i < others.length; i++) {
    809             others[i] = board.select(others[i]);
    810             if (!Type.isPoint(others[i])) {
    811                 isGood = false;
    812                 break;
    813             }
    814         }
    815         if (isGood) {
    816             input = [el1, el2];
    817             // Sort parent elements in order: curve, circle, line
    818             input.sort(function (a, b) { return b.elementClass - a.elementClass; });
    819
    820             // Two lines are forbidden:
    821             if ([Const.OBJECT_CLASS_CIRCLE, Const.OBJECT_CLASS_CURVE].indexOf(input[0].elementClass) < 0) {
    822                 isGood = false;
    823             } else if ([Const.OBJECT_CLASS_CIRCLE, Const.OBJECT_CLASS_CURVE, Const.OBJECT_CLASS_LINE].indexOf(input[1].elementClass) < 0) {
    824                 isGood = false;
    825             }
    826         }
    827     }
    828
    829     if (!isGood) {
    830         throw new Error(
    831             "JSXGraph: Can't create 'other intersection point' with parent types '" +
    832                 typeof parents[0] + "',  '" + typeof parents[1] + "'and  '" + typeof parents[2] + "'." +
    833                 "\nPossible parent types: [circle|curve|line,circle|curve|line, point], not two lines"
    834         );
    835     }
    836
    837     el = board.create('point', [0, 0, 0], attr);
    838     // el.visProp.alwaysintersect is evaluated as late as in the returned function
    839     func = Geometry.otherIntersectionFunction(input, others, el.visProp.alwaysintersect, el.visProp.precision);
    840     el.addConstraint([func]);
    841
    842     el.type = Const.OBJECT_TYPE_INTERSECTION;
    843     el.elType = "otherintersection";
    844     el.setParents([el1.id, el2.id]);
    845     el.addParents(others);
    846
    847     el1.addChild(el);
    848     el2.addChild(el);
    849
    850     if (el1.elementClass === Const.OBJECT_CLASS_CIRCLE) {
    851         // circle, circle|line
    852         el.generatePolynomial = function () {
    853             var poly1 = el1.generatePolynomial(el),
    854                 poly2 = el2.generatePolynomial(el);
    855
    856             if (poly1.length === 0 || poly2.length === 0) {
    857                 return [];
    858             }
    859
    860             return [poly1[0], poly2[0]];
    861         };
    862     }
    863
    864     return el;
    865 };
    866
    867 /**
    868  * @class This element is used to provide a constructor for the pole point of a line with respect to a conic or a circle.
    869  * @pseudo
    870  * @description The pole point is the unique reciprocal relationship of a line with respect to a conic.
    871  * 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.
    872  * A line tangent to a conic has the pole point of that line with respect to that conic as the tangent point.
    873  * See {@link https://en.wikipedia.org/wiki/Pole_and_polar} for more information on pole and polar.
    874  * @name PolePoint
    875  * @augments JXG.Point
    876  * @constructor
    877  * @type JXG.Point
    878  * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
    879  * @param {JXG.Conic,JXG.Circle_JXG.Point} el1,el2 or
    880  * @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.
    881  * @example
    882  * // Create the pole point of a line with respect to a conic
    883  * var p1 = board.create('point', [-1, 2]);
    884  * var p2 = board.create('point', [ 1, 4]);
    885  * var p3 = board.create('point', [-1,-2]);
    886  * var p4 = board.create('point', [ 0, 0]);
    887  * var p5 = board.create('point', [ 4,-2]);
    888  * var c1 = board.create('conic',[p1,p2,p3,p4,p5]);
    889  * var p6 = board.create('point', [-1, 4]);
    890  * var p7 = board.create('point', [2, -2]);
    891  * var l1 = board.create('line', [p6, p7]);
    892  * var p8 = board.create('polepoint', [c1, l1]);
    893  * </pre><div class="jxgbox" id="JXG7b7233a0-f363-47dd-9df5-8018d0d17a98" class="jxgbox" style="width:400px; height:400px;"></div>
    894  * <script type='text/javascript'>
    895  * var ppex1_board = JXG.JSXGraph.initBoard('JXG7b7233a0-f363-47dd-9df5-8018d0d17a98', {boundingbox: [-3, 5, 5, -3], axis: true, showcopyright: false, shownavigation: false});
    896  * var ppex1_p1 = ppex1_board.create('point', [-1, 2]);
    897  * var ppex1_p2 = ppex1_board.create('point', [ 1, 4]);
    898  * var ppex1_p3 = ppex1_board.create('point', [-1,-2]);
    899  * var ppex1_p4 = ppex1_board.create('point', [ 0, 0]);
    900  * var ppex1_p5 = ppex1_board.create('point', [ 4,-2]);
    901  * var ppex1_c1 = ppex1_board.create('conic',[ppex1_p1,ppex1_p2,ppex1_p3,ppex1_p4,ppex1_p5]);
    902  * var ppex1_p6 = ppex1_board.create('point', [-1, 4]);
    903  * var ppex1_p7 = ppex1_board.create('point', [2, -2]);
    904  * var ppex1_l1 = ppex1_board.create('line', [ppex1_p6, ppex1_p7]);
    905  * var ppex1_p8 = ppex1_board.create('polepoint', [ppex1_c1, ppex1_l1]);
    906  * </script><pre>
    907  * @example
    908  * // Create the pole point of a line with respect to a circle
    909  * var p1 = board.create('point', [1, 1]);
    910  * var p2 = board.create('point', [2, 3]);
    911  * var c1 = board.create('circle',[p1,p2]);
    912  * var p3 = board.create('point', [-1, 4]);
    913  * var p4 = board.create('point', [4, -1]);
    914  * var l1 = board.create('line', [p3, p4]);
    915  * var p5 = board.create('polepoint', [c1, l1]);
    916  * </pre><div class="jxgbox" id="JXG7b7233a0-f363-47dd-9df5-9018d0d17a98" class="jxgbox" style="width:400px; height:400px;"></div>
    917  * <script type='text/javascript'>
    918  * var ppex2_board = JXG.JSXGraph.initBoard('JXG7b7233a0-f363-47dd-9df5-9018d0d17a98', {boundingbox: [-3, 7, 7, -3], axis: true, showcopyright: false, shownavigation: false});
    919  * var ppex2_p1 = ppex2_board.create('point', [1, 1]);
    920  * var ppex2_p2 = ppex2_board.create('point', [2, 3]);
    921  * var ppex2_c1 = ppex2_board.create('circle',[ppex2_p1,ppex2_p2]);
    922  * var ppex2_p3 = ppex2_board.create('point', [-1, 4]);
    923  * var ppex2_p4 = ppex2_board.create('point', [4, -1]);
    924  * var ppex2_l1 = ppex2_board.create('line', [ppex2_p3, ppex2_p4]);
    925  * var ppex2_p5 = ppex2_board.create('polepoint', [ppex2_c1, ppex2_l1]);
    926  * </script><pre>
    927  */
    928 JXG.createPolePoint = function (board, parents, attributes) {
    929     var el,
    930         el1,
    931         el2,
    932         firstParentIsConic,
    933         secondParentIsConic,
    934         firstParentIsLine,
    935         secondParentIsLine;
    936
    937     if (parents.length > 1) {
    938         firstParentIsConic =
    939             parents[0].type === Const.OBJECT_TYPE_CONIC ||
    940             parents[0].elementClass === Const.OBJECT_CLASS_CIRCLE;
    941         secondParentIsConic =
    942             parents[1].type === Const.OBJECT_TYPE_CONIC ||
    943             parents[1].elementClass === Const.OBJECT_CLASS_CIRCLE;
    944
    945         firstParentIsLine = parents[0].elementClass === Const.OBJECT_CLASS_LINE;
    946         secondParentIsLine = parents[1].elementClass === Const.OBJECT_CLASS_LINE;
    947     }
    948
    949     /*        if (parents.length !== 2 || !((
    950                 parents[0].type === Const.OBJECT_TYPE_CONIC ||
    951                 parents[0].elementClass === Const.OBJECT_CLASS_CIRCLE) &&
    952                 parents[1].elementClass === Const.OBJECT_CLASS_LINE ||
    953                 parents[0].elementClass === Const.OBJECT_CLASS_LINE && (
    954                 parents[1].type === Const.OBJECT_TYPE_CONIC ||
    955                 parents[1].elementClass === Const.OBJECT_CLASS_CIRCLE))) {*/
    956     if (
    957         parents.length !== 2 ||
    958         !(
    959             (firstParentIsConic && secondParentIsLine) ||
    960             (firstParentIsLine && secondParentIsConic)
    961         )
    962     ) {
    963         // Failure
    964         throw new Error(
    965             "JSXGraph: Can't create 'pole point' with parent types '" +
    966                 typeof parents[0] +
    967                 "' and '" +
    968                 typeof parents[1] +
    969                 "'." +
    970                 "\nPossible parent type: [conic|circle,line], [line,conic|circle]"
    971         );
    972     }
    973
    974     if (secondParentIsLine) {
    975         el1 = board.select(parents[0]);
    976         el2 = board.select(parents[1]);
    977     } else {
    978         el1 = board.select(parents[1]);
    979         el2 = board.select(parents[0]);
    980     }
    981
    982     el = board.create(
    983         "point",
    984         [
    985             function () {
    986                 var q = el1.quadraticform,
    987                     s = el2.stdform.slice(0, 3);
    988
    989                 return [
    990                     JXG.Math.Numerics.det([s, q[1], q[2]]),
    991                     JXG.Math.Numerics.det([q[0], s, q[2]]),
    992                     JXG.Math.Numerics.det([q[0], q[1], s])
    993                 ];
    994             }
    995         ],
    996         attributes
    997     );
    998
    999     el.elType = "polepoint";
    1000     el.setParents([el1.id, el2.id]);
    1001
    1002     el1.addChild(el);
    1003     el2.addChild(el);
    1004
    1005     return el;
    1006 };
    1007
    1008 JXG.registerElement("point", JXG.createPoint);
    1009 JXG.registerElement("glider", JXG.createGlider);
    1010 JXG.registerElement("intersection", JXG.createIntersectionPoint);
    1011 JXG.registerElement("otherintersection", JXG.createOtherIntersectionPoint);
    1012 JXG.registerElement("polepoint", JXG.createPolePoint);
    1013
    1014 export default JXG.Point;
    1015 // export default {
    1016 //     Point: JXG.Point,
    1017 //     createPoint: JXG.createPoint,
    1018 //     createGlider: JXG.createGlider,
    1019 //     createIntersection: JXG.createIntersectionPoint,
    1020 //     createOtherIntersection: JXG.createOtherIntersectionPoint,
    1021 //     createPolePoint: JXG.createPolePoint
    1022 // };
    1023