      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*/
     33 /*jslint nomen: true, plusplus: true*/
     34
     35 /**
     36  * @fileoverview In this file the geometry element Curve is defined.
     37  */
     38
     39 import JXG from "../jxg";
     40 import Const from "./constants";
     41 import Coords from "./coords";
     42 import GeometryElement from "./element";
     43 import Mat from "../math/math";
     44 import Numerics from "../math/numerics";
     45 import Plot from "../math/plot";
     46 import Geometry from "../math/geometry";
     47 import GeonextParser from "../parser/geonext";
     48 import Type from "../utils/type";
     49 import QDT from "../math/qdt";
     50
     51 /**
     52  * Curves are the common object for function graphs, parametric curves, polar curves, and data plots.
     53  * @class Creates a new curve object. Do not use this constructor to create a curve. Use {@link JXG.Board#create} with
     54  * type {@link Curve}, or {@link Functiongraph} instead.
     55  * @augments JXG.GeometryElement
     56  * @param {String|JXG.Board} board The board the new curve is drawn on.
     57  * @param {Array} parents defining terms An array with the function terms or the data points of the curve.
     58  * @param {Object} attributes Defines the visual appearance of the curve.
     59  * @see JXG.Board#generateName
     60  * @see JXG.Board#addCurve
     61  */
     62 JXG.Curve = function (board, parents, attributes) {
     63     this.constructor(board, attributes, Const.OBJECT_TYPE_CURVE, Const.OBJECT_CLASS_CURVE);
     64
     65     this.points = [];
     66     /**
     67      * Number of points on curves. This value changes
     68      * between numberPointsLow and numberPointsHigh.
     69      * It is set in {@link JXG.Curve#updateCurve}.
     70      */
     71     this.numberPoints = Type.evaluate(this.visProp.numberpointshigh);
     72
     73     this.bezierDegree = 1;
     74
     75     /**
     76      * Array holding the x-coordinates of a data plot.
     77      * This array can be updated during run time by overwriting
     78      * the method {@link JXG.Curve#updateDataArray}.
     79      * @type array
     80      */
     81     this.dataX = null;
     82
     83     /**
     84      * Array holding the y-coordinates of a data plot.
     85      * This array can be updated during run time by overwriting
     86      * the method {@link JXG.Curve#updateDataArray}.
     87      * @type array
     88      */
     89     this.dataY = null;
     90
     91     /**
     92      * Array of ticks storing all the ticks on this curve. Do not set this field directly and use
     93      * {@link JXG.Curve#addTicks} and {@link JXG.Curve#removeTicks} to add and remove ticks to and
     94      * from the curve.
     95      * @type Array
     96      * @see JXG.Ticks
     97      */
     98     this.ticks = [];
     99
    100     /**
    101      * Stores a quadtree if it is required. The quadtree is generated in the curve
    102      * updates and can be used to speed up the hasPoint method.
    103      * @type JXG.Math.Quadtree
    104      */
    105     this.qdt = null;
    106
    107     if (Type.exists(parents[0])) {
    108         this.varname = parents[0];
    109     } else {
    110         this.varname = "x";
    111     }
    112
    113     // function graphs: "x"
    114     this.xterm = parents[1];
    115     // function graphs: e.g. "x^2"
    116     this.yterm = parents[2];
    117
    118     // Converts GEONExT syntax into JavaScript syntax
    119     this.generateTerm(this.varname, this.xterm, this.yterm, parents[3], parents[4]);
    120     // First evaluation of the curve
    121     this.updateCurve();
    122
    123     this.id = this.board.setId(this, "G");
    124     this.board.renderer.drawCurve(this);
    125
    126     this.board.finalizeAdding(this);
    127
    128     this.createGradient();
    129     this.elType = "curve";
    130     this.createLabel();
    131
    132     if (Type.isString(this.xterm)) {
    133         this.notifyParents(this.xterm);
    134     }
    135     if (Type.isString(this.yterm)) {
    136         this.notifyParents(this.yterm);
    137     }
    138
    139     this.methodMap = Type.deepCopy(this.methodMap, {
    140         generateTerm: "generateTerm",
    141         setTerm: "generateTerm",
    142         move: "moveTo",
    143         moveTo: "moveTo"
    144     });
    145 };
    146
    147 JXG.Curve.prototype = new GeometryElement();
    148
    149 JXG.extend(
    150     JXG.Curve.prototype,
    151     /** @lends JXG.Curve.prototype */ {
    152         /**
    153          * Gives the default value of the left bound for the curve.
    154          * May be overwritten in {@link JXG.Curve#generateTerm}.
    155          * @returns {Number} Left bound for the curve.
    156          */
    157         minX: function () {
    158             var leftCoords;
    159
    160             if (Type.evaluate(this.visProp.curvetype) === "polar") {
    161                 return 0;
    162             }
    163
    164             leftCoords = new Coords(
    165                 Const.COORDS_BY_SCREEN,
    166                 [-this.board.canvasWidth * 0.1, 0],
    167                 this.board,
    168                 false
    169             );
    170             return leftCoords.usrCoords[1];
    171         },
    172
    173         /**
    174          * Gives the default value of the right bound for the curve.
    175          * May be overwritten in {@link JXG.Curve#generateTerm}.
    176          * @returns {Number} Right bound for the curve.
    177          */
    178         maxX: function () {
    179             var rightCoords;
    180
    181             if (Type.evaluate(this.visProp.curvetype) === "polar") {
    182                 return 2 * Math.PI;
    183             }
    184             rightCoords = new Coords(
    185                 Const.COORDS_BY_SCREEN,
    186                 [this.board.canvasWidth * 1.1, 0],
    187                 this.board,
    188                 false
    189             );
    190
    191             return rightCoords.usrCoords[1];
    192         },
    193
    194         /**
    195          * The parametric function which defines the x-coordinate of the curve.
    196          * @param {Number} t A number between {@link JXG.Curve#minX} and {@link JXG.Curve#maxX}.
    197          * @param {Boolean} suspendUpdate A boolean flag which is false for the
    198          * first call of the function during a fresh plot of the curve and true
    199          * for all subsequent calls of the function. This may be used to speed up the
    200          * plotting of the curve, if the e.g. the curve depends on some input elements.
    201          * @returns {Number} x-coordinate of the curve at t.
    202          */
    203         X: function (t) {
    204             return NaN;
    205         },
    206
    207         /**
    208          * The parametric function which defines the y-coordinate of the curve.
    209          * @param {Number} t A number between {@link JXG.Curve#minX} and {@link JXG.Curve#maxX}.
    210          * @param {Boolean} suspendUpdate A boolean flag which is false for the
    211          * first call of the function during a fresh plot of the curve and true
    212          * for all subsequent calls of the function. This may be used to speed up the
    213          * plotting of the curve, if the e.g. the curve depends on some input elements.
    214          * @returns {Number} y-coordinate of the curve at t.
    215          */
    216         Y: function (t) {
    217             return NaN;
    218         },
    219
    220         /**
    221          * Treat the curve as curve with homogeneous coordinates.
    222          * @param {Number} t A number between {@link JXG.Curve#minX} and {@link JXG.Curve#maxX}.
    223          * @returns {Number} Always 1.0
    224          */
    225         Z: function (t) {
    226             return 1;
    227         },
    228
    229         /**
    230          * Checks whether (x,y) is near the curve.
    231          * @param {Number} x Coordinate in x direction, screen coordinates.
    232          * @param {Number} y Coordinate in y direction, screen coordinates.
    233          * @param {Number} start Optional start index for search on data plots.
    234          * @returns {Boolean} True if (x,y) is near the curve, False otherwise.
    235          */
    236         hasPoint: function (x, y, start) {
    237             var t, c, i, tX, tY,
    238                 checkPoint, len, invMat, isIn,
    239                 res = [],
    240                 points,
    241                 qdt,
    242                 steps = Type.evaluate(this.visProp.numberpointslow),
    243                 d = (this.maxX() - this.minX()) / steps,
    244                 prec, type,
    245                 dist = Infinity,
    246                 ux2, uy2,
    247                 ev_ct,
    248                 mi, ma,
    249                 suspendUpdate = true;
    250
    251             if (Type.isObject(Type.evaluate(this.visProp.precision))) {
    252                 type = this.board._inputDevice;
    253                 prec = Type.evaluate(this.visProp.precision[type]);
    254             } else {
    255                 // 'inherit'
    256                 prec = this.board.options.precision.hasPoint;
    257             }
    258
    259             // From now on, x,y are usrCoords
    260             checkPoint = new Coords(Const.COORDS_BY_SCREEN, [x, y], this.board, false);
    261             x = checkPoint.usrCoords[1];
    262             y = checkPoint.usrCoords[2];
    263
    264             // Handle inner points of the curve
    265             if (this.bezierDegree === 1 && Type.evaluate(this.visProp.hasinnerpoints)) {
    266                 isIn = Geometry.windingNumber([1, x, y], this.points, true);
    267                 if (isIn !== 0) {
    268                     return true;
    269                 }
    270             }
    271
    272             // We use usrCoords. Only in the final distance calculation
    273             // screen coords are used
    274             prec += Type.evaluate(this.visProp.strokewidth) * 0.5;
    275             prec *= prec; // We do not want to take sqrt
    276             ux2 = this.board.unitX * this.board.unitX;
    277             uy2 = this.board.unitY * this.board.unitY;
    278
    279             mi = this.minX();
    280             ma = this.maxX();
    281             if (Type.exists(this._visibleArea)) {
    282                 mi = this._visibleArea[0];
    283                 ma = this._visibleArea[1];
    284                 d = (ma - mi) / steps;
    285             }
    286
    287             ev_ct = Type.evaluate(this.visProp.curvetype);
    288             if (ev_ct === "parameter" || ev_ct === "polar") {
    289                 // Transform the mouse/touch coordinates
    290                 // back to the original position of the curve.
    291                 // This is needed, because we work with the function terms, not the points.
    292                 if (this.transformations.length > 0) {
    293                     this.updateTransformMatrix();
    294                     invMat = Mat.inverse(this.transformMat);
    295                     c = Mat.matVecMult(invMat, [1, x, y]);
    296                     x = c[1];
    297                     y = c[2];
    298                 }
    299
    300                 // Brute force search for a point on the curve close to the mouse pointer
    301                 for (i = 0, t = mi; i < steps; i++) {
    302                     tX = this.X(t, suspendUpdate);
    303                     tY = this.Y(t, suspendUpdate);
    304
    305                     dist = (x - tX) * (x - tX) * ux2 + (y - tY) * (y - tY) * uy2;
    306
    307                     if (dist <= prec) {
    308                         return true;
    309                     }
    310
    311                     t += d;
    312                 }
    313             } else if (ev_ct === "plot" || ev_ct === "functiongraph") {
    314                 // Here, we can ignore transformations of the curve,
    315                 // since we are working directly with the points.
    316
    317                 if (!Type.exists(start) || start < 0) {
    318                     start = 0;
    319                 }
    320
    321                 if (
    322                     Type.exists(this.qdt) &&
    323                     Type.evaluate(this.visProp.useqdt) &&
    324                     this.bezierDegree !== 3
    325                 ) {
    326                     qdt = this.qdt.query(new Coords(Const.COORDS_BY_USER, [x, y], this.board));
    327                     points = qdt.points;
    328                     len = points.length;
    329                 } else {
    330                     points = this.points;
    331                     len = this.numberPoints - 1;
    332                 }
    333
    334                 for (i = start; i < len; i++) {
    335                     if (this.bezierDegree === 3) {
    336                         //res.push(Geometry.projectCoordsToBeziersegment([1, x, y], this, i));
    337                         res = Geometry.projectCoordsToBeziersegment([1, x, y], this, i);
    338                     } else {
    339                         if (qdt) {
    340                             if (points[i].prev) {
    341                                 res = Geometry.projectCoordsToSegment(
    342                                     [1, x, y],
    343                                     points[i].prev.usrCoords,
    344                                     points[i].usrCoords
    345                                 );
    346                             }
    347
    348                             // If the next point in the array is the same as the current points
    349                             // next neighbor we don't have to project it onto that segment because
    350                             // that will already be done in the next iteration of this loop.
    351                             if (points[i].next && points[i + 1] !== points[i].next) {
    352                                 res = Geometry.projectCoordsToSegment(
    353                                     [1, x, y],
    354                                     points[i].usrCoords,
    355                                     points[i].next.usrCoords
    356                                 );
    357                             }
    358                         } else {
    359                             res = Geometry.projectCoordsToSegment(
    360                                 [1, x, y],
    361                                 points[i].usrCoords,
    362                                 points[i + 1].usrCoords
    363                             );
    364                         }
    365                     }
    366
    367                     if (
    368                         res[1] >= 0 &&
    369                         res[1] <= 1 &&
    370                         (x - res[0][1]) * (x - res[0][1]) * ux2 +
    371                             (y - res[0][2]) * (y - res[0][2]) * uy2 <=
    372                             prec
    373                     ) {
    374                         return true;
    375                     }
    376                 }
    377                 return false;
    378             }
    379             return dist < prec;
    380         },
    381
    382         /**
    383          * Allocate points in the Coords array this.points
    384          */
    385         allocatePoints: function () {
    386             var i, len;
    387
    388             len = this.numberPoints;
    389
    390             if (this.points.length < this.numberPoints) {
    391                 for (i = this.points.length; i < len; i++) {
    392                     this.points[i] = new Coords(
    393                         Const.COORDS_BY_USER,
    394                         [0, 0],
    395                         this.board,
    396                         false
    397                     );
    398                 }
    399             }
    400         },
    401
    402         /**
    403          * Computes for equidistant points on the x-axis the values of the function
    404          * @returns {JXG.Curve} Reference to the curve object.
    405          * @see JXG.Curve#updateCurve
    406          */
    407         update: function () {
    408             if (this.needsUpdate) {
    409                 if (Type.evaluate(this.visProp.trace)) {
    410                     this.cloneToBackground(true);
    411                 }
    412                 this.updateCurve();
    413             }
    414
    415             return this;
    416         },
    417
    418         /**
    419          * Updates the visual contents of the curve.
    420          * @returns {JXG.Curve} Reference to the curve object.
    421          */
    422         updateRenderer: function () {
    423             //var wasReal;
    424
    425             if (!this.needsUpdate) {
    426                 return this;
    427             }
    428
    429             if (this.visPropCalc.visible) {
    430                 // wasReal = this.isReal;
    431
    432                 this.isReal = Plot.checkReal(this.points);
    433
    434                 if (
    435                     //wasReal &&
    436                     !this.isReal
    437                 ) {
    438                     this.updateVisibility(false);
    439                 }
    440             }
    441
    442             if (this.visPropCalc.visible) {
    443                 this.board.renderer.updateCurve(this);
    444             }
    445
    446             /* Update the label if visible. */
    447             if (
    448                 this.hasLabel &&
    449                 this.visPropCalc.visible &&
    450                 this.label &&
    451                 this.label.visPropCalc.visible &&
    452                 this.isReal
    453             ) {
    454                 this.label.update();
    455                 this.board.renderer.updateText(this.label);
    456             }
    457
    458             // Update rendNode display
    459             this.setDisplayRendNode();
    460             // if (this.visPropCalc.visible !== this.visPropOld.visible) {
    461             //     this.board.renderer.display(this, this.visPropCalc.visible);
    462             //     this.visPropOld.visible = this.visPropCalc.visible;
    463             //
    464             //     if (this.hasLabel) {
    465             //         this.board.renderer.display(this.label, this.label.visPropCalc.visible);
    466             //     }
    467             // }
    468
    469             this.needsUpdate = false;
    470             return this;
    471         },
    472
    473         /**
    474          * For dynamic dataplots updateCurve can be used to compute new entries
    475          * for the arrays {@link JXG.Curve#dataX} and {@link JXG.Curve#dataY}. It
    476          * is used in {@link JXG.Curve#updateCurve}. Default is an empty method, can
    477          * be overwritten by the user.
    478          *
    479          *
    480          * @example
    481          * // This example overwrites the updateDataArray method.
    482          * // There, new values for the arrays JXG.Curve.dataX and JXG.Curve.dataY
    483          * // are computed from the value of the slider N
    484          *
    485          * var N = board.create('slider', [[0,1.5],[3,1.5],[1,3,40]], {name:'n',snapWidth:1});
    486          * var circ = board.create('circle',[[4,-1.5],1],{strokeWidth:1, strokecolor:'black', strokeWidth:2,
    487          * 		fillColor:'#0055ff13'});
    488          *
    489          * var c = board.create('curve', [[0],[0]],{strokecolor:'red', strokeWidth:2});
    490          * c.updateDataArray = function() {
    491          *         var r = 1, n = Math.floor(N.Value()),
    492          *             x = [0], y = [0],
    493          *             phi = Math.PI/n,
    494          *             h = r*Math.cos(phi),
    495          *             s = r*Math.sin(phi),
    496          *             i, j,
    497          *             px = 0, py = 0, sgn = 1,
    498          *             d = 16,
    499          *             dt = phi/d,
    500          *             pt;
    501          *
    502          *         for (i = 0; i < n; i++) {
    503          *             for (j = -d; j <= d; j++) {
    504          *                 pt = dt*j;
    505          *                 x.push(px + r*Math.sin(pt));
    506          *                 y.push(sgn*r*Math.cos(pt) - (sgn-1)*h*0.5);
    507          *             }
    508          *             px += s;
    509          *             sgn *= (-1);
    510          *         }
    511          *         x.push((n - 1)*s);
    512          *         y.push(h + (sgn - 1)*h*0.5);
    513          *         this.dataX = x;
    514          *         this.dataY = y;
    515          *     }
    516          *
    517          * var c2 = board.create('curve', [[0],[0]],{strokecolor:'red', strokeWidth:1});
    518          * c2.updateDataArray = function() {
    519          *         var r = 1, n = Math.floor(N.Value()),
    520          *             px = circ.midpoint.X(), py = circ.midpoint.Y(),
    521          *             x = [px], y = [py],
    522          *             phi = Math.PI/n,
    523          *             s = r*Math.sin(phi),
    524          *             i, j,
    525          *             d = 16,
    526          *             dt = phi/d,
    527          *             pt = Math.PI*0.5+phi;
    528          *
    529          *         for (i = 0; i < n; i++) {
    530          *             for (j= -d; j <= d; j++) {
    531          *                 x.push(px + r*Math.cos(pt));
    532          *                 y.push(py + r*Math.sin(pt));
    533          *                 pt -= dt;
    534          *             }
    535          *             x.push(px);
    536          *             y.push(py);
    537          *             pt += dt;
    538          *         }
    539          *         this.dataX = x;
    540          *         this.dataY = y;
    541          *     }
    542          *     board.update();
    543          *
    544          * </pre><div id="JXG20bc7802-e69e-11e5-b1bf-901b0e1b8723" class="jxgbox" style="width: 600px; height: 400px;"></div>
    545          * <script type="text/javascript">
    546          *     (function() {
    547          *         var board = JXG.JSXGraph.initBoard('JXG20bc7802-e69e-11e5-b1bf-901b0e1b8723',
    548          *             {boundingbox: [-1.5,2,8,-3], keepaspectratio: true, axis: true, showcopyright: false, shownavigation: false});
    549          *             var N = board.create('slider', [[0,1.5],[3,1.5],[1,3,40]], {name:'n',snapWidth:1});
    550          *             var circ = board.create('circle',[[4,-1.5],1],{strokeWidth:1, strokecolor:'black',
    551          *             strokeWidth:2, fillColor:'#0055ff13'});
    552          *
    553          *             var c = board.create('curve', [[0],[0]],{strokecolor:'red', strokeWidth:2});
    554          *             c.updateDataArray = function() {
    555          *                     var r = 1, n = Math.floor(N.Value()),
    556          *                         x = [0], y = [0],
    557          *                         phi = Math.PI/n,
    558          *                         h = r*Math.cos(phi),
    559          *                         s = r*Math.sin(phi),
    560          *                         i, j,
    561          *                         px = 0, py = 0, sgn = 1,
    562          *                         d = 16,
    563          *                         dt = phi/d,
    564          *                         pt;
    565          *
    566          *                     for (i=0;i<n;i++) {
    567          *                         for (j=-d;j<=d;j++) {
    568          *                             pt = dt*j;
    569          *                             x.push(px+r*Math.sin(pt));
    570          *                             y.push(sgn*r*Math.cos(pt)-(sgn-1)*h*0.5);
    571          *                         }
    572          *                         px += s;
    573          *                         sgn *= (-1);
    574          *                     }
    575          *                     x.push((n-1)*s);
    576          *                     y.push(h+(sgn-1)*h*0.5);
    577          *                     this.dataX = x;
    578          *                     this.dataY = y;
    579          *                 }
    580          *
    581          *             var c2 = board.create('curve', [[0],[0]],{strokecolor:'red', strokeWidth:1});
    582          *             c2.updateDataArray = function() {
    583          *                     var r = 1, n = Math.floor(N.Value()),
    584          *                         px = circ.midpoint.X(), py = circ.midpoint.Y(),
    585          *                         x = [px], y = [py],
    586          *                         phi = Math.PI/n,
    587          *                         s = r*Math.sin(phi),
    588          *                         i, j,
    589          *                         d = 16,
    590          *                         dt = phi/d,
    591          *                         pt = Math.PI*0.5+phi;
    592          *
    593          *                     for (i=0;i<n;i++) {
    594          *                         for (j=-d;j<=d;j++) {
    595          *                             x.push(px+r*Math.cos(pt));
    596          *                             y.push(py+r*Math.sin(pt));
    597          *                             pt -= dt;
    598          *                         }
    599          *                         x.push(px);
    600          *                         y.push(py);
    601          *                         pt += dt;
    602          *                     }
    603          *                     this.dataX = x;
    604          *                     this.dataY = y;
    605          *                 }
    606          *                 board.update();
    607          *
    608          *     })();
    609          *
    610          * </script><pre>
    611          *
    612          * @example
    613          * // This is an example which overwrites updateDataArray and produces
    614          * // a Bezier curve of degree three.
    615          * var A = board.create('point', [-3,3]);
    616          * var B = board.create('point', [3,-2]);
    617          * var line = board.create('segment', [A,B]);
    618          *
    619          * var height = 0.5; // height of the curly brace
    620          *
    621          * // Curly brace
    622          * var crl = board.create('curve', [[0],[0]], {strokeWidth:1, strokeColor:'black'});
    623          * crl.bezierDegree = 3;
    624          * crl.updateDataArray = function() {
    625          *     var d = [B.X()-A.X(), B.Y()-A.Y()],
    626          *         dl = Math.sqrt(d[0]*d[0]+d[1]*d[1]),
    627          *         mid = [(A.X()+B.X())*0.5, (A.Y()+B.Y())*0.5];
    628          *
    629          *     d[0] *= height/dl;
    630          *     d[1] *= height/dl;
    631          *
    632          *     this.dataX = [ A.X(), A.X()-d[1], mid[0], mid[0]-d[1], mid[0], B.X()-d[1], B.X() ];
    633          *     this.dataY = [ A.Y(), A.Y()+d[0], mid[1], mid[1]+d[0], mid[1], B.Y()+d[0], B.Y() ];
    634          * };
    635          *
    636          * // Text
    637          * var txt = board.create('text', [
    638          *                     function() {
    639          *                         var d = [B.X()-A.X(), B.Y()-A.Y()],
    640          *                             dl = Math.sqrt(d[0]*d[0]+d[1]*d[1]),
    641          *                             mid = (A.X()+B.X())*0.5;
    642          *
    643          *                         d[1] *= height/dl;
    644          *                         return mid-d[1]+0.1;
    645          *                     },
    646          *                     function() {
    647          *                         var d = [B.X()-A.X(), B.Y()-A.Y()],
    648          *                             dl = Math.sqrt(d[0]*d[0]+d[1]*d[1]),
    649          *                             mid = (A.Y()+B.Y())*0.5;
    650          *
    651          *                         d[0] *= height/dl;
    652          *                         return mid+d[0]+0.1;
    653          *                     },
    654          *                     function() { return "length=" + JXG.toFixed(B.Dist(A), 2); }
    655          *                 ]);
    656          *
    657          *
    658          * board.update(); // This update is necessary to call updateDataArray the first time.
    659          *
    660          * </pre><div id="JXGa61a4d66-e69f-11e5-b1bf-901b0e1b8723"  class="jxgbox" style="width: 300px; height: 300px;"></div>
    661          * <script type="text/javascript">
    662          *     (function() {
    663          *      var board = JXG.JSXGraph.initBoard('JXGa61a4d66-e69f-11e5-b1bf-901b0e1b8723',
    664          *             {boundingbox: [-4, 4, 4,-4], axis: true, showcopyright: false, shownavigation: false});
    665          *     var A = board.create('point', [-3,3]);
    666          *     var B = board.create('point', [3,-2]);
    667          *     var line = board.create('segment', [A,B]);
    668          *
    669          *     var height = 0.5; // height of the curly brace
    670          *
    671          *     // Curly brace
    672          *     var crl = board.create('curve', [[0],[0]], {strokeWidth:1, strokeColor:'black'});
    673          *     crl.bezierDegree = 3;
    674          *     crl.updateDataArray = function() {
    675          *         var d = [B.X()-A.X(), B.Y()-A.Y()],
    676          *             dl = Math.sqrt(d[0]*d[0]+d[1]*d[1]),
    677          *             mid = [(A.X()+B.X())*0.5, (A.Y()+B.Y())*0.5];
    678          *
    679          *         d[0] *= height/dl;
    680          *         d[1] *= height/dl;
    681          *
    682          *         this.dataX = [ A.X(), A.X()-d[1], mid[0], mid[0]-d[1], mid[0], B.X()-d[1], B.X() ];
    683          *         this.dataY = [ A.Y(), A.Y()+d[0], mid[1], mid[1]+d[0], mid[1], B.Y()+d[0], B.Y() ];
    684          *     };
    685          *
    686          *     // Text
    687          *     var txt = board.create('text', [
    688          *                         function() {
    689          *                             var d = [B.X()-A.X(), B.Y()-A.Y()],
    690          *                                 dl = Math.sqrt(d[0]*d[0]+d[1]*d[1]),
    691          *                                 mid = (A.X()+B.X())*0.5;
    692          *
    693          *                             d[1] *= height/dl;
    694          *                             return mid-d[1]+0.1;
    695          *                         },
    696          *                         function() {
    697          *                             var d = [B.X()-A.X(), B.Y()-A.Y()],
    698          *                                 dl = Math.sqrt(d[0]*d[0]+d[1]*d[1]),
    699          *                                 mid = (A.Y()+B.Y())*0.5;
    700          *
    701          *                             d[0] *= height/dl;
    702          *                             return mid+d[0]+0.1;
    703          *                         },
    704          *                         function() { return "length="+JXG.toFixed(B.Dist(A), 2); }
    705          *                     ]);
    706          *
    707          *
    708          *     board.update(); // This update is necessary to call updateDataArray the first time.
    709          *
    710          *     })();
    711          *
    712          * </script><pre>
    713          *
    714          *
    715          */
    716         updateDataArray: function () {
    717             // this used to return this, but we shouldn't rely on the user to implement it.
    718         },
    719
    720         /**
    721          * Computes the curve path
    722          * @see JXG.Curve#update
    723          * @returns {JXG.Curve} Reference to the curve object.
    724          */
    725         updateCurve: function () {
    726             var len,
    727                 mi,
    728                 ma,
    729                 x,
    730                 y,
    731                 i,
    732                 version = this.visProp.plotversion,
    733                 //t1, t2, l1,
    734                 suspendUpdate = false;
    735
    736             this.updateTransformMatrix();
    737             this.updateDataArray();
    738             mi = this.minX();
    739             ma = this.maxX();
    740
    741             // Discrete data points
    742             // x-coordinates are in an array
    743             if (Type.exists(this.dataX)) {
    744                 this.numberPoints = this.dataX.length;
    745                 len = this.numberPoints;
    746
    747                 // It is possible, that the array length has increased.
    748                 this.allocatePoints();
    749
    750                 for (i = 0; i < len; i++) {
    751                     x = i;
    752
    753                     // y-coordinates are in an array
    754                     if (Type.exists(this.dataY)) {
    755                         y = i;
    756                         // The last parameter prevents rounding in usr2screen().
    757                         this.points[i].setCoordinates(
    758                             Const.COORDS_BY_USER,
    759                             [this.dataX[i], this.dataY[i]],
    760                             false
    761                         );
    762                     } else {
    763                         // discrete x data, continuous y data
    764                         y = this.X(x);
    765                         // The last parameter prevents rounding in usr2screen().
    766                         this.points[i].setCoordinates(
    767                             Const.COORDS_BY_USER,
    768                             [this.dataX[i], this.Y(y, suspendUpdate)],
    769                             false
    770                         );
    771                     }
    772                     this.points[i]._t = i;
    773
    774                     // this.updateTransform(this.points[i]);
    775                     suspendUpdate = true;
    776                 }
    777                 // continuous x data
    778             } else {
    779                 if (Type.evaluate(this.visProp.doadvancedplot)) {
    780                     // console.time("plot");
    781
    782                     if (version === 1 || Type.evaluate(this.visProp.doadvancedplotold)) {
    783                         Plot.updateParametricCurveOld(this, mi, ma);
    784                     } else if (version === 2) {
    785                         Plot.updateParametricCurve_v2(this, mi, ma);
    786                     } else if (version === 3) {
    787                         Plot.updateParametricCurve_v3(this, mi, ma);
    788                     } else if (version === 4) {
    789                         Plot.updateParametricCurve_v4(this, mi, ma);
    790                     } else {
    791                         Plot.updateParametricCurve_v2(this, mi, ma);
    792                     }
    793                     // console.timeEnd("plot");
    794                 } else {
    795                     if (this.board.updateQuality === this.board.BOARD_QUALITY_HIGH) {
    796                         this.numberPoints = Type.evaluate(this.visProp.numberpointshigh);
    797                     } else {
    798                         this.numberPoints = Type.evaluate(this.visProp.numberpointslow);
    799                     }
    800
    801                     // It is possible, that the array length has increased.
    802                     this.allocatePoints();
    803                     Plot.updateParametricCurveNaive(this, mi, ma, this.numberPoints);
    804                 }
    805                 len = this.numberPoints;
    806
    807                 if (
    808                     Type.evaluate(this.visProp.useqdt) &&
    809                     this.board.updateQuality === this.board.BOARD_QUALITY_HIGH
    810                 ) {
    811                     this.qdt = new QDT(this.board.getBoundingBox());
    812                     for (i = 0; i < this.points.length; i++) {
    813                         this.qdt.insert(this.points[i]);
    814
    815                         if (i > 0) {
    816                             this.points[i].prev = this.points[i - 1];
    817                         }
    818
    819                         if (i < len - 1) {
    820                             this.points[i].next = this.points[i + 1];
    821                         }
    822                     }
    823                 }
    824
    825                 // for (i = 0; i < len; i++) {
    826                 //     this.updateTransform(this.points[i]);
    827                 // }
    828             }
    829
    830             if (
    831                 Type.evaluate(this.visProp.curvetype) !== "plot" &&
    832                 Type.evaluate(this.visProp.rdpsmoothing)
    833             ) {
    834                 // console.time("rdp");
    835                 this.points = Numerics.RamerDouglasPeucker(this.points, 0.2);
    836                 this.numberPoints = this.points.length;
    837                 // console.timeEnd("rdp");
    838                 // console.log(this.numberPoints);
    839             }
    840
    841             len = this.numberPoints;
    842             for (i = 0; i < len; i++) {
    843                 this.updateTransform(this.points[i]);
    844             }
    845
    846             return this;
    847         },
    848
    849         updateTransformMatrix: function () {
    850             var t,
    851                 i,
    852                 len = this.transformations.length;
    853
    854             this.transformMat = [
    855                 [1, 0, 0],
    856                 [0, 1, 0],
    857                 [0, 0, 1]
    858             ];
    859
    860             for (i = 0; i < len; i++) {
    861                 t = this.transformations[i];
    862                 t.update();
    863                 this.transformMat = Mat.matMatMult(t.matrix, this.transformMat);
    864             }
    865
    866             return this;
    867         },
    868
    869         /**
    870          * Applies the transformations of the curve to the given point <tt>p</tt>.
    871          * Before using it, {@link JXG.Curve#updateTransformMatrix} has to be called.
    872          * @param {JXG.Point} p
    873          * @returns {JXG.Point} The given point.
    874          */
    875         updateTransform: function (p) {
    876             var c,
    877                 len = this.transformations.length;
    878
    879             if (len > 0) {
    880                 c = Mat.matVecMult(this.transformMat, p.usrCoords);
    881                 p.setCoordinates(Const.COORDS_BY_USER, c, false, true);
    882             }
    883
    884             return p;
    885         },
    886
    887         /**
    888          * Add transformations to this curve.
    889          * @param {JXG.Transformation|Array} transform Either one {@link JXG.Transformation} or an array of {@link JXG.Transformation}s.
    890          * @returns {JXG.Curve} Reference to the curve object.
    891          */
    892         addTransform: function (transform) {
    893             var i,
    894                 list = Type.isArray(transform) ? transform : [transform],
    895                 len = list.length;
    896
    897             for (i = 0; i < len; i++) {
    898                 this.transformations.push(list[i]);
    899             }
    900
    901             return this;
    902         },
    903
    904         /**
    905          * Generate the method curve.X() in case curve.dataX is an array
    906          * and generate the method curve.Y() in case curve.dataY is an array.
    907          * @private
    908          * @param {String} which Either 'X' or 'Y'
    909          * @returns {function}
    910          **/
    911         interpolationFunctionFromArray: function (which) {
    912             var data = "data" + which,
    913                 that = this;
    914
    915             return function (t, suspendedUpdate) {
    916                 var i,
    917                     j,
    918                     t0,
    919                     t1,
    920                     arr = that[data],
    921                     len = arr.length,
    922                     last,
    923                     f = [];
    924
    925                 if (isNaN(t)) {
    926                     return NaN;
    927                 }
    928
    929                 if (t < 0) {
    930                     if (Type.isFunction(arr[0])) {
    931                         return arr[0]();
    932                     }
    933
    934                     return arr[0];
    935                 }
    936
    937                 if (that.bezierDegree === 3) {
    938                     last = (len - 1) / 3;
    939
    940                     if (t >= last) {
    941                         if (Type.isFunction(arr[arr.length - 1])) {
    942                             return arr[arr.length - 1]();
    943                         }
    944
    945                         return arr[arr.length - 1];
    946                     }
    947
    948                     i = Math.floor(t) * 3;
    949                     t0 = t % 1;
    950                     t1 = 1 - t0;
    951
    952                     for (j = 0; j < 4; j++) {
    953                         if (Type.isFunction(arr[i + j])) {
    954                             f[j] = arr[i + j]();
    955                         } else {
    956                             f[j] = arr[i + j];
    957                         }
    958                     }
    959
    960                     return (
    961                         t1 * t1 * (t1 * f[0] + 3 * t0 * f[1]) +
    962                         (3 * t1 * f[2] + t0 * f[3]) * t0 * t0
    963                     );
    964                 }
    965
    966                 if (t > len - 2) {
    967                     i = len - 2;
    968                 } else {
    969                     i = parseInt(Math.floor(t), 10);
    970                 }
    971
    972                 if (i === t) {
    973                     if (Type.isFunction(arr[i])) {
    974                         return arr[i]();
    975                     }
    976                     return arr[i];
    977                 }
    978
    979                 for (j = 0; j < 2; j++) {
    980                     if (Type.isFunction(arr[i + j])) {
    981                         f[j] = arr[i + j]();
    982                     } else {
    983                         f[j] = arr[i + j];
    984                     }
    985                 }
    986                 return f[0] + (f[1] - f[0]) * (t - i);
    987             };
    988         },
    989
    990         /**
    991          * Converts the JavaScript/JessieCode/GEONExT syntax of the defining function term into JavaScript.
    992          * New methods X() and Y() for the Curve object are generated, further
    993          * new methods for minX() and maxX().
    994          * If mi or ma are not supplied, default functions are set.
    995          *
    996          * @param {String} varname Name of the parameter in xterm and yterm, e.g. 'x' or 't'
    997          * @param {String|Number|Function|Array} xterm Term for the x coordinate. Can also be an array consisting of discrete values.
    998          * @param {String|Number|Function|Array} yterm Term for the y coordinate. Can also be an array consisting of discrete values.
    999          * @param {String|Number|Function} [mi] Lower bound on the parameter
    1000          * @param {String|Number|Function} [ma] Upper bound on the parameter
    1001          * @see JXG.GeonextParser.geonext2JS
    1002          */
    1003         generateTerm: function (varname, xterm, yterm, mi, ma) {
    1004             var fx, fy;
    1005
    1006             // Generate the methods X() and Y()
    1007             if (Type.isArray(xterm)) {
    1008                 // Discrete data
    1009                 this.dataX = xterm;
    1010
    1011                 this.numberPoints = this.dataX.length;
    1012                 this.X = this.interpolationFunctionFromArray.apply(this, ["X"]);
    1013                 this.visProp.curvetype = "plot";
    1014                 this.isDraggable = true;
    1015             } else {
    1016                 // Continuous data
    1017                 this.X = Type.createFunction(xterm, this.board, varname);
    1018                 if (Type.isString(xterm)) {
    1019                     this.visProp.curvetype = "functiongraph";
    1020                 } else if (Type.isFunction(xterm) || Type.isNumber(xterm)) {
    1021                     this.visProp.curvetype = "parameter";
    1022                 }
    1023
    1024                 this.isDraggable = true;
    1025             }
    1026
    1027             if (Type.isArray(yterm)) {
    1028                 this.dataY = yterm;
    1029                 this.Y = this.interpolationFunctionFromArray.apply(this, ["Y"]);
    1030             } else {
    1031                 this.Y = Type.createFunction(yterm, this.board, varname);
    1032             }
    1033
    1034             /**
    1035              * Polar form
    1036              * Input data is function xterm() and offset coordinates yterm
    1037              */
    1038             if (Type.isFunction(xterm) && Type.isArray(yterm)) {
    1039                 // Xoffset, Yoffset
    1040                 fx = Type.createFunction(yterm[0], this.board, "");
    1041                 fy = Type.createFunction(yterm[1], this.board, "");
    1042
    1043                 this.X = function (phi) {
    1044                     return xterm(phi) * Math.cos(phi) + fx();
    1045                 };
    1046                 this.X.deps = fx.deps;
    1047
    1048                 this.Y = function (phi) {
    1049                     return xterm(phi) * Math.sin(phi) + fy();
    1050                 };
    1051                 this.Y.deps = fy.deps;
    1052
    1053                 this.visProp.curvetype = "polar";
    1054             }
    1055
    1056             // Set the upper and lower bounds for the parameter of the curve.
    1057             // If not defined, reset the bounds to the default values
    1058             // given in Curve.prototype.minX, Curve.prototype.maxX
    1059             if (Type.exists(mi)) {
    1060                 this.minX = Type.createFunction(mi, this.board, "");
    1061             } else {
    1062                 delete this.minX;
    1063             }
    1064             if (Type.exists(ma)) {
    1065                 this.maxX = Type.createFunction(ma, this.board, "");
    1066             } else {
    1067                 delete this.maxX;
    1068             }
    1069
    1070             this.addParentsFromJCFunctions([this.X, this.Y, this.minX, this.maxX]);
    1071         },
    1072
    1073         /**
    1074          * Finds dependencies in a given term and notifies the parents by adding the
    1075          * dependent object to the found objects child elements.
    1076          * @param {String} contentStr String containing dependencies for the given object.
    1077          */
    1078         notifyParents: function (contentStr) {
    1079             var fstr,
    1080                 dep,
    1081                 isJessieCode = false,
    1082                 obj;
    1083
    1084             // Read dependencies found by the JessieCode parser
    1085             obj = { xterm: 1, yterm: 1 };
    1086             for (fstr in obj) {
    1087                 if (
    1088                     obj.hasOwnProperty(fstr) &&
    1089                     this.hasOwnProperty(fstr) &&
    1090                     this[fstr].origin
    1091                 ) {
    1092                     isJessieCode = true;
    1093                     for (dep in this[fstr].origin.deps) {
    1094                         if (this[fstr].origin.deps.hasOwnProperty(dep)) {
    1095                             this[fstr].origin.deps[dep].addChild(this);
    1096                         }
    1097                     }
    1098                 }
    1099             }
    1100
    1101             if (!isJessieCode) {
    1102                 GeonextParser.findDependencies(this, contentStr, this.board);
    1103             }
    1104         },
    1105
    1106         // documented in geometry element
    1107         getLabelAnchor: function () {
    1108             var c,
    1109                 x,
    1110                 y,
    1111                 ax = 0.05 * this.board.canvasWidth,
    1112                 ay = 0.05 * this.board.canvasHeight,
    1113                 bx = 0.95 * this.board.canvasWidth,
    1114                 by = 0.95 * this.board.canvasHeight;
    1115
    1116             switch (Type.evaluate(this.visProp.label.position)) {
    1117                 case "ulft":
    1118                     x = ax;
    1119                     y = ay;
    1120                     break;
    1121                 case "llft":
    1122                     x = ax;
    1123                     y = by;
    1124                     break;
    1125                 case "rt":
    1126                     x = bx;
    1127                     y = 0.5 * by;
    1128                     break;
    1129                 case "lrt":
    1130                     x = bx;
    1131                     y = by;
    1132                     break;
    1133                 case "urt":
    1134                     x = bx;
    1135                     y = ay;
    1136                     break;
    1137                 case "top":
    1138                     x = 0.5 * bx;
    1139                     y = ay;
    1140                     break;
    1141                 case "bot":
    1142                     x = 0.5 * bx;
    1143                     y = by;
    1144                     break;
    1145                 default:
    1146                     // includes case 'lft'
    1147                     x = ax;
    1148                     y = 0.5 * by;
    1149             }
    1150
    1151             c = new Coords(Const.COORDS_BY_SCREEN, [x, y], this.board, false);
    1152             return Geometry.projectCoordsToCurve(
    1153                 c.usrCoords[1],
    1154                 c.usrCoords[2],
    1155                 0,
    1156                 this,
    1157                 this.board
    1158             )[0];
    1159         },
    1160
    1161         // documented in geometry element
    1162         cloneToBackground: function () {
    1163             var er,
    1164                 copy = {
    1165                     id: this.id + "T" + this.numTraces,
    1166                     elementClass: Const.OBJECT_CLASS_CURVE,
    1167
    1168                     points: this.points.slice(0),
    1169                     bezierDegree: this.bezierDegree,
    1170                     numberPoints: this.numberPoints,
    1171                     board: this.board,
    1172                     visProp: Type.deepCopy(this.visProp, this.visProp.traceattributes, true)
    1173                 };
    1174
    1175             copy.visProp.layer = this.board.options.layer.trace;
    1176             copy.visProp.curvetype = this.visProp.curvetype;
    1177             this.numTraces++;
    1178
    1179             Type.clearVisPropOld(copy);
    1180             copy.visPropCalc = {
    1181                 visible: Type.evaluate(copy.visProp.visible)
    1182             };
    1183             er = this.board.renderer.enhancedRendering;
    1184             this.board.renderer.enhancedRendering = true;
    1185             this.board.renderer.drawCurve(copy);
    1186             this.board.renderer.enhancedRendering = er;
    1187             this.traces[copy.id] = copy.rendNode;
    1188
    1189             return this;
    1190         },
    1191
    1192         // Already documented in GeometryElement
    1193         bounds: function () {
    1194             var minX = Infinity,
    1195                 maxX = -Infinity,
    1196                 minY = Infinity,
    1197                 maxY = -Infinity,
    1198                 l = this.points.length,
    1199                 i,
    1200                 bezier,
    1201                 up;
    1202
    1203             if (this.bezierDegree === 3) {
    1204                 // Add methods X(), Y()
    1205                 for (i = 0; i < l; i++) {
    1206                     this.points[i].X = Type.bind(function () {
    1207                         return this.usrCoords[1];
    1208                     }, this.points[i]);
    1209                     this.points[i].Y = Type.bind(function () {
    1210                         return this.usrCoords[2];
    1211                     }, this.points[i]);
    1212                 }
    1213                 bezier = Numerics.bezier(this.points);
    1214                 up = bezier[3]();
    1215                 minX = Numerics.fminbr(
    1216                     function (t) {
    1217                         return bezier[0](t);
    1218                     },
    1219                     [0, up]
    1220                 );
    1221                 maxX = Numerics.fminbr(
    1222                     function (t) {
    1223                         return -bezier[0](t);
    1224                     },
    1225                     [0, up]
    1226                 );
    1227                 minY = Numerics.fminbr(
    1228                     function (t) {
    1229                         return bezier[1](t);
    1230                     },
    1231                     [0, up]
    1232                 );
    1233                 maxY = Numerics.fminbr(
    1234                     function (t) {
    1235                         return -bezier[1](t);
    1236                     },
    1237                     [0, up]
    1238                 );
    1239
    1240                 minX = bezier[0](minX);
    1241                 maxX = bezier[0](maxX);
    1242                 minY = bezier[1](minY);
    1243                 maxY = bezier[1](maxY);
    1244                 return [minX, maxY, maxX, minY];
    1245             }
    1246
    1247             // Linear segments
    1248             for (i = 0; i < l; i++) {
    1249                 if (minX > this.points[i].usrCoords[1]) {
    1250                     minX = this.points[i].usrCoords[1];
    1251                 }
    1252
    1253                 if (maxX < this.points[i].usrCoords[1]) {
    1254                     maxX = this.points[i].usrCoords[1];
    1255                 }
    1256
    1257                 if (minY > this.points[i].usrCoords[2]) {
    1258                     minY = this.points[i].usrCoords[2];
    1259                 }
    1260
    1261                 if (maxY < this.points[i].usrCoords[2]) {
    1262                     maxY = this.points[i].usrCoords[2];
    1263                 }
    1264             }
    1265
    1266             return [minX, maxY, maxX, minY];
    1267         },
    1268
    1269         // documented in element.js
    1270         getParents: function () {
    1271             var p = [this.xterm, this.yterm, this.minX(), this.maxX()];
    1272
    1273             if (this.parents.length !== 0) {
    1274                 p = this.parents;
    1275             }
    1276
    1277             return p;
    1278         },
    1279
    1280         /**
    1281          * Shift the curve by the vector 'where'.
    1282          *
    1283          * @param {Array} where Array containing the x and y coordinate of the target location.
    1284          * @returns {JXG.Curve} Reference to itself.
    1285          */
    1286         moveTo: function (where) {
    1287             // TODO add animation
    1288             var delta = [],
    1289                 p;
    1290             if (this.points.length > 0 && !Type.evaluate(this.visProp.fixed)) {
    1291                 p = this.points[0];
    1292                 if (where.length === 3) {
    1293                     delta = [
    1294                         where[0] - p.usrCoords[0],
    1295                         where[1] - p.usrCoords[1],
    1296                         where[2] - p.usrCoords[2]
    1297                     ];
    1298                 } else {
    1299                     delta = [where[0] - p.usrCoords[1], where[1] - p.usrCoords[2]];
    1300                 }
    1301                 this.setPosition(Const.COORDS_BY_USER, delta);
    1302             }
    1303             return this;
    1304         },
    1305
    1306         /**
    1307          * If the curve is the result of a transformation applied
    1308          * to a continuous curve, the glider projection has to be done
    1309          * on the original curve. Otherwise there will be problems
    1310          * when changing between high and low precision plotting,
    1311          * since there number of points changes.
    1312          *
    1313          * @private
    1314          * @returns {Array} [Boolean, curve]: Array contining 'true' if curve is result of a transformation,
    1315          *   and the source curve of the transformation.
    1316          */
    1317         getTransformationSource: function () {
    1318             var isTransformed, curve_org;
    1319             if (Type.exists(this._transformationSource)) {
    1320                 curve_org = this._transformationSource;
    1321                 if (
    1322                     curve_org.elementClass === Const.OBJECT_CLASS_CURVE //&&
    1323                     //Type.evaluate(curve_org.visProp.curvetype) !== 'plot'
    1324                 ) {
    1325                     isTransformed = true;
    1326                 }
    1327             }
    1328             return [isTransformed, curve_org];
    1329         },
    1330
    1331         pnpoly: function (x_in, y_in, coord_type) {
    1332             var i,
    1333                 j,
    1334                 len,
    1335                 x,
    1336                 y,
    1337                 crds,
    1338                 v = this.points,
    1339                 isIn = false;
    1340
    1341             if (coord_type === Const.COORDS_BY_USER) {
    1342                 crds = new Coords(Const.COORDS_BY_USER, [x_in, y_in], this.board);
    1343                 x = crds.scrCoords[1];
    1344                 y = crds.scrCoords[2];
    1345             } else {
    1346                 x = x_in;
    1347                 y = y_in;
    1348             }
    1349
    1350             len = this.points.length;
    1351             for (i = 0, j = len - 2; i < len - 1; j = i++) {
    1352                 if (
    1353                     v[i].scrCoords[2] > y !== v[j].scrCoords[2] > y &&
    1354                     x <
    1355                         ((v[j].scrCoords[1] - v[i].scrCoords[1]) * (y - v[i].scrCoords[2])) /
    1356                             (v[j].scrCoords[2] - v[i].scrCoords[2]) +
    1357                             v[i].scrCoords[1]
    1358                 ) {
    1359                     isIn = !isIn;
    1360                 }
    1361             }
    1362
    1363             return isIn;
    1364         }
    1365     }
    1366 );
    1367
    1368 /**
    1369  * @class  This element is used to provide a constructor for curve, which is just a wrapper for element {@link Curve}.
    1370  * A curve is a mapping from R to R^2. t mapsto (x(t),y(t)). The graph is drawn for t in the interval [a,b].
    1371  * <p>
    1372  * The following types of curves can be plotted:
    1373  * <ul>
    1374  *  <li> parametric curves: t mapsto (x(t),y(t)), where x() and y() are univariate functions.
    1375  *  <li> polar curves: curves commonly written with polar equations like spirals and cardioids.
    1376  *  <li> data plots: plot line segments through a given list of coordinates.
    1377  * </ul>
    1378  * @pseudo
    1379  * @name Curve
    1380  * @augments JXG.Curve
    1381  * @constructor
    1382  * @type Object
    1383  * @description JXG.Curve
    1384
    1385  * @param {function,number_function,number_function,number_function,number}  x,y,a_,b_ Parent elements for Parametric Curves.
    1386  *                     <p>
    1387  *                     x describes the x-coordinate of the curve. It may be a function term in one variable, e.g. x(t).
    1388  *                     In case of x being of type number, x(t) is set to  a constant function.
    1389  *                     this function at the values of the array.
    1390  *                     </p>
    1391  *                     <p>
    1392  *                     y describes the y-coordinate of the curve. In case of a number, y(t) is set to the constant function
    1393  *                     returning this number.
    1394  *                     </p>
    1395  *                     <p>
    1396  *                     Further parameters are an optional number or function for the left interval border a,
    1397  *                     and an optional number or function for the right interval border b.
    1398  *                     </p>
    1399  *                     <p>
    1400  *                     Default values are a=-10 and b=10.
    1401  *                     </p>
    1402  *
    1403  * @param {array_array,function,number}
    1404  *
    1405  * @description x,y Parent elements for Data Plots.
    1406  *                     <p>
    1407  *                     x and y are arrays contining the x and y coordinates of the data points which are connected by
    1408  *                     line segments. The individual entries of x and y may also be functions.
    1409  *                     In case of x being an array the curve type is data plot, regardless of the second parameter and
    1410  *                     if additionally the second parameter y is a function term the data plot evaluates.
    1411  *                     </p>
    1412  * @param {function_array,function,number_function,number_function,number}
    1413  * @description r,offset_,a_,b_ Parent elements for Polar Curves.
    1414  *                     <p>
    1415  *                     The first parameter is a function term r(phi) describing the polar curve.
    1416  *                     </p>
    1417  *                     <p>
    1418  *                     The second parameter is the offset of the curve. It has to be
    1419  *                     an array containing numbers or functions describing the offset. Default value is the origin [0,0].
    1420  *                     </p>
    1421  *                     <p>
    1422  *                     Further parameters are an optional number or function for the left interval border a,
    1423  *                     and an optional number or function for the right interval border b.
    1424  *                     </p>
    1425  *                     <p>
    1426  *                     Default values are a=-10 and b=10.
    1427  *                     </p>
    1428  * <p>
    1429  * Additionally, a curve can be created by providing a curve and a transformation (or an array of transformations).
    1430  * The result is a curve which is the transformation of the supplied curve.
    1431  *
    1432  * @see JXG.Curve
    1433  * @example
    1434  * // Parametric curve
    1435  * // Create a curve of the form (t-sin(t), 1-cos(t), i.e.
    1436  * // the cycloid curve.
    1437  *   var graph = board.create('curve',
    1438  *                        [function(t){ return t-Math.sin(t);},
    1439  *                         function(t){ return 1-Math.cos(t);},
    1440  *                         0, 2*Math.PI]
    1441  *                     );
    1442  * </pre><div class="jxgbox" id="JXGaf9f818b-f3b6-4c4d-8c4c-e4a4078b726d" style="width: 300px; height: 300px;"></div>
    1443  * <script type="text/javascript">
    1444  *   var c1_board = JXG.JSXGraph.initBoard('JXGaf9f818b-f3b6-4c4d-8c4c-e4a4078b726d', {boundingbox: [-1, 5, 7, -1], axis: true, showcopyright: false, shownavigation: false});
    1445  *   var graph1 = c1_board.create('curve', [function(t){ return t-Math.sin(t);},function(t){ return 1-Math.cos(t);},0, 2*Math.PI]);
    1446  * </script><pre>
    1447  * @example
    1448  * // Data plots
    1449  * // Connect a set of points given by coordinates with dashed line segments.
    1450  * // The x- and y-coordinates of the points are given in two separate
    1451  * // arrays.
    1452  *   var x = [0,1,2,3,4,5,6,7,8,9];
    1453  *   var y = [9.2,1.3,7.2,-1.2,4.0,5.3,0.2,6.5,1.1,0.0];
    1454  *   var graph = board.create('curve', [x,y], {dash:2});
    1455  * </pre><div class="jxgbox" id="JXG7dcbb00e-b6ff-481d-b4a8-887f5d8c6a83" style="width: 300px; height: 300px;"></div>
    1456  * <script type="text/javascript">
    1457  *   var c3_board = JXG.JSXGraph.initBoard('JXG7dcbb00e-b6ff-481d-b4a8-887f5d8c6a83', {boundingbox: [-1,10,10,-1], axis: true, showcopyright: false, shownavigation: false});
    1458  *   var x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
    1459  *   var y = [9.2, 1.3, 7.2, -1.2, 4.0, 5.3, 0.2, 6.5, 1.1, 0.0];
    1460  *   var graph3 = c3_board.create('curve', [x,y], {dash:2});
    1461  * </script><pre>
    1462  * @example
    1463  * // Polar plot
    1464  * // Create a curve with the equation r(phi)= a*(1+phi), i.e.
    1465  * // a cardioid.
    1466  *   var a = board.create('slider',[[0,2],[2,2],[0,1,2]]);
    1467  *   var graph = board.create('curve',
    1468  *                        [function(phi){ return a.Value()*(1-Math.cos(phi));},
    1469  *                         [1,0],
    1470  *                         0, 2*Math.PI],
    1471  *                         {curveType: 'polar'}
    1472  *                     );
    1473  * </pre><div class="jxgbox" id="JXGd0bc7a2a-8124-45ca-a6e7-142321a8f8c2" style="width: 300px; height: 300px;"></div>
    1474  * <script type="text/javascript">
    1475  *   var c2_board = JXG.JSXGraph.initBoard('JXGd0bc7a2a-8124-45ca-a6e7-142321a8f8c2', {boundingbox: [-3,3,3,-3], axis: true, showcopyright: false, shownavigation: false});
    1476  *   var a = c2_board.create('slider',[[0,2],[2,2],[0,1,2]]);
    1477  *   var graph2 = c2_board.create('curve', [function(phi){ return a.Value()*(1-Math.cos(phi));}, [1,0], 0, 2*Math.PI], {curveType: 'polar'});
    1478  * </script><pre>
    1479  *
    1480  * @example
    1481  *  // Draggable Bezier curve
    1482  *  var col, p, c;
    1483  *  col = 'blue';
    1484  *  p = [];
    1485  *  p.push(board.create('point',[-2, -1 ], {size: 5, strokeColor:col, fillColor:col}));
    1486  *  p.push(board.create('point',[1, 2.5 ], {size: 5, strokeColor:col, fillColor:col}));
    1487  *  p.push(board.create('point',[-1, -2.5 ], {size: 5, strokeColor:col, fillColor:col}));
    1488  *  p.push(board.create('point',[2, -2], {size: 5, strokeColor:col, fillColor:col}));
    1489  *
    1490  *  c = board.create('curve', JXG.Math.Numerics.bezier(p),
    1491  *              {strokeColor:'red', name:"curve", strokeWidth:5, fixed: false}); // Draggable curve
    1492  *  c.addParents(p);
    1493  * </pre><div class="jxgbox" id="JXG7bcc6280-f6eb-433e-8281-c837c3387849" style="width: 300px; height: 300px;"></div>
    1494  * <script type="text/javascript">
    1495  * (function(){
    1496  *  var board, col, p, c;
    1497  *  board = JXG.JSXGraph.initBoard('JXG7bcc6280-f6eb-433e-8281-c837c3387849', {boundingbox: [-3,3,3,-3], axis: true, showcopyright: false, shownavigation: false});
    1498  *  col = 'blue';
    1499  *  p = [];
    1500  *  p.push(board.create('point',[-2, -1 ], {size: 5, strokeColor:col, fillColor:col}));
    1501  *  p.push(board.create('point',[1, 2.5 ], {size: 5, strokeColor:col, fillColor:col}));
    1502  *  p.push(board.create('point',[-1, -2.5 ], {size: 5, strokeColor:col, fillColor:col}));
    1503  *  p.push(board.create('point',[2, -2], {size: 5, strokeColor:col, fillColor:col}));
    1504  *
    1505  *  c = board.create('curve', JXG.Math.Numerics.bezier(p),
    1506  *              {strokeColor:'red', name:"curve", strokeWidth:5, fixed: false}); // Draggable curve
    1507  *  c.addParents(p);
    1508  * })();
    1509  * </script><pre>
    1510  *
    1511  * @example
    1512  *         // The curve cu2 is the reflection of cu1 against line li
    1513  *         var li = board.create('line', [1,1,1], {strokeColor: '#aaaaaa'});
    1514  *         var reflect = board.create('transform', [li], {type: 'reflect'});
    1515  *         var cu1 = board.create('curve', [[-1, -1, -0.5, -1, -1, -0.5], [-3, -2, -2, -2, -2.5, -2.5]]);
    1516  *         var cu2 = board.create('curve', [cu1, reflect], {strokeColor: 'red'});
    1517  *
    1518  * </pre><div id="JXG866dc7a2-d448-11e7-93b3-901b0e1b8723" class="jxgbox" style="width: 300px; height: 300px;"></div>
    1519  * <script type="text/javascript">
    1520  *     (function() {
    1521  *         var board = JXG.JSXGraph.initBoard('JXG866dc7a2-d448-11e7-93b3-901b0e1b8723',
    1522  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
    1523  *             var li = board.create('line', [1,1,1], {strokeColor: '#aaaaaa'});
    1524  *             var reflect = board.create('transform', [li], {type: 'reflect'});
    1525  *             var cu1 = board.create('curve', [[-1, -1, -0.5, -1, -1, -0.5], [-3, -2, -2, -2, -2.5, -2.5]]);
    1526  *             var cu2 = board.create('curve', [cu1, reflect], {strokeColor: 'red'});
    1527  *
    1528  *     })();
    1529  *
    1530  * </script><pre>
    1531  */
    1532 JXG.createCurve = function (board, parents, attributes) {
    1533     var obj,
    1534         cu,
    1535         attr = Type.copyAttributes(attributes, board.options, "curve");
    1536
    1537     obj = board.select(parents[0], true);
    1538     if (
    1539         Type.isTransformationOrArray(parents[1]) &&
    1540         Type.isObject(obj) &&
    1541         (obj.type === Const.OBJECT_TYPE_CURVE ||
    1542             obj.type === Const.OBJECT_TYPE_ANGLE ||
    1543             obj.type === Const.OBJECT_TYPE_ARC ||
    1544             obj.type === Const.OBJECT_TYPE_CONIC ||
    1545             obj.type === Const.OBJECT_TYPE_SECTOR)
    1546     ) {
    1547         if (obj.type === Const.OBJECT_TYPE_SECTOR) {
    1548             attr = Type.copyAttributes(attributes, board.options, "sector");
    1549         } else if (obj.type === Const.OBJECT_TYPE_ARC) {
    1550             attr = Type.copyAttributes(attributes, board.options, "arc");
    1551         } else if (obj.type === Const.OBJECT_TYPE_ANGLE) {
    1552             if (!Type.exists(attributes.withLabel)) {
    1553                 attributes.withLabel = false;
    1554             }
    1555             attr = Type.copyAttributes(attributes, board.options, "angle");
    1556         } else {
    1557             attr = Type.copyAttributes(attributes, board.options, "curve");
    1558         }
    1559         attr = Type.copyAttributes(attr, board.options, "curve");
    1560
    1561         cu = new JXG.Curve(board, ["x", [], []], attr);
    1562         /**
    1563          * @class
    1564          * @ignore
    1565          */
    1566         cu.updateDataArray = function () {
    1567             var i,
    1568                 le = obj.numberPoints;
    1569             this.bezierDegree = obj.bezierDegree;
    1570             this.dataX = [];
    1571             this.dataY = [];
    1572             for (i = 0; i < le; i++) {
    1573                 this.dataX.push(obj.points[i].usrCoords[1]);
    1574                 this.dataY.push(obj.points[i].usrCoords[2]);
    1575             }
    1576             return this;
    1577         };
    1578         cu.addTransform(parents[1]);
    1579         obj.addChild(cu);
    1580         cu.setParents([obj]);
    1581         cu._transformationSource = obj;
    1582
    1583         return cu;
    1584     }
    1585     attr = Type.copyAttributes(attributes, board.options, "curve");
    1586     return new JXG.Curve(board, ["x"].concat(parents), attr);
    1587 };
    1588
    1589 JXG.registerElement("curve", JXG.createCurve);
    1590
    1591 /**
    1592  * @class This element is used to provide a constructor for functiongraph,
    1593  * which is just a wrapper for element {@link Curve} with {@link JXG.Curve#X}()
    1594  * set to x. The graph is drawn for x in the interval [a,b].
    1595  * @pseudo
    1596  * @name Functiongraph
    1597  * @augments JXG.Curve
    1598  * @constructor
    1599  * @type JXG.Curve
    1600  * @param {function_number,function_number,function} f,a_,b_ Parent elements are a function term f(x) describing the function graph.
    1601  *         <p>
    1602  *         Further, an optional number or function for the left interval border a,
    1603  *         and an optional number or function for the right interval border b.
    1604  *         <p>
    1605  *         Default values are a=-10 and b=10.
    1606  * @see JXG.Curve
    1607  * @example
    1608  * // Create a function graph for f(x) = 0.5*x*x-2*x
    1609  *   var graph = board.create('functiongraph',
    1610  *                        [function(x){ return 0.5*x*x-2*x;}, -2, 4]
    1611  *                     );
    1612  * </pre><div class="jxgbox" id="JXGefd432b5-23a3-4846-ac5b-b471e668b437" style="width: 300px; height: 300px;"></div>
    1613  * <script type="text/javascript">
    1614  *   var alex1_board = JXG.JSXGraph.initBoard('JXGefd432b5-23a3-4846-ac5b-b471e668b437', {boundingbox: [-3, 7, 5, -3], axis: true, showcopyright: false, shownavigation: false});
    1615  *   var graph = alex1_board.create('functiongraph', [function(x){ return 0.5*x*x-2*x;}, -2, 4]);
    1616  * </script><pre>
    1617  * @example
    1618  * // Create a function graph for f(x) = 0.5*x*x-2*x with variable interval
    1619  *   var s = board.create('slider',[[0,4],[3,4],[-2,4,5]]);
    1620  *   var graph = board.create('functiongraph',
    1621  *                        [function(x){ return 0.5*x*x-2*x;},
    1622  *                         -2,
    1623  *                         function(){return s.Value();}]
    1624  *                     );
    1625  * </pre><div class="jxgbox" id="JXG4a203a84-bde5-4371-ad56-44619690bb50" style="width: 300px; height: 300px;"></div>
    1626  * <script type="text/javascript">
    1627  *   var alex2_board = JXG.JSXGraph.initBoard('JXG4a203a84-bde5-4371-ad56-44619690bb50', {boundingbox: [-3, 7, 5, -3], axis: true, showcopyright: false, shownavigation: false});
    1628  *   var s = alex2_board.create('slider',[[0,4],[3,4],[-2,4,5]]);
    1629  *   var graph = alex2_board.create('functiongraph', [function(x){ return 0.5*x*x-2*x;}, -2, function(){return s.Value();}]);
    1630  * </script><pre>
    1631  */
    1632 JXG.createFunctiongraph = function (board, parents, attributes) {
    1633     var attr,
    1634         par = ["x", "x"].concat(parents); // variable name and identity function for x-coordinate
    1635         // par = ["x", function(x) { return x; }].concat(parents);
    1636
    1637     attr = Type.copyAttributes(attributes, board.options, "functiongraph");
    1638     attr = Type.copyAttributes(attr, board.options, "curve");
    1639     attr.curvetype = "functiongraph";
    1640     return new JXG.Curve(board, par, attr);
    1641 };
    1642
    1643 JXG.registerElement("functiongraph", JXG.createFunctiongraph);
    1644 JXG.registerElement("plot", JXG.createFunctiongraph);
    1645
    1646 /**
    1647  * @class This element is used to provide a constructor for (natural) cubic spline curves.
    1648  * Create a dynamic spline interpolated curve given by sample points p_1 to p_n.
    1649  * @pseudo
    1650  * @name Spline
    1651  * @augments JXG.Curve
    1652  * @constructor
    1653  * @type JXG.Curve
    1654  * @param {JXG.Board} board Reference to the board the spline is drawn on.
    1655  * @param {Array} parents Array of points the spline interpolates. This can be
    1656  *   <ul>
    1657  *   <li> an array of JSXGraph points</li>
    1658  *   <li> an array of coordinate pairs</li>
    1659  *   <li> an array of functions returning coordinate pairs</li>
    1660  *   <li> an array consisting of an array with x-coordinates and an array of y-coordinates</li>
    1661  *   </ul>
    1662  *   All individual entries of coordinates arrays may be numbers or functions returning numbers.
    1663  * @param {Object} attributes Define color, width, ... of the spline
    1664  * @returns {JXG.Curve} Returns reference to an object of type JXG.Curve.
    1665  * @see JXG.Curve
    1666  * @example
    1667  *
    1668  * var p = [];
    1669  * p[0] = board.create('point', [-2,2], {size: 4, face: 'o'});
    1670  * p[1] = board.create('point', [0,-1], {size: 4, face: 'o'});
    1671  * p[2] = board.create('point', [2,0], {size: 4, face: 'o'});
    1672  * p[3] = board.create('point', [4,1], {size: 4, face: 'o'});
    1673  *
    1674  * var c = board.create('spline', p, {strokeWidth:3});
    1675  * </pre><div id="JXG6c197afc-e482-11e5-b1bf-901b0e1b8723" style="width: 300px; height: 300px;"></div>
    1676  * <script type="text/javascript">
    1677  *     (function() {
    1678  *         var board = JXG.JSXGraph.initBoard('JXG6c197afc-e482-11e5-b1bf-901b0e1b8723',
    1679  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
    1680  *
    1681  *     var p = [];
    1682  *     p[0] = board.create('point', [-2,2], {size: 4, face: 'o'});
    1683  *     p[1] = board.create('point', [0,-1], {size: 4, face: 'o'});
    1684  *     p[2] = board.create('point', [2,0], {size: 4, face: 'o'});
    1685  *     p[3] = board.create('point', [4,1], {size: 4, face: 'o'});
    1686  *
    1687  *     var c = board.create('spline', p, {strokeWidth:3});
    1688  *     })();
    1689  *
    1690  * </script><pre>
    1691  *
    1692  */
    1693 JXG.createSpline = function (board, parents, attributes) {
    1694     var el, funcs, ret;
    1695
    1696     funcs = function () {
    1697         var D,
    1698             x = [],
    1699             y = [];
    1700
    1701         return [
    1702             function (t, suspended) {
    1703                 // Function term
    1704                 var i, j, c;
    1705
    1706                 if (!suspended) {
    1707                     x = [];
    1708                     y = [];
    1709
    1710                     // given as [x[], y[]]
    1711                     if (
    1712                         parents.length === 2 &&
    1713                         Type.isArray(parents[0]) &&
    1714                         Type.isArray(parents[1]) &&
    1715                         parents[0].length === parents[1].length
    1716                     ) {
    1717                         for (i = 0; i < parents[0].length; i++) {
    1718                             if (Type.isFunction(parents[0][i])) {
    1719                                 x.push(parents[0][i]());
    1720                             } else {
    1721                                 x.push(parents[0][i]);
    1722                             }
    1723
    1724                             if (Type.isFunction(parents[1][i])) {
    1725                                 y.push(parents[1][i]());
    1726                             } else {
    1727                                 y.push(parents[1][i]);
    1728                             }
    1729                         }
    1730                     } else {
    1731                         for (i = 0; i < parents.length; i++) {
    1732                             if (Type.isPoint(parents[i])) {
    1733                                 x.push(parents[i].X());
    1734                                 y.push(parents[i].Y());
    1735                                 // given as [[x1,y1], [x2, y2], ...]
    1736                             } else if (Type.isArray(parents[i]) && parents[i].length === 2) {
    1737                                 for (j = 0; j < parents.length; j++) {
    1738                                     if (Type.isFunction(parents[j][0])) {
    1739                                         x.push(parents[j][0]());
    1740                                     } else {
    1741                                         x.push(parents[j][0]);
    1742                                     }
    1743
    1744                                     if (Type.isFunction(parents[j][1])) {
    1745                                         y.push(parents[j][1]());
    1746                                     } else {
    1747                                         y.push(parents[j][1]);
    1748                                     }
    1749                                 }
    1750                             } else if (
    1751                                 Type.isFunction(parents[i]) &&
    1752                                 parents[i]().length === 2
    1753                             ) {
    1754                                 c = parents[i]();
    1755                                 x.push(c[0]);
    1756                                 y.push(c[1]);
    1757                             }
    1758                         }
    1759                     }
    1760
    1761                     // The array D has only to be calculated when the position of one or more sample points
    1762                     // changes. Otherwise D is always the same for all points on the spline.
    1763                     D = Numerics.splineDef(x, y);
    1764                 }
    1765
    1766                 return Numerics.splineEval(t, x, y, D);
    1767             },
    1768             // minX()
    1769             function () {
    1770                 return x[0];
    1771             },
    1772             //maxX()
    1773             function () {
    1774                 return x[x.length - 1];
    1775             }
    1776         ];
    1777     };
    1778
    1779     attributes = Type.copyAttributes(attributes, board.options, "curve");
    1780     attributes.curvetype = "functiongraph";
    1781     ret = funcs();
    1782     el = new JXG.Curve(board, ["x", "x", ret[0], ret[1], ret[2]], attributes);
    1783     el.setParents(parents);
    1784     el.elType = "spline";
    1785
    1786     return el;
    1787 };
    1788
    1789 /**
    1790  * Register the element type spline at JSXGraph
    1791  * @private
    1792  */
    1793 JXG.registerElement("spline", JXG.createSpline);
    1794
    1795 /**
    1796  * @class This element is used to provide a constructor for cardinal spline curves.
    1797  * Create a dynamic cardinal spline interpolated curve given by sample points p_1 to p_n.
    1798  * @pseudo
    1799  * @name Cardinalspline
    1800  * @augments JXG.Curve
    1801  * @constructor
    1802  * @type JXG.Curve
    1803  * @param {JXG.Board} board Reference to the board the cardinal spline is drawn on.
    1804  * @param {Array} parents Array with three entries.
    1805  * <p>
    1806  *   First entry: Array of points the spline interpolates. This can be
    1807  *   <ul>
    1808  *   <li> an array of JSXGraph points</li>
    1809  *   <li> an array of coordinate pairs</li>
    1810  *   <li> an array of functions returning coordinate pairs</li>
    1811  *   <li> an array consisting of an array with x-coordinates and an array of y-coordinates</li>
    1812  *   </ul>
    1813  *   All individual entries of coordinates arrays may be numbers or functions returning numbers.
    1814  *   <p>
    1815  *   Second entry: tau number or function
    1816  *   <p>
    1817  *   Third entry: type string containing 'uniform' (default) or 'centripetal'.
    1818  * @param {Object} attributes Define color, width, ... of the cardinal spline
    1819  * @returns {JXG.Curve} Returns reference to an object of type JXG.Curve.
    1820  * @see JXG.Curve
    1821  * @example
    1822  * //create a cardinal spline out of an array of JXG points with adjustable tension
    1823  * //create array of points
    1824  * var p1 = board.create('point',[0,0])
    1825  * var p2 = board.create('point',[1,4])
    1826  * var p3 = board.create('point',[4,5])
    1827  * var p4 = board.create('point',[2,3])
    1828  * var p5 = board.create('point',[3,0])
    1829  * var p = [p1,p2,p3,p4,p5]
    1830  *
    1831  * // tension
    1832  * tau = board.create('slider', [[4,3],[9,3],[0.001,0.5,1]], {name:'tau'});
    1833  * c = board.create('curve', JXG.Math.Numerics.CardinalSpline(p, function(){ return tau.Value();}), {strokeWidth:3});
    1834  * </pre><div id="JXG6c197afc-e482-11e5-b2af-901b0e1b8723" style="width: 300px; height: 300px;"></div>
    1835  * <script type="text/javascript">
    1836  *     (function() {
    1837  *         var board = JXG.JSXGraph.initBoard('JXG6c197afc-e482-11e5-b2af-901b0e1b8723',
    1838  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
    1839  *
    1840  *     var p = [];
    1841  *     p[0] = board.create('point', [-2,2], {size: 4, face: 'o'});
    1842  *     p[1] = board.create('point', [0,-1], {size: 4, face: 'o'});
    1843  *     p[2] = board.create('point', [2,0], {size: 4, face: 'o'});
    1844  *     p[3] = board.create('point', [4,1], {size: 4, face: 'o'});
    1845  *
    1846  *     var c = board.create('spline', p, {strokeWidth:3});
    1847  *     })();
    1848  *
    1849  * </script><pre>
    1850  */
    1851 JXG.createCardinalSpline = function (board, parents, attributes) {
    1852     var el,
    1853         getPointLike,
    1854         points,
    1855         tau,
    1856         type,
    1857         p,
    1858         q,
    1859         i,
    1860         le,
    1861         splineArr,
    1862         errStr = "\nPossible parent types: [points:array, tau:number|function, type:string]";
    1863
    1864     if (!Type.exists(parents[0]) || !Type.isArray(parents[0])) {
    1865         throw new Error(
    1866             "JSXGraph: JXG.createCardinalSpline: argument 1 'points' has to be array of points or coordinate pairs" +
    1867                 errStr
    1868         );
    1869     }
    1870     if (
    1871         !Type.exists(parents[1]) ||
    1872         (!Type.isNumber(parents[1]) && !Type.isFunction(parents[1]))
    1873     ) {
    1874         throw new Error(
    1875             "JSXGraph: JXG.createCardinalSpline: argument 2 'tau' has to be number between [0,1] or function'" +
    1876                 errStr
    1877         );
    1878     }
    1879     if (!Type.exists(parents[2]) || !Type.isString(parents[2])) {
    1880         throw new Error(
    1881             "JSXGraph: JXG.createCardinalSpline: argument 3 'type' has to be string 'uniform' or 'centripetal'" +
    1882                 errStr
    1883         );
    1884     }
    1885
    1886     attributes = Type.copyAttributes(attributes, board.options, "curve");
    1887     attributes = Type.copyAttributes(attributes, board.options, "cardinalspline");
    1888     attributes.curvetype = "parameter";
    1889
    1890     p = parents[0];
    1891     q = [];
    1892
    1893     // Given as [x[], y[]]
    1894     if (
    1895         !attributes.isarrayofcoordinates &&
    1896         p.length === 2 &&
    1897         Type.isArray(p[0]) &&
    1898         Type.isArray(p[1]) &&
    1899         p[0].length === p[1].length
    1900     ) {
    1901         for (i = 0; i < p[0].length; i++) {
    1902             q[i] = [];
    1903             if (Type.isFunction(p[0][i])) {
    1904                 q[i].push(p[0][i]());
    1905             } else {
    1906                 q[i].push(p[0][i]);
    1907             }
    1908
    1909             if (Type.isFunction(p[1][i])) {
    1910                 q[i].push(p[1][i]());
    1911             } else {
    1912                 q[i].push(p[1][i]);
    1913             }
    1914         }
    1915     } else {
    1916         // given as [[x0, y0], [x1, y1], point, ...]
    1917         for (i = 0; i < p.length; i++) {
    1918             if (Type.isString(p[i])) {
    1919                 q.push(board.select(p[i]));
    1920             } else if (Type.isPoint(p[i])) {
    1921                 q.push(p[i]);
    1922                 // given as [[x0,y0], [x1, y2], ...]
    1923             } else if (Type.isArray(p[i]) && p[i].length === 2) {
    1924                 q[i] = [];
    1925                 if (Type.isFunction(p[i][0])) {
    1926                     q[i].push(p[i][0]());
    1927                 } else {
    1928                     q[i].push(p[i][0]);
    1929                 }
    1930
    1931                 if (Type.isFunction(p[i][1])) {
    1932                     q[i].push(p[i][1]());
    1933                 } else {
    1934                     q[i].push(p[i][1]);
    1935                 }
    1936             } else if (Type.isFunction(p[i]) && p[i]().length === 2) {
    1937                 q.push(parents[i]());
    1938             }
    1939         }
    1940     }
    1941
    1942     if (attributes.createpoints === true) {
    1943         points = Type.providePoints(board, q, attributes, "cardinalspline", ["points"]);
    1944     } else {
    1945         points = [];
    1946
    1947         /**
    1948          * @ignore
    1949          */
    1950         getPointLike = function (ii) {
    1951             return {
    1952                 X: function () {
    1953                     return q[ii][0];
    1954                 },
    1955                 Y: function () {
    1956                     return q[ii][1];
    1957                 },
    1958                 Dist: function (p) {
    1959                     var dx = this.X() - p.X(),
    1960                         dy = this.Y() - p.Y();
    1961
    1962                     return Mat.hypot(dx, dy);
    1963                 }
    1964             };
    1965         };
    1966
    1967         for (i = 0; i < q.length; i++) {
    1968             if (Type.isPoint(q[i])) {
    1969                 points.push(q[i]);
    1970             } else {
    1971                 points.push(getPointLike(i));
    1972             }
    1973         }
    1974     }
    1975
    1976     tau = parents[1];
    1977     type = parents[2];
    1978
    1979     splineArr = ["x"].concat(Numerics.CardinalSpline(points, tau, type));
    1980
    1981     el = new JXG.Curve(board, splineArr, attributes);
    1982     le = points.length;
    1983     el.setParents(points);
    1984     for (i = 0; i < le; i++) {
    1985         p = points[i];
    1986         if (Type.isPoint(p)) {
    1987             if (Type.exists(p._is_new)) {
    1988                 el.addChild(p);
    1989                 delete p._is_new;
    1990             } else {
    1991                 p.addChild(el);
    1992             }
    1993         }
    1994     }
    1995     el.elType = "cardinalspline";
    1996
    1997     return el;
    1998 };
    1999
    2000 /**
    2001  * Register the element type cardinalspline at JSXGraph
    2002  * @private
    2003  */
    2004 JXG.registerElement("cardinalspline", JXG.createCardinalSpline);
    2005
    2006 /**
    2007  * @class This element is used to provide a constructor for metapost spline curves.
    2008  * Create a dynamic metapost spline interpolated curve given by sample points p_1 to p_n.
    2009  * @pseudo
    2010  * @name Metapostspline
    2011  * @augments JXG.Curve
    2012  * @constructor
    2013  * @type JXG.Curve
    2014  * @param {JXG.Board} board Reference to the board the metapost spline is drawn on.
    2015  * @param {Array} parents Array with two entries.
    2016  * <p>
    2017  *   First entry: Array of points the spline interpolates. This can be
    2018  *   <ul>
    2019  *   <li> an array of JSXGraph points</li>
    2020  *   <li> an object of coordinate pairs</li>
    2021  *   <li> an array of functions returning coordinate pairs</li>
    2022  *   <li> an array consisting of an array with x-coordinates and an array of y-coordinates</li>
    2023  *   </ul>
    2024  *   All individual entries of coordinates arrays may be numbers or functions returning numbers.
    2025  *   <p>
    2026  *   Second entry: JavaScript object containing the control values like tension, direction, curl.
    2027  * @param {Object} attributes Define color, width, ... of the metapost spline
    2028  * @returns {JXG.Curve} Returns reference to an object of type JXG.Curve.
    2029  * @see JXG.Curve
    2030  * @example
    2031  *     var po = [],
    2032  *         attr = {
    2033  *             size: 5,
    2034  *             color: 'red'
    2035  *         },
    2036  *         controls;
    2037  *
    2038  *     var tension = board.create('slider', [[-3, 6], [3, 6], [0, 1, 20]], {name: 'tension'});
    2039  *     var curl = board.create('slider', [[-3, 5], [3, 5], [0, 1, 30]], {name: 'curl A, D'});
    2040  *     var dir = board.create('slider', [[-3, 4], [3, 4], [-180, 0, 180]], {name: 'direction B'});
    2041  *
    2042  *     po.push(board.create('point', [-3, -3]));
    2043  *     po.push(board.create('point', [0, -3]));
    2044  *     po.push(board.create('point', [4, -5]));
    2045  *     po.push(board.create('point', [6, -2]));
    2046  *
    2047  *     var controls = {
    2048  *         tension: function() {return tension.Value(); },
    2049  *         direction: { 1: function() {return dir.Value(); } },
    2050  *         curl: { 0: function() {return curl.Value(); },
    2051  *                 3: function() {return curl.Value(); }
    2052  *             },
    2053  *         isClosed: false
    2054  *     };
    2055  *
    2056  *     // Plot a metapost curve
    2057  *     var cu = board.create('metapostspline', [po, controls], {strokeColor: 'blue', strokeWidth: 2});
    2058  *
    2059  *
    2060  * </pre><div id="JXGb8c6ffed-7419-41a3-9e55-3754b2327ae9" class="jxgbox" style="width: 300px; height: 300px;"></div>
    2061  * <script type="text/javascript">
    2062  *     (function() {
    2063  *         var board = JXG.JSXGraph.initBoard('JXGb8c6ffed-7419-41a3-9e55-3754b2327ae9',
    2064  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
    2065  *         var po = [],
    2066  *             attr = {
    2067  *                 size: 5,
    2068  *                 color: 'red'
    2069  *             },
    2070  *             controls;
    2071  *
    2072  *         var tension = board.create('slider', [[-3, 6], [3, 6], [0, 1, 20]], {name: 'tension'});
    2073  *         var curl = board.create('slider', [[-3, 5], [3, 5], [0, 1, 30]], {name: 'curl A, D'});
    2074  *         var dir = board.create('slider', [[-3, 4], [3, 4], [-180, 0, 180]], {name: 'direction B'});
    2075  *
    2076  *         po.push(board.create('point', [-3, -3]));
    2077  *         po.push(board.create('point', [0, -3]));
    2078  *         po.push(board.create('point', [4, -5]));
    2079  *         po.push(board.create('point', [6, -2]));
    2080  *
    2081  *         var controls = {
    2082  *             tension: function() {return tension.Value(); },
    2083  *             direction: { 1: function() {return dir.Value(); } },
    2084  *             curl: { 0: function() {return curl.Value(); },
    2085  *                     3: function() {return curl.Value(); }
    2086  *                 },
    2087  *             isClosed: false
    2088  *         };
    2089  *
    2090  *         // Plot a metapost curve
    2091  *         var cu = board.create('metapostspline', [po, controls], {strokeColor: 'blue', strokeWidth: 2});
    2092  *
    2093  *
    2094  *     })();
    2095  *
    2096  * </script><pre>
    2097  *
    2098  */
    2099 JXG.createMetapostSpline = function (board, parents, attributes) {
    2100     var el,
    2101         getPointLike,
    2102         points,
    2103         controls,
    2104         p,
    2105         q,
    2106         i,
    2107         le,
    2108         errStr = "\nPossible parent types: [points:array, controls:object";
    2109
    2110     if (!Type.exists(parents[0]) || !Type.isArray(parents[0])) {
    2111         throw new Error(
    2112             "JSXGraph: JXG.createMetapostSpline: argument 1 'points' has to be array of points or coordinate pairs" +
    2113                 errStr
    2114         );
    2115     }
    2116     if (!Type.exists(parents[1]) || !Type.isObject(parents[1])) {
    2117         throw new Error(
    2118             "JSXGraph: JXG.createMetapostSpline: argument 2 'controls' has to be a JavaScript object'" +
    2119                 errStr
    2120         );
    2121     }
    2122
    2123     attributes = Type.copyAttributes(attributes, board.options, "curve");
    2124     attributes = Type.copyAttributes(attributes, board.options, "metapostspline");
    2125     attributes.curvetype = "parameter";
    2126
    2127     p = parents[0];
    2128     q = [];
    2129
    2130     // given as [x[], y[]]
    2131     if (
    2132         !attributes.isarrayofcoordinates &&
    2133         p.length === 2 &&
    2134         Type.isArray(p[0]) &&
    2135         Type.isArray(p[1]) &&
    2136         p[0].length === p[1].length
    2137     ) {
    2138         for (i = 0; i < p[0].length; i++) {
    2139             q[i] = [];
    2140             if (Type.isFunction(p[0][i])) {
    2141                 q[i].push(p[0][i]());
    2142             } else {
    2143                 q[i].push(p[0][i]);
    2144             }
    2145
    2146             if (Type.isFunction(p[1][i])) {
    2147                 q[i].push(p[1][i]());
    2148             } else {
    2149                 q[i].push(p[1][i]);
    2150             }
    2151         }
    2152     } else {
    2153         // given as [[x0, y0], [x1, y1], point, ...]
    2154         for (i = 0; i < p.length; i++) {
    2155             if (Type.isString(p[i])) {
    2156                 q.push(board.select(p[i]));
    2157             } else if (Type.isPoint(p[i])) {
    2158                 q.push(p[i]);
    2159                 // given as [[x0,y0], [x1, y2], ...]
    2160             } else if (Type.isArray(p[i]) && p[i].length === 2) {
    2161                 q[i] = [];
    2162                 if (Type.isFunction(p[i][0])) {
    2163                     q[i].push(p[i][0]());
    2164                 } else {
    2165                     q[i].push(p[i][0]);
    2166                 }
    2167
    2168                 if (Type.isFunction(p[i][1])) {
    2169                     q[i].push(p[i][1]());
    2170                 } else {
    2171                     q[i].push(p[i][1]);
    2172                 }
    2173             } else if (Type.isFunction(p[i]) && p[i]().length === 2) {
    2174                 q.push(parents[i]());
    2175             }
    2176         }
    2177     }
    2178
    2179     if (attributes.createpoints === true) {
    2180         points = Type.providePoints(board, q, attributes, 'metapostspline', ['points']);
    2181     } else {
    2182         points = [];
    2183
    2184         /**
    2185          * @ignore
    2186          */
    2187         getPointLike = function (ii) {
    2188             return {
    2189                 X: function () {
    2190                     return q[ii][0];
    2191                 },
    2192                 Y: function () {
    2193                     return q[ii][1];
    2194                 }
    2195             };
    2196         };
    2197
    2198         for (i = 0; i < q.length; i++) {
    2199             if (Type.isPoint(q[i])) {
    2200                 points.push(q[i]);
    2201             } else {
    2202                 points.push(getPointLike);
    2203             }
    2204         }
    2205     }
    2206
    2207     controls = parents[1];
    2208
    2209     el = new JXG.Curve(board, ["t", [], [], 0, p.length - 1], attributes);
    2210     /**
    2211      * @class
    2212      * @ignore
    2213      */
    2214     el.updateDataArray = function () {
    2215         var res,
    2216             i,
    2217             len = points.length,
    2218             p = [];
    2219
    2220         for (i = 0; i < len; i++) {
    2221             p.push([points[i].X(), points[i].Y()]);
    2222         }
    2223
    2224         res = JXG.Math.Metapost.curve(p, controls);
    2225         this.dataX = res[0];
    2226         this.dataY = res[1];
    2227     };
    2228     el.bezierDegree = 3;
    2229
    2230     le = points.length;
    2231     el.setParents(points);
    2232     for (i = 0; i < le; i++) {
    2233         if (Type.isPoint(points[i])) {
    2234             points[i].addChild(el);
    2235         }
    2236     }
    2237     el.elType = "metapostspline";
    2238
    2239     return el;
    2240 };
    2241
    2242 JXG.registerElement("metapostspline", JXG.createMetapostSpline);
    2243
    2244 /**
    2245  * @class This element is used to provide a constructor for Riemann sums, which is realized as a special curve.
    2246  * The returned element has the method Value() which returns the sum of the areas of the bars.
    2247  * <p>
    2248  * In case of type "simpson" and "trapezoidal", the horizontal line approximating the function value
    2249  * is replaced by a parabola or a secant. IN case of "simpson",
    2250  * the parabola is approximated visually by a polygonal chain of fixed step width.
    2251  *
    2252  * @pseudo
    2253  * @name Riemannsum
    2254  * @augments JXG.Curve
    2255  * @constructor
    2256  * @type Curve
    2257  * @param {function,array_number,function_string,function_function,number_function,number} f,n,type_,a_,b_ Parent elements of Riemannsum are a
    2258  *         Either a function term f(x) describing the function graph which is filled by the Riemann bars, or
    2259  *         an array consisting of two functions and the area between is filled by the Riemann bars.
    2260  *         <p>
    2261  *         n determines the number of bars, it is either a fixed number or a function.
    2262  *         <p>
    2263  *         type is a string or function returning one of the values:  'left', 'right', 'middle', 'lower', 'upper', 'random', 'simpson', or 'trapezoidal'.
    2264  *         Default value is 'left'. "simpson" is Simpson's 1/3 rule.
    2265  *         <p>
    2266  *         Further parameters are an optional number or function for the left interval border a,
    2267  *         and an optional number or function for the right interval border b.
    2268  *         <p>
    2269  *         Default values are a=-10 and b=10.
    2270  * @see JXG.Curve
    2271  * @example
    2272  * // Create Riemann sums for f(x) = 0.5*x*x-2*x.
    2273  *   var s = board.create('slider',[[0,4],[3,4],[0,4,10]],{snapWidth:1});
    2274  *   var f = function(x) { return 0.5*x*x-2*x; };
    2275  *   var r = board.create('riemannsum',
    2276  *               [f, function(){return s.Value();}, 'upper', -2, 5],
    2277  *               {fillOpacity:0.4}
    2278  *               );
    2279  *   var g = board.create('functiongraph',[f, -2, 5]);
    2280  *   var t = board.create('text',[-2,-2, function(){ return 'Sum=' + JXG.toFixed(r.Value(), 4); }]);
    2281  * </pre><div class="jxgbox" id="JXG940f40cc-2015-420d-9191-c5d83de988cf" style="width: 300px; height: 300px;"></div>
    2282  * <script type="text/javascript">
    2283  * (function(){
    2284  *   var board = JXG.JSXGraph.initBoard('JXG940f40cc-2015-420d-9191-c5d83de988cf', {boundingbox: [-3, 7, 5, -3], axis: true, showcopyright: false, shownavigation: false});
    2285  *   var f = function(x) { return 0.5*x*x-2*x; };
    2286  *   var s = board.create('slider',[[0,4],[3,4],[0,4,10]],{snapWidth:1});
    2287  *   var r = board.create('riemannsum', [f, function(){return s.Value();}, 'upper', -2, 5], {fillOpacity:0.4});
    2288  *   var g = board.create('functiongraph', [f, -2, 5]);
    2289  *   var t = board.create('text',[-2,-2, function(){ return 'Sum=' + JXG.toFixed(r.Value(), 4); }]);
    2290  * })();
    2291  * </script><pre>
    2292  *
    2293  * @example
    2294  *   // Riemann sum between two functions
    2295  *   var s = board.create('slider',[[0,4],[3,4],[0,4,10]],{snapWidth:1});
    2296  *   var g = function(x) { return 0.5*x*x-2*x; };
    2297  *   var f = function(x) { return -x*(x-4); };
    2298  *   var r = board.create('riemannsum',
    2299  *               [[g,f], function(){return s.Value();}, 'lower', 0, 4],
    2300  *               {fillOpacity:0.4}
    2301  *               );
    2302  *   var f = board.create('functiongraph',[f, -2, 5]);
    2303  *   var g = board.create('functiongraph',[g, -2, 5]);
    2304  *   var t = board.create('text',[-2,-2, function(){ return 'Sum=' + JXG.toFixed(r.Value(), 4); }]);
    2305  * </pre><div class="jxgbox" id="JXGf9a7ba38-b50f-4a32-a873-2f3bf9caee79" style="width: 300px; height: 300px;"></div>
    2306  * <script type="text/javascript">
    2307  * (function(){
    2308  *   var board = JXG.JSXGraph.initBoard('JXGf9a7ba38-b50f-4a32-a873-2f3bf9caee79', {boundingbox: [-3, 7, 5, -3], axis: true, showcopyright: false, shownavigation: false});
    2309  *   var s = board.create('slider',[[0,4],[3,4],[0,4,10]],{snapWidth:1});
    2310  *   var g = function(x) { return 0.5*x*x-2*x; };
    2311  *   var f = function(x) { return -x*(x-4); };
    2312  *   var r = board.create('riemannsum',
    2313  *               [[g,f], function(){return s.Value();}, 'lower', 0, 4],
    2314  *               {fillOpacity:0.4}
    2315  *               );
    2316  *   var f = board.create('functiongraph',[f, -2, 5]);
    2317  *   var g = board.create('functiongraph',[g, -2, 5]);
    2318  *   var t = board.create('text',[-2,-2, function(){ return 'Sum=' + JXG.toFixed(r.Value(), 4); }]);
    2319  * })();
    2320  * </script><pre>
    2321  */
    2322 JXG.createRiemannsum = function (board, parents, attributes) {
    2323     var n, type, f, par, c, attr;
    2324
    2325     attr = Type.copyAttributes(attributes, board.options, "riemannsum");
    2326     attr.curvetype = "plot";
    2327
    2328     f = parents[0];
    2329     n = Type.createFunction(parents[1], board, "");
    2330
    2331     if (!Type.exists(n)) {
    2332         throw new Error(
    2333             "JSXGraph: JXG.createRiemannsum: argument '2' n has to be number or function." +
    2334                 "\nPossible parent types: [function,n:number|function,type,start:number|function,end:number|function]"
    2335         );
    2336     }
    2337
    2338     type = Type.createFunction(parents[2], board, "");
    2339     if (!Type.exists(type)) {
    2340         throw new Error(
    2341             "JSXGraph: JXG.createRiemannsum: argument 3 'type' has to be string or function." +
    2342                 "\nPossible parent types: [function,n:number|function,type,start:number|function,end:number|function]"
    2343         );
    2344     }
    2345
    2346     par = [[0], [0]].concat(parents.slice(3));
    2347
    2348     c = board.create("curve", par, attr);
    2349
    2350     c.sum = 0.0;
    2351     /**
    2352      * Returns the value of the Riemann sum, i.e. the sum of the (signed) areas of the rectangles.
    2353      * @name Value
    2354      * @memberOf Riemannsum.prototype
    2355      * @function
    2356      * @returns {Number} value of Riemann sum.
    2357      */
    2358     c.Value = function () {
    2359         return this.sum;
    2360     };
    2361
    2362     /**
    2363      * @class
    2364      * @ignore
    2365      */
    2366     c.updateDataArray = function () {
    2367         var u = Numerics.riemann(f, n(), type(), this.minX(), this.maxX());
    2368         this.dataX = u[0];
    2369         this.dataY = u[1];
    2370
    2371         // Update "Riemann sum"
    2372         this.sum = u[2];
    2373     };
    2374
    2375     c.addParentsFromJCFunctions([n, type]);
    2376
    2377     return c;
    2378 };
    2379
    2380 JXG.registerElement("riemannsum", JXG.createRiemannsum);
    2381
    2382 /**
    2383  * @class This element is used to provide a constructor for trace curve (simple locus curve), which is realized as a special curve.
    2384  * @pseudo
    2385  * @name Tracecurve
    2386  * @augments JXG.Curve
    2387  * @constructor
    2388  * @type Object
    2389  * @descript JXG.Curve
    2390  * @param {Point} Parent elements of Tracecurve are a
    2391  *         glider point and a point whose locus is traced.
    2392  * @param {point}
    2393  * @see JXG.Curve
    2394  * @example
    2395  * // Create trace curve.
    2396  * var c1 = board.create('circle',[[0, 0], [2, 0]]),
    2397  * p1 = board.create('point',[-3, 1]),
    2398  * g1 = board.create('glider',[2, 1, c1]),
    2399  * s1 = board.create('segment',[g1, p1]),
    2400  * p2 = board.create('midpoint',[s1]),
    2401  * curve = board.create('tracecurve', [g1, p2]);
    2402  *
    2403  * </pre><div class="jxgbox" id="JXG5749fb7d-04fc-44d2-973e-45c1951e29ad" style="width: 300px; height: 300px;"></div>
    2404  * <script type="text/javascript">
    2405  *   var tc1_board = JXG.JSXGraph.initBoard('JXG5749fb7d-04fc-44d2-973e-45c1951e29ad', {boundingbox: [-4, 4, 4, -4], axis: false, showcopyright: false, shownavigation: false});
    2406  *   var c1 = tc1_board.create('circle',[[0, 0], [2, 0]]),
    2407  *       p1 = tc1_board.create('point',[-3, 1]),
    2408  *       g1 = tc1_board.create('glider',[2, 1, c1]),
    2409  *       s1 = tc1_board.create('segment',[g1, p1]),
    2410  *       p2 = tc1_board.create('midpoint',[s1]),
    2411  *       curve = tc1_board.create('tracecurve', [g1, p2]);
    2412  * </script><pre>
    2413  */
    2414 JXG.createTracecurve = function (board, parents, attributes) {
    2415     var c, glider, tracepoint, attr;
    2416
    2417     if (parents.length !== 2) {
    2418         throw new Error(
    2419             "JSXGraph: Can't create trace curve with given parent'" +
    2420                 "\nPossible parent types: [glider, point]"
    2421         );
    2422     }
    2423
    2424     glider = board.select(parents[0]);
    2425     tracepoint = board.select(parents[1]);
    2426
    2427     if (glider.type !== Const.OBJECT_TYPE_GLIDER || !Type.isPoint(tracepoint)) {
    2428         throw new Error(
    2429             "JSXGraph: Can't create trace curve with parent types '" +
    2430                 typeof parents[0] +
    2431                 "' and '" +
    2432                 typeof parents[1] +
    2433                 "'." +
    2434                 "\nPossible parent types: [glider, point]"
    2435         );
    2436     }
    2437
    2438     attr = Type.copyAttributes(attributes, board.options, "tracecurve");
    2439     attr.curvetype = "plot";
    2440     c = board.create("curve", [[0], [0]], attr);
    2441
    2442     /**
    2443      * @class
    2444      * @ignore
    2445      */
    2446     c.updateDataArray = function () {
    2447         var i, step, t, el, pEl, x, y, from,
    2448             savetrace,
    2449             le = attr.numberpoints,
    2450             savePos = glider.position,
    2451             slideObj = glider.slideObject,
    2452             mi = slideObj.minX(),
    2453             ma = slideObj.maxX();
    2454
    2455         // set step width
    2456         step = (ma - mi) / le;
    2457         this.dataX = [];
    2458         this.dataY = [];
    2459
    2460         /*
    2461          * For gliders on circles and lines a closed curve is computed.
    2462          * For gliders on curves the curve is not closed.
    2463          */
    2464         if (slideObj.elementClass !== Const.OBJECT_CLASS_CURVE) {
    2465             le++;
    2466         }
    2467
    2468         // Loop over all steps
    2469         for (i = 0; i < le; i++) {
    2470             t = mi + i * step;
    2471             x = slideObj.X(t) / slideObj.Z(t);
    2472             y = slideObj.Y(t) / slideObj.Z(t);
    2473
    2474             // Position the glider
    2475             glider.setPositionDirectly(Const.COORDS_BY_USER, [x, y]);
    2476             from = false;
    2477
    2478             // Update all elements from the glider up to the trace element
    2479             for (el in this.board.objects) {
    2480                 if (this.board.objects.hasOwnProperty(el)) {
    2481                     pEl = this.board.objects[el];
    2482
    2483                     if (pEl === glider) {
    2484                         from = true;
    2485                     }
    2486
    2487                     if (from && pEl.needsRegularUpdate) {
    2488                         // Save the trace mode of the element
    2489                         savetrace = pEl.visProp.trace;
    2490                         pEl.visProp.trace = false;
    2491                         pEl.needsUpdate = true;
    2492                         pEl.update(true);
    2493
    2494                         // Restore the trace mode
    2495                         pEl.visProp.trace = savetrace;
    2496                         if (pEl === tracepoint) {
    2497                             break;
    2498                         }
    2499                     }
    2500                 }
    2501             }
    2502
    2503             // Store the position of the trace point
    2504             this.dataX[i] = tracepoint.X();
    2505             this.dataY[i] = tracepoint.Y();
    2506         }
    2507
    2508         // Restore the original position of the glider
    2509         glider.position = savePos;
    2510         from = false;
    2511
    2512         // Update all elements from the glider to the trace point
    2513         for (el in this.board.objects) {
    2514             if (this.board.objects.hasOwnProperty(el)) {
    2515                 pEl = this.board.objects[el];
    2516                 if (pEl === glider) {
    2517                     from = true;
    2518                 }
    2519
    2520                 if (from && pEl.needsRegularUpdate) {
    2521                     savetrace = pEl.visProp.trace;
    2522                     pEl.visProp.trace = false;
    2523                     pEl.needsUpdate = true;
    2524                     pEl.update(true);
    2525                     pEl.visProp.trace = savetrace;
    2526
    2527                     if (pEl === tracepoint) {
    2528                         break;
    2529                     }
    2530                 }
    2531             }
    2532         }
    2533     };
    2534
    2535     return c;
    2536 };
    2537
    2538 JXG.registerElement("tracecurve", JXG.createTracecurve);
    2539
    2540 /**
    2541      * @class This element is used to provide a constructor for step function, which is realized as a special curve.
    2542      *
    2543      * In case the data points should be updated after creation time, they can be accessed by curve.xterm and curve.yterm.
    2544      * @pseudo
    2545      * @name Stepfunction
    2546      * @augments JXG.Curve
    2547      * @constructor
    2548      * @type Curve
    2549      * @description JXG.Curve
    2550      * @param {Array|Function} Parent1 elements of Stepfunction are two arrays containing the coordinates.
    2551      * @param {Array|Function} Parent2
    2552      * @see JXG.Curve
    2553      * @example
    2554      * // Create step function.
    2555      var curve = board.create('stepfunction', [[0,1,2,3,4,5], [1,3,0,2,2,1]]);
    2556
    2557      * </pre><div class="jxgbox" id="JXG32342ec9-ad17-4339-8a97-ff23dc34f51a" style="width: 300px; height: 300px;"></div>
    2558      * <script type="text/javascript">
    2559      *   var sf1_board = JXG.JSXGraph.initBoard('JXG32342ec9-ad17-4339-8a97-ff23dc34f51a', {boundingbox: [-1, 5, 6, -2], axis: true, showcopyright: false, shownavigation: false});
    2560      *   var curve = sf1_board.create('stepfunction', [[0,1,2,3,4,5], [1,3,0,2,2,1]]);
    2561      * </script><pre>
    2562      */
    2563 JXG.createStepfunction = function (board, parents, attributes) {
    2564     var c, attr;
    2565     if (parents.length !== 2) {
    2566         throw new Error(
    2567             "JSXGraph: Can't create step function with given parent'" +
    2568                 "\nPossible parent types: [array, array|function]"
    2569         );
    2570     }
    2571
    2572     attr = Type.copyAttributes(attributes, board.options, "stepfunction");
    2573     c = board.create("curve", parents, attr);
    2574     /**
    2575      * @class
    2576      * @ignore
    2577      */
    2578     c.updateDataArray = function () {
    2579         var i,
    2580             j = 0,
    2581             len = this.xterm.length;
    2582
    2583         this.dataX = [];
    2584         this.dataY = [];
    2585
    2586         if (len === 0) {
    2587             return;
    2588         }
    2589
    2590         this.dataX[j] = this.xterm[0];
    2591         this.dataY[j] = this.yterm[0];
    2592         ++j;
    2593
    2594         for (i = 1; i < len; ++i) {
    2595             this.dataX[j] = this.xterm[i];
    2596             this.dataY[j] = this.dataY[j - 1];
    2597             ++j;
    2598             this.dataX[j] = this.xterm[i];
    2599             this.dataY[j] = this.yterm[i];
    2600             ++j;
    2601         }
    2602     };
    2603
    2604     return c;
    2605 };
    2606
    2607 JXG.registerElement("stepfunction", JXG.createStepfunction);
    2608
    2609 /**
    2610  * @class This element is used to provide a constructor for the graph showing
    2611  * the (numerical) derivative of a given curve.
    2612  *
    2613  * @pseudo
    2614  * @name Derivative
    2615  * @augments JXG.Curve
    2616  * @constructor
    2617  * @type JXG.Curve
    2618  * @param {JXG.Curve} Parent Curve for which the derivative is generated.
    2619  * @see JXG.Curve
    2620  * @example
    2621  * var cu = board.create('cardinalspline', [[[-3,0], [-1,2], [0,1], [2,0], [3,1]], 0.5, 'centripetal'], {createPoints: false});
    2622  * var d = board.create('derivative', [cu], {dash: 2});
    2623  *
    2624  * </pre><div id="JXGb9600738-1656-11e8-8184-901b0e1b8723" class="jxgbox" style="width: 300px; height: 300px;"></div>
    2625  * <script type="text/javascript">
    2626  *     (function() {
    2627  *         var board = JXG.JSXGraph.initBoard('JXGb9600738-1656-11e8-8184-901b0e1b8723',
    2628  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
    2629  *     var cu = board.create('cardinalspline', [[[-3,0], [-1,2], [0,1], [2,0], [3,1]], 0.5, 'centripetal'], {createPoints: false});
    2630  *     var d = board.create('derivative', [cu], {dash: 2});
    2631  *
    2632  *     })();
    2633  *
    2634  * </script><pre>
    2635  *
    2636  */
    2637 JXG.createDerivative = function (board, parents, attributes) {
    2638     var c, curve, dx, dy, attr;
    2639
    2640     if (parents.length !== 1 && parents[0].class !== Const.OBJECT_CLASS_CURVE) {
    2641         throw new Error(
    2642             "JSXGraph: Can't create derivative curve with given parent'" +
    2643                 "\nPossible parent types: [curve]"
    2644         );
    2645     }
    2646
    2647     attr = Type.copyAttributes(attributes, board.options, "curve");
    2648
    2649     curve = parents[0];
    2650     dx = Numerics.D(curve.X);
    2651     dy = Numerics.D(curve.Y);
    2652
    2653     c = board.create(
    2654         "curve",
    2655         [
    2656             function (t) {
    2657                 return curve.X(t);
    2658             },
    2659             function (t) {
    2660                 return dy(t) / dx(t);
    2661             },
    2662             curve.minX(),
    2663             curve.maxX()
    2664         ],
    2665         attr
    2666     );
    2667
    2668     c.setParents(curve);
    2669
    2670     return c;
    2671 };
    2672
    2673 JXG.registerElement("derivative", JXG.createDerivative);
    2674
    2675 /**
    2676  * @class Intersection of two closed path elements. The elements may be of type curve, circle, polygon, inequality.
    2677  * If one element is a curve, it has to be closed.
    2678  * The resulting element is of type curve.
    2679  * @pseudo
    2680  * @name CurveIntersection
    2681  * @param {JXG.Curve|JXG.Polygon|JXG.Circle} curve1 First element which is intersected
    2682  * @param {JXG.Curve|JXG.Polygon|JXG.Circle} curve2 Second element which is intersected
    2683  * @augments JXG.Curve
    2684  * @constructor
    2685  * @type JXG.Curve
    2686  *
    2687  * @example
    2688  * var f = board.create('functiongraph', ['cos(x)']);
    2689  * var ineq = board.create('inequality', [f], {inverse: true, fillOpacity: 0.1});
    2690  * var circ = board.create('circle', [[0,0], 4]);
    2691  * var clip = board.create('curveintersection', [ineq, circ], {fillColor: 'yellow', fillOpacity: 0.6});
    2692  *
    2693  * </pre><div id="JXGe2948257-8835-4276-9164-8acccb48e8d4" class="jxgbox" style="width: 300px; height: 300px;"></div>
    2694  * <script type="text/javascript">
    2695  *     (function() {
    2696  *         var board = JXG.JSXGraph.initBoard('JXGe2948257-8835-4276-9164-8acccb48e8d4',
    2697  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
    2698  *     var f = board.create('functiongraph', ['cos(x)']);
    2699  *     var ineq = board.create('inequality', [f], {inverse: true, fillOpacity: 0.1});
    2700  *     var circ = board.create('circle', [[0,0], 4]);
    2701  *     var clip = board.create('curveintersection', [ineq, circ], {fillColor: 'yellow', fillOpacity: 0.6});
    2702  *
    2703  *     })();
    2704  *
    2705  * </script><pre>
    2706  *
    2707  */
    2708 JXG.createCurveIntersection = function (board, parents, attributes) {
    2709     var c;
    2710
    2711     if (parents.length !== 2) {
    2712         throw new Error(
    2713             "JSXGraph: Can't create curve intersection with given parent'" +
    2714                 "\nPossible parent types: [array, array|function]"
    2715         );
    2716     }
    2717
    2718     c = board.create("curve", [[], []], attributes);
    2719     /**
    2720      * @class
    2721      * @ignore
    2722      */
    2723     c.updateDataArray = function () {
    2724         var a = JXG.Math.Clip.intersection(parents[0], parents[1], this.board);
    2725         this.dataX = a[0];
    2726         this.dataY = a[1];
    2727     };
    2728     return c;
    2729 };
    2730
    2731 /**
    2732  * @class Union of two closed path elements. The elements may be of type curve, circle, polygon, inequality.
    2733  * If one element is a curve, it has to be closed.
    2734  * The resulting element is of type curve.
    2735  * @pseudo
    2736  * @name CurveUnion
    2737  * @param {JXG.Curve|JXG.Polygon|JXG.Circle} curve1 First element defining the union
    2738  * @param {JXG.Curve|JXG.Polygon|JXG.Circle} curve2 Second element defining the union
    2739  * @augments JXG.Curve
    2740  * @constructor
    2741  * @type JXG.Curve
    2742  *
    2743  * @example
    2744  * var f = board.create('functiongraph', ['cos(x)']);
    2745  * var ineq = board.create('inequality', [f], {inverse: true, fillOpacity: 0.1});
    2746  * var circ = board.create('circle', [[0,0], 4]);
    2747  * var clip = board.create('curveunion', [ineq, circ], {fillColor: 'yellow', fillOpacity: 0.6});
    2748  *
    2749  * </pre><div id="JXGe2948257-8835-4276-9164-8acccb48e8d4" class="jxgbox" style="width: 300px; height: 300px;"></div>
    2750  * <script type="text/javascript">
    2751  *     (function() {
    2752  *         var board = JXG.JSXGraph.initBoard('JXGe2948257-8835-4276-9164-8acccb48e8d4',
    2753  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
    2754  *     var f = board.create('functiongraph', ['cos(x)']);
    2755  *     var ineq = board.create('inequality', [f], {inverse: true, fillOpacity: 0.1});
    2756  *     var circ = board.create('circle', [[0,0], 4]);
    2757  *     var clip = board.create('curveunion', [ineq, circ], {fillColor: 'yellow', fillOpacity: 0.6});
    2758  *
    2759  *     })();
    2760  *
    2761  * </script><pre>
    2762  *
    2763  */
    2764 JXG.createCurveUnion = function (board, parents, attributes) {
    2765     var c;
    2766
    2767     if (parents.length !== 2) {
    2768         throw new Error(
    2769             "JSXGraph: Can't create curve union with given parent'" +
    2770                 "\nPossible parent types: [array, array|function]"
    2771         );
    2772     }
    2773
    2774     c = board.create("curve", [[], []], attributes);
    2775     /**
    2776      * @class
    2777      * @ignore
    2778      */
    2779     c.updateDataArray = function () {
    2780         var a = JXG.Math.Clip.union(parents[0], parents[1], this.board);
    2781         this.dataX = a[0];
    2782         this.dataY = a[1];
    2783     };
    2784     return c;
    2785 };
    2786
    2787 /**
    2788  * @class Difference of two closed path elements. The elements may be of type curve, circle, polygon, inequality.
    2789  * If one element is a curve, it has to be closed.
    2790  * The resulting element is of type curve.
    2791  * @pseudo
    2792  * @name CurveDifference
    2793  * @param {JXG.Curve|JXG.Polygon|JXG.Circle} curve1 First element from which the second element is "subtracted"
    2794  * @param {JXG.Curve|JXG.Polygon|JXG.Circle} curve2 Second element which is subtracted from the first element
    2795  * @augments JXG.Curve
    2796  * @constructor
    2797  * @type JXG.Curve
    2798  *
    2799  * @example
    2800  * var f = board.create('functiongraph', ['cos(x)']);
    2801  * var ineq = board.create('inequality', [f], {inverse: true, fillOpacity: 0.1});
    2802  * var circ = board.create('circle', [[0,0], 4]);
    2803  * var clip = board.create('curvedifference', [ineq, circ], {fillColor: 'yellow', fillOpacity: 0.6});
    2804  *
    2805  * </pre><div id="JXGe2948257-8835-4276-9164-8acccb48e8d4" class="jxgbox" style="width: 300px; height: 300px;"></div>
    2806  * <script type="text/javascript">
    2807  *     (function() {
    2808  *         var board = JXG.JSXGraph.initBoard('JXGe2948257-8835-4276-9164-8acccb48e8d4',
    2809  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
    2810  *     var f = board.create('functiongraph', ['cos(x)']);
    2811  *     var ineq = board.create('inequality', [f], {inverse: true, fillOpacity: 0.1});
    2812  *     var circ = board.create('circle', [[0,0], 4]);
    2813  *     var clip = board.create('curvedifference', [ineq, circ], {fillColor: 'yellow', fillOpacity: 0.6});
    2814  *
    2815  *     })();
    2816  *
    2817  * </script><pre>
    2818  *
    2819  */
    2820 JXG.createCurveDifference = function (board, parents, attributes) {
    2821     var c;
    2822
    2823     if (parents.length !== 2) {
    2824         throw new Error(
    2825             "JSXGraph: Can't create curve difference with given parent'" +
    2826                 "\nPossible parent types: [array, array|function]"
    2827         );
    2828     }
    2829
    2830     c = board.create("curve", [[], []], attributes);
    2831     /**
    2832      * @class
    2833      * @ignore
    2834      */
    2835     c.updateDataArray = function () {
    2836         var a = JXG.Math.Clip.difference(parents[0], parents[1], this.board);
    2837         this.dataX = a[0];
    2838         this.dataY = a[1];
    2839     };
    2840     return c;
    2841 };
    2842
    2843 JXG.registerElement("curvedifference", JXG.createCurveDifference);
    2844 JXG.registerElement("curveintersection", JXG.createCurveIntersection);
    2845 JXG.registerElement("curveunion", JXG.createCurveUnion);
    2846
    2847 /**
    2848  * @class Box plot curve. The direction of the box plot can be either vertical or horizontal which
    2849  * is controlled by the attribute "dir".
    2850  * @pseudo
    2851  * @name Boxplot
    2852  * @param {Array} quantiles Array containing at least five quantiles. The elements can be of type number, function or string.
    2853  * @param {Number|Function} axis Axis position of the box plot
    2854  * @param {Number|Function} width Width of the rectangle part of the box plot. The width of the first and 4th quantile
    2855  * is relative to this width and can be controlled by the attribute "smallWidth".
    2856  * @augments JXG.Curve
    2857  * @constructor
    2858  * @type JXG.Curve
    2859  *
    2860  * @example
    2861  * var Q = [ -1, 2, 3, 3.5, 5 ];
    2862  *
    2863  * var b = board.create('boxplot', [Q, 2, 4], {strokeWidth: 3});
    2864  *
    2865  * </pre><div id="JXG13eb23a1-a641-41a2-be11-8e03e400a947" class="jxgbox" style="width: 300px; height: 300px;"></div>
    2866  * <script type="text/javascript">
    2867  *     (function() {
    2868  *         var board = JXG.JSXGraph.initBoard('JXG13eb23a1-a641-41a2-be11-8e03e400a947',
    2869  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
    2870  *     var Q = [ -1, 2, 3, 3.5, 5 ];
    2871  *     var b = board.create('boxplot', [Q, 2, 4], {strokeWidth: 3});
    2872  *
    2873  *     })();
    2874  *
    2875  * </script><pre>
    2876  *
    2877  * @example
    2878  * var Q = [ -1, 2, 3, 3.5, 5 ];
    2879  * var b = board.create('boxplot', [Q, 3, 4], {dir: 'horizontal', smallWidth: 0.25, color:'red'});
    2880  *
    2881  * </pre><div id="JXG0deb9cb2-84bc-470d-a6db-8be9a5694813" class="jxgbox" style="width: 300px; height: 300px;"></div>
    2882  * <script type="text/javascript">
    2883  *     (function() {
    2884  *         var board = JXG.JSXGraph.initBoard('JXG0deb9cb2-84bc-470d-a6db-8be9a5694813',
    2885  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
    2886  *     var Q = [ -1, 2, 3, 3.5, 5 ];
    2887  *     var b = board.create('boxplot', [Q, 3, 4], {dir: 'horizontal', smallWidth: 0.25, color:'red'});
    2888  *
    2889  *     })();
    2890  *
    2891  * </script><pre>
    2892  *
    2893  * @example
    2894  * var data = [57, 57, 57, 58, 63, 66, 66, 67, 67, 68, 69, 70, 70, 70, 70, 72, 73, 75, 75, 76, 76, 78, 79, 81];
    2895  * var Q = [];
    2896  *
    2897  * Q[0] = JXG.Math.Statistics.min(data);
    2898  * Q = Q.concat(JXG.Math.Statistics.percentile(data, [25, 50, 75]));
    2899  * Q[4] = JXG.Math.Statistics.max(data);
    2900  *
    2901  * var b = board.create('boxplot', [Q, 0, 3]);
    2902  *
    2903  * </pre><div id="JXGef079e76-ae99-41e4-af29-1d07d83bf85a" class="jxgbox" style="width: 300px; height: 300px;"></div>
    2904  * <script type="text/javascript">
    2905  *     (function() {
    2906  *         var board = JXG.JSXGraph.initBoard('JXGef079e76-ae99-41e4-af29-1d07d83bf85a',
    2907  *             {boundingbox: [-5,90,5,30], axis: true, showcopyright: false, shownavigation: false});
    2908  *     var data = [57, 57, 57, 58, 63, 66, 66, 67, 67, 68, 69, 70, 70, 70, 70, 72, 73, 75, 75, 76, 76, 78, 79, 81];
    2909  *     var Q = [];
    2910  *
    2911  *     Q[0] = JXG.Math.Statistics.min(data);
    2912  *     Q = Q.concat(JXG.Math.Statistics.percentile(data, [25, 50, 75]));
    2913  *     Q[4] = JXG.Math.Statistics.max(data);
    2914  *
    2915  *     var b = board.create('boxplot', [Q, 0, 3]);
    2916  *
    2917  *     })();
    2918  *
    2919  * </script><pre>
    2920  *
    2921  * @example
    2922  * var mi = board.create('glider', [0, -1, board.defaultAxes.y]);
    2923  * var ma = board.create('glider', [0, 5, board.defaultAxes.y]);
    2924  * var Q = [function() { return mi.Y(); }, 2, 3, 3.5, function() { return ma.Y(); }];
    2925  *
    2926  * var b = board.create('boxplot', [Q, 0, 2]);
    2927  *
    2928  * </pre><div id="JXG3b3225da-52f0-42fe-8396-be9016bf289b" class="jxgbox" style="width: 300px; height: 300px;"></div>
    2929  * <script type="text/javascript">
    2930  *     (function() {
    2931  *         var board = JXG.JSXGraph.initBoard('JXG3b3225da-52f0-42fe-8396-be9016bf289b',
    2932  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
    2933  *     var mi = board.create('glider', [0, -1, board.defaultAxes.y]);
    2934  *     var ma = board.create('glider', [0, 5, board.defaultAxes.y]);
    2935  *     var Q = [function() { return mi.Y(); }, 2, 3, 3.5, function() { return ma.Y(); }];
    2936  *
    2937  *     var b = board.create('boxplot', [Q, 0, 2]);
    2938  *
    2939  *     })();
    2940  *
    2941  * </script><pre>
    2942  *
    2943  */
    2944 JXG.createBoxPlot = function (board, parents, attributes) {
    2945     var box, i, len,
    2946         attr = Type.copyAttributes(attributes, board.options, "boxplot");
    2947
    2948     if (parents.length !== 3) {
    2949         throw new Error(
    2950             "JSXGraph: Can't create box plot with given parent'" +
    2951                 "\nPossible parent types: [array, number|function, number|function] containing quantiles, axis, width"
    2952         );
    2953     }
    2954     if (parents[0].length < 5) {
    2955         throw new Error(
    2956             "JSXGraph: Can't create box plot with given parent[0]'" +
    2957                 "\nparent[0] has to contain at least 5 quantiles."
    2958         );
    2959     }
    2960     box = board.create("curve", [[], []], attr);
    2961
    2962     len = parents[0].length; // Quantiles
    2963     box.Q = [];
    2964     for (i = 0; i < len; i++) {
    2965         box.Q[i] = Type.createFunction(parents[0][i], board);
    2966     }
    2967     box.x = Type.createFunction(parents[1], board);
    2968     box.w = Type.createFunction(parents[2], board);
    2969
    2970     /**
    2971      * @class
    2972      * @ignore
    2973      */
    2974     box.updateDataArray = function () {
    2975         var v1, v2, l1, l2, r1, r2, w2, dir, x;
    2976
    2977         w2 = Type.evaluate(this.visProp.smallwidth);
    2978         dir = Type.evaluate(this.visProp.dir);
    2979         x = this.x();
    2980         l1 = x - this.w() * 0.5;
    2981         l2 = x - this.w() * 0.5 * w2;
    2982         r1 = x + this.w() * 0.5;
    2983         r2 = x + this.w() * 0.5 * w2;
    2984         v1 = [x, l2, r2, x, x, l1, l1, r1, r1, x, NaN, l1, r1, NaN, x, x, l2, r2, x];
    2985         v2 = [
    2986             this.Q[0](),
    2987             this.Q[0](),
    2988             this.Q[0](),
    2989             this.Q[0](),
    2990             this.Q[1](),
    2991             this.Q[1](),
    2992             this.Q[3](),
    2993             this.Q[3](),
    2994             this.Q[1](),
    2995             this.Q[1](),
    2996             NaN,
    2997             this.Q[2](),
    2998             this.Q[2](),
    2999             NaN,
    3000             this.Q[3](),
    3001             this.Q[4](),
    3002             this.Q[4](),
    3003             this.Q[4](),
    3004             this.Q[4]()
    3005         ];
    3006         if (dir === "vertical") {
    3007             this.dataX = v1;
    3008             this.dataY = v2;
    3009         } else {
    3010             this.dataX = v2;
    3011             this.dataY = v1;
    3012         }
    3013     };
    3014
    3015     box.addParentsFromJCFunctions([box.Q, box.x, box.w]);
    3016
    3017     return box;
    3018 };
    3019
    3020 JXG.registerElement("boxplot", JXG.createBoxPlot);
    3021
    3022 /**
    3023  *
    3024  * @class
    3025  * From <a href="https://en.wikipedia.org/wiki/Implicit_curve">Wikipedia</a>:
    3026  * "An implicit curve is a plane curve defined by an implicit equation
    3027  * relating two coordinate variables, commonly <i>x</i> and <i>y</i>.
    3028  * For example, the unit circle is defined by the implicit equation
    3029  * x<sup>2</sup> + y<sup>2</sup> = 1.
    3030  * In general, every implicit curve is defined by an equation of the form
    3031  * <i>f(x, y) = 0</i>
    3032  * for some function <i>f</i> of two variables."
    3033  * <p>
    3034  * The partial derivatives for <i>f</i> are optional. If not given, numerical
    3035  * derivatives are used instead. This is good enough for most practical use cases.
    3036  * But if supplied, both partial derivatives must be supplied.
    3037  * <p>
    3038  * The most effective attributes to tinker with if the implicit curve algorithm fails are
    3039  * {@link ImplicitCurve#resolution_outer},
    3040  * {@link ImplicitCurve#resolution_inner},
    3041  * {@link ImplicitCurve#alpha_0},
    3042  * {@link ImplicitCurve#h_initial},
    3043  * {@link ImplicitCurve#h_max}, and
    3044  * {@link ImplicitCurve#qdt_box}.
    3045  *
    3046  * @pseudo
    3047  * @name ImplicitCurve
    3048  * @param {Function|String} f Function of two variables for the left side of the equation <i>f(x,y)=0</i>.
    3049  * If f is supplied as string, it has to use the variables 'x' and 'y'.
    3050  * @param {Function|String} [dfx=null] Optional partial derivative in respect to the first variable
    3051  * If dfx is supplied as string, it has to use the variables 'x' and 'y'.
    3052  * @param {Function|String} [dfy=null] Optional partial derivative in respect to the second variable
    3053  * If dfy is supplied as string, it has to use the variables 'x' and 'y'.
    3054  * @augments JXG.Curve
    3055  * @constructor
    3056  * @type JXG.Curve
    3057  *
    3058  * @example
    3059  *   var f, c;
    3060  *   f = (x, y) => 1 / 16 * x ** 2 + y ** 2 - 1;
    3061  *   c = board.create('implicitcurve', [f], {
    3062  *       strokeWidth: 3,
    3063  *       strokeColor: JXG.palette.red,
    3064  *       strokeOpacity: 0.8
    3065  *   });
    3066  *
    3067  * </pre><div id="JXGa6e86701-1a82-48d0-b007-3a3d32075076" class="jxgbox" style="width: 300px; height: 300px;"></div>
    3068  * <script type="text/javascript">
    3069  *     (function() {
    3070  *         var board = JXG.JSXGraph.initBoard('JXGa6e86701-1a82-48d0-b007-3a3d32075076',
    3071  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
    3072  *             var f, c;
    3073  *             f = (x, y) => 1 / 16 * x ** 2 + y ** 2 - 1;
    3074  *             c = board.create('implicitcurve', [f], {
    3075  *                 strokeWidth: 3,
    3076  *                 strokeColor: JXG.palette.red,
    3077  *                 strokeOpacity: 0.8
    3078  *             });
    3079  *
    3080  *     })();
    3081  *
    3082  * </script><pre>
    3083  *
    3084  * @example
    3085  *  var a, c, f;
    3086  *  a = board.create('slider', [[-3, 6], [3, 6], [-3, 1, 3]], {
    3087  *      name: 'a', stepWidth: 0.1
    3088  *  });
    3089  *  f = (x, y) => x ** 2 - 2 * x * y - 2 * x + (a.Value() + 1) * y ** 2 + (4 * a.Value() + 2) * y + 4 * a.Value() - 3;
    3090  *  c = board.create('implicitcurve', [f], {
    3091  *      strokeWidth: 3,
    3092  *      strokeColor: JXG.palette.red,
    3093  *      strokeOpacity: 0.8,
    3094  *      resolution_outer: 20,
    3095  *      resolution_inner: 20
    3096  *  });
    3097  *
    3098  * </pre><div id="JXG0b133a54-9509-4a65-9722-9c5145e23b40" class="jxgbox" style="width: 300px; height: 300px;"></div>
    3099  * <script type="text/javascript">
    3100  *     (function() {
    3101  *         var board = JXG.JSXGraph.initBoard('JXG0b133a54-9509-4a65-9722-9c5145e23b40',
    3102  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
    3103  *             var a, c, f;
    3104  *             a = board.create('slider', [[-3, 6], [3, 6], [-3, 1, 3]], {
    3105  *                 name: 'a', stepWidth: 0.1
    3106  *             });
    3107  *             f = (x, y) => x ** 2 - 2 * x * y - 2 * x + (a.Value() + 1) * y ** 2 + (4 * a.Value() + 2) * y + 4 * a.Value() - 3;
    3108  *             c = board.create('implicitcurve', [f], {
    3109  *                 strokeWidth: 3,
    3110  *                 strokeColor: JXG.palette.red,
    3111  *                 strokeOpacity: 0.8,
    3112  *                 resolution_outer: 20,
    3113  *                 resolution_inner: 20
    3114  *             });
    3115  *
    3116  *     })();
    3117  *
    3118  * </script><pre>
    3119  *
    3120  * @example
    3121  *  var c = board.create('implicitcurve', ['abs(x * y) - 3'], {
    3122  *      strokeWidth: 3,
    3123  *      strokeColor: JXG.palette.red,
    3124  *      strokeOpacity: 0.8
    3125  *  });
    3126  *
    3127  * </pre><div id="JXG02802981-0abb-446b-86ea-ee588f02ed1a" class="jxgbox" style="width: 300px; height: 300px;"></div>
    3128  * <script type="text/javascript">
    3129  *     (function() {
    3130  *         var board = JXG.JSXGraph.initBoard('JXG02802981-0abb-446b-86ea-ee588f02ed1a',
    3131  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
    3132  *             var c = board.create('implicitcurve', ['abs(x * y) - 3'], {
    3133  *                 strokeWidth: 3,
    3134  *                 strokeColor: JXG.palette.red,
    3135  *                 strokeOpacity: 0.8
    3136  *             });
    3137  *
    3138  *     })();
    3139  *
    3140  * </script><pre>
    3141  *
    3142  */
    3143 JXG.createImplicitCurve = function(board, parents, attributes) {
    3144     var c, attr;
    3145     if (parents.length !== 1 && parents.length !== 3) {
    3146         throw new Error(
    3147             "JSXGraph: Can't create curve implicitCurve with given parent'" +
    3148                 "\nPossible parent types: [f] or [f, dfx, dfy]" +
    3149                 "\nwith functions f, dfx, dfy"
    3150         );
    3151     }
    3152
    3153     attr = Type.copyAttributes(attributes, board.options, "implicitcurve");
    3154     c = board.create("curve", [[], []], attr);
    3155
    3156     /**
    3157      * Function of two variables for the left side of the equation <i>f(x,y)=0</i>.
    3158      *
    3159      * @name f
    3160      * @memberOf ImplicitCurve.prototype
    3161      * @function
    3162      * @returns {Number}
    3163      */
    3164     c.f = Type.createFunction(parents[0], board, 'x, y');
    3165
    3166     /**
    3167      * Partial derivative in the first variable of
    3168      * the left side of the equation <i>f(x,y)=0</i>.
    3169      * If null, then numerical derivative is used.
    3170      *
    3171      * @name dfx
    3172      * @memberOf ImplicitCurve.prototype
    3173      * @function
    3174      * @returns {Number}
    3175      */
    3176     c.dfx = Type.createFunction(parents[1], board, 'x, y');
    3177
    3178     /**
    3179      * Partial derivative in the second variable of
    3180      * the left side of the equation <i>f(x,y)=0</i>.
    3181      * If null, then numerical derivative is used.
    3182      *
    3183      * @name dfy
    3184      * @memberOf ImplicitCurve.prototype
    3185      * @function
    3186      * @returns {Number}
    3187      */
    3188     c.dfy = Type.createFunction(parents[2], board, 'x, y');
    3189
    3190     /**
    3191      * @class
    3192      * @ignore
    3193      */
    3194     c.updateDataArray = function () {
    3195         var bbox = this.board.getBoundingBox(),
    3196             ip, cfg,
    3197             ret = [],
    3198             mgn = Type.evaluate(this.visProp.margin);
    3199
    3200         bbox[0] -= mgn;
    3201         bbox[1] += mgn;
    3202         bbox[2] += mgn;
    3203         bbox[3] -= mgn;
    3204
    3205         cfg = {
    3206             resolution_out: Math.max(0.01, Type.evaluate(this.visProp.resolution_outer)),
    3207             resolution_in: Math.max(0.01, Type.evaluate(this.visProp.resolution_inner)),
    3208             max_steps: Type.evaluate(this.visProp.max_steps),
    3209             alpha_0: Type.evaluate(this.visProp.alpha_0),
    3210             tol_u0: Type.evaluate(this.visProp.tol_u0),
    3211             tol_newton: Type.evaluate(this.visProp.tol_newton),
    3212             tol_cusp: Type.evaluate(this.visProp.tol_cusp),
    3213             tol_progress: Type.evaluate(this.visProp.tol_progress),
    3214             qdt_box: Type.evaluate(this.visProp.qdt_box),
    3215             kappa_0: Type.evaluate(this.visProp.kappa_0),
    3216             delta_0: Type.evaluate(this.visProp.delta_0),
    3217             h_initial: Type.evaluate(this.visProp.h_initial),
    3218             h_critical: Type.evaluate(this.visProp.h_critical),
    3219             h_max: Type.evaluate(this.visProp.h_max),
    3220             loop_dist: Type.evaluate(this.visProp.loop_dist),
    3221             loop_dir: Type.evaluate(this.visProp.loop_dir),
    3222             loop_detection: Type.evaluate(this.visProp.loop_detection),
    3223             unitX: this.board.unitX,
    3224             unitY: this.board.unitY
    3225         };
    3226         this.dataX = [];
    3227         this.dataY = [];
    3228
    3229         // console.time("implicit plot");
    3230         ip = new JXG.Math.ImplicitPlot(bbox, cfg, this.f, this.dfx, this.dfy);
    3231         this.qdt = ip.qdt;
    3232
    3233         ret = ip.plot();
    3234         // console.timeEnd("implicit plot");
    3235
    3236         this.dataX = ret[0];
    3237         this.dataY = ret[1];
    3238     };
    3239
    3240     c.elType = 'implicitcurve';
    3241
    3242     return c;
    3243 };
    3244
    3245 JXG.registerElement("implicitcurve", JXG.createImplicitCurve);
    3246
    3247
    3248 export default JXG.Curve;
    3249
    3250 // export default {
    3251 //     Curve: JXG.Curve,
    3252 //     createCardinalSpline: JXG.createCardinalSpline,
    3253 //     createCurve: JXG.createCurve,
    3254 //     createCurveDifference: JXG.createCurveDifference,
    3255 //     createCurveIntersection: JXG.createCurveIntersection,
    3256 //     createCurveUnion: JXG.createCurveUnion,
    3257 //     createDerivative: JXG.createDerivative,
    3258 //     createFunctiongraph: JXG.createFunctiongraph,
    3259 //     createMetapostSpline: JXG.createMetapostSpline,
    3260 //     createPlot: JXG.createFunctiongraph,
    3261 //     createSpline: JXG.createSpline,
    3262 //     createRiemannsum: JXG.createRiemannsum,
    3263 //     createStepfunction: JXG.createStepfunction,
    3264 //     createTracecurve: JXG.createTracecurve
    3265 // };
    3266
    3267 // const Curve = JXG.Curve;
    3268 // export { Curve as default, Curve};
    3269