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