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