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