      1 /*
      2     Copyright 2008-2024
      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 = Type.evaluate(this.visProp.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 (Type.evaluate(this.visProp.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 (Type.evaluate(this.visProp.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 = Type.evaluate(this.visProp.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(Type.evaluate(this.visProp.precision))) {
    257                 type = this.board._inputDevice;
    258                 prec = Type.evaluate(this.visProp.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 && Type.evaluate(this.visProp.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 += Type.evaluate(this.visProp.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 = Type.evaluate(this.visProp.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                     Type.evaluate(this.visProp.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 (Type.evaluate(this.visProp.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 (Type.evaluate(this.visProp.doadvancedplot)) {
    781                     // console.time("plot");
    782
    783                     if (version === 1 || Type.evaluate(this.visProp.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 = Type.evaluate(this.visProp.numberpointshigh);
    798                     } else {
    799                         this.numberPoints = Type.evaluate(this.visProp.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                     Type.evaluate(this.visProp.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                 Type.evaluate(this.visProp.curvetype) !== "plot" &&
    833                 Type.evaluate(this.visProp.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;
    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 {
    1032                 this.Y = Type.createFunction(yterm, this.board, varname);
    1033             }
    1034
    1035             /**
    1036              * Polar form
    1037              * Input data is function xterm() and offset coordinates yterm
    1038              */
    1039             if (Type.isFunction(xterm) && Type.isArray(yterm)) {
    1040                 // Xoffset, Yoffset
    1041                 fx = Type.createFunction(yterm[0], this.board, "");
    1042                 fy = Type.createFunction(yterm[1], this.board, "");
    1043
    1044                 this.X = function (phi) {
    1045                     return xterm(phi) * Math.cos(phi) + fx();
    1046                 };
    1047                 this.X.deps = fx.deps;
    1048
    1049                 this.Y = function (phi) {
    1050                     return xterm(phi) * Math.sin(phi) + fy();
    1051                 };
    1052                 this.Y.deps = fy.deps;
    1053
    1054                 this.visProp.curvetype = "polar";
    1055             }
    1056
    1057             // Set the upper and lower bounds for the parameter of the curve.
    1058             // If not defined, reset the bounds to the default values
    1059             // given in Curve.prototype.minX, Curve.prototype.maxX
    1060             if (Type.exists(mi)) {
    1061                 this.minX = Type.createFunction(mi, this.board, "");
    1062             } else {
    1063                 delete this.minX;
    1064             }
    1065             if (Type.exists(ma)) {
    1066                 this.maxX = Type.createFunction(ma, this.board, "");
    1067             } else {
    1068                 delete this.maxX;
    1069             }
    1070
    1071             this.addParentsFromJCFunctions([this.X, this.Y, this.minX, this.maxX]);
    1072         },
    1073
    1074         /**
    1075          * Finds dependencies in a given term and notifies the parents by adding the
    1076          * dependent object to the found objects child elements.
    1077          * @param {String} contentStr String containing dependencies for the given object.
    1078          */
    1079         notifyParents: function (contentStr) {
    1080             var fstr,
    1081                 dep,
    1082                 isJessieCode = false,
    1083                 obj;
    1084
    1085             // Read dependencies found by the JessieCode parser
    1086             obj = { xterm: 1, yterm: 1 };
    1087             for (fstr in obj) {
    1088                 if (
    1089                     obj.hasOwnProperty(fstr) &&
    1090                     this.hasOwnProperty(fstr) &&
    1091                     this[fstr].origin
    1092                 ) {
    1093                     isJessieCode = true;
    1094                     for (dep in this[fstr].origin.deps) {
    1095                         if (this[fstr].origin.deps.hasOwnProperty(dep)) {
    1096                             this[fstr].origin.deps[dep].addChild(this);
    1097                         }
    1098                     }
    1099                 }
    1100             }
    1101
    1102             if (!isJessieCode) {
    1103                 GeonextParser.findDependencies(this, contentStr, this.board);
    1104             }
    1105         },
    1106
    1107         // documented in geometry element
    1108         getLabelAnchor: function () {
    1109             var x, y, pos,
    1110                 xy, lbda, e,
    1111                 t, dx, dy, d,
    1112                 dist = 1.5,
    1113                 c,
    1114                 ax = 0.05 * this.board.canvasWidth,
    1115                 ay = 0.05 * this.board.canvasHeight,
    1116                 bx = 0.95 * this.board.canvasWidth,
    1117                 by = 0.95 * this.board.canvasHeight;
    1118
    1119             if (!Type.exists(this.label)) {
    1120                 return new Coords(Const.COORDS_BY_SCREEN, [NaN, NaN], this.board);
    1121             }
    1122             pos = Type.evaluate(this.label.visProp.position);
    1123             if (!Type.isString(pos)) {
    1124                 return new Coords(Const.COORDS_BY_SCREEN, [NaN, NaN], this.board);
    1125             }
    1126
    1127             if (pos.indexOf('right') < 0 && pos.indexOf('left') < 0) {
    1128                 switch (Type.evaluate(this.visProp.label.position)) {
    1129                     case "ulft":
    1130                         x = ax;
    1131                         y = ay;
    1132                         break;
    1133                     case "llft":
    1134                         x = ax;
    1135                         y = by;
    1136                         break;
    1137                     case "rt":
    1138                         x = bx;
    1139                         y = 0.5 * by;
    1140                         break;
    1141                     case "lrt":
    1142                         x = bx;
    1143                         y = by;
    1144                         break;
    1145                     case "urt":
    1146                         x = bx;
    1147                         y = ay;
    1148                         break;
    1149                     case "top":
    1150                         x = 0.5 * bx;
    1151                         y = ay;
    1152                         break;
    1153                     case "bot":
    1154                         x = 0.5 * bx;
    1155                         y = by;
    1156                         break;
    1157                     default:
    1158                         // includes case 'lft'
    1159                         x = ax;
    1160                         y = 0.5 * by;
    1161                 }
    1162             } else {
    1163                 // New positioning
    1164                 xy = Type.parsePosition(pos);
    1165                 lbda = Type.parseNumber(xy.pos, this.maxX() - this.minX(), 1);
    1166
    1167                 if (xy.pos.indexOf('fr') < 0 &&
    1168                     xy.pos.indexOf('%') < 0) {
    1169                     // 'px' or numbers are not supported
    1170                     lbda = 0;
    1171                 }
    1172
    1173                 t = this.minX() + lbda;
    1174                 x = this.X(t);
    1175                 y = this.Y(t);
    1176                 c = (new Coords(Const.COORDS_BY_USER, [x, y], this.board)).scrCoords;
    1177
    1178                 e = Mat.eps;
    1179                 if (t < this.minX() + e) {
    1180                     dx = (this.X(t + e) - this.X(t)) / e;
    1181                     dy = (this.Y(t + e) - this.Y(t)) / e;
    1182                 } else if (t > this.maxX() - e) {
    1183                     dx = (this.X(t) - this.X(t - e)) / e;
    1184                     dy = (this.Y(t) - this.Y(t - e)) / e;
    1185                 } else {
    1186                     dx = 0.5 * (this.X(t + e) - this.X(t - e)) / e;
    1187                     dy = 0.5 * (this.Y(t + e) - this.Y(t - e)) / e;
    1188                 }
    1189                 d = Mat.hypot(dx, dy);
    1190
    1191                 if (xy.side === 'left') {
    1192                     dy *= -1;
    1193                 } else {
    1194                     dx *= -1;
    1195                 }
    1196
    1197                 // Position left or right
    1198
    1199                 if (Type.exists(this.label)) {
    1200                     dist = 0.5 * Type.evaluate(this.label.visProp.distance) / d;
    1201                 }
    1202
    1203                 x = c[1] + dy * this.label.size[0] * dist;
    1204                 y = c[2] - dx * this.label.size[1] * dist;
    1205
    1206                 return new Coords(Const.COORDS_BY_SCREEN, [x, y], this.board);
    1207
    1208             }
    1209             c = new Coords(Const.COORDS_BY_SCREEN, [x, y], this.board, false);
    1210             return Geometry.projectCoordsToCurve(
    1211                 c.usrCoords[1], c.usrCoords[2], 0, this, this.board
    1212             )[0];
    1213         },
    1214
    1215         // documented in geometry element
    1216         cloneToBackground: function () {
    1217             var er,
    1218                 copy = {
    1219                     id: this.id + "T" + this.numTraces,
    1220                     elementClass: Const.OBJECT_CLASS_CURVE,
    1221
    1222                     points: this.points.slice(0),
    1223                     bezierDegree: this.bezierDegree,
    1224                     numberPoints: this.numberPoints,
    1225                     board: this.board,
    1226                     visProp: Type.deepCopy(this.visProp, this.visProp.traceattributes, true)
    1227                 };
    1228
    1229             copy.visProp.layer = this.board.options.layer.trace;
    1230             copy.visProp.curvetype = this.visProp.curvetype;
    1231             this.numTraces++;
    1232
    1233             Type.clearVisPropOld(copy);
    1234             copy.visPropCalc = {
    1235                 visible: Type.evaluate(copy.visProp.visible)
    1236             };
    1237             er = this.board.renderer.enhancedRendering;
    1238             this.board.renderer.enhancedRendering = true;
    1239             this.board.renderer.drawCurve(copy);
    1240             this.board.renderer.enhancedRendering = er;
    1241             this.traces[copy.id] = copy.rendNode;
    1242
    1243             return this;
    1244         },
    1245
    1246         // Already documented in GeometryElement
    1247         bounds: function () {
    1248             var minX = Infinity,
    1249                 maxX = -Infinity,
    1250                 minY = Infinity,
    1251                 maxY = -Infinity,
    1252                 l = this.points.length,
    1253                 i,
    1254                 bezier,
    1255                 up;
    1256
    1257             if (this.bezierDegree === 3) {
    1258                 // Add methods X(), Y()
    1259                 for (i = 0; i < l; i++) {
    1260                     this.points[i].X = Type.bind(function () {
    1261                         return this.usrCoords[1];
    1262                     }, this.points[i]);
    1263                     this.points[i].Y = Type.bind(function () {
    1264                         return this.usrCoords[2];
    1265                     }, this.points[i]);
    1266                 }
    1267                 bezier = Numerics.bezier(this.points);
    1268                 up = bezier[3]();
    1269                 minX = Numerics.fminbr(
    1270                     function (t) {
    1271                         return bezier[0](t);
    1272                     },
    1273                     [0, up]
    1274                 );
    1275                 maxX = Numerics.fminbr(
    1276                     function (t) {
    1277                         return -bezier[0](t);
    1278                     },
    1279                     [0, up]
    1280                 );
    1281                 minY = Numerics.fminbr(
    1282                     function (t) {
    1283                         return bezier[1](t);
    1284                     },
    1285                     [0, up]
    1286                 );
    1287                 maxY = Numerics.fminbr(
    1288                     function (t) {
    1289                         return -bezier[1](t);
    1290                     },
    1291                     [0, up]
    1292                 );
    1293
    1294                 minX = bezier[0](minX);
    1295                 maxX = bezier[0](maxX);
    1296                 minY = bezier[1](minY);
    1297                 maxY = bezier[1](maxY);
    1298                 return [minX, maxY, maxX, minY];
    1299             }
    1300
    1301             // Linear segments
    1302             for (i = 0; i < l; i++) {
    1303                 if (minX > this.points[i].usrCoords[1]) {
    1304                     minX = this.points[i].usrCoords[1];
    1305                 }
    1306
    1307                 if (maxX < this.points[i].usrCoords[1]) {
    1308                     maxX = this.points[i].usrCoords[1];
    1309                 }
    1310
    1311                 if (minY > this.points[i].usrCoords[2]) {
    1312                     minY = this.points[i].usrCoords[2];
    1313                 }
    1314
    1315                 if (maxY < this.points[i].usrCoords[2]) {
    1316                     maxY = this.points[i].usrCoords[2];
    1317                 }
    1318             }
    1319
    1320             return [minX, maxY, maxX, minY];
    1321         },
    1322
    1323         // documented in element.js
    1324         getParents: function () {
    1325             var p = [this.xterm, this.yterm, this.minX(), this.maxX()];
    1326
    1327             if (this.parents.length !== 0) {
    1328                 p = this.parents;
    1329             }
    1330
    1331             return p;
    1332         },
    1333
    1334         /**
    1335          * Shift the curve by the vector 'where'.
    1336          *
    1337          * @param {Array} where Array containing the x and y coordinate of the target location.
    1338          * @returns {JXG.Curve} Reference to itself.
    1339          */
    1340         moveTo: function (where) {
    1341             // TODO add animation
    1342             var delta = [],
    1343                 p;
    1344             if (this.points.length > 0 && !Type.evaluate(this.visProp.fixed)) {
    1345                 p = this.points[0];
    1346                 if (where.length === 3) {
    1347                     delta = [
    1348                         where[0] - p.usrCoords[0],
    1349                         where[1] - p.usrCoords[1],
    1350                         where[2] - p.usrCoords[2]
    1351                     ];
    1352                 } else {
    1353                     delta = [where[0] - p.usrCoords[1], where[1] - p.usrCoords[2]];
    1354                 }
    1355                 this.setPosition(Const.COORDS_BY_USER, delta);
    1356                 return this.board.update(this);
    1357             }
    1358             return this;
    1359         },
    1360
    1361         /**
    1362          * If the curve is the result of a transformation applied
    1363          * to a continuous curve, the glider projection has to be done
    1364          * on the original curve. Otherwise there will be problems
    1365          * when changing between high and low precision plotting,
    1366          * since there number of points changes.
    1367          *
    1368          * @private
    1369          * @returns {Array} [Boolean, curve]: Array contining 'true' if curve is result of a transformation,
    1370          *   and the source curve of the transformation.
    1371          */
    1372         getTransformationSource: function () {
    1373             var isTransformed, curve_org;
    1374             if (Type.exists(this._transformationSource)) {
    1375                 curve_org = this._transformationSource;
    1376                 if (
    1377                     curve_org.elementClass === Const.OBJECT_CLASS_CURVE //&&
    1378                     //Type.evaluate(curve_org.visProp.curvetype) !== 'plot'
    1379                 ) {
    1380                     isTransformed = true;
    1381                 }
    1382             }
    1383             return [isTransformed, curve_org];
    1384         }
    1385
    1386         // See JXG.Math.Geometry.pnpoly
    1387         // pnpoly: function (x_in, y_in, coord_type) {
    1388         //     var i,
    1389         //         j,
    1390         //         len,
    1391         //         x,
    1392         //         y,
    1393         //         crds,
    1394         //         v = this.points,
    1395         //         isIn = false;
    1396
    1397         //     if (coord_type === Const.COORDS_BY_USER) {
    1398         //         crds = new Coords(Const.COORDS_BY_USER, [x_in, y_in], this.board);
    1399         //         x = crds.scrCoords[1];
    1400         //         y = crds.scrCoords[2];
    1401         //     } else {
    1402         //         x = x_in;
    1403         //         y = y_in;
    1404         //     }
    1405
    1406         //     len = this.points.length;
    1407         //     for (i = 0, j = len - 2; i < len - 1; j = i++) {
    1408         //         if (
    1409         //             v[i].scrCoords[2] > y !== v[j].scrCoords[2] > y &&
    1410         //             x <
    1411         //                 ((v[j].scrCoords[1] - v[i].scrCoords[1]) * (y - v[i].scrCoords[2])) /
    1412         //                     (v[j].scrCoords[2] - v[i].scrCoords[2]) +
    1413         //                     v[i].scrCoords[1]
    1414         //         ) {
    1415         //             isIn = !isIn;
    1416         //         }
    1417         //     }
    1418
    1419         //     return isIn;
    1420         // }
    1421     }
    1422 );
    1423
    1424 /**
    1425  * @class  This element is used to provide a constructor for curve, which is just a wrapper for element {@link Curve}.
    1426  * A curve is a mapping from R to R^2. t mapsto (x(t),y(t)). The graph is drawn for t in the interval [a,b].
    1427  * <p>
    1428  * The following types of curves can be plotted:
    1429  * <ul>
    1430  *  <li> parametric curves: t mapsto (x(t),y(t)), where x() and y() are univariate functions.
    1431  *  <li> polar curves: curves commonly written with polar equations like spirals and cardioids.
    1432  *  <li> data plots: plot line segments through a given list of coordinates.
    1433  * </ul>
    1434  * @pseudo
    1435  * @name Curve
    1436  * @augments JXG.Curve
    1437  * @constructor
    1438  * @type Object
    1439  * @description JXG.Curve
    1440
    1441  * @param {function,number_function,number_function,number_function,number}  x,y,a_,b_ Parent elements for Parametric Curves.
    1442  *                     <p>
    1443  *                     x describes the x-coordinate of the curve. It may be a function term in one variable, e.g. x(t).
    1444  *                     In case of x being of type number, x(t) is set to  a constant function.
    1445  *                     this function at the values of the array.
    1446  *                     </p>
    1447  *                     <p>
    1448  *                     y describes the y-coordinate of the curve. In case of a number, y(t) is set to the constant function
    1449  *                     returning this number.
    1450  *                     </p>
    1451  *                     <p>
    1452  *                     Further parameters are an optional number or function for the left interval border a,
    1453  *                     and an optional number or function for the right interval border b.
    1454  *                     </p>
    1455  *                     <p>
    1456  *                     Default values are a=-10 and b=10.
    1457  *                     </p>
    1458  *
    1459  * @param {array_array,function,number}
    1460  *
    1461  * @description x,y Parent elements for Data Plots.
    1462  *                     <p>
    1463  *                     x and y are arrays contining the x and y coordinates of the data points which are connected by
    1464  *                     line segments. The individual entries of x and y may also be functions.
    1465  *                     In case of x being an array the curve type is data plot, regardless of the second parameter and
    1466  *                     if additionally the second parameter y is a function term the data plot evaluates.
    1467  *                     </p>
    1468  * @param {function_array,function,number_function,number_function,number}
    1469  * @description r,offset_,a_,b_ Parent elements for Polar Curves.
    1470  *                     <p>
    1471  *                     The first parameter is a function term r(phi) describing the polar curve.
    1472  *                     </p>
    1473  *                     <p>
    1474  *                     The second parameter is the offset of the curve. It has to be
    1475  *                     an array containing numbers or functions describing the offset. Default value is the origin [0,0].
    1476  *                     </p>
    1477  *                     <p>
    1478  *                     Further parameters are an optional number or function for the left interval border a,
    1479  *                     and an optional number or function for the right interval border b.
    1480  *                     </p>
    1481  *                     <p>
    1482  *                     Default values are a=-10 and b=10.
    1483  *                     </p>
    1484  * <p>
    1485  * Additionally, a curve can be created by providing a curve and a transformation (or an array of transformations).
    1486  * The result is a curve which is the transformation of the supplied curve.
    1487  *
    1488  * @see JXG.Curve
    1489  * @example
    1490  * // Parametric curve
    1491  * // Create a curve of the form (t-sin(t), 1-cos(t), i.e.
    1492  * // the cycloid curve.
    1493  *   var graph = board.create('curve',
    1494  *                        [function(t){ return t-Math.sin(t);},
    1495  *                         function(t){ return 1-Math.cos(t);},
    1496  *                         0, 2*Math.PI]
    1497  *                     );
    1498  * </pre><div class="jxgbox" id="JXGaf9f818b-f3b6-4c4d-8c4c-e4a4078b726d" style="width: 300px; height: 300px;"></div>
    1499  * <script type="text/javascript">
    1500  *   var c1_board = JXG.JSXGraph.initBoard('JXGaf9f818b-f3b6-4c4d-8c4c-e4a4078b726d', {boundingbox: [-1, 5, 7, -1], axis: true, showcopyright: false, shownavigation: false});
    1501  *   var graph1 = c1_board.create('curve', [function(t){ return t-Math.sin(t);},function(t){ return 1-Math.cos(t);},0, 2*Math.PI]);
    1502  * </script><pre>
    1503  * @example
    1504  * // Data plots
    1505  * // Connect a set of points given by coordinates with dashed line segments.
    1506  * // The x- and y-coordinates of the points are given in two separate
    1507  * // arrays.
    1508  *   var x = [0,1,2,3,4,5,6,7,8,9];
    1509  *   var y = [9.2,1.3,7.2,-1.2,4.0,5.3,0.2,6.5,1.1,0.0];
    1510  *   var graph = board.create('curve', [x,y], {dash:2});
    1511  * </pre><div class="jxgbox" id="JXG7dcbb00e-b6ff-481d-b4a8-887f5d8c6a83" style="width: 300px; height: 300px;"></div>
    1512  * <script type="text/javascript">
    1513  *   var c3_board = JXG.JSXGraph.initBoard('JXG7dcbb00e-b6ff-481d-b4a8-887f5d8c6a83', {boundingbox: [-1,10,10,-1], axis: true, showcopyright: false, shownavigation: false});
    1514  *   var x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
    1515  *   var y = [9.2, 1.3, 7.2, -1.2, 4.0, 5.3, 0.2, 6.5, 1.1, 0.0];
    1516  *   var graph3 = c3_board.create('curve', [x,y], {dash:2});
    1517  * </script><pre>
    1518  * @example
    1519  * // Polar plot
    1520  * // Create a curve with the equation r(phi)= a*(1+phi), i.e.
    1521  * // a cardioid.
    1522  *   var a = board.create('slider',[[0,2],[2,2],[0,1,2]]);
    1523  *   var graph = board.create('curve',
    1524  *                        [function(phi){ return a.Value()*(1-Math.cos(phi));},
    1525  *                         [1,0],
    1526  *                         0, 2*Math.PI],
    1527  *                         {curveType: 'polar'}
    1528  *                     );
    1529  * </pre><div class="jxgbox" id="JXGd0bc7a2a-8124-45ca-a6e7-142321a8f8c2" style="width: 300px; height: 300px;"></div>
    1530  * <script type="text/javascript">
    1531  *   var c2_board = JXG.JSXGraph.initBoard('JXGd0bc7a2a-8124-45ca-a6e7-142321a8f8c2', {boundingbox: [-3,3,3,-3], axis: true, showcopyright: false, shownavigation: false});
    1532  *   var a = c2_board.create('slider',[[0,2],[2,2],[0,1,2]]);
    1533  *   var graph2 = c2_board.create('curve', [function(phi){ return a.Value()*(1-Math.cos(phi));}, [1,0], 0, 2*Math.PI], {curveType: 'polar'});
    1534  * </script><pre>
    1535  *
    1536  * @example
    1537  *  // Draggable Bezier curve
    1538  *  var col, p, c;
    1539  *  col = 'blue';
    1540  *  p = [];
    1541  *  p.push(board.create('point',[-2, -1 ], {size: 5, strokeColor:col, fillColor:col}));
    1542  *  p.push(board.create('point',[1, 2.5 ], {size: 5, strokeColor:col, fillColor:col}));
    1543  *  p.push(board.create('point',[-1, -2.5 ], {size: 5, strokeColor:col, fillColor:col}));
    1544  *  p.push(board.create('point',[2, -2], {size: 5, strokeColor:col, fillColor:col}));
    1545  *
    1546  *  c = board.create('curve', JXG.Math.Numerics.bezier(p),
    1547  *              {strokeColor:'red', name:"curve", strokeWidth:5, fixed: false}); // Draggable curve
    1548  *  c.addParents(p);
    1549  * </pre><div class="jxgbox" id="JXG7bcc6280-f6eb-433e-8281-c837c3387849" style="width: 300px; height: 300px;"></div>
    1550  * <script type="text/javascript">
    1551  * (function(){
    1552  *  var board, col, p, c;
    1553  *  board = JXG.JSXGraph.initBoard('JXG7bcc6280-f6eb-433e-8281-c837c3387849', {boundingbox: [-3,3,3,-3], axis: true, showcopyright: false, shownavigation: false});
    1554  *  col = 'blue';
    1555  *  p = [];
    1556  *  p.push(board.create('point',[-2, -1 ], {size: 5, strokeColor:col, fillColor:col}));
    1557  *  p.push(board.create('point',[1, 2.5 ], {size: 5, strokeColor:col, fillColor:col}));
    1558  *  p.push(board.create('point',[-1, -2.5 ], {size: 5, strokeColor:col, fillColor:col}));
    1559  *  p.push(board.create('point',[2, -2], {size: 5, strokeColor:col, fillColor:col}));
    1560  *
    1561  *  c = board.create('curve', JXG.Math.Numerics.bezier(p),
    1562  *              {strokeColor:'red', name:"curve", strokeWidth:5, fixed: false}); // Draggable curve
    1563  *  c.addParents(p);
    1564  * })();
    1565  * </script><pre>
    1566  *
    1567  * @example
    1568  *         // The curve cu2 is the reflection of cu1 against line li
    1569  *         var li = board.create('line', [1,1,1], {strokeColor: '#aaaaaa'});
    1570  *         var reflect = board.create('transform', [li], {type: 'reflect'});
    1571  *         var cu1 = board.create('curve', [[-1, -1, -0.5, -1, -1, -0.5], [-3, -2, -2, -2, -2.5, -2.5]]);
    1572  *         var cu2 = board.create('curve', [cu1, reflect], {strokeColor: 'red'});
    1573  *
    1574  * </pre><div id="JXG866dc7a2-d448-11e7-93b3-901b0e1b8723" class="jxgbox" style="width: 300px; height: 300px;"></div>
    1575  * <script type="text/javascript">
    1576  *     (function() {
    1577  *         var board = JXG.JSXGraph.initBoard('JXG866dc7a2-d448-11e7-93b3-901b0e1b8723',
    1578  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
    1579  *             var li = board.create('line', [1,1,1], {strokeColor: '#aaaaaa'});
    1580  *             var reflect = board.create('transform', [li], {type: 'reflect'});
    1581  *             var cu1 = board.create('curve', [[-1, -1, -0.5, -1, -1, -0.5], [-3, -2, -2, -2, -2.5, -2.5]]);
    1582  *             var cu2 = board.create('curve', [cu1, reflect], {strokeColor: 'red'});
    1583  *
    1584  *     })();
    1585  *
    1586  * </script><pre>
    1587  */
    1588 JXG.createCurve = function (board, parents, attributes) {
    1589     var obj,
    1590         cu,
    1591         attr = Type.copyAttributes(attributes, board.options, "curve");
    1592
    1593     obj = board.select(parents[0], true);
    1594     if (
    1595         Type.isTransformationOrArray(parents[1]) &&
    1596         Type.isObject(obj) &&
    1597         (obj.type === Const.OBJECT_TYPE_CURVE ||
    1598             obj.type === Const.OBJECT_TYPE_ANGLE ||
    1599             obj.type === Const.OBJECT_TYPE_ARC ||
    1600             obj.type === Const.OBJECT_TYPE_CONIC ||
    1601             obj.type === Const.OBJECT_TYPE_SECTOR)
    1602     ) {
    1603         if (obj.type === Const.OBJECT_TYPE_SECTOR) {
    1604             attr = Type.copyAttributes(attributes, board.options, "sector");
    1605         } else if (obj.type === Const.OBJECT_TYPE_ARC) {
    1606             attr = Type.copyAttributes(attributes, board.options, "arc");
    1607         } else if (obj.type === Const.OBJECT_TYPE_ANGLE) {
    1608             if (!Type.exists(attributes.withLabel)) {
    1609                 attributes.withLabel = false;
    1610             }
    1611             attr = Type.copyAttributes(attributes, board.options, "angle");
    1612         } else {
    1613             attr = Type.copyAttributes(attributes, board.options, "curve");
    1614         }
    1615         attr = Type.copyAttributes(attr, board.options, "curve");
    1616
    1617         cu = new JXG.Curve(board, ["x", [], []], attr);
    1618         /**
    1619          * @class
    1620          * @ignore
    1621          */
    1622         cu.updateDataArray = function () {
    1623             var i,
    1624                 le = obj.numberPoints;
    1625             this.bezierDegree = obj.bezierDegree;
    1626             this.dataX = [];
    1627             this.dataY = [];
    1628             for (i = 0; i < le; i++) {
    1629                 this.dataX.push(obj.points[i].usrCoords[1]);
    1630                 this.dataY.push(obj.points[i].usrCoords[2]);
    1631             }
    1632             return this;
    1633         };
    1634         cu.addTransform(parents[1]);
    1635         obj.addChild(cu);
    1636         cu.setParents([obj]);
    1637         cu._transformationSource = obj;
    1638
    1639         return cu;
    1640     }
    1641     attr = Type.copyAttributes(attributes, board.options, "curve");
    1642     return new JXG.Curve(board, ["x"].concat(parents), attr);
    1643 };
    1644
    1645 JXG.registerElement("curve", JXG.createCurve);
    1646
    1647 /**
    1648  * @class This element is used to provide a constructor for functiongraph,
    1649  * which is just a wrapper for element {@link Curve} with {@link JXG.Curve#X}()
    1650  * set to x. The graph is drawn for x in the interval [a,b].
    1651  * @pseudo
    1652  * @name Functiongraph
    1653  * @augments JXG.Curve
    1654  * @constructor
    1655  * @type JXG.Curve
    1656  * @param {function_number,function_number,function} f,a_,b_ Parent elements are a function term f(x) describing the function graph.
    1657  *         <p>
    1658  *         Further, an optional number or function for the left interval border a,
    1659  *         and an optional number or function for the right interval border b.
    1660  *         <p>
    1661  *         Default values are a=-10 and b=10.
    1662  * @see JXG.Curve
    1663  * @example
    1664  * // Create a function graph for f(x) = 0.5*x*x-2*x
    1665  *   var graph = board.create('functiongraph',
    1666  *                        [function(x){ return 0.5*x*x-2*x;}, -2, 4]
    1667  *                     );
    1668  * </pre><div class="jxgbox" id="JXGefd432b5-23a3-4846-ac5b-b471e668b437" style="width: 300px; height: 300px;"></div>
    1669  * <script type="text/javascript">
    1670  *   var alex1_board = JXG.JSXGraph.initBoard('JXGefd432b5-23a3-4846-ac5b-b471e668b437', {boundingbox: [-3, 7, 5, -3], axis: true, showcopyright: false, shownavigation: false});
    1671  *   var graph = alex1_board.create('functiongraph', [function(x){ return 0.5*x*x-2*x;}, -2, 4]);
    1672  * </script><pre>
    1673  * @example
    1674  * // Create a function graph for f(x) = 0.5*x*x-2*x with variable interval
    1675  *   var s = board.create('slider',[[0,4],[3,4],[-2,4,5]]);
    1676  *   var graph = board.create('functiongraph',
    1677  *                        [function(x){ return 0.5*x*x-2*x;},
    1678  *                         -2,
    1679  *                         function(){return s.Value();}]
    1680  *                     );
    1681  * </pre><div class="jxgbox" id="JXG4a203a84-bde5-4371-ad56-44619690bb50" style="width: 300px; height: 300px;"></div>
    1682  * <script type="text/javascript">
    1683  *   var alex2_board = JXG.JSXGraph.initBoard('JXG4a203a84-bde5-4371-ad56-44619690bb50', {boundingbox: [-3, 7, 5, -3], axis: true, showcopyright: false, shownavigation: false});
    1684  *   var s = alex2_board.create('slider',[[0,4],[3,4],[-2,4,5]]);
    1685  *   var graph = alex2_board.create('functiongraph', [function(x){ return 0.5*x*x-2*x;}, -2, function(){return s.Value();}]);
    1686  * </script><pre>
    1687  */
    1688 JXG.createFunctiongraph = function (board, parents, attributes) {
    1689     var attr,
    1690         par = ["x", "x"].concat(parents); // variable name and identity function for x-coordinate
    1691     // par = ["x", function(x) { return x; }].concat(parents);
    1692
    1693     attr = Type.copyAttributes(attributes, board.options, "functiongraph");
    1694     attr = Type.copyAttributes(attr, board.options, "curve");
    1695     attr.curvetype = "functiongraph";
    1696     return new JXG.Curve(board, par, attr);
    1697 };
    1698
    1699 JXG.registerElement("functiongraph", JXG.createFunctiongraph);
    1700 JXG.registerElement("plot", JXG.createFunctiongraph);
    1701
    1702 /**
    1703  * @class This element is used to provide a constructor for (natural) cubic spline curves.
    1704  * Create a dynamic spline interpolated curve given by sample points p_1 to p_n.
    1705  * @pseudo
    1706  * @name Spline
    1707  * @augments JXG.Curve
    1708  * @constructor
    1709  * @type JXG.Curve
    1710  * @param {JXG.Board} board Reference to the board the spline is drawn on.
    1711  * @param {Array} parents Array of points the spline interpolates. This can be
    1712  *   <ul>
    1713  *   <li> an array of JSXGraph points</li>
    1714  *   <li> an array of coordinate pairs</li>
    1715  *   <li> an array of functions returning coordinate pairs</li>
    1716  *   <li> an array consisting of an array with x-coordinates and an array of y-coordinates</li>
    1717  *   </ul>
    1718  *   All individual entries of coordinates arrays may be numbers or functions returning numbers.
    1719  * @param {Object} attributes Define color, width, ... of the spline
    1720  * @returns {JXG.Curve} Returns reference to an object of type JXG.Curve.
    1721  * @see JXG.Curve
    1722  * @example
    1723  *
    1724  * var p = [];
    1725  * p[0] = board.create('point', [-2,2], {size: 4, face: 'o'});
    1726  * p[1] = board.create('point', [0,-1], {size: 4, face: 'o'});
    1727  * p[2] = board.create('point', [2,0], {size: 4, face: 'o'});
    1728  * p[3] = board.create('point', [4,1], {size: 4, face: 'o'});
    1729  *
    1730  * var c = board.create('spline', p, {strokeWidth:3});
    1731  * </pre><div id="JXG6c197afc-e482-11e5-b1bf-901b0e1b8723" style="width: 300px; height: 300px;"></div>
    1732  * <script type="text/javascript">
    1733  *     (function() {
    1734  *         var board = JXG.JSXGraph.initBoard('JXG6c197afc-e482-11e5-b1bf-901b0e1b8723',
    1735  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
    1736  *
    1737  *     var p = [];
    1738  *     p[0] = board.create('point', [-2,2], {size: 4, face: 'o'});
    1739  *     p[1] = board.create('point', [0,-1], {size: 4, face: 'o'});
    1740  *     p[2] = board.create('point', [2,0], {size: 4, face: 'o'});
    1741  *     p[3] = board.create('point', [4,1], {size: 4, face: 'o'});
    1742  *
    1743  *     var c = board.create('spline', p, {strokeWidth:3});
    1744  *     })();
    1745  *
    1746  * </script><pre>
    1747  *
    1748  */
    1749 JXG.createSpline = function (board, parents, attributes) {
    1750     var el, funcs, ret;
    1751
    1752     funcs = function () {
    1753         var D,
    1754             x = [],
    1755             y = [];
    1756
    1757         return [
    1758             function (t, suspended) {
    1759                 // Function term
    1760                 var i, j, c;
    1761
    1762                 if (!suspended) {
    1763                     x = [];
    1764                     y = [];
    1765
    1766                     // given as [x[], y[]]
    1767                     if (
    1768                         parents.length === 2 &&
    1769                         Type.isArray(parents[0]) &&
    1770                         Type.isArray(parents[1]) &&
    1771                         parents[0].length === parents[1].length
    1772                     ) {
    1773                         for (i = 0; i < parents[0].length; i++) {
    1774                             if (Type.isFunction(parents[0][i])) {
    1775                                 x.push(parents[0][i]());
    1776                             } else {
    1777                                 x.push(parents[0][i]);
    1778                             }
    1779
    1780                             if (Type.isFunction(parents[1][i])) {
    1781                                 y.push(parents[1][i]());
    1782                             } else {
    1783                                 y.push(parents[1][i]);
    1784                             }
    1785                         }
    1786                     } else {
    1787                         for (i = 0; i < parents.length; i++) {
    1788                             if (Type.isPoint(parents[i])) {
    1789                                 x.push(parents[i].X());
    1790                                 y.push(parents[i].Y());
    1791                                 // given as [[x1,y1], [x2, y2], ...]
    1792                             } else if (Type.isArray(parents[i]) && parents[i].length === 2) {
    1793                                 for (j = 0; j < parents.length; j++) {
    1794                                     if (Type.isFunction(parents[j][0])) {
    1795                                         x.push(parents[j][0]());
    1796                                     } else {
    1797                                         x.push(parents[j][0]);
    1798                                     }
    1799
    1800                                     if (Type.isFunction(parents[j][1])) {
    1801                                         y.push(parents[j][1]());
    1802                                     } else {
    1803                                         y.push(parents[j][1]);
    1804                                     }
    1805                                 }
    1806                             } else if (
    1807                                 Type.isFunction(parents[i]) &&
    1808                                 parents[i]().length === 2
    1809                             ) {
    1810                                 c = parents[i]();
    1811                                 x.push(c[0]);
    1812                                 y.push(c[1]);
    1813                             }
    1814                         }
    1815                     }
    1816
    1817                     // The array D has only to be calculated when the position of one or more sample points
    1818                     // changes. Otherwise D is always the same for all points on the spline.
    1819                     D = Numerics.splineDef(x, y);
    1820                 }
    1821
    1822                 return Numerics.splineEval(t, x, y, D);
    1823             },
    1824             // minX()
    1825             function () {
    1826                 return x[0];
    1827             },
    1828             //maxX()
    1829             function () {
    1830                 return x[x.length - 1];
    1831             }
    1832         ];
    1833     };
    1834
    1835     attributes = Type.copyAttributes(attributes, board.options, "curve");
    1836     attributes.curvetype = "functiongraph";
    1837     ret = funcs();
    1838     el = new JXG.Curve(board, ["x", "x", ret[0], ret[1], ret[2]], attributes);
    1839     el.setParents(parents);
    1840     el.elType = "spline";
    1841
    1842     return el;
    1843 };
    1844
    1845 /**
    1846  * Register the element type spline at JSXGraph
    1847  * @private
    1848  */
    1849 JXG.registerElement("spline", JXG.createSpline);
    1850
    1851 /**
    1852  * @class This element is used to provide a constructor for cardinal spline curves.
    1853  * Create a dynamic cardinal spline interpolated curve given by sample points p_1 to p_n.
    1854  * @pseudo
    1855  * @name Cardinalspline
    1856  * @augments JXG.Curve
    1857  * @constructor
    1858  * @type JXG.Curve
    1859  * @param {JXG.Board} board Reference to the board the cardinal spline is drawn on.
    1860  * @param {Array} parents Array with three entries.
    1861  * <p>
    1862  *   First entry: Array of points the spline interpolates. This can be
    1863  *   <ul>
    1864  *   <li> an array of JSXGraph points</li>
    1865  *   <li> an array of coordinate pairs</li>
    1866  *   <li> an array of functions returning coordinate pairs</li>
    1867  *   <li> an array consisting of an array with x-coordinates and an array of y-coordinates</li>
    1868  *   </ul>
    1869  *   All individual entries of coordinates arrays may be numbers or functions returning numbers.
    1870  *   <p>
    1871  *   Second entry: tau number or function
    1872  *   <p>
    1873  *   Third entry: type string containing 'uniform' (default) or 'centripetal'.
    1874  * @param {Object} attributes Define color, width, ... of the cardinal spline
    1875  * @returns {JXG.Curve} Returns reference to an object of type JXG.Curve.
    1876  * @see JXG.Curve
    1877  * @example
    1878  * //create a cardinal spline out of an array of JXG points with adjustable tension
    1879  * //create array of points
    1880  * var p1 = board.create('point',[0,0])
    1881  * var p2 = board.create('point',[1,4])
    1882  * var p3 = board.create('point',[4,5])
    1883  * var p4 = board.create('point',[2,3])
    1884  * var p5 = board.create('point',[3,0])
    1885  * var p = [p1,p2,p3,p4,p5]
    1886  *
    1887  * // tension
    1888  * tau = board.create('slider', [[4,3],[9,3],[0.001,0.5,1]], {name:'tau'});
    1889  * c = board.create('curve', JXG.Math.Numerics.CardinalSpline(p, function(){ return tau.Value();}), {strokeWidth:3});
    1890  * </pre><div id="JXG6c197afc-e482-11e5-b2af-901b0e1b8723" style="width: 300px; height: 300px;"></div>
    1891  * <script type="text/javascript">
    1892  *     (function() {
    1893  *         var board = JXG.JSXGraph.initBoard('JXG6c197afc-e482-11e5-b2af-901b0e1b8723',
    1894  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
    1895  *
    1896  *     var p = [];
    1897  *     p[0] = board.create('point', [-2,2], {size: 4, face: 'o'});
    1898  *     p[1] = board.create('point', [0,-1], {size: 4, face: 'o'});
    1899  *     p[2] = board.create('point', [2,0], {size: 4, face: 'o'});
    1900  *     p[3] = board.create('point', [4,1], {size: 4, face: 'o'});
    1901  *
    1902  *     var c = board.create('spline', p, {strokeWidth:3});
    1903  *     })();
    1904  *
    1905  * </script><pre>
    1906  */
    1907 JXG.createCardinalSpline = function (board, parents, attributes) {
    1908     var el,
    1909         getPointLike,
    1910         points,
    1911         tau,
    1912         type,
    1913         p,
    1914         q,
    1915         i,
    1916         le,
    1917         splineArr,
    1918         errStr = "\nPossible parent types: [points:array, tau:number|function, type:string]";
    1919
    1920     if (!Type.exists(parents[0]) || !Type.isArray(parents[0])) {
    1921         throw new Error(
    1922             "JSXGraph: JXG.createCardinalSpline: argument 1 'points' has to be array of points or coordinate pairs" +
    1923             errStr
    1924         );
    1925     }
    1926     if (
    1927         !Type.exists(parents[1]) ||
    1928         (!Type.isNumber(parents[1]) && !Type.isFunction(parents[1]))
    1929     ) {
    1930         throw new Error(
    1931             "JSXGraph: JXG.createCardinalSpline: argument 2 'tau' has to be number between [0,1] or function'" +
    1932             errStr
    1933         );
    1934     }
    1935     if (!Type.exists(parents[2]) || !Type.isString(parents[2])) {
    1936         throw new Error(
    1937             "JSXGraph: JXG.createCardinalSpline: argument 3 'type' has to be string 'uniform' or 'centripetal'" +
    1938             errStr
    1939         );
    1940     }
    1941
    1942     attributes = Type.copyAttributes(attributes, board.options, "curve");
    1943     attributes = Type.copyAttributes(attributes, board.options, "cardinalspline");
    1944     attributes.curvetype = "parameter";
    1945
    1946     p = parents[0];
    1947     q = [];
    1948
    1949     // Given as [x[], y[]]
    1950     if (
    1951         !attributes.isarrayofcoordinates &&
    1952         p.length === 2 &&
    1953         Type.isArray(p[0]) &&
    1954         Type.isArray(p[1]) &&
    1955         p[0].length === p[1].length
    1956     ) {
    1957         for (i = 0; i < p[0].length; i++) {
    1958             q[i] = [];
    1959             if (Type.isFunction(p[0][i])) {
    1960                 q[i].push(p[0][i]());
    1961             } else {
    1962                 q[i].push(p[0][i]);
    1963             }
    1964
    1965             if (Type.isFunction(p[1][i])) {
    1966                 q[i].push(p[1][i]());
    1967             } else {
    1968                 q[i].push(p[1][i]);
    1969             }
    1970         }
    1971     } else {
    1972         // given as [[x0, y0], [x1, y1], point, ...]
    1973         for (i = 0; i < p.length; i++) {
    1974             if (Type.isString(p[i])) {
    1975                 q.push(board.select(p[i]));
    1976             } else if (Type.isPoint(p[i])) {
    1977                 q.push(p[i]);
    1978                 // given as [[x0,y0], [x1, y2], ...]
    1979             } else if (Type.isArray(p[i]) && p[i].length === 2) {
    1980                 q[i] = [];
    1981                 if (Type.isFunction(p[i][0])) {
    1982                     q[i].push(p[i][0]());
    1983                 } else {
    1984                     q[i].push(p[i][0]);
    1985                 }
    1986
    1987                 if (Type.isFunction(p[i][1])) {
    1988                     q[i].push(p[i][1]());
    1989                 } else {
    1990                     q[i].push(p[i][1]);
    1991                 }
    1992             } else if (Type.isFunction(p[i]) && p[i]().length === 2) {
    1993                 q.push(parents[i]());
    1994             }
    1995         }
    1996     }
    1997
    1998     if (attributes.createpoints === true) {
    1999         points = Type.providePoints(board, q, attributes, "cardinalspline", ["points"]);
    2000     } else {
    2001         points = [];
    2002
    2003         /**
    2004          * @ignore
    2005          */
    2006         getPointLike = function (ii) {
    2007             return {
    2008                 X: function () {
    2009                     return q[ii][0];
    2010                 },
    2011                 Y: function () {
    2012                     return q[ii][1];
    2013                 },
    2014                 Dist: function (p) {
    2015                     var dx = this.X() - p.X(),
    2016                         dy = this.Y() - p.Y();
    2017
    2018                     return Mat.hypot(dx, dy);
    2019                 }
    2020             };
    2021         };
    2022
    2023         for (i = 0; i < q.length; i++) {
    2024             if (Type.isPoint(q[i])) {
    2025                 points.push(q[i]);
    2026             } else {
    2027                 points.push(getPointLike(i));
    2028             }
    2029         }
    2030     }
    2031
    2032     tau = parents[1];
    2033     type = parents[2];
    2034
    2035     splineArr = ["x"].concat(Numerics.CardinalSpline(points, tau, type));
    2036
    2037     el = new JXG.Curve(board, splineArr, attributes);
    2038     le = points.length;
    2039     el.setParents(points);
    2040     for (i = 0; i < le; i++) {
    2041         p = points[i];
    2042         if (Type.isPoint(p)) {
    2043             if (Type.exists(p._is_new)) {
    2044                 el.addChild(p);
    2045                 delete p._is_new;
    2046             } else {
    2047                 p.addChild(el);
    2048             }
    2049         }
    2050     }
    2051     el.elType = "cardinalspline";
    2052
    2053     return el;
    2054 };
    2055
    2056 /**
    2057  * Register the element type cardinalspline at JSXGraph
    2058  * @private
    2059  */
    2060 JXG.registerElement("cardinalspline", JXG.createCardinalSpline);
    2061
    2062 /**
    2063  * @class This element is used to provide a constructor for metapost spline curves.
    2064  * Create a dynamic metapost spline interpolated curve given by sample points p_1 to p_n.
    2065  * @pseudo
    2066  * @name Metapostspline
    2067  * @augments JXG.Curve
    2068  * @constructor
    2069  * @type JXG.Curve
    2070  * @param {JXG.Board} board Reference to the board the metapost spline is drawn on.
    2071  * @param {Array} parents Array with two entries.
    2072  * <p>
    2073  *   First entry: Array of points the spline interpolates. This can be
    2074  *   <ul>
    2075  *   <li> an array of JSXGraph points</li>
    2076  *   <li> an object of coordinate pairs</li>
    2077  *   <li> an array of functions returning coordinate pairs</li>
    2078  *   <li> an array consisting of an array with x-coordinates and an array of y-coordinates</li>
    2079  *   </ul>
    2080  *   All individual entries of coordinates arrays may be numbers or functions returning numbers.
    2081  *   <p>
    2082  *   Second entry: JavaScript object containing the control values like tension, direction, curl.
    2083  * @param {Object} attributes Define color, width, ... of the metapost spline
    2084  * @returns {JXG.Curve} Returns reference to an object of type JXG.Curve.
    2085  * @see JXG.Curve
    2086  * @example
    2087  *     var po = [],
    2088  *         attr = {
    2089  *             size: 5,
    2090  *             color: 'red'
    2091  *         },
    2092  *         controls;
    2093  *
    2094  *     var tension = board.create('slider', [[-3, 6], [3, 6], [0, 1, 20]], {name: 'tension'});
    2095  *     var curl = board.create('slider', [[-3, 5], [3, 5], [0, 1, 30]], {name: 'curl A, D'});
    2096  *     var dir = board.create('slider', [[-3, 4], [3, 4], [-180, 0, 180]], {name: 'direction B'});
    2097  *
    2098  *     po.push(board.create('point', [-3, -3]));
    2099  *     po.push(board.create('point', [0, -3]));
    2100  *     po.push(board.create('point', [4, -5]));
    2101  *     po.push(board.create('point', [6, -2]));
    2102  *
    2103  *     var controls = {
    2104  *         tension: function() {return tension.Value(); },
    2105  *         direction: { 1: function() {return dir.Value(); } },
    2106  *         curl: { 0: function() {return curl.Value(); },
    2107  *                 3: function() {return curl.Value(); }
    2108  *             },
    2109  *         isClosed: false
    2110  *     };
    2111  *
    2112  *     // Plot a metapost curve
    2113  *     var cu = board.create('metapostspline', [po, controls], {strokeColor: 'blue', strokeWidth: 2});
    2114  *
    2115  *
    2116  * </pre><div id="JXGb8c6ffed-7419-41a3-9e55-3754b2327ae9" class="jxgbox" style="width: 300px; height: 300px;"></div>
    2117  * <script type="text/javascript">
    2118  *     (function() {
    2119  *         var board = JXG.JSXGraph.initBoard('JXGb8c6ffed-7419-41a3-9e55-3754b2327ae9',
    2120  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
    2121  *         var po = [],
    2122  *             attr = {
    2123  *                 size: 5,
    2124  *                 color: 'red'
    2125  *             },
    2126  *             controls;
    2127  *
    2128  *         var tension = board.create('slider', [[-3, 6], [3, 6], [0, 1, 20]], {name: 'tension'});
    2129  *         var curl = board.create('slider', [[-3, 5], [3, 5], [0, 1, 30]], {name: 'curl A, D'});
    2130  *         var dir = board.create('slider', [[-3, 4], [3, 4], [-180, 0, 180]], {name: 'direction B'});
    2131  *
    2132  *         po.push(board.create('point', [-3, -3]));
    2133  *         po.push(board.create('point', [0, -3]));
    2134  *         po.push(board.create('point', [4, -5]));
    2135  *         po.push(board.create('point', [6, -2]));
    2136  *
    2137  *         var controls = {
    2138  *             tension: function() {return tension.Value(); },
    2139  *             direction: { 1: function() {return dir.Value(); } },
    2140  *             curl: { 0: function() {return curl.Value(); },
    2141  *                     3: function() {return curl.Value(); }
    2142  *                 },
    2143  *             isClosed: false
    2144  *         };
    2145  *
    2146  *         // Plot a metapost curve
    2147  *         var cu = board.create('metapostspline', [po, controls], {strokeColor: 'blue', strokeWidth: 2});
    2148  *
    2149  *
    2150  *     })();
    2151  *
    2152  * </script><pre>
    2153  *
    2154  */
    2155 JXG.createMetapostSpline = function (board, parents, attributes) {
    2156     var el,
    2157         getPointLike,
    2158         points,
    2159         controls,
    2160         p,
    2161         q,
    2162         i,
    2163         le,
    2164         errStr = "\nPossible parent types: [points:array, controls:object";
    2165
    2166     if (!Type.exists(parents[0]) || !Type.isArray(parents[0])) {
    2167         throw new Error(
    2168             "JSXGraph: JXG.createMetapostSpline: argument 1 'points' has to be array of points or coordinate pairs" +
    2169             errStr
    2170         );
    2171     }
    2172     if (!Type.exists(parents[1]) || !Type.isObject(parents[1])) {
    2173         throw new Error(
    2174             "JSXGraph: JXG.createMetapostSpline: argument 2 'controls' has to be a JavaScript object'" +
    2175             errStr
    2176         );
    2177     }
    2178
    2179     attributes = Type.copyAttributes(attributes, board.options, "curve");
    2180     attributes = Type.copyAttributes(attributes, board.options, "metapostspline");
    2181     attributes.curvetype = "parameter";
    2182
    2183     p = parents[0];
    2184     q = [];
    2185
    2186     // given as [x[], y[]]
    2187     if (
    2188         !attributes.isarrayofcoordinates &&
    2189         p.length === 2 &&
    2190         Type.isArray(p[0]) &&
    2191         Type.isArray(p[1]) &&
    2192         p[0].length === p[1].length
    2193     ) {
    2194         for (i = 0; i < p[0].length; i++) {
    2195             q[i] = [];
    2196             if (Type.isFunction(p[0][i])) {
    2197                 q[i].push(p[0][i]());
    2198             } else {
    2199                 q[i].push(p[0][i]);
    2200             }
    2201
    2202             if (Type.isFunction(p[1][i])) {
    2203                 q[i].push(p[1][i]());
    2204             } else {
    2205                 q[i].push(p[1][i]);
    2206             }
    2207         }
    2208     } else {
    2209         // given as [[x0, y0], [x1, y1], point, ...]
    2210         for (i = 0; i < p.length; i++) {
    2211             if (Type.isString(p[i])) {
    2212                 q.push(board.select(p[i]));
    2213             } else if (Type.isPoint(p[i])) {
    2214                 q.push(p[i]);
    2215                 // given as [[x0,y0], [x1, y2], ...]
    2216             } else if (Type.isArray(p[i]) && p[i].length === 2) {
    2217                 q[i] = [];
    2218                 if (Type.isFunction(p[i][0])) {
    2219                     q[i].push(p[i][0]());
    2220                 } else {
    2221                     q[i].push(p[i][0]);
    2222                 }
    2223
    2224                 if (Type.isFunction(p[i][1])) {
    2225                     q[i].push(p[i][1]());
    2226                 } else {
    2227                     q[i].push(p[i][1]);
    2228                 }
    2229             } else if (Type.isFunction(p[i]) && p[i]().length === 2) {
    2230                 q.push(parents[i]());
    2231             }
    2232         }
    2233     }
    2234
    2235     if (attributes.createpoints === true) {
    2236         points = Type.providePoints(board, q, attributes, 'metapostspline', ['points']);
    2237     } else {
    2238         points = [];
    2239
    2240         /**
    2241          * @ignore
    2242          */
    2243         getPointLike = function (ii) {
    2244             return {
    2245                 X: function () {
    2246                     return q[ii][0];
    2247                 },
    2248                 Y: function () {
    2249                     return q[ii][1];
    2250                 }
    2251             };
    2252         };
    2253
    2254         for (i = 0; i < q.length; i++) {
    2255             if (Type.isPoint(q[i])) {
    2256                 points.push(q[i]);
    2257             } else {
    2258                 points.push(getPointLike);
    2259             }
    2260         }
    2261     }
    2262
    2263     controls = parents[1];
    2264
    2265     el = new JXG.Curve(board, ["t", [], [], 0, p.length - 1], attributes);
    2266     /**
    2267      * @class
    2268      * @ignore
    2269      */
    2270     el.updateDataArray = function () {
    2271         var res,
    2272             i,
    2273             len = points.length,
    2274             p = [];
    2275
    2276         for (i = 0; i < len; i++) {
    2277             p.push([points[i].X(), points[i].Y()]);
    2278         }
    2279
    2280         res = Metapost.curve(p, controls);
    2281         this.dataX = res[0];
    2282         this.dataY = res[1];
    2283     };
    2284     el.bezierDegree = 3;
    2285
    2286     le = points.length;
    2287     el.setParents(points);
    2288     for (i = 0; i < le; i++) {
    2289         if (Type.isPoint(points[i])) {
    2290             points[i].addChild(el);
    2291         }
    2292     }
    2293     el.elType = "metapostspline";
    2294
    2295     return el;
    2296 };
    2297
    2298 JXG.registerElement("metapostspline", JXG.createMetapostSpline);
    2299
    2300 /**
    2301  * @class This element is used to provide a constructor for Riemann sums, which is realized as a special curve.
    2302  * The returned element has the method Value() which returns the sum of the areas of the bars.
    2303  * <p>
    2304  * In case of type "simpson" and "trapezoidal", the horizontal line approximating the function value
    2305  * is replaced by a parabola or a secant. IN case of "simpson",
    2306  * the parabola is approximated visually by a polygonal chain of fixed step width.
    2307  *
    2308  * @pseudo
    2309  * @name Riemannsum
    2310  * @augments JXG.Curve
    2311  * @constructor
    2312  * @type Curve
    2313  * @param {function,array_number,function_string,function_function,number_function,number} f,n,type_,a_,b_ Parent elements of Riemannsum are a
    2314  *         Either a function term f(x) describing the function graph which is filled by the Riemann bars, or
    2315  *         an array consisting of two functions and the area between is filled by the Riemann bars.
    2316  *         <p>
    2317  *         n determines the number of bars, it is either a fixed number or a function.
    2318  *         <p>
    2319  *         type is a string or function returning one of the values:  'left', 'right', 'middle', 'lower', 'upper', 'random', 'simpson', or 'trapezoidal'.
    2320  *         Default value is 'left'. "simpson" is Simpson's 1/3 rule.
    2321  *         <p>
    2322  *         Further parameters are an optional number or function for the left interval border a,
    2323  *         and an optional number or function for the right interval border b.
    2324  *         <p>
    2325  *         Default values are a=-10 and b=10.
    2326  * @see JXG.Curve
    2327  * @example
    2328  * // Create Riemann sums for f(x) = 0.5*x*x-2*x.
    2329  *   var s = board.create('slider',[[0,4],[3,4],[0,4,10]],{snapWidth:1});
    2330  *   var f = function(x) { return 0.5*x*x-2*x; };
    2331  *   var r = board.create('riemannsum',
    2332  *               [f, function(){return s.Value();}, 'upper', -2, 5],
    2333  *               {fillOpacity:0.4}
    2334  *               );
    2335  *   var g = board.create('functiongraph',[f, -2, 5]);
    2336  *   var t = board.create('text',[-2,-2, function(){ return 'Sum=' + JXG.toFixed(r.Value(), 4); }]);
    2337  * </pre><div class="jxgbox" id="JXG940f40cc-2015-420d-9191-c5d83de988cf" style="width: 300px; height: 300px;"></div>
    2338  * <script type="text/javascript">
    2339  * (function(){
    2340  *   var board = JXG.JSXGraph.initBoard('JXG940f40cc-2015-420d-9191-c5d83de988cf', {boundingbox: [-3, 7, 5, -3], axis: true, showcopyright: false, shownavigation: false});
    2341  *   var f = function(x) { return 0.5*x*x-2*x; };
    2342  *   var s = board.create('slider',[[0,4],[3,4],[0,4,10]],{snapWidth:1});
    2343  *   var r = board.create('riemannsum', [f, function(){return s.Value();}, 'upper', -2, 5], {fillOpacity:0.4});
    2344  *   var g = board.create('functiongraph', [f, -2, 5]);
    2345  *   var t = board.create('text',[-2,-2, function(){ return 'Sum=' + JXG.toFixed(r.Value(), 4); }]);
    2346  * })();
    2347  * </script><pre>
    2348  *
    2349  * @example
    2350  *   // Riemann sum between two functions
    2351  *   var s = board.create('slider',[[0,4],[3,4],[0,4,10]],{snapWidth:1});
    2352  *   var g = function(x) { return 0.5*x*x-2*x; };
    2353  *   var f = function(x) { return -x*(x-4); };
    2354  *   var r = board.create('riemannsum',
    2355  *               [[g,f], function(){return s.Value();}, 'lower', 0, 4],
    2356  *               {fillOpacity:0.4}
    2357  *               );
    2358  *   var f = board.create('functiongraph',[f, -2, 5]);
    2359  *   var g = board.create('functiongraph',[g, -2, 5]);
    2360  *   var t = board.create('text',[-2,-2, function(){ return 'Sum=' + JXG.toFixed(r.Value(), 4); }]);
    2361  * </pre><div class="jxgbox" id="JXGf9a7ba38-b50f-4a32-a873-2f3bf9caee79" style="width: 300px; height: 300px;"></div>
    2362  * <script type="text/javascript">
    2363  * (function(){
    2364  *   var board = JXG.JSXGraph.initBoard('JXGf9a7ba38-b50f-4a32-a873-2f3bf9caee79', {boundingbox: [-3, 7, 5, -3], axis: true, showcopyright: false, shownavigation: false});
    2365  *   var s = board.create('slider',[[0,4],[3,4],[0,4,10]],{snapWidth:1});
    2366  *   var g = function(x) { return 0.5*x*x-2*x; };
    2367  *   var f = function(x) { return -x*(x-4); };
    2368  *   var r = board.create('riemannsum',
    2369  *               [[g,f], function(){return s.Value();}, 'lower', 0, 4],
    2370  *               {fillOpacity:0.4}
    2371  *               );
    2372  *   var f = board.create('functiongraph',[f, -2, 5]);
    2373  *   var g = board.create('functiongraph',[g, -2, 5]);
    2374  *   var t = board.create('text',[-2,-2, function(){ return 'Sum=' + JXG.toFixed(r.Value(), 4); }]);
    2375  * })();
    2376  * </script><pre>
    2377  */
    2378 JXG.createRiemannsum = function (board, parents, attributes) {
    2379     var n, type, f, par, c, attr;
    2380
    2381     attr = Type.copyAttributes(attributes, board.options, "riemannsum");
    2382     attr.curvetype = "plot";
    2383
    2384     f = parents[0];
    2385     n = Type.createFunction(parents[1], board, "");
    2386
    2387     if (!Type.exists(n)) {
    2388         throw new Error(
    2389             "JSXGraph: JXG.createRiemannsum: argument '2' n has to be number or function." +
    2390             "\nPossible parent types: [function,n:number|function,type,start:number|function,end:number|function]"
    2391         );
    2392     }
    2393
    2394     if (typeof parents[2] === 'string') {
    2395         parents[2] = '\'' + parents[2] + '\'';
    2396     }
    2397
    2398     type = Type.createFunction(parents[2], board, "");
    2399     if (!Type.exists(type)) {
    2400         throw new Error(
    2401             "JSXGraph: JXG.createRiemannsum: argument 3 'type' has to be string or function." +
    2402             "\nPossible parent types: [function,n:number|function,type,start:number|function,end:number|function]"
    2403         );
    2404     }
    2405
    2406     par = [[0], [0]].concat(parents.slice(3));
    2407
    2408     c = board.create("curve", par, attr);
    2409
    2410     c.sum = 0.0;
    2411     /**
    2412      * Returns the value of the Riemann sum, i.e. the sum of the (signed) areas of the rectangles.
    2413      * @name Value
    2414      * @memberOf Riemannsum.prototype
    2415      * @function
    2416      * @returns {Number} value of Riemann sum.
    2417      */
    2418     c.Value = function () {
    2419         return this.sum;
    2420     };
    2421
    2422     /**
    2423      * @class
    2424      * @ignore
    2425      */
    2426     c.updateDataArray = function () {
    2427         var u = Numerics.riemann(f, n(), type(), this.minX(), this.maxX());
    2428         this.dataX = u[0];
    2429         this.dataY = u[1];
    2430
    2431         // Update "Riemann sum"
    2432         this.sum = u[2];
    2433     };
    2434
    2435     c.addParentsFromJCFunctions([n, type]);
    2436
    2437     return c;
    2438 };
    2439
    2440 JXG.registerElement("riemannsum", JXG.createRiemannsum);
    2441
    2442 /**
    2443  * @class This element is used to provide a constructor for trace curve (simple locus curve), which is realized as a special curve.
    2444  * @pseudo
    2445  * @name Tracecurve
    2446  * @augments JXG.Curve
    2447  * @constructor
    2448  * @type Object
    2449  * @descript JXG.Curve
    2450  * @param {Point} Parent elements of Tracecurve are a
    2451  *         glider point and a point whose locus is traced.
    2452  * @param {point}
    2453  * @see JXG.Curve
    2454  * @example
    2455  * // Create trace curve.
    2456  * var c1 = board.create('circle',[[0, 0], [2, 0]]),
    2457  * p1 = board.create('point',[-3, 1]),
    2458  * g1 = board.create('glider',[2, 1, c1]),
    2459  * s1 = board.create('segment',[g1, p1]),
    2460  * p2 = board.create('midpoint',[s1]),
    2461  * curve = board.create('tracecurve', [g1, p2]);
    2462  *
    2463  * </pre><div class="jxgbox" id="JXG5749fb7d-04fc-44d2-973e-45c1951e29ad" style="width: 300px; height: 300px;"></div>
    2464  * <script type="text/javascript">
    2465  *   var tc1_board = JXG.JSXGraph.initBoard('JXG5749fb7d-04fc-44d2-973e-45c1951e29ad', {boundingbox: [-4, 4, 4, -4], axis: false, showcopyright: false, shownavigation: false});
    2466  *   var c1 = tc1_board.create('circle',[[0, 0], [2, 0]]),
    2467  *       p1 = tc1_board.create('point',[-3, 1]),
    2468  *       g1 = tc1_board.create('glider',[2, 1, c1]),
    2469  *       s1 = tc1_board.create('segment',[g1, p1]),
    2470  *       p2 = tc1_board.create('midpoint',[s1]),
    2471  *       curve = tc1_board.create('tracecurve', [g1, p2]);
    2472  * </script><pre>
    2473  */
    2474 JXG.createTracecurve = function (board, parents, attributes) {
    2475     var c, glider, tracepoint, attr;
    2476
    2477     if (parents.length !== 2) {
    2478         throw new Error(
    2479             "JSXGraph: Can't create trace curve with given parent'" +
    2480             "\nPossible parent types: [glider, point]"
    2481         );
    2482     }
    2483
    2484     glider = board.select(parents[0]);
    2485     tracepoint = board.select(parents[1]);
    2486
    2487     if (glider.type !== Const.OBJECT_TYPE_GLIDER || !Type.isPoint(tracepoint)) {
    2488         throw new Error(
    2489             "JSXGraph: Can't create trace curve with parent types '" +
    2490             typeof parents[0] +
    2491             "' and '" +
    2492             typeof parents[1] +
    2493             "'." +
    2494             "\nPossible parent types: [glider, point]"
    2495         );
    2496     }
    2497
    2498     attr = Type.copyAttributes(attributes, board.options, "tracecurve");
    2499     attr.curvetype = "plot";
    2500     c = board.create("curve", [[0], [0]], attr);
    2501
    2502     /**
    2503      * @class
    2504      * @ignore
    2505      */
    2506     c.updateDataArray = function () {
    2507         var i, step, t, el, pEl, x, y, from,
    2508             savetrace,
    2509             le = attr.numberpoints,
    2510             savePos = glider.position,
    2511             slideObj = glider.slideObject,
    2512             mi = slideObj.minX(),
    2513             ma = slideObj.maxX();
    2514
    2515         // set step width
    2516         step = (ma - mi) / le;
    2517         this.dataX = [];
    2518         this.dataY = [];
    2519
    2520         /*
    2521          * For gliders on circles and lines a closed curve is computed.
    2522          * For gliders on curves the curve is not closed.
    2523          */
    2524         if (slideObj.elementClass !== Const.OBJECT_CLASS_CURVE) {
    2525             le++;
    2526         }
    2527
    2528         // Loop over all steps
    2529         for (i = 0; i < le; i++) {
    2530             t = mi + i * step;
    2531             x = slideObj.X(t) / slideObj.Z(t);
    2532             y = slideObj.Y(t) / slideObj.Z(t);
    2533
    2534             // Position the glider
    2535             glider.setPositionDirectly(Const.COORDS_BY_USER, [x, y]);
    2536             from = false;
    2537
    2538             // Update all elements from the glider up to the trace element
    2539             for (el in this.board.objects) {
    2540                 if (this.board.objects.hasOwnProperty(el)) {
    2541                     pEl = this.board.objects[el];
    2542
    2543                     if (pEl === glider) {
    2544                         from = true;
    2545                     }
    2546
    2547                     if (from && pEl.needsRegularUpdate) {
    2548                         // Save the trace mode of the element
    2549                         savetrace = pEl.visProp.trace;
    2550                         pEl.visProp.trace = false;
    2551                         pEl.needsUpdate = true;
    2552                         pEl.update(true);
    2553
    2554                         // Restore the trace mode
    2555                         pEl.visProp.trace = savetrace;
    2556                         if (pEl === tracepoint) {
    2557                             break;
    2558                         }
    2559                     }
    2560                 }
    2561             }
    2562
    2563             // Store the position of the trace point
    2564             this.dataX[i] = tracepoint.X();
    2565             this.dataY[i] = tracepoint.Y();
    2566         }
    2567
    2568         // Restore the original position of the glider
    2569         glider.position = savePos;
    2570         from = false;
    2571
    2572         // Update all elements from the glider to the trace point
    2573         for (el in this.board.objects) {
    2574             if (this.board.objects.hasOwnProperty(el)) {
    2575                 pEl = this.board.objects[el];
    2576                 if (pEl === glider) {
    2577                     from = true;
    2578                 }
    2579
    2580                 if (from && pEl.needsRegularUpdate) {
    2581                     savetrace = pEl.visProp.trace;
    2582                     pEl.visProp.trace = false;
    2583                     pEl.needsUpdate = true;
    2584                     pEl.update(true);
    2585                     pEl.visProp.trace = savetrace;
    2586
    2587                     if (pEl === tracepoint) {
    2588                         break;
    2589                     }
    2590                 }
    2591             }
    2592         }
    2593     };
    2594
    2595     return c;
    2596 };
    2597
    2598 JXG.registerElement("tracecurve", JXG.createTracecurve);
    2599
    2600 /**
    2601      * @class This element is used to provide a constructor for step function, which is realized as a special curve.
    2602      *
    2603      * In case the data points should be updated after creation time, they can be accessed by curve.xterm and curve.yterm.
    2604      * @pseudo
    2605      * @name Stepfunction
    2606      * @augments JXG.Curve
    2607      * @constructor
    2608      * @type Curve
    2609      * @description JXG.Curve
    2610      * @param {Array|Function} Parent1 elements of Stepfunction are two arrays containing the coordinates.
    2611      * @param {Array|Function} Parent2
    2612      * @see JXG.Curve
    2613      * @example
    2614      * // Create step function.
    2615      var curve = board.create('stepfunction', [[0,1,2,3,4,5], [1,3,0,2,2,1]]);
    2616
    2617      * </pre><div class="jxgbox" id="JXG32342ec9-ad17-4339-8a97-ff23dc34f51a" style="width: 300px; height: 300px;"></div>
    2618      * <script type="text/javascript">
    2619      *   var sf1_board = JXG.JSXGraph.initBoard('JXG32342ec9-ad17-4339-8a97-ff23dc34f51a', {boundingbox: [-1, 5, 6, -2], axis: true, showcopyright: false, shownavigation: false});
    2620      *   var curve = sf1_board.create('stepfunction', [[0,1,2,3,4,5], [1,3,0,2,2,1]]);
    2621      * </script><pre>
    2622      */
    2623 JXG.createStepfunction = function (board, parents, attributes) {
    2624     var c, attr;
    2625     if (parents.length !== 2) {
    2626         throw new Error(
    2627             "JSXGraph: Can't create step function with given parent'" +
    2628             "\nPossible parent types: [array, array|function]"
    2629         );
    2630     }
    2631
    2632     attr = Type.copyAttributes(attributes, board.options, "stepfunction");
    2633     c = board.create("curve", parents, attr);
    2634     /**
    2635      * @class
    2636      * @ignore
    2637      */
    2638     c.updateDataArray = function () {
    2639         var i,
    2640             j = 0,
    2641             len = this.xterm.length;
    2642
    2643         this.dataX = [];
    2644         this.dataY = [];
    2645
    2646         if (len === 0) {
    2647             return;
    2648         }
    2649
    2650         this.dataX[j] = this.xterm[0];
    2651         this.dataY[j] = this.yterm[0];
    2652         ++j;
    2653
    2654         for (i = 1; i < len; ++i) {
    2655             this.dataX[j] = this.xterm[i];
    2656             this.dataY[j] = this.dataY[j - 1];
    2657             ++j;
    2658             this.dataX[j] = this.xterm[i];
    2659             this.dataY[j] = this.yterm[i];
    2660             ++j;
    2661         }
    2662     };
    2663
    2664     return c;
    2665 };
    2666
    2667 JXG.registerElement("stepfunction", JXG.createStepfunction);
    2668
    2669 /**
    2670  * @class This element is used to provide a constructor for the graph showing
    2671  * the (numerical) derivative of a given curve.
    2672  *
    2673  * @pseudo
    2674  * @name Derivative
    2675  * @augments JXG.Curve
    2676  * @constructor
    2677  * @type JXG.Curve
    2678  * @param {JXG.Curve} Parent Curve for which the derivative is generated.
    2679  * @see JXG.Curve
    2680  * @example
    2681  * var cu = board.create('cardinalspline', [[[-3,0], [-1,2], [0,1], [2,0], [3,1]], 0.5, 'centripetal'], {createPoints: false});
    2682  * var d = board.create('derivative', [cu], {dash: 2});
    2683  *
    2684  * </pre><div id="JXGb9600738-1656-11e8-8184-901b0e1b8723" class="jxgbox" style="width: 300px; height: 300px;"></div>
    2685  * <script type="text/javascript">
    2686  *     (function() {
    2687  *         var board = JXG.JSXGraph.initBoard('JXGb9600738-1656-11e8-8184-901b0e1b8723',
    2688  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
    2689  *     var cu = board.create('cardinalspline', [[[-3,0], [-1,2], [0,1], [2,0], [3,1]], 0.5, 'centripetal'], {createPoints: false});
    2690  *     var d = board.create('derivative', [cu], {dash: 2});
    2691  *
    2692  *     })();
    2693  *
    2694  * </script><pre>
    2695  *
    2696  */
    2697 JXG.createDerivative = function (board, parents, attributes) {
    2698     var c, curve, dx, dy, attr;
    2699
    2700     if (parents.length !== 1 && parents[0].class !== Const.OBJECT_CLASS_CURVE) {
    2701         throw new Error(
    2702             "JSXGraph: Can't create derivative curve with given parent'" +
    2703             "\nPossible parent types: [curve]"
    2704         );
    2705     }
    2706
    2707     attr = Type.copyAttributes(attributes, board.options, "curve");
    2708
    2709     curve = parents[0];
    2710     dx = Numerics.D(curve.X);
    2711     dy = Numerics.D(curve.Y);
    2712
    2713     c = board.create(
    2714         "curve",
    2715         [
    2716             function (t) {
    2717                 return curve.X(t);
    2718             },
    2719             function (t) {
    2720                 return dy(t) / dx(t);
    2721             },
    2722             curve.minX(),
    2723             curve.maxX()
    2724         ],
    2725         attr
    2726     );
    2727
    2728     c.setParents(curve);
    2729
    2730     return c;
    2731 };
    2732
    2733 JXG.registerElement("derivative", JXG.createDerivative);
    2734
    2735 /**
    2736  * @class Intersection of two closed path elements. The elements may be of type curve, circle, polygon, inequality.
    2737  * If one element is a curve, it has to be closed.
    2738  * The resulting element is of type curve.
    2739  * @pseudo
    2740  * @name CurveIntersection
    2741  * @param {JXG.Curve|JXG.Polygon|JXG.Circle} curve1 First element which is intersected
    2742  * @param {JXG.Curve|JXG.Polygon|JXG.Circle} curve2 Second element which is intersected
    2743  * @augments JXG.Curve
    2744  * @constructor
    2745  * @type JXG.Curve
    2746  *
    2747  * @example
    2748  * var f = board.create('functiongraph', ['cos(x)']);
    2749  * var ineq = board.create('inequality', [f], {inverse: true, fillOpacity: 0.1});
    2750  * var circ = board.create('circle', [[0,0], 4]);
    2751  * var clip = board.create('curveintersection', [ineq, circ], {fillColor: 'yellow', fillOpacity: 0.6});
    2752  *
    2753  * </pre><div id="JXGe2948257-8835-4276-9164-8acccb48e8d4" class="jxgbox" style="width: 300px; height: 300px;"></div>
    2754  * <script type="text/javascript">
    2755  *     (function() {
    2756  *         var board = JXG.JSXGraph.initBoard('JXGe2948257-8835-4276-9164-8acccb48e8d4',
    2757  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
    2758  *     var f = board.create('functiongraph', ['cos(x)']);
    2759  *     var ineq = board.create('inequality', [f], {inverse: true, fillOpacity: 0.1});
    2760  *     var circ = board.create('circle', [[0,0], 4]);
    2761  *     var clip = board.create('curveintersection', [ineq, circ], {fillColor: 'yellow', fillOpacity: 0.6});
    2762  *
    2763  *     })();
    2764  *
    2765  * </script><pre>
    2766  *
    2767  */
    2768 JXG.createCurveIntersection = function (board, parents, attributes) {
    2769     var c;
    2770
    2771     if (parents.length !== 2) {
    2772         throw new Error(
    2773             "JSXGraph: Can't create curve intersection with given parent'" +
    2774             "\nPossible parent types: [array, array|function]"
    2775         );
    2776     }
    2777
    2778     c = board.create("curve", [[], []], attributes);
    2779     /**
    2780      * @class
    2781      * @ignore
    2782      */
    2783     c.updateDataArray = function () {
    2784         var a = Clip.intersection(parents[0], parents[1], this.board);
    2785         this.dataX = a[0];
    2786         this.dataY = a[1];
    2787     };
    2788     return c;
    2789 };
    2790
    2791 /**
    2792  * @class Union of two closed path elements. The elements may be of type curve, circle, polygon, inequality.
    2793  * If one element is a curve, it has to be closed.
    2794  * The resulting element is of type curve.
    2795  * @pseudo
    2796  * @name CurveUnion
    2797  * @param {JXG.Curve|JXG.Polygon|JXG.Circle} curve1 First element defining the union
    2798  * @param {JXG.Curve|JXG.Polygon|JXG.Circle} curve2 Second element defining the union
    2799  * @augments JXG.Curve
    2800  * @constructor
    2801  * @type JXG.Curve
    2802  *
    2803  * @example
    2804  * var f = board.create('functiongraph', ['cos(x)']);
    2805  * var ineq = board.create('inequality', [f], {inverse: true, fillOpacity: 0.1});
    2806  * var circ = board.create('circle', [[0,0], 4]);
    2807  * var clip = board.create('curveunion', [ineq, circ], {fillColor: 'yellow', fillOpacity: 0.6});
    2808  *
    2809  * </pre><div id="JXGe2948257-8835-4276-9164-8acccb48e8d4" class="jxgbox" style="width: 300px; height: 300px;"></div>
    2810  * <script type="text/javascript">
    2811  *     (function() {
    2812  *         var board = JXG.JSXGraph.initBoard('JXGe2948257-8835-4276-9164-8acccb48e8d4',
    2813  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
    2814  *     var f = board.create('functiongraph', ['cos(x)']);
    2815  *     var ineq = board.create('inequality', [f], {inverse: true, fillOpacity: 0.1});
    2816  *     var circ = board.create('circle', [[0,0], 4]);
    2817  *     var clip = board.create('curveunion', [ineq, circ], {fillColor: 'yellow', fillOpacity: 0.6});
    2818  *
    2819  *     })();
    2820  *
    2821  * </script><pre>
    2822  *
    2823  */
    2824 JXG.createCurveUnion = function (board, parents, attributes) {
    2825     var c;
    2826
    2827     if (parents.length !== 2) {
    2828         throw new Error(
    2829             "JSXGraph: Can't create curve union with given parent'" +
    2830             "\nPossible parent types: [array, array|function]"
    2831         );
    2832     }
    2833
    2834     c = board.create("curve", [[], []], attributes);
    2835     /**
    2836      * @class
    2837      * @ignore
    2838      */
    2839     c.updateDataArray = function () {
    2840         var a = Clip.union(parents[0], parents[1], this.board);
    2841         this.dataX = a[0];
    2842         this.dataY = a[1];
    2843     };
    2844     return c;
    2845 };
    2846
    2847 /**
    2848  * @class Difference of two closed path elements. The elements may be of type curve, circle, polygon, inequality.
    2849  * If one element is a curve, it has to be closed.
    2850  * The resulting element is of type curve.
    2851  * @pseudo
    2852  * @name CurveDifference
    2853  * @param {JXG.Curve|JXG.Polygon|JXG.Circle} curve1 First element from which the second element is "subtracted"
    2854  * @param {JXG.Curve|JXG.Polygon|JXG.Circle} curve2 Second element which is subtracted from the first element
    2855  * @augments JXG.Curve
    2856  * @constructor
    2857  * @type JXG.Curve
    2858  *
    2859  * @example
    2860  * var f = board.create('functiongraph', ['cos(x)']);
    2861  * var ineq = board.create('inequality', [f], {inverse: true, fillOpacity: 0.1});
    2862  * var circ = board.create('circle', [[0,0], 4]);
    2863  * var clip = board.create('curvedifference', [ineq, circ], {fillColor: 'yellow', fillOpacity: 0.6});
    2864  *
    2865  * </pre><div id="JXGe2948257-8835-4276-9164-8acccb48e8d4" class="jxgbox" style="width: 300px; height: 300px;"></div>
    2866  * <script type="text/javascript">
    2867  *     (function() {
    2868  *         var board = JXG.JSXGraph.initBoard('JXGe2948257-8835-4276-9164-8acccb48e8d4',
    2869  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
    2870  *     var f = board.create('functiongraph', ['cos(x)']);
    2871  *     var ineq = board.create('inequality', [f], {inverse: true, fillOpacity: 0.1});
    2872  *     var circ = board.create('circle', [[0,0], 4]);
    2873  *     var clip = board.create('curvedifference', [ineq, circ], {fillColor: 'yellow', fillOpacity: 0.6});
    2874  *
    2875  *     })();
    2876  *
    2877  * </script><pre>
    2878  *
    2879  */
    2880 JXG.createCurveDifference = function (board, parents, attributes) {
    2881     var c;
    2882
    2883     if (parents.length !== 2) {
    2884         throw new Error(
    2885             "JSXGraph: Can't create curve difference with given parent'" +
    2886             "\nPossible parent types: [array, array|function]"
    2887         );
    2888     }
    2889
    2890     c = board.create("curve", [[], []], attributes);
    2891     /**
    2892      * @class
    2893      * @ignore
    2894      */
    2895     c.updateDataArray = function () {
    2896         var a = Clip.difference(parents[0], parents[1], this.board);
    2897         this.dataX = a[0];
    2898         this.dataY = a[1];
    2899     };
    2900     return c;
    2901 };
    2902
    2903 JXG.registerElement("curvedifference", JXG.createCurveDifference);
    2904 JXG.registerElement("curveintersection", JXG.createCurveIntersection);
    2905 JXG.registerElement("curveunion", JXG.createCurveUnion);
    2906
    2907 // /**
    2908 //  * @class Concat of two path elements, in general neither is a closed path. The parent elements have to be curves, too.
    2909 //  * The resulting element is of type curve. The curve points are simply concatenated.
    2910 //  * @pseudo
    2911 //  * @name CurveConcat
    2912 //  * @param {JXG.Curve} curve1 First curve element.
    2913 //  * @param {JXG.Curve} curve2 Second curve element.
    2914 //  * @augments JXG.Curve
    2915 //  * @constructor
    2916 //  * @type JXG.Curve
    2917 //  */
    2918 // JXG.createCurveConcat = function (board, parents, attributes) {
    2919 //     var c;
    2920
    2921 //     if (parents.length !== 2) {
    2922 //         throw new Error(
    2923 //             "JSXGraph: Can't create curve difference with given parent'" +
    2924 //                 "\nPossible parent types: [array, array|function]"
    2925 //         );
    2926 //     }
    2927
    2928 //     c = board.create("curve", [[], []], attributes);
    2929 //     /**
    2930 //      * @class
    2931 //      * @ignore
    2932 //      */
    2933 //     c.updateCurve = function () {
    2934 //         this.points = parents[0].points.concat(
    2935 //                 [new JXG.Coords(Const.COORDS_BY_USER, [NaN, NaN], this.board)]
    2936 //             ).concat(parents[1].points);
    2937 //         this.numberPoints = this.points.length;
    2938 //         return this;
    2939 //     };
    2940
    2941 //     return c;
    2942 // };
    2943
    2944 // JXG.registerElement("curveconcat", JXG.createCurveConcat);
    2945
    2946 /**
    2947  * @class Box plot curve. The direction of the box plot can be either vertical or horizontal which
    2948  * is controlled by the attribute "dir".
    2949  * @pseudo
    2950  * @name Boxplot
    2951  * @param {Array} quantiles Array containing at least five quantiles. The elements can be of type number, function or string.
    2952  * @param {Number|Function} axis Axis position of the box plot
    2953  * @param {Number|Function} width Width of the rectangle part of the box plot. The width of the first and 4th quantile
    2954  * is relative to this width and can be controlled by the attribute "smallWidth".
    2955  * @augments JXG.Curve
    2956  * @constructor
    2957  * @type JXG.Curve
    2958  *
    2959  * @example
    2960  * var Q = [ -1, 2, 3, 3.5, 5 ];
    2961  *
    2962  * var b = board.create('boxplot', [Q, 2, 4], {strokeWidth: 3});
    2963  *
    2964  * </pre><div id="JXG13eb23a1-a641-41a2-be11-8e03e400a947" class="jxgbox" style="width: 300px; height: 300px;"></div>
    2965  * <script type="text/javascript">
    2966  *     (function() {
    2967  *         var board = JXG.JSXGraph.initBoard('JXG13eb23a1-a641-41a2-be11-8e03e400a947',
    2968  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
    2969  *     var Q = [ -1, 2, 3, 3.5, 5 ];
    2970  *     var b = board.create('boxplot', [Q, 2, 4], {strokeWidth: 3});
    2971  *
    2972  *     })();
    2973  *
    2974  * </script><pre>
    2975  *
    2976  * @example
    2977  * var Q = [ -1, 2, 3, 3.5, 5 ];
    2978  * var b = board.create('boxplot', [Q, 3, 4], {dir: 'horizontal', smallWidth: 0.25, color:'red'});
    2979  *
    2980  * </pre><div id="JXG0deb9cb2-84bc-470d-a6db-8be9a5694813" class="jxgbox" style="width: 300px; height: 300px;"></div>
    2981  * <script type="text/javascript">
    2982  *     (function() {
    2983  *         var board = JXG.JSXGraph.initBoard('JXG0deb9cb2-84bc-470d-a6db-8be9a5694813',
    2984  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
    2985  *     var Q = [ -1, 2, 3, 3.5, 5 ];
    2986  *     var b = board.create('boxplot', [Q, 3, 4], {dir: 'horizontal', smallWidth: 0.25, color:'red'});
    2987  *
    2988  *     })();
    2989  *
    2990  * </script><pre>
    2991  *
    2992  * @example
    2993  * 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];
    2994  * var Q = [];
    2995  *
    2996  * Q[0] = JXG.Math.Statistics.min(data);
    2997  * Q = Q.concat(JXG.Math.Statistics.percentile(data, [25, 50, 75]));
    2998  * Q[4] = JXG.Math.Statistics.max(data);
    2999  *
    3000  * var b = board.create('boxplot', [Q, 0, 3]);
    3001  *
    3002  * </pre><div id="JXGef079e76-ae99-41e4-af29-1d07d83bf85a" class="jxgbox" style="width: 300px; height: 300px;"></div>
    3003  * <script type="text/javascript">
    3004  *     (function() {
    3005  *         var board = JXG.JSXGraph.initBoard('JXGef079e76-ae99-41e4-af29-1d07d83bf85a',
    3006  *             {boundingbox: [-5,90,5,30], axis: true, showcopyright: false, shownavigation: false});
    3007  *     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];
    3008  *     var Q = [];
    3009  *
    3010  *     Q[0] = JXG.Math.Statistics.min(data);
    3011  *     Q = Q.concat(JXG.Math.Statistics.percentile(data, [25, 50, 75]));
    3012  *     Q[4] = JXG.Math.Statistics.max(data);
    3013  *
    3014  *     var b = board.create('boxplot', [Q, 0, 3]);
    3015  *
    3016  *     })();
    3017  *
    3018  * </script><pre>
    3019  *
    3020  * @example
    3021  * var mi = board.create('glider', [0, -1, board.defaultAxes.y]);
    3022  * var ma = board.create('glider', [0, 5, board.defaultAxes.y]);
    3023  * var Q = [function() { return mi.Y(); }, 2, 3, 3.5, function() { return ma.Y(); }];
    3024  *
    3025  * var b = board.create('boxplot', [Q, 0, 2]);
    3026  *
    3027  * </pre><div id="JXG3b3225da-52f0-42fe-8396-be9016bf289b" class="jxgbox" style="width: 300px; height: 300px;"></div>
    3028  * <script type="text/javascript">
    3029  *     (function() {
    3030  *         var board = JXG.JSXGraph.initBoard('JXG3b3225da-52f0-42fe-8396-be9016bf289b',
    3031  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
    3032  *     var mi = board.create('glider', [0, -1, board.defaultAxes.y]);
    3033  *     var ma = board.create('glider', [0, 5, board.defaultAxes.y]);
    3034  *     var Q = [function() { return mi.Y(); }, 2, 3, 3.5, function() { return ma.Y(); }];
    3035  *
    3036  *     var b = board.create('boxplot', [Q, 0, 2]);
    3037  *
    3038  *     })();
    3039  *
    3040  * </script><pre>
    3041  *
    3042  */
    3043 JXG.createBoxPlot = function (board, parents, attributes) {
    3044     var box, i, len,
    3045         attr = Type.copyAttributes(attributes, board.options, "boxplot");
    3046
    3047     if (parents.length !== 3) {
    3048         throw new Error(
    3049             "JSXGraph: Can't create box plot with given parent'" +
    3050             "\nPossible parent types: [array, number|function, number|function] containing quantiles, axis, width"
    3051         );
    3052     }
    3053     if (parents[0].length < 5) {
    3054         throw new Error(
    3055             "JSXGraph: Can't create box plot with given parent[0]'" +
    3056             "\nparent[0] has to contain at least 5 quantiles."
    3057         );
    3058     }
    3059     box = board.create("curve", [[], []], attr);
    3060
    3061     len = parents[0].length; // Quantiles
    3062     box.Q = [];
    3063     for (i = 0; i < len; i++) {
    3064         box.Q[i] = Type.createFunction(parents[0][i], board);
    3065     }
    3066     box.x = Type.createFunction(parents[1], board);
    3067     box.w = Type.createFunction(parents[2], board);
    3068
    3069     /**
    3070      * @class
    3071      * @ignore
    3072      */
    3073     box.updateDataArray = function () {
    3074         var v1, v2, l1, l2, r1, r2, w2, dir, x;
    3075
    3076         w2 = Type.evaluate(this.visProp.smallwidth);
    3077         dir = Type.evaluate(this.visProp.dir);
    3078         x = this.x();
    3079         l1 = x - this.w() * 0.5;
    3080         l2 = x - this.w() * 0.5 * w2;
    3081         r1 = x + this.w() * 0.5;
    3082         r2 = x + this.w() * 0.5 * w2;
    3083         v1 = [x, l2, r2, x, x, l1, l1, r1, r1, x, NaN, l1, r1, NaN, x, x, l2, r2, x];
    3084         v2 = [
    3085             this.Q[0](),
    3086             this.Q[0](),
    3087             this.Q[0](),
    3088             this.Q[0](),
    3089             this.Q[1](),
    3090             this.Q[1](),
    3091             this.Q[3](),
    3092             this.Q[3](),
    3093             this.Q[1](),
    3094             this.Q[1](),
    3095             NaN,
    3096             this.Q[2](),
    3097             this.Q[2](),
    3098             NaN,
    3099             this.Q[3](),
    3100             this.Q[4](),
    3101             this.Q[4](),
    3102             this.Q[4](),
    3103             this.Q[4]()
    3104         ];
    3105         if (dir === "vertical") {
    3106             this.dataX = v1;
    3107             this.dataY = v2;
    3108         } else {
    3109             this.dataX = v2;
    3110             this.dataY = v1;
    3111         }
    3112     };
    3113
    3114     box.addParentsFromJCFunctions([box.Q, box.x, box.w]);
    3115
    3116     return box;
    3117 };
    3118
    3119 JXG.registerElement("boxplot", JXG.createBoxPlot);
    3120
    3121 /**
    3122  *
    3123  * @class
    3124  * From <a href="https://en.wikipedia.org/wiki/Implicit_curve">Wikipedia</a>:
    3125  * "An implicit curve is a plane curve defined by an implicit equation
    3126  * relating two coordinate variables, commonly <i>x</i> and <i>y</i>.
    3127  * For example, the unit circle is defined by the implicit equation
    3128  * x<sup>2</sup> + y<sup>2</sup> = 1.
    3129  * In general, every implicit curve is defined by an equation of the form
    3130  * <i>f(x, y) = 0</i>
    3131  * for some function <i>f</i> of two variables."
    3132  * <p>
    3133  * The partial derivatives for <i>f</i> are optional. If not given, numerical
    3134  * derivatives are used instead. This is good enough for most practical use cases.
    3135  * But if supplied, both partial derivatives must be supplied.
    3136  * <p>
    3137  * The most effective attributes to tinker with if the implicit curve algorithm fails are
    3138  * {@link ImplicitCurve#resolution_outer},
    3139  * {@link ImplicitCurve#resolution_inner},
    3140  * {@link ImplicitCurve#alpha_0},
    3141  * {@link ImplicitCurve#h_initial},
    3142  * {@link ImplicitCurve#h_max}, and
    3143  * {@link ImplicitCurve#qdt_box}.
    3144  *
    3145  * @pseudo
    3146  * @name ImplicitCurve
    3147  * @param {Function|String} f Function of two variables for the left side of the equation <i>f(x,y)=0</i>.
    3148  * If f is supplied as string, it has to use the variables 'x' and 'y'.
    3149  * @param {Function|String} [dfx=null] Optional partial derivative in respect to the first variable
    3150  * If dfx is supplied as string, it has to use the variables 'x' and 'y'.
    3151  * @param {Function|String} [dfy=null] Optional partial derivative in respect to the second variable
    3152  * If dfy is supplied as string, it has to use the variables 'x' and 'y'.
    3153  * @param {Array|Function} [rangex=boundingbox] Optional array of length 2
    3154  * of the form [x_min, x_max] setting the domain of the x coordinate of the implicit curve.
    3155  * If not supplied, the board's boundingbox (+ the attribute "margin") is taken.
    3156  * For algorithmic reasons, the plotted curve mighty slightly overflow the given domain.
    3157  * @param {Array|Function} [rangey=boundingbox] Optional array of length 2
    3158  * of the form [y_min, y_max] setting the domain of the y coordinate of the implicit curve.
    3159  * If not supplied, the board's boundingbox (+ the attribute "margin") is taken.
    3160  * For algorithmic reasons, the plotted curve mighty slightly overflow the given domain.
    3161  * @augments JXG.Curve
    3162  * @constructor
    3163  * @type JXG.Curve
    3164  *
    3165  * @example
    3166  *   var f, c;
    3167  *   f = (x, y) => 1 / 16 * x ** 2 + y ** 2 - 1;
    3168  *   c = board.create('implicitcurve', [f], {
    3169  *       strokeWidth: 3,
    3170  *       strokeColor: JXG.palette.red,
    3171  *       strokeOpacity: 0.8
    3172  *   });
    3173  *
    3174  * </pre><div id="JXGa6e86701-1a82-48d0-b007-3a3d32075076" class="jxgbox" style="width: 300px; height: 300px;"></div>
    3175  * <script type="text/javascript">
    3176  *     (function() {
    3177  *         var board = JXG.JSXGraph.initBoard('JXGa6e86701-1a82-48d0-b007-3a3d32075076',
    3178  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
    3179  *             var f, c;
    3180  *             f = (x, y) => 1 / 16 * x ** 2 + y ** 2 - 1;
    3181  *             c = board.create('implicitcurve', [f], {
    3182  *                 strokeWidth: 3,
    3183  *                 strokeColor: JXG.palette.red,
    3184  *                 strokeOpacity: 0.8
    3185  *             });
    3186  *
    3187  *     })();
    3188  *
    3189  * </script><pre>
    3190  *
    3191  * @example
    3192  *  var a, c, f;
    3193  *  a = board.create('slider', [[-3, 6], [3, 6], [-3, 1, 3]], {
    3194  *      name: 'a', stepWidth: 0.1
    3195  *  });
    3196  *  f = (x, y) => x ** 2 - 2 * x * y - 2 * x + (a.Value() + 1) * y ** 2 + (4 * a.Value() + 2) * y + 4 * a.Value() - 3;
    3197  *  c = board.create('implicitcurve', [f], {
    3198  *      strokeWidth: 3,
    3199  *      strokeColor: JXG.palette.red,
    3200  *      strokeOpacity: 0.8,
    3201  *      resolution_outer: 20,
    3202  *      resolution_inner: 20
    3203  *  });
    3204  *
    3205  * </pre><div id="JXG0b133a54-9509-4a65-9722-9c5145e23b40" class="jxgbox" style="width: 300px; height: 300px;"></div>
    3206  * <script type="text/javascript">
    3207  *     (function() {
    3208  *         var board = JXG.JSXGraph.initBoard('JXG0b133a54-9509-4a65-9722-9c5145e23b40',
    3209  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
    3210  *             var a, c, f;
    3211  *             a = board.create('slider', [[-3, 6], [3, 6], [-3, 1, 3]], {
    3212  *                 name: 'a', stepWidth: 0.1
    3213  *             });
    3214  *             f = (x, y) => x ** 2 - 2 * x * y - 2 * x + (a.Value() + 1) * y ** 2 + (4 * a.Value() + 2) * y + 4 * a.Value() - 3;
    3215  *             c = board.create('implicitcurve', [f], {
    3216  *                 strokeWidth: 3,
    3217  *                 strokeColor: JXG.palette.red,
    3218  *                 strokeOpacity: 0.8,
    3219  *                 resolution_outer: 20,
    3220  *                 resolution_inner: 20
    3221  *             });
    3222  *
    3223  *     })();
    3224  *
    3225  * </script><pre>
    3226  *
    3227  * @example
    3228  *  var c = board.create('implicitcurve', ['abs(x * y) - 3'], {
    3229  *      strokeWidth: 3,
    3230  *      strokeColor: JXG.palette.red,
    3231  *      strokeOpacity: 0.8
    3232  *  });
    3233  *
    3234  * </pre><div id="JXG02802981-0abb-446b-86ea-ee588f02ed1a" class="jxgbox" style="width: 300px; height: 300px;"></div>
    3235  * <script type="text/javascript">
    3236  *     (function() {
    3237  *         var board = JXG.JSXGraph.initBoard('JXG02802981-0abb-446b-86ea-ee588f02ed1a',
    3238  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
    3239  *             var c = board.create('implicitcurve', ['abs(x * y) - 3'], {
    3240  *                 strokeWidth: 3,
    3241  *                 strokeColor: JXG.palette.red,
    3242  *                 strokeOpacity: 0.8
    3243  *             });
    3244  *
    3245  *     })();
    3246  *
    3247  * </script><pre>
    3248  *
    3249  * @example
    3250  * var niveauline = [];
    3251  * niveauline = [0.5, 1, 1.5, 2];
    3252  * for (let i = 0; i < niveauline.length; i++) {
    3253  *     board.create("implicitcurve", [
    3254  *         (x, y) => x ** .5 * y ** .5 - niveauline[i],
    3255            [0.25, 3], [0.5, 4] // Domain
    3256  *     ], {
    3257  *         strokeWidth: 2,
    3258  *         strokeColor: JXG.palette.red,
    3259  *         strokeOpacity: (1 + i) / niveauline.length,
    3260  *         needsRegularUpdate: false
    3261  *     });
    3262  * }
    3263  *
    3264  * </pre><div id="JXGccee9aab-6dd9-4a79-827d-3164f70cc6a1" class="jxgbox" style="width: 300px; height: 300px;"></div>
    3265  * <script type="text/javascript">
    3266  *     (function() {
    3267  *         var board = JXG.JSXGraph.initBoard('JXGccee9aab-6dd9-4a79-827d-3164f70cc6a1',
    3268  *             {boundingbox: [-1, 5, 5,-1], axis: true, showcopyright: false, shownavigation: false});
    3269  *         var niveauline = [];
    3270  *         niveauline = [0.5, 1, 1.5, 2];
    3271  *         for (let i = 0; i < niveauline.length; i++) {
    3272  *             board.create("implicitcurve", [
    3273  *                 (x, y) => x ** .5 * y ** .5 - niveauline[i],
    3274  *                 [0.25, 3], [0.5, 4]
    3275  *             ], {
    3276  *                 strokeWidth: 2,
    3277  *                 strokeColor: JXG.palette.red,
    3278  *                 strokeOpacity: (1 + i) / niveauline.length,
    3279  *                 needsRegularUpdate: false
    3280  *             });
    3281  *         }
    3282  *
    3283  *     })();
    3284  *
    3285  * </script><pre>
    3286  *
    3287  */
    3288 JXG.createImplicitCurve = function (board, parents, attributes) {
    3289     var c, attr;
    3290
    3291     if ([1, 3, 5].indexOf(parents.length) < 0) {
    3292         throw new Error(
    3293             "JSXGraph: Can't create curve implicitCurve with given parent'" +
    3294             "\nPossible parent types: [f], [f, rangex, rangey], [f, dfx, dfy] or [f, dfx, dfy, rangex, rangey]" +
    3295             "\nwith functions f, dfx, dfy and arrays of length 2 rangex, rangey."
    3296         );
    3297     }
    3298
    3299     // if (parents.length === 3) {
    3300     //     if (!Type.isArray(parents[1]) && !Type.isArray(parents[2])) {
    3301     //         throw new Error(
    3302     //             "JSXGraph: Can't create curve implicitCurve with given parent'" +
    3303     //             "\nPossible parent types: [f], [f, rangex, rangey], [f, dfx, dfy] or [f, dfx, dfy, rangex, rangey]" +
    3304     //             "\nwith functions f, dfx, dfy and arrays of length 2 rangex, rangey."
    3305     //         );
    3306     //     }
    3307     // }
    3308     // if (parents.length === 5) {
    3309     //     if (!Type.isArray(parents[3]) && !Type.isArray(parents[4])) {
    3310     //         throw new Error(
    3311     //             "JSXGraph: Can't create curve implicitCurve with given parent'" +
    3312     //             "\nPossible parent types: [f], [f, rangex, rangey], [f, dfx, dfy] or [f, dfx, dfy, rangex, rangey]" +
    3313     //             "\nwith functions f, dfx, dfy and arrays of length 2 rangex, rangey."
    3314     //         );
    3315     //     }
    3316     // }
    3317
    3318     attr = Type.copyAttributes(attributes, board.options, "implicitcurve");
    3319     c = board.create("curve", [[], []], attr);
    3320
    3321     /**
    3322      * Function of two variables for the left side of the equation <i>f(x,y)=0</i>.
    3323      *
    3324      * @name f
    3325      * @memberOf ImplicitCurve.prototype
    3326      * @function
    3327      * @returns {Number}
    3328      */
    3329     c.f = Type.createFunction(parents[0], board, 'x, y');
    3330
    3331     /**
    3332      * Partial derivative in the first variable of
    3333      * the left side of the equation <i>f(x,y)=0</i>.
    3334      * If null, then numerical derivative is used.
    3335      *
    3336      * @name dfx
    3337      * @memberOf ImplicitCurve.prototype
    3338      * @function
    3339      * @returns {Number}
    3340      */
    3341     c.dfx = Type.createFunction(parents[1], board, 'x, y');
    3342
    3343     /**
    3344      * Partial derivative in the second variable of
    3345      * the left side of the equation <i>f(x,y)=0</i>.
    3346      * If null, then numerical derivative is used.
    3347      *
    3348      * @name dfy
    3349      * @memberOf ImplicitCurve.prototype
    3350      * @function
    3351      * @returns {Number}
    3352      */
    3353     c.dfy = Type.createFunction(parents[2], board, 'x, y');
    3354
    3355     /**
    3356      * Defines a domain for searching f(x,y)=0. Default is null, meaning
    3357      * the bounding box of the board is used.
    3358      * Using domain, visProp.margin is ignored.
    3359      * @name domain
    3360      * @memberOf ImplicitCurve.prototype
    3361      * @param {Array} of length 4 defining the domain used to compute the implict curve.
    3362      * Syntax: [x_min, y_max, x_max, y_min]
    3363      */
    3364     // c.domain = board.getBoundingBox();
    3365     c.domain = null;
    3366     if (parents.length === 5) {
    3367         c.domain = [parents[3], parents[4]];
    3368         // c.visProp.margin = 0;
    3369     } else if (parents.length === 3) {
    3370         c.domain = [parents[1], parents[2]];
    3371         // c.visProp.margin = 0;
    3372     }
    3373
    3374     /**
    3375      * @class
    3376      * @ignore
    3377      */
    3378     c.updateDataArray = function () {
    3379         var bbox, rx, ry,
    3380             ip, cfg,
    3381             ret = [],
    3382             mgn;
    3383
    3384         if (this.domain === null) {
    3385             mgn = Type.evaluate(this.visProp.margin);
    3386             bbox = this.board.getBoundingBox();
    3387             bbox[0] -= mgn;
    3388             bbox[1] += mgn;
    3389             bbox[2] += mgn;
    3390             bbox[3] -= mgn;
    3391         } else {
    3392             rx = Type.evaluate(this.domain[0]);
    3393             ry = Type.evaluate(this.domain[1]);
    3394             bbox = [rx[0], ry[1], rx[1], ry[0]];
    3395         }
    3396
    3397         cfg = {
    3398             resolution_out: Math.max(0.01, Type.evaluate(this.visProp.resolution_outer)),
    3399             resolution_in: Math.max(0.01, Type.evaluate(this.visProp.resolution_inner)),
    3400             max_steps: Type.evaluate(this.visProp.max_steps),
    3401             alpha_0: Type.evaluate(this.visProp.alpha_0),
    3402             tol_u0: Type.evaluate(this.visProp.tol_u0),
    3403             tol_newton: Type.evaluate(this.visProp.tol_newton),
    3404             tol_cusp: Type.evaluate(this.visProp.tol_cusp),
    3405             tol_progress: Type.evaluate(this.visProp.tol_progress),
    3406             qdt_box: Type.evaluate(this.visProp.qdt_box),
    3407             kappa_0: Type.evaluate(this.visProp.kappa_0),
    3408             delta_0: Type.evaluate(this.visProp.delta_0),
    3409             h_initial: Type.evaluate(this.visProp.h_initial),
    3410             h_critical: Type.evaluate(this.visProp.h_critical),
    3411             h_max: Type.evaluate(this.visProp.h_max),
    3412             loop_dist: Type.evaluate(this.visProp.loop_dist),
    3413             loop_dir: Type.evaluate(this.visProp.loop_dir),
    3414             loop_detection: Type.evaluate(this.visProp.loop_detection),
    3415             unitX: this.board.unitX,
    3416             unitY: this.board.unitY
    3417         };
    3418         this.dataX = [];
    3419         this.dataY = [];
    3420
    3421         // console.time("implicit plot");
    3422         ip = new ImplicitPlot(bbox, cfg, this.f, this.dfx, this.dfy);
    3423         this.qdt = ip.qdt;
    3424
    3425         ret = ip.plot();
    3426         // console.timeEnd("implicit plot");
    3427
    3428         this.dataX = ret[0];
    3429         this.dataY = ret[1];
    3430     };
    3431
    3432     c.elType = 'implicitcurve';
    3433
    3434     return c;
    3435 };
    3436
    3437 JXG.registerElement("implicitcurve", JXG.createImplicitCurve);
    3438
    3439
    3440 export default JXG.Curve;
    3441
    3442 // export default {
    3443 //     Curve: JXG.Curve,
    3444 //     createCardinalSpline: JXG.createCardinalSpline,
    3445 //     createCurve: JXG.createCurve,
    3446 //     createCurveDifference: JXG.createCurveDifference,
    3447 //     createCurveIntersection: JXG.createCurveIntersection,
    3448 //     createCurveUnion: JXG.createCurveUnion,
    3449 //     createDerivative: JXG.createDerivative,
    3450 //     createFunctiongraph: JXG.createFunctiongraph,
    3451 //     createMetapostSpline: JXG.createMetapostSpline,
    3452 //     createPlot: JXG.createFunctiongraph,
    3453 //     createSpline: JXG.createSpline,
    3454 //     createRiemannsum: JXG.createRiemannsum,
    3455 //     createStepfunction: JXG.createStepfunction,
    3456 //     createTracecurve: JXG.createTracecurve
    3457 // };
    3458
    3459 // const Curve = JXG.Curve;
    3460 // export { Curve as default, Curve};
    3461