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