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