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