      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  * @name Point
    467  * @augments JXG.Point
    468  * @constructor
    469  * @type JXG.Point
    470  * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
    471  * @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
    472  * constraint, or a function which takes no parameter and returns a number. Every parent element determines one coordinate. If a coordinate is
    473  * 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
    474  * 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
    475  * 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
    476  * parent elements are given they will be interpreted as homogeneous coordinates.
    477  * @param {JXG.Point_JXG.Transformation_Array} Point,Transformation A point can also be created providing a transformation or an array of transformations.
    478  * The resulting point is a clone of the base point transformed by the given Transformation. {@see JXG.Transformation}.
    479  *
    480  * @example
    481  * // Create a free point using affine Euclidean coordinates
    482  * var p1 = board.create('point', [3.5, 2.0]);
    483  * </pre><div class="jxgbox" id="JXG672f1764-7dfa-4abc-a2c6-81fbbf83e44b" style="width: 200px; height: 200px;"></div>
    484  * <script type="text/javascript">
    485  *   var board = JXG.JSXGraph.initBoard('JXG672f1764-7dfa-4abc-a2c6-81fbbf83e44b', {boundingbox: [-1, 5, 5, -1], axis: true, showcopyright: false, shownavigation: false});
    486  *   var p1 = board.create('point', [3.5, 2.0]);
    487  * </script><pre>
    488  * @example
    489  * // Create a constrained point using anonymous function
    490  * var p2 = board.create('point', [3.5, function () { return p1.X(); }]);
    491  * </pre><div class="jxgbox" id="JXG4fd4410c-3383-4e80-b1bb-961f5eeef224" style="width: 200px; height: 200px;"></div>
    492  * <script type="text/javascript">
    493  *   var fpex1_board = JXG.JSXGraph.initBoard('JXG4fd4410c-3383-4e80-b1bb-961f5eeef224', {boundingbox: [-1, 5, 5, -1], axis: true, showcopyright: false, shownavigation: false});
    494  *   var fpex1_p1 = fpex1_board.create('point', [3.5, 2.0]);
    495  *   var fpex1_p2 = fpex1_board.create('point', [3.5, function () { return fpex1_p1.X(); }]);
    496  * </script><pre>
    497  * @example
    498  * // Create a point using transformations
    499  * var trans = board.create('transform', [2, 0.5], {type:'scale'});
    500  * var p3 = board.create('point', [p2, trans]);
    501  * </pre><div class="jxgbox" id="JXG630afdf3-0a64-46e0-8a44-f51bd197bb8d" style="width: 400px; height: 400px;"></div>
    502  * <script type="text/javascript">
    503  *   var fpex2_board = JXG.JSXGraph.initBoard('JXG630afdf3-0a64-46e0-8a44-f51bd197bb8d', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false});
    504  *   var fpex2_trans = fpex2_board.create('transform', [2, 0.5], {type:'scale'});
    505  *   var fpex2_p2 = fpex2_board.create('point', [3.5, 2.0]);
    506  *   var fpex2_p3 = fpex2_board.create('point', [fpex2_p2, fpex2_trans]);
    507  * </script><pre>
    508  */
    509 JXG.createPoint = function (board, parents, attributes) {
    510     var el, attr;
    511
    512     attr = Type.copyAttributes(attributes, board.options, "point");
    513     el = CoordsElement.create(JXG.Point, board, parents, attr);
    514     if (!el) {
    515         throw new Error(
    516             "JSXGraph: Can't create point with parent types '" +
    517                 typeof parents[0] +
    518                 "' and '" +
    519                 typeof parents[1] +
    520                 "'." +
    521                 "\nPossible parent types: [x,y], [z,x,y], [element,transformation]"
    522         );
    523     }
    524
    525     return el;
    526 };
    527
    528 /**
    529  * @class This element is used to provide a constructor for a glider point.
    530  * @pseudo
    531  * @description A glider is a point which lives on another geometric element like a line, circle, curve, turtle.
    532  * @name Glider
    533  * @augments JXG.Point
    534  * @constructor
    535  * @type JXG.Point
    536  * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
    537  * @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.
    538  * 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
    539  * coordinates, otherwise they will be interpreted as homogeneous coordinates. In any case the point will be projected on the glide object.
    540  * @example
    541  * // Create a glider with user defined coordinates. If the coordinates are not on
    542  * // the circle (like in this case) the point will be projected onto the circle.
    543  * var p1 = board.create('point', [2.0, 2.0]);
    544  * var c1 = board.create('circle', [p1, 2.0]);
    545  * var p2 = board.create('glider', [2.0, 1.5, c1]);
    546  * </pre><div class="jxgbox" id="JXG4f65f32f-e50a-4b50-9b7c-f6ec41652930" style="width: 300px; height: 300px;"></div>
    547  * <script type="text/javascript">
    548  *   var gpex1_board = JXG.JSXGraph.initBoard('JXG4f65f32f-e50a-4b50-9b7c-f6ec41652930', {boundingbox: [-1, 5, 5, -1], axis: true, showcopyright: false, shownavigation: false});
    549  *   var gpex1_p1 = gpex1_board.create('point', [2.0, 2.0]);
    550  *   var gpex1_c1 = gpex1_board.create('circle', [gpex1_p1, 2.0]);
    551  *   var gpex1_p2 = gpex1_board.create('glider', [2.0, 1.5, gpex1_c1]);
    552  * </script><pre>
    553  * @example
    554  * // Create a glider with default coordinates (1,0,0). Same premises as above.
    555  * var p1 = board.create('point', [2.0, 2.0]);
    556  * var c1 = board.create('circle', [p1, 2.0]);
    557  * var p2 = board.create('glider', [c1]);
    558  * </pre><div class="jxgbox" id="JXG4de7f181-631a-44b1-a12f-bc4d995609e8" style="width: 200px; height: 200px;"></div>
    559  * <script type="text/javascript">
    560  *   var gpex2_board = JXG.JSXGraph.initBoard('JXG4de7f181-631a-44b1-a12f-bc4d995609e8', {boundingbox: [-1, 5, 5, -1], axis: true, showcopyright: false, shownavigation: false});
    561  *   var gpex2_p1 = gpex2_board.create('point', [2.0, 2.0]);
    562  *   var gpex2_c1 = gpex2_board.create('circle', [gpex2_p1, 2.0]);
    563  *   var gpex2_p2 = gpex2_board.create('glider', [gpex2_c1]);
    564  * </script><pre>
    565  *@example
    566  * //animate example 2
    567  * var p1 = board.create('point', [2.0, 2.0]);
    568  * var c1 = board.create('circle', [p1, 2.0]);
    569  * var p2 = board.create('glider', [c1]);
    570  * var button1 = board.create('button', [1, 7, 'start animation',function(){p2.startAnimation(1,4)}]);
    571  * var button2 = board.create('button', [1, 5, 'stop animation',function(){p2.stopAnimation()}]);
    572  * </pre><div class="jxgbox" id="JXG4de7f181-631a-44b1-a12f-bc4d133709e8" style="width: 200px; height: 200px;"></div>
    573  * <script type="text/javascript">
    574  *   var gpex3_board = JXG.JSXGraph.initBoard('JXG4de7f181-631a-44b1-a12f-bc4d133709e8', {boundingbox: [-1, 10, 10, -1], axis: true, showcopyright: false, shownavigation: false});
    575  *   var gpex3_p1 = gpex3_board.create('point', [2.0, 2.0]);
    576  *   var gpex3_c1 = gpex3_board.create('circle', [gpex3_p1, 2.0]);
    577  *   var gpex3_p2 = gpex3_board.create('glider', [gpex3_c1]);
    578  *   gpex3_board.create('button', [1, 7, 'start animation',function(){gpex3_p2.startAnimation(1,4)}]);
    579  *   gpex3_board.create('button', [1, 5, 'stop animation',function(){gpex3_p2.stopAnimation()}]);
    580  * </script><pre>
    581  */
    582 JXG.createGlider = function (board, parents, attributes) {
    583     var el,
    584         coords,
    585         attr = Type.copyAttributes(attributes, board.options, "glider");
    586
    587     if (parents.length === 1) {
    588         coords = [0, 0];
    589     } else {
    590         coords = parents.slice(0, 2);
    591     }
    592     el = board.create("point", coords, attr);
    593
    594     // eltype is set in here
    595     el.makeGlider(parents[parents.length - 1]);
    596
    597     return el;
    598 };
    599
    600 /**
    601  * @class An intersection point is a point which lives on two JSXGraph elements, i.e. it is one point of the set
    602  * consisting of the intersection points of the two elements. The following element types can be (mutually) intersected: line, circle,
    603  * curve, polygon, polygonal chain.
    604  *
    605  * @pseudo
    606  * @name Intersection
    607  * @augments JXG.Point
    608  * @constructor
    609  * @type JXG.Point
    610  * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
    611  * @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
    612  * intersection point if two points are available: <ul>
    613  *   <li>i==0: use the positive square root,</li>
    614  *   <li>i==1: use the negative square root.</li></ul>
    615  * @example
    616  * // Create an intersection point of circle and line
    617  * var p1 = board.create('point', [2.0, 2.0]);
    618  * var c1 = board.create('circle', [p1, 2.0]);
    619  *
    620  * var p2 = board.create('point', [2.0, 2.0]);
    621  * var p3 = board.create('point', [2.0, 2.0]);
    622  * var l1 = board.create('line', [p2, p3]);
    623  *
    624  * var i = board.create('intersection', [c1, l1, 0]);
    625  * </pre><div class="jxgbox" id="JXGe5b0e190-5200-4bc3-b995-b6cc53dc5dc0" style="width: 300px; height: 300px;"></div>
    626  * <script type="text/javascript">
    627  *   var ipex1_board = JXG.JSXGraph.initBoard('JXGe5b0e190-5200-4bc3-b995-b6cc53dc5dc0', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false});
    628  *   var ipex1_p1 = ipex1_board.create('point', [4.0, 4.0]);
    629  *   var ipex1_c1 = ipex1_board.create('circle', [ipex1_p1, 2.0]);
    630  *   var ipex1_p2 = ipex1_board.create('point', [1.0, 1.0]);
    631  *   var ipex1_p3 = ipex1_board.create('point', [5.0, 3.0]);
    632  *   var ipex1_l1 = ipex1_board.create('line', [ipex1_p2, ipex1_p3]);
    633  *   var ipex1_i = ipex1_board.create('intersection', [ipex1_c1, ipex1_l1, 0]);
    634  * </script><pre>
    635  */
    636 JXG.createIntersectionPoint = function (board, parents, attributes) {
    637     var el,
    638         el1,
    639         el2,
    640         func,
    641         i,
    642         j,
    643         attr = Type.copyAttributes(attributes, board.options, "intersection");
    644
    645     // make sure we definitely have the indices
    646     parents.push(0, 0);
    647
    648     el1 = board.select(parents[0]);
    649     el2 = board.select(parents[1]);
    650
    651     i = parents[2] || 0;
    652     j = parents[3] || 0;
    653
    654     el = board.create("point", [0, 0, 0], attr);
    655
    656     // el.visProp.alwaysintersect is evaluated as late as in the returned function
    657     func = Geometry.intersectionFunction(board, el1, el2, i, j, el.visProp.alwaysintersect);
    658     el.addConstraint([func]);
    659
    660     try {
    661         el1.addChild(el);
    662         el2.addChild(el);
    663     } catch (e) {
    664         throw new Error(
    665             "JSXGraph: Can't create 'intersection' with parent types '" +
    666                 typeof parents[0] +
    667                 "' and '" +
    668                 typeof parents[1] +
    669                 "'."
    670         );
    671     }
    672
    673     el.type = Const.OBJECT_TYPE_INTERSECTION;
    674     el.elType = "intersection";
    675     el.setParents([el1.id, el2.id]);
    676
    677     /**
    678      * Array of length 2 containing the numbers i and j.
    679      * The intersection point is i-th intersection point.
    680      * j is unused.
    681      * @type Array
    682      * @name intersectionNumbers
    683      * @memberOf Intersection
    684      * @private
    685      */
    686     el.intersectionNumbers = [i, j];
    687     el.getParents = function () {
    688         return this.parents.concat(this.intersectionNumbers);
    689     };
    690
    691     el.generatePolynomial = function () {
    692         var poly1 = el1.generatePolynomial(el),
    693             poly2 = el2.generatePolynomial(el);
    694
    695         if (poly1.length === 0 || poly2.length === 0) {
    696             return [];
    697         }
    698
    699         return [poly1[0], poly2[0]];
    700     };
    701
    702     return el;
    703 };
    704
    705 /**
    706  * @class This element is used to provide a constructor for the "other" intersection point.
    707  * @pseudo
    708  * @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.
    709  * an intersection point of the two elements. Additionally, one intersection point is provided. The function returns the other intersection point.
    710  * @name OtherIntersection
    711  * @augments JXG.Point
    712  * @constructor
    713  * @type JXG.Point
    714  * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
    715  * @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
    716  * intersection point different from p:
    717  * @example
    718  * // Create an intersection point of circle and line
    719  * var p1 = board.create('point', [2.0, 2.0]);
    720  * var c1 = board.create('circle', [p1, 2.0]);
    721  *
    722  * var p2 = board.create('point', [2.0, 2.0]);
    723  * var p3 = board.create('point', [2.0, 2.0]);
    724  * var l1 = board.create('line', [p2, p3]);
    725  *
    726  * var i = board.create('intersection', [c1, l1, 0]);
    727  * var j = board.create('otherintersection', [c1, l1, i]);
    728  * </pre><div class="jxgbox" id="JXG45e25f12-a1de-4257-a466-27a2ae73614c" style="width: 300px; height: 300px;"></div>
    729  * <script type="text/javascript">
    730  *   var ipex2_board = JXG.JSXGraph.initBoard('JXG45e25f12-a1de-4257-a466-27a2ae73614c', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false});
    731  *   var ipex2_p1 = ipex2_board.create('point', [4.0, 4.0]);
    732  *   var ipex2_c1 = ipex2_board.create('circle', [ipex2_p1, 2.0]);
    733  *   var ipex2_p2 = ipex2_board.create('point', [1.0, 1.0]);
    734  *   var ipex2_p3 = ipex2_board.create('point', [5.0, 3.0]);
    735  *   var ipex2_l1 = ipex2_board.create('line', [ipex2_p2, ipex2_p3]);
    736  *   var ipex2_i = ipex2_board.create('intersection', [ipex2_c1, ipex2_l1, 0], {name:'D'});
    737  *   var ipex2_j = ipex2_board.create('otherintersection', [ipex2_c1, ipex2_l1, ipex2_i], {name:'E'});
    738  * </script><pre>
    739  */
    740 JXG.createOtherIntersectionPoint = function (board, parents, attributes) {
    741     var el, el1, el2, other;
    742
    743     if (
    744         parents.length !== 3 ||
    745         !Type.isPoint(parents[2]) ||
    746         (parents[0].elementClass !== Const.OBJECT_CLASS_LINE &&
    747             parents[0].elementClass !== Const.OBJECT_CLASS_CIRCLE) ||
    748         (parents[1].elementClass !== Const.OBJECT_CLASS_LINE &&
    749             parents[1].elementClass !== Const.OBJECT_CLASS_CIRCLE)
    750     ) {
    751         // Failure
    752         throw new Error(
    753             "JSXGraph: Can't create 'other intersection point' with parent types '" +
    754                 typeof parents[0] +
    755                 "',  '" +
    756                 typeof parents[1] +
    757                 "'and  '" +
    758                 typeof parents[2] +
    759                 "'." +
    760                 "\nPossible parent types: [circle|line,circle|line,point]"
    761         );
    762     }
    763
    764     el1 = board.select(parents[0]);
    765     el2 = board.select(parents[1]);
    766     other = board.select(parents[2]);
    767
    768     el = board.create(
    769         "point",
    770         [
    771             function () {
    772                 var c = Geometry.meet(el1.stdform, el2.stdform, 0, el1.board);
    773
    774                 if (
    775                     Math.abs(other.X() - c.usrCoords[1]) > Mat.eps ||
    776                     Math.abs(other.Y() - c.usrCoords[2]) > Mat.eps ||
    777                     Math.abs(other.Z() - c.usrCoords[0]) > Mat.eps
    778                 ) {
    779                     return c;
    780                 }
    781
    782                 return Geometry.meet(el1.stdform, el2.stdform, 1, el1.board);
    783             }
    784         ],
    785         attributes
    786     );
    787
    788     el.type = Const.OBJECT_TYPE_INTERSECTION;
    789     el.elType = "otherintersection";
    790     el.setParents([el1.id, el2.id, other]);
    791
    792     el1.addChild(el);
    793     el2.addChild(el);
    794
    795     el.generatePolynomial = function () {
    796         var poly1 = el1.generatePolynomial(el),
    797             poly2 = el2.generatePolynomial(el);
    798
    799         if (poly1.length === 0 || poly2.length === 0) {
    800             return [];
    801         }
    802
    803         return [poly1[0], poly2[0]];
    804     };
    805
    806     return el;
    807 };
    808
    809 /**
    810  * @class This element is used to provide a constructor for the pole point of a line with respect to a conic or a circle.
    811  * @pseudo
    812  * @description The pole point is the unique reciprocal relationship of a line with respect to a conic.
    813  * 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.
    814  * A line tangent to a conic has the pole point of that line with respect to that conic as the tangent point.
    815  * See {@link https://en.wikipedia.org/wiki/Pole_and_polar} for more information on pole and polar.
    816  * @name PolePoint
    817  * @augments JXG.Point
    818  * @constructor
    819  * @type JXG.Point
    820  * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
    821  * @param {JXG.Conic,JXG.Circle_JXG.Point} el1,el2 or
    822  * @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.
    823  * @example
    824  * // Create the pole point of a line with respect to a conic
    825  * var p1 = board.create('point', [-1, 2]);
    826  * var p2 = board.create('point', [ 1, 4]);
    827  * var p3 = board.create('point', [-1,-2]);
    828  * var p4 = board.create('point', [ 0, 0]);
    829  * var p5 = board.create('point', [ 4,-2]);
    830  * var c1 = board.create('conic',[p1,p2,p3,p4,p5]);
    831  * var p6 = board.create('point', [-1, 4]);
    832  * var p7 = board.create('point', [2, -2]);
    833  * var l1 = board.create('line', [p6, p7]);
    834  * var p8 = board.create('polepoint', [c1, l1]);
    835  * </pre><div class="jxgbox" id="JXG7b7233a0-f363-47dd-9df5-8018d0d17a98" class="jxgbox" style="width:400px; height:400px;"></div>
    836  * <script type='text/javascript'>
    837  * var ppex1_board = JXG.JSXGraph.initBoard('JXG7b7233a0-f363-47dd-9df5-8018d0d17a98', {boundingbox: [-3, 5, 5, -3], axis: true, showcopyright: false, shownavigation: false});
    838  * var ppex1_p1 = ppex1_board.create('point', [-1, 2]);
    839  * var ppex1_p2 = ppex1_board.create('point', [ 1, 4]);
    840  * var ppex1_p3 = ppex1_board.create('point', [-1,-2]);
    841  * var ppex1_p4 = ppex1_board.create('point', [ 0, 0]);
    842  * var ppex1_p5 = ppex1_board.create('point', [ 4,-2]);
    843  * var ppex1_c1 = ppex1_board.create('conic',[ppex1_p1,ppex1_p2,ppex1_p3,ppex1_p4,ppex1_p5]);
    844  * var ppex1_p6 = ppex1_board.create('point', [-1, 4]);
    845  * var ppex1_p7 = ppex1_board.create('point', [2, -2]);
    846  * var ppex1_l1 = ppex1_board.create('line', [ppex1_p6, ppex1_p7]);
    847  * var ppex1_p8 = ppex1_board.create('polepoint', [ppex1_c1, ppex1_l1]);
    848  * </script><pre>
    849  * @example
    850  * // Create the pole point of a line with respect to a circle
    851  * var p1 = board.create('point', [1, 1]);
    852  * var p2 = board.create('point', [2, 3]);
    853  * var c1 = board.create('circle',[p1,p2]);
    854  * var p3 = board.create('point', [-1, 4]);
    855  * var p4 = board.create('point', [4, -1]);
    856  * var l1 = board.create('line', [p3, p4]);
    857  * var p5 = board.create('polepoint', [c1, l1]);
    858  * </pre><div class="jxgbox" id="JXG7b7233a0-f363-47dd-9df5-9018d0d17a98" class="jxgbox" style="width:400px; height:400px;"></div>
    859  * <script type='text/javascript'>
    860  * var ppex2_board = JXG.JSXGraph.initBoard('JXG7b7233a0-f363-47dd-9df5-9018d0d17a98', {boundingbox: [-3, 7, 7, -3], axis: true, showcopyright: false, shownavigation: false});
    861  * var ppex2_p1 = ppex2_board.create('point', [1, 1]);
    862  * var ppex2_p2 = ppex2_board.create('point', [2, 3]);
    863  * var ppex2_c1 = ppex2_board.create('circle',[ppex2_p1,ppex2_p2]);
    864  * var ppex2_p3 = ppex2_board.create('point', [-1, 4]);
    865  * var ppex2_p4 = ppex2_board.create('point', [4, -1]);
    866  * var ppex2_l1 = ppex2_board.create('line', [ppex2_p3, ppex2_p4]);
    867  * var ppex2_p5 = ppex2_board.create('polepoint', [ppex2_c1, ppex2_l1]);
    868  * </script><pre>
    869  */
    870 JXG.createPolePoint = function (board, parents, attributes) {
    871     var el,
    872         el1,
    873         el2,
    874         firstParentIsConic,
    875         secondParentIsConic,
    876         firstParentIsLine,
    877         secondParentIsLine;
    878
    879     if (parents.length > 1) {
    880         firstParentIsConic =
    881             parents[0].type === Const.OBJECT_TYPE_CONIC ||
    882             parents[0].elementClass === Const.OBJECT_CLASS_CIRCLE;
    883         secondParentIsConic =
    884             parents[1].type === Const.OBJECT_TYPE_CONIC ||
    885             parents[1].elementClass === Const.OBJECT_CLASS_CIRCLE;
    886
    887         firstParentIsLine = parents[0].elementClass === Const.OBJECT_CLASS_LINE;
    888         secondParentIsLine = parents[1].elementClass === Const.OBJECT_CLASS_LINE;
    889     }
    890
    891     /*        if (parents.length !== 2 || !((
    892                 parents[0].type === Const.OBJECT_TYPE_CONIC ||
    893                 parents[0].elementClass === Const.OBJECT_CLASS_CIRCLE) &&
    894                 parents[1].elementClass === Const.OBJECT_CLASS_LINE ||
    895                 parents[0].elementClass === Const.OBJECT_CLASS_LINE && (
    896                 parents[1].type === Const.OBJECT_TYPE_CONIC ||
    897                 parents[1].elementClass === Const.OBJECT_CLASS_CIRCLE))) {*/
    898     if (
    899         parents.length !== 2 ||
    900         !(
    901             (firstParentIsConic && secondParentIsLine) ||
    902             (firstParentIsLine && secondParentIsConic)
    903         )
    904     ) {
    905         // Failure
    906         throw new Error(
    907             "JSXGraph: Can't create 'pole point' with parent types '" +
    908                 typeof parents[0] +
    909                 "' and '" +
    910                 typeof parents[1] +
    911                 "'." +
    912                 "\nPossible parent type: [conic|circle,line], [line,conic|circle]"
    913         );
    914     }
    915
    916     if (secondParentIsLine) {
    917         el1 = board.select(parents[0]);
    918         el2 = board.select(parents[1]);
    919     } else {
    920         el1 = board.select(parents[1]);
    921         el2 = board.select(parents[0]);
    922     }
    923
    924     el = board.create(
    925         "point",
    926         [
    927             function () {
    928                 var q = el1.quadraticform,
    929                     s = el2.stdform.slice(0, 3);
    930
    931                 return [
    932                     JXG.Math.Numerics.det([s, q[1], q[2]]),
    933                     JXG.Math.Numerics.det([q[0], s, q[2]]),
    934                     JXG.Math.Numerics.det([q[0], q[1], s])
    935                 ];
    936             }
    937         ],
    938         attributes
    939     );
    940
    941     el.elType = "polepoint";
    942     el.setParents([el1.id, el2.id]);
    943
    944     el1.addChild(el);
    945     el2.addChild(el);
    946
    947     return el;
    948 };
    949
    950 JXG.registerElement("point", JXG.createPoint);
    951 JXG.registerElement("glider", JXG.createGlider);
    952 JXG.registerElement("intersection", JXG.createIntersectionPoint);
    953 JXG.registerElement("otherintersection", JXG.createOtherIntersectionPoint);
    954 JXG.registerElement("polepoint", JXG.createPolePoint);
    955
    956 export default JXG.Point;
    957 // export default {
    958 //     Point: JXG.Point,
    959 //     createPoint: JXG.createPoint,
    960 //     createGlider: JXG.createGlider,
    961 //     createIntersection: JXG.createIntersectionPoint,
    962 //     createOtherIntersection: JXG.createOtherIntersectionPoint,
    963 //     createPolePoint: JXG.createPolePoint
    964 // };
    965