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