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