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