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