      1 /*
      2     Copyright 2008-2011,
      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: you can redistribute it and/or modify
     13     it under the terms of the GNU Lesser General Public License as published by
     14     the Free Software Foundation, either version 3 of the License, or
     15     (at your option) any later version.
     16
     17     JSXGraph is distributed in the hope that it will be useful,
     18     but WITHOUT ANY WARRANTY; without even the implied warranty of
     19     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     20     GNU Lesser General Public License for more details.
     21
     22     You should have received a copy of the GNU Lesser General Public License
     23     along with JSXGraph.  If not, see <http://www.gnu.org/licenses/>.
     24 */
     25
     26 /**
     27  * @fileoverview In this file the geometry element Curve is defined.
     28  */
     29
     30 /**
     31  * Curves are the common object for function graphs, parametric curves, polar curves, and data plots.
     32  * @class Creates a new curve object. Do not use this constructor to create a curve. Use {@link JXG.Board#create} with
     33  * type {@link Curve}, or {@link Functiongraph} instead.
     34  * @augments JXG.GeometryElement
     35  * @param {string|JXG.Board} board The board the new curve is drawn on.
     36  * @param {Array} parents defining terms An array with the functon terms or the data points of the curve.
     37  * @param {Object} attributes Defines the visual appearance of the curve.
     38  * @see JXG.Board#generateName
     39  * @see JXG.Board#addCurve
     40   */
     41 JXG.Curve = function (board, parents, attributes) {
     42     this.constructor(board, attributes, JXG.OBJECT_TYPE_CURVE, JXG.OBJECT_CLASS_CURVE);
     43
     44     this.points = [];
     45     /**
     46      * Number of points on curves. This value changes
     47      * between numberPointsLow and numberPointsHigh.
     48      * It is set in {@link JXG.Curve#updateCurve}.
     49      */
     50     this.numberPoints = this.visProp.numberpointshigh;
     51
     52     this.dataX = null;
     53     this.dataY = null;
     54
     55     if (parents[0]!=null) {
     56         this.varname = parents[0];
     57     } else {
     58         this.varname = 'x';
     59     }
     60     this.xterm = parents[1];  // function graphs: "x"
     61     this.yterm = parents[2];  // function graphs: e.g. "x^2"
     62     this.generateTerm(this.varname,this.xterm,this.yterm,parents[3],parents[4]);  // Converts GEONExT syntax into JavaScript syntax
     63     this.updateCurve();                        // First evaluation of the curve
     64
     65     this.id = this.board.setId(this,'G');
     66     this.board.renderer.drawCurve(this);
     67
     68     this.board.finalizeAdding(this);
     69
     70     this.createGradient();
     71     this.elType = 'curve';
     72     this.createLabel();
     73
     74     if (typeof this.xterm=='string') {
     75         this.notifyParents(this.xterm);
     76     }
     77     if (typeof this.yterm=='string') {
     78         this.notifyParents(this.yterm);
     79     }
     80 };
     81 JXG.Curve.prototype = new JXG.GeometryElement;
     82
     83
     84 JXG.extend(JXG.Curve.prototype, /** @lends JXG.Curve.prototype */ {
     85
     86     /**
     87      * Gives the default value of the left bound for the curve.
     88      * May be overwritten in {@link JXG.Curve#generateTerm}.
     89      * @returns {Number} Left bound for the curve.
     90      */
     91     minX: function () {
     92         if (this.visProp.curvetype=='polar') {
     93             return 0.0;
     94         } else {
     95             var leftCoords = new JXG.Coords(JXG.COORDS_BY_SCREEN, [0, 0], this.board);
     96             return leftCoords.usrCoords[1];
     97         }
     98     },
     99
    100     /**
    101      * Gives the default value of the right bound for the curve.
    102      * May be overwritten in {@link JXG.Curve#generateTerm}.
    103      * @returns {Number} Right bound for the curve.
    104      */
    105     maxX: function () {
    106         var rightCoords;
    107         if (this.visProp.curvetype=='polar') {
    108             return 2.0*Math.PI;
    109         } else {
    110             rightCoords = new JXG.Coords(JXG.COORDS_BY_SCREEN, [this.board.canvasWidth, 0], this.board);
    111             return rightCoords.usrCoords[1];
    112         }
    113     },
    114
    115     /**
    116      * Treat the curve as curve with homogeneous coordinates
    117      * @param {Number} t A number between 0.0 and 1.0.
    118      * @return {Number} Always 1.0
    119      */
    120     Z: function (t) {
    121         return 1.0;
    122     },
    123
    124     /**
    125      * Checks whether (x,y) is near the curve.
    126      * @param {Number} x Coordinate in x direction, screen coordinates.
    127      * @param {Number} y Coordinate in y direction, screen coordinates.
    128      * @return {Boolean} True if (x,y) is near the curve, False otherwise.
    129      */
    130     hasPoint: function (x,y) {
    131         var t, dist = Infinity,
    132             c, trans, i, j, tX, tY,
    133             xi, xi1, yi, yi1,
    134             lbda, x0, y0, x1, y1, xy, den,
    135             steps = this.visProp.numberpointslow,
    136             d = (this.maxX()-this.minX())/steps,
    137             prec = this.board.options.precision.hasPoint/this.board.unitX,
    138             checkPoint, len,
    139             suspendUpdate = true;
    140
    141         prec = prec*prec;
    142         checkPoint = new JXG.Coords(JXG.COORDS_BY_SCREEN, [x,y], this.board);
    143         x = checkPoint.usrCoords[1];
    144         y = checkPoint.usrCoords[2];
    145         if (this.visProp.curvetype=='parameter' || this.visProp.curvetype=='polar' || this.visProp.curvetype=='functiongraph') {
    146             // Brute fore search for a point on the curve close to the mouse pointer
    147             len = this.transformations.length;
    148             for (i=0,t=this.minX(); i<steps; i++) {
    149                 tX = this.X(t,suspendUpdate);
    150                 tY = this.Y(t,suspendUpdate);
    151                 for (j=0; j<len; j++) {
    152                     trans = this.transformations[j];
    153                     trans.update();
    154                     c = JXG.Math.matVecMult(trans.matrix,[1,tX,tY]);
    155                     tX = c[1];
    156                     tY = c[2];
    157                 }
    158                 dist = (x-tX)*(x-tX)+(y-tY)*(y-tY);
    159                 if (dist<prec) { return true; }
    160                 t+=d;
    161             }
    162         } else if (this.visProp.curvetype == 'plot') {
    163             len = this.numberPoints; // Rough search quality
    164             for (i=0;i<len-1;i++) {
    165                 xi  = this.X(i);
    166                 xi1 = this.X(i+1);
    167                 yi  = this.Y(i);
    168                 yi1 = this.Y(i+1);
    169                 for (j=0; j<this.transformations.length; j++) {
    170                     trans = this.transformations[j];
    171                     trans.update();
    172                     c = JXG.Math.matVecMult(trans.matrix,[1,xi,yi]);
    173                     xi = c[1];
    174                     yi = c[2];
    175                     c = JXG.Math.matVecMult(trans.matrix,[1,xi1,yi1]);
    176                     xi1 = c[1];
    177                     yi1 = c[2];
    178                 }
    179
    180                 x1  = xi1 - xi;
    181                 y1  = yi1 - yi;
    182
    183                 x0  = x - xi;
    184                 y0  = y - yi;
    185                 den = x1*x1+y1*y1;
    186
    187                 if (den>=JXG.Math.eps) {
    188                     xy = x0*x1+y0*y1;
    189                     lbda = xy/den;
    190                     dist = x0*x0+y0*y0 - lbda*xy;
    191                 } else {
    192                     lbda = 0.0;
    193                     dist = x0*x0+y0*y0;
    194                 }
    195                 if (lbda>=0.0 && lbda<=1.0 && dist<prec) {
    196                     return true;
    197                 }
    198             }
    199             return false;
    200         }
    201         return (dist<prec);
    202     },
    203
    204     /**
    205      * Allocate points in the Coords array this.points
    206      */
    207     allocatePoints: function () {
    208         var i, len;
    209
    210         len = this.numberPoints;
    211
    212         if (this.points.length < this.numberPoints) {
    213             for (i = this.points.length; i < len; i++) {
    214                 this.points[i] = new JXG.Coords(JXG.COORDS_BY_USER, [0,0], this.board);
    215             }
    216         }
    217     },
    218
    219     /**
    220      * Computes for equidistant points on the x-axis the values of the function
    221      * @returns {JXG.Curve} Reference to the curve object.
    222      * @see JXG.Curve#updateCurve
    223      */
    224     update: function () {
    225         if (this.needsUpdate && !this.board.needsFullUpdate && this.Y && this.Y.toJS) {
    226             if (this.Y.toJS() == this.YtoJS) {
    227                 return this;
    228             }
    229
    230             this.YtoJS = this.Y.toJS();
    231         }
    232         if (this.needsUpdate) {
    233             if (this.visProp.trace) {
    234                 this.cloneToBackground(true);
    235             }
    236             this.updateCurve();
    237         }
    238         return this;
    239     },
    240
    241     /**
    242      * Updates the visual contents of the curve.
    243      * @returns {JXG.Curve} Reference to the curve object.
    244      */
    245     updateRenderer: function () {
    246         if (this.needsUpdate) {
    247             this.board.renderer.updateCurve(this);
    248             this.needsUpdate = false;
    249
    250             // Update the label if visible.
    251             if(this.hasLabel && this.label.content.visProp.visible) {
    252                 this.label.content.update();
    253                 this.board.renderer.updateText(this.label.content);
    254             }
    255         }
    256         return this;
    257     },
    258
    259     /**
    260      * For dynamic dataplots updateCurve can be used to compute new entries
    261      * for the arrays {@link JXG.Curve#dataX} and {@link JXG.Curve#dataY}. It
    262      * is used in {@link JXG.Curve#updateCurve}. Default is an empty method, can
    263      * be overwritten by the user.
    264      */
    265     updateDataArray: function () {
    266         // this used to return this, but we shouldn't rely on the user to implement it.
    267     },
    268
    269     /**
    270      * Computes for equidistant points on the x-axis the values
    271      * of the function.
    272      * If the mousemove event triggers this update, we use only few
    273      * points. Otherwise, e.g. on mouseup, many points are used.
    274      * @see JXG.Curve#update
    275      * @returns {JXG.Curve} Reference to the curve object.
    276      */
    277     updateCurve: function () {
    278         var len, mi, ma, x, y, i,
    279             suspendUpdate = false;
    280
    281         this.updateDataArray();
    282         mi = this.minX();
    283         ma = this.maxX();
    284
    285         // Discrete data points
    286         if (this.dataX != null) { // x-coordinates are in an array
    287             this.numberPoints = this.dataX.length;
    288             len = this.numberPoints;
    289             this.allocatePoints();  // It is possible, that the array length has increased.
    290             for (i=0; i<len; i++) {
    291                 x = i;
    292                 if (this.dataY!=null) { // y-coordinates are in an array
    293                     y = i;
    294                 } else {
    295                     y = this.X(x); // discrete x data, continuous y data
    296                 }
    297                 this.points[i].setCoordinates(JXG.COORDS_BY_USER, [this.X(x,suspendUpdate),this.Y(y,suspendUpdate)], false); // The last parameter prevents rounding in usr2screen().
    298                 this.updateTransform(this.points[i]);
    299                 suspendUpdate = true;
    300             }
    301         } else { // continuous x data
    302             if (this.visProp.doadvancedplot) {
    303                 this.updateParametricCurve(mi, ma, len);
    304             } else {
    305                 if (this.board.updateQuality==this.board.BOARD_QUALITY_HIGH) {
    306                     this.numberPoints = this.visProp.numberpointshigh;
    307                 } else {
    308                     this.numberPoints = this.visProp.numberpointslow;
    309                 }
    310                 this.allocatePoints();  // It is possible, that the array length has increased.
    311                 this.updateParametricCurveNaive(mi, ma, this.numberPoints);
    312             }
    313         }
    314
    315         return this;
    316     },
    317
    318     /**
    319      * Updates the data points of a parametric curve. This version is used if {@link JXG.Curve#visProp.doadvancedplot} is <tt>false</tt>.
    320      * @param {Number} mi Left bound of curve
    321      * @param {Number} ma Right bound of curve
    322      * @param {Number} len Number of data points
    323      * @returns {JXG.Curve} Reference to the curve object.
    324      */
    325     updateParametricCurveNaive: function(mi, ma, len) {
    326         var i, t,
    327             suspendUpdate = false,
    328             stepSize = (ma-mi)/len;
    329
    330         for (i=0; i<len; i++) {
    331             t = mi+i*stepSize;
    332             this.points[i].setCoordinates(JXG.COORDS_BY_USER, [this.X(t,suspendUpdate),this.Y(t,suspendUpdate)], false); // The last parameter prevents rounding in usr2screen().
    333             this.updateTransform(this.points[i]);
    334             suspendUpdate = true;
    335         }
    336         return this;
    337     },
    338
    339     /**
    340      * Updates the data points of a parametric curve. This version is used if {@link JXG.Curve#visProp.doadvancedplot} is <tt>true</tt>.
    341      * @param {Number} mi Left bound of curve
    342      * @param {Number} ma Right bound of curve
    343      * @param {Number} len Number of data points
    344      * @returns {JXG.Curve} Reference to the curve object.
    345      */
    346     updateParametricCurve: function(mi, ma) {
    347         var i, t, t0,
    348             suspendUpdate = false,
    349             po = new JXG.Coords(JXG.COORDS_BY_USER, [0,0], this.board),
    350             x, y, x0, y0, top, depth,
    351             MAX_DEPTH,
    352             MAX_XDIST,
    353             MAX_YDIST,
    354             dyadicStack = [],
    355             depthStack = [],
    356             pointStack = [],
    357             divisors = [],
    358             distOK = false,
    359             j = 0,
    360             d,
    361             distFromLine = function(p1, p2, p0) {
    362                 var x0 = p0[1] - p1[1],
    363                     y0 = p0[2] - p1[2],
    364                     x1 = p2[0] - p1[1],
    365                     y1 = p2[1] - p1[2],
    366                     den = x1 * x1 + y1 * y1,
    367                     lbda, d;
    368
    369                 if (den >= JXG.Math.eps) {
    370                     lbda = (x0 * x1 + y0 * y1) / den;
    371                     if (lbda>0.0) {
    372                         if (lbda<=1.0) {
    373                             x0 -= lbda*x1;
    374                             y0 -= lbda*y1;
    375
    376                         } else { // lbda = 1.0;
    377                             x0 -= x1;
    378                             y0 -= y1;
    379                         }
    380                     }
    381                 }
    382                 d = x0*x0 + y0*y0;
    383                 return Math.sqrt(d);
    384             };
    385
    386         if (this.board.updateQuality == this.board.BOARD_QUALITY_LOW) {
    387             MAX_DEPTH = 15;
    388             MAX_XDIST = 10;
    389             MAX_YDIST = 10;
    390         } else {
    391             MAX_DEPTH = 21;
    392             MAX_XDIST = 0.7;
    393             MAX_YDIST = 0.7;
    394         }
    395
    396         divisors[0] = ma-mi;
    397         for (i = 1; i < MAX_DEPTH; i++) {
    398             divisors[i] = divisors[i-1]*0.5;
    399         }
    400
    401         i = 1;
    402         dyadicStack[0] = 1;
    403         depthStack[0] = 0;
    404
    405         t = mi;
    406         po.setCoordinates(JXG.COORDS_BY_USER, [this.X(t,suspendUpdate),this.Y(t,suspendUpdate)], false);
    407
    408         // Now, there was a first call to the functions defining the curve.
    409         // Defining elements like sliders have been evaluated.
    410         // Therefore, we can set suspendUpdate to false, so that these defining elements
    411         // need not be evaluated anymore for the rest of the plotting.
    412         suspendUpdate = true;
    413         x0 = po.scrCoords[1];
    414         y0 = po.scrCoords[2];
    415         t0 = t;
    416
    417         t = ma;
    418         po.setCoordinates(JXG.COORDS_BY_USER, [this.X(t,suspendUpdate),this.Y(t,suspendUpdate)], false);
    419         x = po.scrCoords[1];
    420         y = po.scrCoords[2];
    421
    422         pointStack[0] = [x,y];
    423
    424         top = 1;
    425         depth = 0;
    426
    427         this.points = [];
    428         this.points[j++] = new JXG.Coords(JXG.COORDS_BY_SCREEN, [x0, y0], this.board);
    429
    430         do {
    431             distOK = this.isDistOK(x-x0, y-y0, MAX_XDIST, MAX_YDIST) || this.isSegmentOutside(x0,y0,x,y);
    432             while (depth < MAX_DEPTH
    433                    && (!distOK || depth < 3)
    434                    && (this.isSegmentDefined(x0, y0, x, y) || depth <= 7) ) {
    435
    436                 dyadicStack[top] = i;
    437                 depthStack[top] = depth;
    438                 pointStack[top] = [x,y];
    439                 top++;
    440
    441                 i = 2*i-1;
    442                 depth++;                   // Here, depth is increased and may reach MAX_DEPTH
    443                 t = mi+i*divisors[depth];  // In that case, t is undefined and we will see a jump
    444                                            // in the curve.
    445
    446                 po.setCoordinates(JXG.COORDS_BY_USER, [this.X(t,suspendUpdate),this.Y(t,suspendUpdate)], false);
    447                 x = po.scrCoords[1];
    448                 y = po.scrCoords[2];
    449                 distOK = this.isDistOK(x-x0, y-y0, MAX_XDIST, MAX_YDIST) || this.isSegmentOutside(x0,y0,x,y);
    450             }
    451
    452             if (j > 1) {
    453                 d = distFromLine(this.points[j-2].scrCoords, [x,y], this.points[j-1].scrCoords);
    454                 if (d<0.015) {
    455                     j--;
    456                 }
    457             }
    458             this.points[j] = new JXG.Coords(JXG.COORDS_BY_SCREEN, [x, y], this.board);
    459             this.updateTransform(this.points[j]);
    460             j++;
    461
    462             x0 = x;
    463             y0 = y;
    464             t0 = t;
    465
    466             top--;
    467             x = pointStack[top][0];
    468             y = pointStack[top][1];
    469             depth = depthStack[top]+1;
    470             i = dyadicStack[top]*2;
    471
    472         } while (top > 0 && j<500000);
    473         this.numberPoints = this.points.length;
    474
    475         return this;
    476     },
    477
    478     /**
    479      * Determines if the segment defined by the two points <tt>(x0, y0)</tt> and <tt>(x1, y1)</tt> is
    480      * outside the viewport of the board. All parameters have to be given in screen coordinates.
    481      * @param {Number} x0
    482      * @param {Number} y0
    483      * @param {Number} x1
    484      * @param {Number} y1
    485      * @returns {Boolean} <tt>true</tt> if the given segment is outside the visible area.
    486      */
    487     isSegmentOutside: function (x0, y0, x1, y1) {
    488         return (y0 < 0 && y1 < 0) || (y0 > this.board.canvasHeight && y1 > this.board.canvasHeight) ||
    489                (x0 < 0 && x1 < 0) || (x0 > this.board.canvasWidth && x1 > this.board.canvasWidth);
    490     },
    491
    492     /**
    493      * Compares the absolute value of <tt>dx</tt> with <tt>MAXX</tt> and the absolute value of <tt>dy</tt>
    494      * with <tt>MAXY</tt>.
    495      * @param {Number} dx
    496      * @param {Number} dy
    497      * @param {Number} MAXX
    498      * @param {Number} MAXY
    499      * @returns {Boolean} <tt>true</tt>, if <tt>|dx| < MAXX</tt> and <tt>|dy| < MAXY</tt>.
    500      */
    501     isDistOK: function (dx, dy, MAXX, MAXY) {
    502         return (Math.abs(dx) < MAXX && Math.abs(dy) < MAXY) && !isNaN(dx+dy);
    503     },
    504
    505     isSegmentDefined: function (x0,y0,x1,y1) {
    506         return !(isNaN(x0 + y0) && isNaN(x1 + y1));
    507     },
    508
    509     /**
    510      * Applies the transformations of the curve to the given point <tt>p</tt>.
    511      * @param {JXG.Point} p
    512      * @returns {JXG.Point} The given point.
    513      */
    514     updateTransform: function (p) {
    515         var t, c, i,
    516             len = this.transformations.length;
    517
    518         for (i = 0; i < len; i++) {
    519             t = this.transformations[i];
    520             t.update();
    521             c = JXG.Math.matVecMult(t.matrix, p.usrCoords);
    522             p.setCoordinates(JXG.COORDS_BY_USER, [c[1], c[2]]);
    523         }
    524         return p;
    525     },
    526
    527     /**
    528      * Add transformations to this curve.
    529      * @param {JXG.Transform|Array} transform Either one {@link JXG.Transform} or an array of {@link JXG.Transform}s.
    530      * @returns {JXG.Curve} Reference to the curve object.
    531      */
    532     addTransform: function (transform) {
    533         var i,
    534             list = JXG.isArray(transform) ? transform : [transform],
    535             len = list.length;
    536
    537         for (i = 0; i < len; i++) {
    538             this.transformations.push(list[i]);
    539         }
    540
    541         return this;
    542     },
    543
    544     /**
    545      * Translates the object by <tt>(x, y)</tt>.
    546      * @param {null} method ignored
    547      * @param {Number} x
    548      * @param {Number} y
    549      * @returns {JXG.Curve} Reference to the curve object.
    550      */
    551     setPosition: function (method, x, y) {
    552         var t = this.board.create('transform',[x,y],{type:'translate'});
    553
    554         if (this.transformations.length > 0 && this.transformations[this.transformations.length-1].isNumericMatrix) {
    555             this.transformations[this.transformations.length-1].melt(t);
    556         } else {
    557             this.addTransform(t);
    558         }
    559
    560         return this;
    561     },
    562
    563     /**
    564      * Converts the GEONExT syntax of the defining function term into JavaScript.
    565      * New methods X() and Y() for the Curve object are generated, further
    566      * new methods for minX() and maxX().
    567      * @see JXG.GeonextParser#geonext2JS.
    568      */
    569     generateTerm: function (varname, xterm, yterm, mi, ma) {
    570         var fx, fy;
    571
    572         // Generate the methods X() and Y()
    573         if (JXG.isArray(xterm)) {
    574             this.dataX = xterm;
    575             this.X = function(t) {
    576                 var i = parseInt(Math.floor(t)), f1, f2;
    577                 if (t<0) i = 0;
    578                 else if (t>this.dataX.length-2) i = this.dataX.length-2;
    579                 if (i==t) {
    580                     return this.dataX[i];
    581                 } else {
    582                     f1 = this.dataX[i];
    583                     f2 = this.dataX[i+1];
    584                     return f1+(f2-f1)*(t-i);
    585                 }
    586             };
    587             this.visProp.curvetype = 'plot';
    588             this.numberPoints = this.dataX.length;
    589         } else {
    590             this.X = JXG.createFunction(xterm, this.board, varname);
    591             if (JXG.isString(xterm)) {
    592                 this.visProp.curvetype = 'functiongraph';
    593             } else if (JXG.isFunction(xterm) || JXG.isNumber(xterm)) {
    594                 this.visProp.curvetype = 'parameter';
    595             }
    596         }
    597
    598         if (JXG.isArray(yterm)) {
    599             this.dataY = yterm;
    600             this.Y = function(t) {
    601                 var i = parseInt(Math.floor(t)), f1, f2;
    602                 if (t<0) i = 0;
    603                 else if (t>this.dataY.length-2) i = this.dataY.length-2;
    604                 if (i==t) {
    605                     if (JXG.isFunction(this.dataY[i])) {
    606                         return this.dataY[i]();
    607                     } else {
    608                         return this.dataY[i];
    609                     }
    610                 } else {
    611                     if (JXG.isFunction(this.dataY[i])) {
    612                         f1 = this.dataY[i]();
    613                     } else {
    614                         f1 = this.dataY[i];
    615                     }
    616                     if (JXG.isFunction(this.dataY[i+1])) {
    617                         f2 = this.dataY[i+1]();
    618                     } else {
    619                         f2 = this.dataY[i+1];
    620                     }
    621                     return f1+(f2-f1)*(t-i);
    622                 }
    623             };
    624         } else {
    625             this.Y = JXG.createFunction(yterm,this.board,varname);
    626         }
    627
    628         // polar form
    629         if (JXG.isFunction(xterm) && JXG.isArray(yterm)) {
    630             // Xoffset, Yoffset
    631             fx = JXG.createFunction(yterm[0],this.board,'');
    632             fy = JXG.createFunction(yterm[1],this.board,'');
    633             this.X = function(phi){return (xterm)(phi)*Math.cos(phi)+fx();};
    634             this.Y = function(phi){return (xterm)(phi)*Math.sin(phi)+fy();};
    635             this.visProp.curvetype = 'polar';
    636         }
    637
    638         // Set the bounds
    639         // lower bound
    640         if (mi!=null) this.minX = JXG.createFunction(mi,this.board,'');
    641         if (ma!=null) this.maxX = JXG.createFunction(ma,this.board,'');
    642     },
    643
    644     /**
    645      * Finds dependencies in a given term and notifies the parents by adding the
    646      * dependent object to the found objects child elements.
    647      * @param {String} contentStr String containing dependencies for the given object.
    648      */
    649     notifyParents: function (contentStr) {
    650         JXG.GeonextParser.findDependencies(this,contentStr, this.board);
    651     },
    652
    653     // documented in geometry element
    654     getLabelAnchor: function() {
    655         var c, x, y,
    656             ax = 0.05*this.board.canvasWidth,
    657             ay = 0.05*this.board.canvasHeight,
    658             bx = 0.95*this.board.canvasWidth,
    659             by = 0.95*this.board.canvasHeight;
    660
    661         switch (this.visProp.label.position) {
    662             case 'ulft':
    663                 x = ax; y = ay; break;
    664             case 'llft':
    665                 x = ax; y = by; break;
    666             case 'rt':
    667                 x = bx; y = 0.5*by; break;
    668             case 'lrt':
    669                 x = bx; y = by; break;
    670             case 'urt':
    671                 x = bx; y = ay; break;
    672             case 'top':
    673                 x = 0.5*bx; y = ay; break;
    674             case 'bot':
    675                 x = 0.5*bx; y = by; break;
    676             case 'lft':
    677             default:
    678                 x = ax; y = 0.5*by; break;
    679         }
    680         c = new JXG.Coords(JXG.COORDS_BY_SCREEN, [x, y], this.board);
    681         return JXG.Math.Geometry.projectCoordsToCurve(c.usrCoords[1],c.usrCoords[2], 0.0, this, this.board)[0];
    682     },
    683
    684     // documented in geometry element
    685     cloneToBackground: function () {
    686         var copy = {}, er;
    687
    688         copy.id = this.id + 'T' + this.numTraces;
    689         copy.elementClass = JXG.OBJECT_CLASS_CURVE;
    690         this.numTraces++;
    691
    692         copy.points = this.points.slice(0);
    693         copy.numberPoints = this.numberPoints;
    694         copy.board = this.board;
    695         copy.visProp = JXG.deepCopy(this.visProp, this.visProp.traceattributes, true);
    696         copy.visProp.layer = this.board.options.layer.trace;
    697         copy.visProp.curvetype = this.visProp.curvetype;
    698
    699         JXG.clearVisPropOld(copy);
    700
    701         er = this.board.renderer.enhancedRendering;
    702         this.board.renderer.enhancedRendering = true;
    703         this.board.renderer.drawCurve(copy);
    704         this.board.renderer.enhancedRendering = er;
    705         this.traces[copy.id] = copy.rendNode;
    706
    707         return this;
    708     },
    709
    710     // already documented in GeometryElement
    711     bounds: function () {
    712         var steps = this.visProp.numberpointslow,
    713             d = (this.maxX()-this.minX())/steps,
    714             i, j, trans, t, c, len, tX, tY, box = [this.minX(), 0, this.maxX(), 0];
    715
    716         if (this.visProp.curvetype=='parameter' || this.visProp.curvetype=='polar' || this.visProp.curvetype=='functiongraph') {
    717             len = this.transformations.length;
    718             t = this.minX();
    719             for (i = 0; i < steps; i++) {
    720                 tX = this.X(t, true);
    721                 tY = this.Y(t, true);
    722                 for (j = 0; j < len; j++) {
    723                     trans = this.transformations[j];
    724                     trans.update();
    725                     c = JXG.Math.matVecMult(trans.matrix,[1,tX,tY]);
    726                     tX = c[1];
    727                     tY = c[2];
    728                 }
    729                 if (box[1] < tY) {
    730                     box[1] = tY;
    731                 }
    732                 if (box[3] > tY) {
    733                     box[3] = tY;
    734                 }
    735                 t+=d;
    736             }
    737         } else if (this.visProp.curvetype == 'plot') {
    738             len = this.numberPoints;
    739             for (i = 0; i < len; i++) {
    740                 tY = this.Y(i);
    741                 if (box[1] < tY) {
    742                     box[1] = tY;
    743                 }
    744                 if (box[3] > tY) {
    745                     box[3] = tY;
    746                 }
    747             }
    748         }
    749         return box;
    750     }
    751 });
    752
    753
    754 /**
    755  * @class This element is used to provide a constructor for curve, which is just a wrapper for element {@link Curve}.
    756  * 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].
    757  * <p>
    758  * The following types of curves can be plotted:
    759  * <ul>
    760  *  <li> parametric curves: t mapsto (x(t),y(t)), where x() and y() are univariate functions.
    761  *  <li> polar curves: curves commonly written with polar equations like spirals and cardioids.
    762  *  <li> data plots: plot linbe segments through a given list of coordinates.
    763  * </ul>
    764  * @pseudo
    765  * @description
    766  * @name Curve
    767  * @augments JXG.Curve
    768  * @constructor
    769  * @type JXG.Curve
    770  *
    771  * @param {function,number_function,number_function,number_function,number} x,y,a_,b_ Parent elements for Parametric Curves.
    772  *                     <p>
    773  *                     x describes the x-coordinate of the curve. It may be a function term in one variable, e.g. x(t).
    774  *                     In case of x being of type number, x(t) is set to  a constant function.
    775  *                     this function at the values of the array.
    776  *                     </p>
    777  *                     <p>
    778  *                     y describes the y-coordinate of the curve. In case of a number, y(t) is set to the constant function
    779  *                     returning this number.
    780  *                     </p>
    781  *                     <p>
    782  *                     Further parameters are an optional number or function for the left interval border a,
    783  *                     and an optional number or function for the right interval border b.
    784  *                     </p>
    785  *                     <p>
    786  *                     Default values are a=-10 and b=10.
    787  *                     </p>
    788  * @param {array_array,function,number} x,y Parent elements for Data Plots.
    789  *                     <p>
    790  *                     x and y are arrays contining the x and y coordinates of the data points which are connected by
    791  *                     line segments. The individual entries of x and y may also be functions.
    792  *                     In case of x being an array the curve type is data plot, regardless of the second parameter and
    793  *                     if additionally the second parameter y is a function term the data plot evaluates.
    794  *                     </p>
    795  * @param {function_array,function,number_function,number_function,number} r,offset_,a_,b_ Parent elements for Polar Curves.
    796  *                     <p>
    797  *                     The first parameter is a function term r(phi) describing the polar curve.
    798  *                     </p>
    799  *                     <p>
    800  *                     The second parameter is the offset of the curve. It has to be
    801  *                     an array containing numbers or functions describing the offset. Default value is the origin [0,0].
    802  *                     </p>
    803  *                     <p>
    804  *                     Further parameters are an optional number or function for the left interval border a,
    805  *                     and an optional number or function for the right interval border b.
    806  *                     </p>
    807  *                     <p>
    808  *                     Default values are a=-10 and b=10.
    809  *                     </p>
    810  * @see JXG.Curve
    811  * @example
    812  * // Parametric curve
    813  * // Create a curve of the form (t-sin(t), 1-cos(t), i.e.
    814  * // the cycloid curve.
    815  *   var graph = board.create('curve',
    816  *                        [function(t){ return t-Math.sin(t);},
    817  *                         function(t){ return 1-Math.cos(t);},
    818  *                         0, 2*Math.PI]
    819  *                     );
    820  * </pre><div id="af9f818b-f3b6-4c4d-8c4c-e4a4078b726d" style="width: 300px; height: 300px;"></div>
    821  * <script type="text/javascript">
    822  *   var c1_board = JXG.JSXGraph.initBoard('af9f818b-f3b6-4c4d-8c4c-e4a4078b726d', {boundingbox: [-1, 5, 7, -1], axis: true, showcopyright: false, shownavigation: false});
    823  *   var graph1 = c1_board.create('curve', [function(t){ return t-Math.sin(t);},function(t){ return 1-Math.cos(t);},0, 2*Math.PI]);
    824  * </script><pre>
    825  * @example
    826  * // Data plots
    827  * // Connect a set of points given by coordinates with dashed line segments.
    828  * // The x- and y-coordinates of the points are given in two separate
    829  * // arrays.
    830  *   var x = [0,1,2,3,4,5,6,7,8,9];
    831  *   var y = [9.2,1.3,7.2,-1.2,4.0,5.3,0.2,6.5,1.1,0.0];
    832  *   var graph = board.create('curve', [x,y], {dash:2});
    833  * </pre><div id="7dcbb00e-b6ff-481d-b4a8-887f5d8c6a83" style="width: 300px; height: 300px;"></div>
    834  * <script type="text/javascript">
    835  *   var c3_board = JXG.JSXGraph.initBoard('7dcbb00e-b6ff-481d-b4a8-887f5d8c6a83', {boundingbox: [-1,10,10,-1], axis: true, showcopyright: false, shownavigation: false});
    836  *   var x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
    837  *   var y = [9.2, 1.3, 7.2, -1.2, 4.0, 5.3, 0.2, 6.5, 1.1, 0.0];
    838  *   var graph3 = c3_board.create('curve', [x,y], {dash:2});
    839  * </script><pre>
    840  * @example
    841  * // Polar plot
    842  * // Create a curve with the equation r(phi)= a*(1+phi), i.e.
    843  * // a cardioid.
    844  *   var a = board.create('slider',[[0,2],[2,2],[0,1,2]]);
    845  *   var graph = board.create('curve',
    846  *                        [function(phi){ return a.Value()*(1-Math.cos(phi));},
    847  *                         [1,0],
    848  *                         0, 2*Math.PI]
    849  *                     );
    850  * </pre><div id="d0bc7a2a-8124-45ca-a6e7-142321a8f8c2" style="width: 300px; height: 300px;"></div>
    851  * <script type="text/javascript">
    852  *   var c2_board = JXG.JSXGraph.initBoard('d0bc7a2a-8124-45ca-a6e7-142321a8f8c2', {boundingbox: [-3,3,3,-3], axis: true, showcopyright: false, shownavigation: false});
    853  *   var a = c2_board.create('slider',[[0,2],[2,2],[0,1,2]]);
    854  *   var graph2 = c2_board.create('curve', [function(phi){ return a.Value()*(1-Math.cos(phi));}, [1,0], 0, 2*Math.PI]);
    855  * </script><pre>
    856  */
    857 JXG.createCurve = function(board, parents, attributes) {
    858     var attr = JXG.copyAttributes(attributes, board.options, 'curve');
    859     return new JXG.Curve(board, ['x'].concat(parents), attr);
    860 };
    861
    862 JXG.JSXGraph.registerElement('curve', JXG.createCurve);
    863
    864 /**
    865  * @class This element is used to provide a constructor for functiongraph, which is just a wrapper for element {@link Curve} with {@link JXG.Curve#X()}
    866  * set to x. The graph is drawn for x in the interval [a,b].
    867  * @pseudo
    868  * @description
    869  * @name Functiongraph
    870  * @augments JXG.Curve
    871  * @constructor
    872  * @type JXG.Curve
    873  * @param {function_number,function_number,function} f,a_,b_ Parent elements are a function term f(x) describing the function graph.
    874  *         <p>
    875  *         Further, an optional number or function for the left interval border a,
    876  *         and an optional number or function for the right interval border b.
    877  *         <p>
    878  *         Default values are a=-10 and b=10.
    879  * @see JXG.Curve
    880  * @example
    881  * // Create a function graph for f(x) = 0.5*x*x-2*x
    882  *   var graph = board.create('functiongraph',
    883  *                        [function(x){ return 0.5*x*x-2*x;}, -2, 4]
    884  *                     );
    885  * </pre><div id="efd432b5-23a3-4846-ac5b-b471e668b437" style="width: 300px; height: 300px;"></div>
    886  * <script type="text/javascript">
    887  *   var alex1_board = JXG.JSXGraph.initBoard('efd432b5-23a3-4846-ac5b-b471e668b437', {boundingbox: [-3, 7, 5, -3], axis: true, showcopyright: false, shownavigation: false});
    888  *   var graph = alex1_board.create('functiongraph', [function(x){ return 0.5*x*x-2*x;}, -2, 4]);
    889  * </script><pre>
    890  * @example
    891  * // Create a function graph for f(x) = 0.5*x*x-2*x with variable interval
    892  *   var s = board.create('slider',[[0,4],[3,4],[-2,4,5]]);
    893  *   var graph = board.create('functiongraph',
    894  *                        [function(x){ return 0.5*x*x-2*x;},
    895  *                         -2,
    896  *                         function(){return s.Value();}]
    897  *                     );
    898  * </pre><div id="4a203a84-bde5-4371-ad56-44619690bb50" style="width: 300px; height: 300px;"></div>
    899  * <script type="text/javascript">
    900  *   var alex2_board = JXG.JSXGraph.initBoard('4a203a84-bde5-4371-ad56-44619690bb50', {boundingbox: [-3, 7, 5, -3], axis: true, showcopyright: false, shownavigation: false});
    901  *   var s = alex2_board.create('slider',[[0,4],[3,4],[-2,4,5]]);
    902  *   var graph = alex2_board.create('functiongraph', [function(x){ return 0.5*x*x-2*x;}, -2, function(){return s.Value();}]);
    903  * </script><pre>
    904  */
    905 JXG.createFunctiongraph = function(board, parents, attributes) {
    906     var attr, par = ["x","x"].concat(parents);
    907
    908     attr = JXG.copyAttributes(attributes, board.options, 'curve');
    909     attr['curvetype'] = 'functiongraph';
    910     return new JXG.Curve(board, par, attr);
    911 };
    912
    913 JXG.JSXGraph.registerElement('functiongraph', JXG.createFunctiongraph);
    914 JXG.JSXGraph.registerElement('plot', JXG.createFunctiongraph);
    915
    916
    917 /**
    918  * TODO
    919  * Create a dynamic spline interpolated curve given by sample points p_1 to p_n.
    920  * @param {JXG.Board} board Reference to the board the spline is drawn on.
    921  * @param {Array} parents Array of points the spline interpolates
    922  * @param {Object} attributes Define color, width, ... of the spline
    923  * @type JXG.Curve
    924  * @return Returns reference to an object of type JXG.Curve.
    925  */
    926 JXG.createSpline = function(board, parents, attributes) {
    927     var F;
    928     F = function() {
    929         var D, x=[], y=[];
    930
    931         var fct = function (t,suspended) {
    932             var i, j;
    933
    934             if (!suspended) {
    935                 x = [];
    936                 y = [];
    937
    938                 // given as [x[], y[]]
    939                 if(parents.length == 2 && JXG.isArray(parents[0]) && JXG.isArray(parents[1]) && parents[0].length == parents[1].length) {
    940                     for(i=0; i<parents[0].length; i++) {
    941                         if(typeof parents[0][i] == 'function')
    942                             x.push(parents[0][i]());
    943                         else
    944                             x.push(parents[0][i]);
    945                         if(typeof parents[1][i] == 'function')
    946                             y.push(parents[1][i]());
    947                         else
    948                             y.push(parents[1][i]);
    949                     }
    950                 } else {
    951                     for(i=0; i<parents.length; i++) {
    952                         if(JXG.isPoint(parents[i])) {
    953                             //throw new Error("JSXGraph: JXG.createSpline: Parents has to be an array of JXG.Point.");
    954                             x.push(parents[i].X());
    955                             y.push(parents[i].Y());
    956                         } else if (JXG.isArray(parents[i]) && parents[i].length == 2) {     // given as [[x1,y1], [x2, y2], ...]
    957                             for(i=0; i<parents.length; i++) {
    958                                 if(typeof parents[i][0] == 'function')
    959                                     x.push(parents[i][0]());
    960                                 else
    961                                     x.push(parents[i][0]);
    962                                 if(typeof parents[i][1] == 'function')
    963                                     y.push(parents[i][1]());
    964                                 else
    965                                     y.push(parents[i][1]);
    966                             }
    967                         }
    968                     }
    969                 }
    970
    971                 // The array D has only to be calculated when the position of one or more sample point
    972                 // changes. otherwise D is always the same for all points on the spline.
    973                 D = JXG.Math.Numerics.splineDef(x, y);
    974             }
    975             return JXG.Math.Numerics.splineEval(t, x, y, D);
    976         };
    977         return fct;
    978     };
    979     return board.create('curve', ["x", F()], attributes);
    980 };
    981
    982 /**
    983  * Register the element type spline at JSXGraph
    984  * @private
    985  */
    986 JXG.JSXGraph.registerElement('spline', JXG.createSpline);
    987
    988 /**
    989  * @class This element is used to provide a constructor for Riemann sums, which is realized as a special curve.
    990  * @pseudo
    991  * @description
    992  * @name Riemannsum
    993  * @augments JXG.Curve
    994  * @constructor
    995  * @type JXG.Curve
    996  * @param {function_number,function_string,function_function,number_function,number} f,n,type_,a_,b_ Parent elements of Riemannsum are a
    997  *         function term f(x) describing the function graph which is filled by the Riemann rectangles.
    998  *         <p>
    999  *         n determines the number of rectangles, it is either a fixed number or a function.
    1000  *         <p>
    1001  *         type is a string or function returning one of the values:  'left', 'right', 'middle', 'lower', 'upper', or 'trapezodial'.
    1002  *         Default value is 'left'.
    1003  *         <p>
    1004  *         Further parameters are an optional number or function for the left interval border a,
    1005  *         and an optional number or function for the right interval border b.
    1006  *         <p>
    1007  *         Default values are a=-10 and b=10.
    1008  * @see JXG.Curve
    1009  * @example
    1010  * // Create Riemann sums for f(x) = 0.5*x*x-2*x.
    1011  *   var s = board.create('slider',[[0,4],[3,4],[0,4,10]],{snapWidth:1});
    1012  *   var f = function(x) { return 0.5*x*x-2*x; };
    1013  *   var r = board.create('riemannsum',
    1014  *               [f, function(){return s.Value();}, 'upper', -2, 5],
    1015  *               {fillOpacity:0.4}
    1016  *               );
    1017  *   var g = board.create('functiongraph',[f, -2, 5]);
    1018  * </pre><div id="940f40cc-2015-420d-9191-c5d83de988cf" style="width: 300px; height: 300px;"></div>
    1019  * <script type="text/javascript">
    1020  *   var rs1_board = JXG.JSXGraph.initBoard('940f40cc-2015-420d-9191-c5d83de988cf', {boundingbox: [-3, 7, 5, -3], axis: true, showcopyright: false, shownavigation: false});
    1021  *   var f = function(x) { return 0.5*x*x-2*x; };
    1022  *   var s = rs1_board.create('slider',[[0,4],[3,4],[0,4,10]],{snapWidth:1});
    1023  *   var r = rs1_board.create('riemannsum', [f, function(){return s.Value();}, 'upper', -2, 5], {fillOpacity:0.4});
    1024  *   var g = rs1_board.create('functiongraph', [f, -2, 5]);
    1025  * </script><pre>
    1026  */
    1027 JXG.createRiemannsum = function(board, parents, attributes) {
    1028     var n, type, f, par, c, attr;
    1029
    1030     attr = JXG.copyAttributes(attributes, board.options, 'riemannsum');
    1031     attr['curvetype'] = 'plot';
    1032
    1033     f = parents[0];
    1034     n = JXG.createFunction(parents[1],board,'');
    1035     if (n==null) {
    1036         throw new Error("JSXGraph: JXG.createRiemannsum: argument '2' n has to be number or function." +
    1037                         "\nPossible parent types: [function,n:number|function,type,start:number|function,end:number|function]");
    1038     }
    1039
    1040     type = JXG.createFunction(parents[2],board,'',false);
    1041     if (type==null) {
    1042         throw new Error("JSXGraph: JXG.createRiemannsum: argument 3 'type' has to be string or function." +
    1043                         "\nPossible parent types: [function,n:number|function,type,start:number|function,end:number|function]");
    1044     }
    1045
    1046     par = [[0], [0]].concat(parents.slice(3));
    1047
    1048     c = board.create('curve', par, attr);
    1049     c.updateDataArray = function() {
    1050         var u = JXG.Math.Numerics.riemann(f, n(), type(), this.minX(), this.maxX());
    1051         this.dataX = u[0];
    1052         this.dataY = u[1];
    1053     };
    1054
    1055     return c;
    1056 };
    1057
    1058 JXG.JSXGraph.registerElement('riemannsum', JXG.createRiemannsum);
    1059
    1060 /**
    1061  * @class This element is used to provide a constructor for travce curve (simple locus curve), which is realized as a special curve.
    1062  * @pseudo
    1063  * @description
    1064  * @name Tracecurve
    1065  * @augments JXG.Curve
    1066  * @constructor
    1067  * @type JXG.Curve
    1068  * @param {Point,Point} Parent elements of Tracecurve are a
    1069  *         glider point and a point whose locus is traced.
    1070  * @see JXG.Curve
    1071  * @example
    1072  * // Create trace curve.
    1073     var c1 = board.create('circle',[[0, 0], [2, 0]]),
    1074         p1 = board.create('point',[-3, 1]),
    1075         g1 = board.create('glider',[2, 1, c1]),
    1076         s1 = board.create('segment',[g1, p1]),
    1077         p2 = board.create('midpoint',[s1]),
    1078         curve = board.create('tracecurve', [g1, p2]);
    1079
    1080  * </pre><div id="5749fb7d-04fc-44d2-973e-45c1951e29ad" style="width: 300px; height: 300px;"></div>
    1081  * <script type="text/javascript">
    1082  *   var tc1_board = JXG.JSXGraph.initBoard('5749fb7d-04fc-44d2-973e-45c1951e29ad', {boundingbox: [-4, 4, 4, -4], axis: false, showcopyright: false, shownavigation: false});
    1083  *   var c1 = tc1_board.create('circle',[[0, 0], [2, 0]]),
    1084  *       p1 = tc1_board.create('point',[-3, 1]),
    1085  *       g1 = tc1_board.create('glider',[2, 1, c1]),
    1086  *       s1 = tc1_board.create('segment',[g1, p1]),
    1087  *       p2 = tc1_board.create('midpoint',[s1]),
    1088  *       curve = tc1_board.create('tracecurve', [g1, p2]);
    1089  * </script><pre>
    1090  */
    1091 JXG.createTracecurve = function(board, parents, attributes) {
    1092     var c, glider, tracepoint, attr;
    1093
    1094     if (parents.length!=2) {
    1095         throw new Error("JSXGraph: Can't create trace curve with given parent'" +
    1096                         "\nPossible parent types: [glider, point]");
    1097     }
    1098
    1099     glider = JXG.getRef(this.board, parents[0]);
    1100     tracepoint = JXG.getRef(this.board, parents[1]);
    1101
    1102     if (glider.type != JXG.OBJECT_TYPE_GLIDER || !JXG.isPoint(tracepoint)) {
    1103         throw new Error("JSXGraph: Can't create trace curve with parent types '" +
    1104                         (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." +
    1105                         "\nPossible parent types: [glider, point]");
    1106     }
    1107
    1108     attr = JXG.copyAttributes(attributes, board.options, 'tracecurve');
    1109     attr['curvetype'] = 'plot';
    1110
    1111     c = board.create('curve',[[0],[0]], attr);
    1112     c.updateDataArray = function(){
    1113         var i, step, t, el, pEl, x, y, v,
    1114             le = attr.numberpoints,
    1115             from,
    1116             savePos = glider.position,
    1117             slideObj = glider.slideObject,
    1118             mi = slideObj.minX(),
    1119             ma = slideObj.maxX(), savetrace;
    1120
    1121         step = (ma-mi)/le;                    // set step width
    1122         this.dataX = [];
    1123         this.dataY = [];
    1124         /*
    1125          * For gliders on circles and lines a closed curve is computed.
    1126          * For gliders on curves the curve is not closed.
    1127          */
    1128         if (slideObj.elementClass!=JXG.OBJECT_CLASS_CURVE) {
    1129             le++;
    1130         }
    1131         for (i=0; i<le; i++) {                    // Loop over all steps
    1132             t = mi + i*step;
    1133             x = slideObj.X(t)/slideObj.Z(t);
    1134             y = slideObj.Y(t)/slideObj.Z(t);
    1135             glider.setPositionDirectly(JXG.COORDS_BY_USER, x, y);    // Position the glider
    1136             from = false;
    1137             for (el in this.board.objects) {                         // Update all elements from the glider up to the trace element
    1138                 pEl = this.board.objects[el];
    1139                 if (pEl==glider) {
    1140                     from = true;
    1141                 }
    1142                 if (!from) {
    1143                     continue;
    1144                 }
    1145                 if (!pEl.needsRegularUpdate) { continue; }
    1146                 savetrace = pEl.visProp.trace;                       // Save the trace mode of the element
    1147                 pEl.visProp.trace = false;
    1148                 pEl.needsUpdate = true;
    1149                 pEl.update(true);
    1150                 pEl.visProp.trace = savetrace;                       // Restore the trace mode
    1151                 if (pEl==tracepoint) { break; }
    1152             }
    1153             this.dataX[i] = tracepoint.X();                          // Store the position of the trace point
    1154             this.dataY[i] = tracepoint.Y();
    1155         }
    1156         glider.position = savePos;                                   // Restore the original position of the glider
    1157         from = false;
    1158         for (el in this.board.objects) {                             // Update all elements from the glider to the trace point
    1159             pEl = this.board.objects[el];
    1160             if (pEl==glider) {
    1161                 from = true;
    1162             }
    1163             if (!from) {
    1164                 continue;
    1165             }
    1166             if (!pEl.needsRegularUpdate) { continue; }
    1167             savetrace = pEl.visProp.trace;
    1168             pEl.visProp.trace = false;
    1169             pEl.needsUpdate = true;
    1170             pEl.update(true); //.updateRenderer();
    1171             pEl.visProp.trace = savetrace;
    1172             if (pEl==tracepoint) {
    1173                 break;
    1174             }
    1175         }
    1176     };
    1177
    1178     return c;
    1179 };
    1180
    1181 JXG.JSXGraph.registerElement('tracecurve', JXG.createTracecurve);
    1182
    1183