1 /*
  2     Copyright 2008-2026
  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.js";
 40 import Clip from "../math/clip.js";
 41 import Const from "./constants.js";
 42 import Coords from "./coords.js";
 43 import Geometry from "../math/geometry.js";
 44 import GeometryElement from "./element.js";
 45 import GeonextParser from "../parser/geonext.js";
 46 import ImplicitPlot from "../math/implicitplot.js";
 47 import Mat from "../math/math.js";
 48 import Metapost from "../math/metapost.js";
 49 import Numerics from "../math/numerics.js";
 50 import Plot from "../math/plot.js";
 51 import QDT from "../math/qdt.js";
 52 import Type from "../utils/type.js";
 53 
 54 /**
 55  * Curves are the common object for function graphs, parametric curves, polar curves, and data plots.
 56  * @class Creates a new curve object. Do not use this constructor to create a curve. Use {@link JXG.Board#create} with
 57  * type {@link Curve}, or {@link Functiongraph} instead.
 58  * @augments JXG.GeometryElement
 59  * @param {String|JXG.Board} board The board the new curve is drawn on.
 60  * @param {Array} parents defining terms An array with the function terms or the data points of the curve.
 61  * @param {Object} attributes Defines the visual appearance of the curve.
 62  * @see JXG.Board#generateName
 63  * @see JXG.Board#addCurve
 64  */
 65 JXG.Curve = function (board, parents, attributes) {
 66     this.constructor(board, attributes, Const.OBJECT_TYPE_CURVE, Const.OBJECT_CLASS_CURVE);
 67 
 68     this.points = [];
 69     /**
 70      * Number of points on curves. This value changes
 71      * between numberPointsLow and numberPointsHigh.
 72      * It is set in {@link JXG.Curve#updateCurve}.
 73      */
 74     this.numberPoints = this.evalVisProp('numberpointshigh');
 75 
 76     this.bezierDegree = 1;
 77 
 78     /**
 79      * Array holding the x-coordinates of a data plot.
 80      * This array can be updated during run time by overwriting
 81      * the method {@link JXG.Curve#updateDataArray}.
 82      * @type array
 83      */
 84     this.dataX = null;
 85 
 86     /**
 87      * Array holding the y-coordinates of a data plot.
 88      * This array can be updated during run time by overwriting
 89      * the method {@link JXG.Curve#updateDataArray}.
 90      * @type array
 91      */
 92     this.dataY = null;
 93 
 94     /**
 95      * Array of ticks storing all the ticks on this curve. Do not set this field directly and use
 96      * {@link JXG.Curve#addTicks} and {@link JXG.Curve#removeTicks} to add and remove ticks to and
 97      * from the curve.
 98      * @type Array
 99      * @see JXG.Ticks
100      */
101     this.ticks = [];
102 
103     /**
104      * Stores a quadtree if it is required. The quadtree is generated in the curve
105      * updates and can be used to speed up the hasPoint method.
106      * @type JXG.Math.Quadtree
107      */
108     this.qdt = null;
109 
110     if (Type.exists(parents[0])) {
111         this.varname = parents[0];
112     } else {
113         this.varname = 'x';
114     }
115 
116     // function graphs: "x"
117     this.xterm = parents[1];
118     // function graphs: e.g. "x^2"
119     this.yterm = parents[2];
120 
121     // Converts GEONExT syntax into JavaScript syntax
122     this.generateTerm(this.varname, this.xterm, this.yterm, parents[3], parents[4]);
123     // First evaluation of the curve
124     this.updateCurve();
125 
126     this.id = this.board.setId(this, 'G');
127     this.board.renderer.drawCurve(this);
128 
129     this.board.finalizeAdding(this);
130 
131     this.createGradient();
132     this.elType = 'curve';
133     this.createLabel();
134 
135     if (Type.isString(this.xterm)) {
136         this.notifyParents(this.xterm);
137     }
138     if (Type.isString(this.yterm)) {
139         this.notifyParents(this.yterm);
140     }
141 };
142 
143 JXG.Curve.prototype = new GeometryElement();
144 
145 Type.copyMethodMap(JXG.Curve, {
146     generateTerm: "generateTerm",
147     setTerm: "generateTerm",
148     move: "moveTo",
149     moveTo: "moveTo",
150     MinX: "minX",
151     MaxX: "maxX"
152 });
153 
154 JXG.extend(
155     JXG.Curve.prototype,
156     /** @lends JXG.Curve.prototype */ {
157         /**
158          * Gives the default value of the left bound for the curve.
159          * May be overwritten in {@link JXG.Curve#generateTerm}.
160          * @returns {Number} Left bound for the curve.
161          */
162         minX: function () {
163             var leftCoords;
164 
165             if (this.evalVisProp('curvetype') === 'polar') {
166                 return 0;
167             }
168 
169             leftCoords = new Coords(
170                 Const.COORDS_BY_SCREEN,
171                 [-this.board.canvasWidth * 0.1, 0],
172                 this.board,
173                 false
174             );
175             return leftCoords.usrCoords[1];
176         },
177 
178         /**
179          * Gives the default value of the right bound for the curve.
180          * May be overwritten in {@link JXG.Curve#generateTerm}.
181          * @returns {Number} Right bound for the curve.
182          */
183         maxX: function () {
184             var rightCoords;
185 
186             if (this.evalVisProp('curvetype') === 'polar') {
187                 return 2 * Math.PI;
188             }
189             rightCoords = new Coords(
190                 Const.COORDS_BY_SCREEN,
191                 [this.board.canvasWidth * 1.1, 0],
192                 this.board,
193                 false
194             );
195 
196             return rightCoords.usrCoords[1];
197         },
198 
199         /**
200          * The parametric function which defines the x-coordinate of the curve.
201          * @param {Number} t A number between {@link JXG.Curve#minX} and {@link JXG.Curve#maxX}.
202          * @param {Boolean} suspendUpdate A boolean flag which is false for the
203          * first call of the function during a fresh plot of the curve and true
204          * for all subsequent calls of the function. This may be used to speed up the
205          * plotting of the curve, if the e.g. the curve depends on some input elements.
206          * @returns {Number} x-coordinate of the curve at t.
207          */
208         X: function (t) {
209             return NaN;
210         },
211 
212         /**
213          * The parametric function which defines the y-coordinate of the curve.
214          * @param {Number} t A number between {@link JXG.Curve#minX} and {@link JXG.Curve#maxX}.
215          * @param {Boolean} suspendUpdate A boolean flag which is false for the
216          * first call of the function during a fresh plot of the curve and true
217          * for all subsequent calls of the function. This may be used to speed up the
218          * plotting of the curve, if the e.g. the curve depends on some input elements.
219          * @returns {Number} y-coordinate of the curve at t.
220          */
221         Y: function (t) {
222             return NaN;
223         },
224 
225         /**
226          * Treat the curve as curve with homogeneous coordinates.
227          * @param {Number} t A number between {@link JXG.Curve#minX} and {@link JXG.Curve#maxX}.
228          * @returns {Number} Always 1.0
229          */
230         Z: function (t) {
231             return 1;
232         },
233 
234         /**
235          * Return the homogeneous coordinates of the curve at t - including all transformations
236          * applied to the curve.
237          * @param {Number} t A number between {@link JXG.Curve#minX} and {@link JXG.Curve#maxX}.
238          * @returns {Array} [Z(t), X(t), Y(t)] plus transformations
239          */
240         Ft: function(t) {
241             var c = [this.Z(t), this.X(t), this.Y(t)],
242                 len = this.transformations.length;
243 
244             if (len > 0) {
245                 c = Mat.matVecMult(this.transformMat, c);
246             }
247             c[1] /= c[0];
248             c[2] /= c[0];
249             c[0] /= c[0];
250 
251             return c;
252         },
253 
254         /**
255          * Checks whether (x,y) is near the curve.
256          * @param {Number} x Coordinate in x direction, screen coordinates.
257          * @param {Number} y Coordinate in y direction, screen coordinates.
258          * @param {Number} start Optional start index for search on data plots.
259          * @returns {Boolean} True if (x,y) is near the curve, False otherwise.
260          */
261         hasPoint: function (x, y, start) {
262             var t, c, i, tX, tY,
263                 checkPoint, len, invMat, isIn,
264                 res = [],
265                 points,
266                 qdt,
267                 steps = this.evalVisProp('numberpointslow'),
268                 d = (this.maxX() - this.minX()) / steps,
269                 prec, type,
270                 dist = Infinity,
271                 ux2, uy2,
272                 ev_ct,
273                 mi, ma,
274                 suspendUpdate = true;
275 
276             if (Type.isObject(this.evalVisProp('precision'))) {
277                 type = this.board._inputDevice;
278                 prec = this.evalVisProp('precision.' + type);
279             } else {
280                 // 'inherit'
281                 prec = this.board.options.precision.hasPoint;
282             }
283 
284             // From now on, x,y are usrCoords
285             checkPoint = new Coords(Const.COORDS_BY_SCREEN, [x, y], this.board, false);
286             x = checkPoint.usrCoords[1];
287             y = checkPoint.usrCoords[2];
288 
289             // Handle inner points of the curve
290             if (this.bezierDegree === 1 && this.evalVisProp('hasinnerpoints')) {
291                 isIn = Geometry.windingNumber([1, x, y], this.points, true);
292                 if (isIn !== 0) {
293                     return true;
294                 }
295             }
296 
297             // We use usrCoords. Only in the final distance calculation
298             // screen coords are used
299             prec += this.evalVisProp('strokewidth') * 0.5;
300             prec *= prec; // We do not want to take sqrt
301             ux2 = this.board.unitX * this.board.unitX;
302             uy2 = this.board.unitY * this.board.unitY;
303 
304             mi = this.minX();
305             ma = this.maxX();
306             if (Type.exists(this._visibleArea)) {
307                 mi = this._visibleArea[0];
308                 ma = this._visibleArea[1];
309                 d = (ma - mi) / steps;
310             }
311 
312             ev_ct = this.evalVisProp('curvetype');
313             if (ev_ct === "parameter" || ev_ct === 'polar') {
314                 // Transform the mouse/touch coordinates
315                 // back to the original position of the curve.
316                 // This is needed, because we work with the function terms, not the points.
317                 if (this.transformations.length > 0) {
318                     this.updateTransformMatrix();
319                     invMat = Mat.inverse(this.transformMat);
320                     c = Mat.matVecMult(invMat, [1, x, y]);
321                     x = c[1];
322                     y = c[2];
323                 }
324 
325                 // Brute force search for a point on the curve close to the mouse pointer
326                 for (i = 0, t = mi; i < steps; i++) {
327                     tX = this.X(t, suspendUpdate);
328                     tY = this.Y(t, suspendUpdate);
329 
330                     dist = (x - tX) * (x - tX) * ux2 + (y - tY) * (y - tY) * uy2;
331 
332                     if (dist <= prec) {
333                         return true;
334                     }
335 
336                     t += d;
337                 }
338             } else if (ev_ct === "plot" || ev_ct === 'functiongraph') {
339                 // Here, we can ignore transformations of the curve,
340                 // since we are working directly with the points.
341 
342                 if (!Type.exists(start) || start < 0) {
343                     start = 0;
344                 }
345 
346                 if (
347                     Type.exists(this.qdt) &&
348                     this.evalVisProp('useqdt') &&
349                     this.bezierDegree !== 3
350                 ) {
351                     qdt = this.qdt.query(new Coords(Const.COORDS_BY_USER, [x, y], this.board));
352                     points = qdt.points;
353                     len = points.length;
354                 } else {
355                     points = this.points;
356                     len = this.numberPoints - 1;
357                 }
358 
359                 for (i = start; i < len; i++) {
360                     if (this.bezierDegree === 3) {
361                         //res.push(Geometry.projectCoordsToBeziersegment([1, x, y], this, i));
362                         res = Geometry.projectCoordsToBeziersegment([1, x, y], this, i);
363                     } else {
364                         if (qdt) {
365                             if (points[i].prev) {
366                                 res = Geometry.projectCoordsToSegment(
367                                     [1, x, y],
368                                     points[i].prev.usrCoords,
369                                     points[i].usrCoords
370                                 );
371                             }
372 
373                             // If the next point in the array is the same as the current points
374                             // next neighbor we don't have to project it onto that segment because
375                             // that will already be done in the next iteration of this loop.
376                             if (points[i].next && points[i + 1] !== points[i].next) {
377                                 res = Geometry.projectCoordsToSegment(
378                                     [1, x, y],
379                                     points[i].usrCoords,
380                                     points[i].next.usrCoords
381                                 );
382                             }
383                         } else {
384                             res = Geometry.projectCoordsToSegment(
385                                 [1, x, y],
386                                 points[i].usrCoords,
387                                 points[i + 1].usrCoords
388                             );
389                         }
390                     }
391 
392                     if (
393                         res[1] >= 0 &&
394                         res[1] <= 1 &&
395                         (x - res[0][1]) * (x - res[0][1]) * ux2 +
396                         (y - res[0][2]) * (y - res[0][2]) * uy2 <=
397                         prec
398                     ) {
399                         return true;
400                     }
401                 }
402                 return false;
403             }
404             return dist < prec;
405         },
406 
407         /**
408          * Allocate points in the Coords array this.points
409          */
410         allocatePoints: function () {
411             var i, len;
412 
413             len = this.numberPoints;
414 
415             if (this.points.length < this.numberPoints) {
416                 for (i = this.points.length; i < len; i++) {
417                     this.points[i] = new Coords(
418                         Const.COORDS_BY_USER,
419                         [0, 0],
420                         this.board,
421                         false
422                     );
423                 }
424             }
425         },
426 
427         /**
428          * Generates points of the curve to be plotted.
429          * @returns {JXG.Curve} Reference to the curve object.
430          * @see JXG.Curve#updateCurve
431          */
432         update: function () {
433             if (this.needsUpdate) {
434                 if (this.evalVisProp('trace')) {
435                     this.cloneToBackground(true);
436                 }
437                 this.updateCurve();
438             }
439 
440             return this;
441         },
442 
443         /**
444          * Updates the visual contents of the curve.
445          * @returns {JXG.Curve} Reference to the curve object.
446          */
447         updateRenderer: function () {
448             //var wasReal;
449 
450             if (!this.needsUpdate) {
451                 return this;
452             }
453 
454             if (this.visPropCalc.visible) {
455                 // wasReal = this.isReal;
456 
457                 this.isReal = Plot.checkReal(this.points);
458 
459                 if (
460                     //wasReal &&
461                     !this.isReal
462                 ) {
463                     this.updateVisibility(false);
464                 }
465             }
466 
467             if (this.visPropCalc.visible) {
468                 this.board.renderer.updateCurve(this);
469             }
470 
471             /* Update the label if visible. */
472             if (
473                 this.hasLabel &&
474                 this.visPropCalc.visible &&
475                 this.label &&
476                 this.label.visPropCalc.visible &&
477                 this.isReal
478             ) {
479                 this.label.update();
480                 this.board.renderer.updateText(this.label);
481             }
482 
483             // Update rendNode display
484             this.setDisplayRendNode();
485             // if (this.visPropCalc.visible !== this.visPropOld.visible) {
486             //     this.board.renderer.display(this, this.visPropCalc.visible);
487             //     this.visPropOld.visible = this.visPropCalc.visible;
488             //
489             //     if (this.hasLabel) {
490             //         this.board.renderer.display(this.label, this.label.visPropCalc.visible);
491             //     }
492             // }
493 
494             this.needsUpdate = false;
495             return this;
496         },
497 
498         /**
499          * For dynamic dataplots updateCurve can be used to compute new entries
500          * for the arrays {@link JXG.Curve#dataX} and {@link JXG.Curve#dataY}. It
501          * is used in {@link JXG.Curve#updateCurve}. Default is an empty method, can
502          * be overwritten by the user.
503          *
504          *
505          * @example
506          * // This example overwrites the updateDataArray method.
507          * // There, new values for the arrays JXG.Curve.dataX and JXG.Curve.dataY
508          * // are computed from the value of the slider N
509          *
510          * var N = board.create('slider', [[0,1.5],[3,1.5],[1,3,40]], {name:'n',snapWidth:1});
511          * var circ = board.create('circle',[[4,-1.5],1],{strokeWidth:1, strokecolor:'black', strokeWidth:2,
512          * 		fillColor:'#0055ff13'});
513          *
514          * var c = board.create('curve', [[0],[0]],{strokecolor:'red', strokeWidth:2});
515          * c.updateDataArray = function() {
516          *         var r = 1, n = Math.floor(N.Value()),
517          *             x = [0], y = [0],
518          *             phi = Math.PI/n,
519          *             h = r*Math.cos(phi),
520          *             s = r*Math.sin(phi),
521          *             i, j,
522          *             px = 0, py = 0, sgn = 1,
523          *             d = 16,
524          *             dt = phi/d,
525          *             pt;
526          *
527          *         for (i = 0; i < n; i++) {
528          *             for (j = -d; j <= d; j++) {
529          *                 pt = dt*j;
530          *                 x.push(px + r*Math.sin(pt));
531          *                 y.push(sgn*r*Math.cos(pt) - (sgn-1)*h*0.5);
532          *             }
533          *             px += s;
534          *             sgn *= (-1);
535          *         }
536          *         x.push((n - 1)*s);
537          *         y.push(h + (sgn - 1)*h*0.5);
538          *         this.dataX = x;
539          *         this.dataY = y;
540          *     }
541          *
542          * var c2 = board.create('curve', [[0],[0]],{strokecolor:'red', strokeWidth:1});
543          * c2.updateDataArray = function() {
544          *         var r = 1, n = Math.floor(N.Value()),
545          *             px = circ.midpoint.X(), py = circ.midpoint.Y(),
546          *             x = [px], y = [py],
547          *             phi = Math.PI/n,
548          *             s = r*Math.sin(phi),
549          *             i, j,
550          *             d = 16,
551          *             dt = phi/d,
552          *             pt = Math.PI*0.5+phi;
553          *
554          *         for (i = 0; i < n; i++) {
555          *             for (j= -d; j <= d; j++) {
556          *                 x.push(px + r*Math.cos(pt));
557          *                 y.push(py + r*Math.sin(pt));
558          *                 pt -= dt;
559          *             }
560          *             x.push(px);
561          *             y.push(py);
562          *             pt += dt;
563          *         }
564          *         this.dataX = x;
565          *         this.dataY = y;
566          *     }
567          *     board.update();
568          *
569          * </pre><div id="JXG20bc7802-e69e-11e5-b1bf-901b0e1b8723" class="jxgbox" style="width: 600px; height: 400px;"></div>
570          * <script type="text/javascript">
571          *     (function() {
572          *         var board = JXG.JSXGraph.initBoard('JXG20bc7802-e69e-11e5-b1bf-901b0e1b8723',
573          *             {boundingbox: [-1.5,2,8,-3], keepaspectratio: true, axis: true, showcopyright: false, shownavigation: false});
574          *             var N = board.create('slider', [[0,1.5],[3,1.5],[1,3,40]], {name:'n',snapWidth:1});
575          *             var circ = board.create('circle',[[4,-1.5],1],{strokeWidth:1, strokecolor:'black',
576          *             strokeWidth:2, fillColor:'#0055ff13'});
577          *
578          *             var c = board.create('curve', [[0],[0]],{strokecolor:'red', strokeWidth:2});
579          *             c.updateDataArray = function() {
580          *                     var r = 1, n = Math.floor(N.Value()),
581          *                         x = [0], y = [0],
582          *                         phi = Math.PI/n,
583          *                         h = r*Math.cos(phi),
584          *                         s = r*Math.sin(phi),
585          *                         i, j,
586          *                         px = 0, py = 0, sgn = 1,
587          *                         d = 16,
588          *                         dt = phi/d,
589          *                         pt;
590          *
591          *                     for (i=0;i<n;i++) {
592          *                         for (j=-d;j<=d;j++) {
593          *                             pt = dt*j;
594          *                             x.push(px+r*Math.sin(pt));
595          *                             y.push(sgn*r*Math.cos(pt)-(sgn-1)*h*0.5);
596          *                         }
597          *                         px += s;
598          *                         sgn *= (-1);
599          *                     }
600          *                     x.push((n-1)*s);
601          *                     y.push(h+(sgn-1)*h*0.5);
602          *                     this.dataX = x;
603          *                     this.dataY = y;
604          *                 }
605          *
606          *             var c2 = board.create('curve', [[0],[0]],{strokecolor:'red', strokeWidth:1});
607          *             c2.updateDataArray = function() {
608          *                     var r = 1, n = Math.floor(N.Value()),
609          *                         px = circ.midpoint.X(), py = circ.midpoint.Y(),
610          *                         x = [px], y = [py],
611          *                         phi = Math.PI/n,
612          *                         s = r*Math.sin(phi),
613          *                         i, j,
614          *                         d = 16,
615          *                         dt = phi/d,
616          *                         pt = Math.PI*0.5+phi;
617          *
618          *                     for (i=0;i<n;i++) {
619          *                         for (j=-d;j<=d;j++) {
620          *                             x.push(px+r*Math.cos(pt));
621          *                             y.push(py+r*Math.sin(pt));
622          *                             pt -= dt;
623          *                         }
624          *                         x.push(px);
625          *                         y.push(py);
626          *                         pt += dt;
627          *                     }
628          *                     this.dataX = x;
629          *                     this.dataY = y;
630          *                 }
631          *                 board.update();
632          *
633          *     })();
634          *
635          * </script><pre>
636          *
637          * @example
638          * // This is an example which overwrites updateDataArray and produces
639          * // a Bezier curve of degree three.
640          * var A = board.create('point', [-3,3]);
641          * var B = board.create('point', [3,-2]);
642          * var line = board.create('segment', [A,B]);
643          *
644          * var height = 0.5; // height of the curly brace
645          *
646          * // Curly brace
647          * var crl = board.create('curve', [[0],[0]], {strokeWidth:1, strokeColor:'black'});
648          * crl.bezierDegree = 3;
649          * crl.updateDataArray = function() {
650          *     var d = [B.X()-A.X(), B.Y()-A.Y()],
651          *         dl = Math.sqrt(d[0]*d[0]+d[1]*d[1]),
652          *         mid = [(A.X()+B.X())*0.5, (A.Y()+B.Y())*0.5];
653          *
654          *     d[0] *= height/dl;
655          *     d[1] *= height/dl;
656          *
657          *     this.dataX = [ A.X(), A.X()-d[1], mid[0], mid[0]-d[1], mid[0], B.X()-d[1], B.X() ];
658          *     this.dataY = [ A.Y(), A.Y()+d[0], mid[1], mid[1]+d[0], mid[1], B.Y()+d[0], B.Y() ];
659          * };
660          *
661          * // Text
662          * var txt = board.create('text', [
663          *                     function() {
664          *                         var d = [B.X()-A.X(), B.Y()-A.Y()],
665          *                             dl = Math.sqrt(d[0]*d[0]+d[1]*d[1]),
666          *                             mid = (A.X()+B.X())*0.5;
667          *
668          *                         d[1] *= height/dl;
669          *                         return mid-d[1]+0.1;
670          *                     },
671          *                     function() {
672          *                         var d = [B.X()-A.X(), B.Y()-A.Y()],
673          *                             dl = Math.sqrt(d[0]*d[0]+d[1]*d[1]),
674          *                             mid = (A.Y()+B.Y())*0.5;
675          *
676          *                         d[0] *= height/dl;
677          *                         return mid+d[0]+0.1;
678          *                     },
679          *                     function() { return "length=" + JXG.toFixed(B.Dist(A), 2); }
680          *                 ]);
681          *
682          *
683          * board.update(); // This update is necessary to call updateDataArray the first time.
684          *
685          * </pre><div id="JXGa61a4d66-e69f-11e5-b1bf-901b0e1b8723"  class="jxgbox" style="width: 300px; height: 300px;"></div>
686          * <script type="text/javascript">
687          *     (function() {
688          *      var board = JXG.JSXGraph.initBoard('JXGa61a4d66-e69f-11e5-b1bf-901b0e1b8723',
689          *             {boundingbox: [-4, 4, 4,-4], axis: true, showcopyright: false, shownavigation: false});
690          *     var A = board.create('point', [-3,3]);
691          *     var B = board.create('point', [3,-2]);
692          *     var line = board.create('segment', [A,B]);
693          *
694          *     var height = 0.5; // height of the curly brace
695          *
696          *     // Curly brace
697          *     var crl = board.create('curve', [[0],[0]], {strokeWidth:1, strokeColor:'black'});
698          *     crl.bezierDegree = 3;
699          *     crl.updateDataArray = function() {
700          *         var d = [B.X()-A.X(), B.Y()-A.Y()],
701          *             dl = Math.sqrt(d[0]*d[0]+d[1]*d[1]),
702          *             mid = [(A.X()+B.X())*0.5, (A.Y()+B.Y())*0.5];
703          *
704          *         d[0] *= height/dl;
705          *         d[1] *= height/dl;
706          *
707          *         this.dataX = [ A.X(), A.X()-d[1], mid[0], mid[0]-d[1], mid[0], B.X()-d[1], B.X() ];
708          *         this.dataY = [ A.Y(), A.Y()+d[0], mid[1], mid[1]+d[0], mid[1], B.Y()+d[0], B.Y() ];
709          *     };
710          *
711          *     // Text
712          *     var txt = board.create('text', [
713          *                         function() {
714          *                             var d = [B.X()-A.X(), B.Y()-A.Y()],
715          *                                 dl = Math.sqrt(d[0]*d[0]+d[1]*d[1]),
716          *                                 mid = (A.X()+B.X())*0.5;
717          *
718          *                             d[1] *= height/dl;
719          *                             return mid-d[1]+0.1;
720          *                         },
721          *                         function() {
722          *                             var d = [B.X()-A.X(), B.Y()-A.Y()],
723          *                                 dl = Math.sqrt(d[0]*d[0]+d[1]*d[1]),
724          *                                 mid = (A.Y()+B.Y())*0.5;
725          *
726          *                             d[0] *= height/dl;
727          *                             return mid+d[0]+0.1;
728          *                         },
729          *                         function() { return "length="+JXG.toFixed(B.Dist(A), 2); }
730          *                     ]);
731          *
732          *
733          *     board.update(); // This update is necessary to call updateDataArray the first time.
734          *
735          *     })();
736          *
737          * </script><pre>
738          *
739          *
740          */
741         updateDataArray: function () {
742             // this used to return this, but we shouldn't rely on the user to implement it.
743         },
744 
745         /**
746          * Computes the curve path
747          * @see JXG.Curve#update
748          * @returns {JXG.Curve} Reference to the curve object.
749          */
750         updateCurve: function () {
751             var i, len, mi, ma,
752                 x, y,
753                 bb, eps,
754                 version = this.visProp.plotversion,
755                 //t1, t2, l1,
756                 suspendUpdate = false;
757 
758             this.updateTransformMatrix();
759             this.updateDataArray();
760             mi = this.minX();
761             ma = this.maxX();
762 
763             if (Type.exists(this.dataX)) {
764                 // Discrete data points, i.e. x-coordinates are given in an array
765                 this.numberPoints = this.dataX.length;
766                 len = this.numberPoints;
767 
768                 // It is possible, that the array length has increased.
769                 this.allocatePoints();
770 
771                 for (i = 0; i < len; i++) {
772                     x = i;
773 
774                     // y-coordinates are in an array
775                     if (Type.exists(this.dataY)) {
776                         y = i;
777                         // The last parameter prevents rounding in usr2screen().
778                         this.points[i].setCoordinates(
779                             Const.COORDS_BY_USER,
780                             [this.dataX[i], this.dataY[i]],
781                             false
782                         );
783                     } else {
784                         // discrete x data, continuous y data
785                         y = this.X(x);
786                         // The last parameter prevents rounding in usr2screen().
787                         this.points[i].setCoordinates(
788                             Const.COORDS_BY_USER,
789                             [this.dataX[i], this.Y(y, suspendUpdate)],
790                             false
791                         );
792                     }
793                     this.points[i]._t = i;
794 
795                     // this.updateTransform(this.points[i]);
796                     suspendUpdate = true;
797                 }
798 
799             } else {
800                 // Continuous x-data, i.e. given as a function
801                 if (this.evalVisProp('doadvancedplot')) {
802                     // console.time('plot');
803 
804                     if (version === 1 || this.evalVisProp('doadvancedplotold')) {
805                         Plot.updateParametricCurveOld(this, mi, ma);
806                     } else if (version === 2) {
807                         Plot.updateParametricCurve_v2(this, mi, ma);
808                     } else if (version === 3) {
809                         Plot.updateParametricCurve_v3(this, mi, ma);
810                     } else if (version === 4) {
811                         Plot.updateParametricCurve_v4(this, mi, ma);
812                     } else {
813                         Plot.updateParametricCurve_v2(this, mi, ma);
814                     }
815                     // console.timeEnd('plot');
816                 } else {
817                     if (this.board.updateQuality === this.board.BOARD_QUALITY_HIGH) {
818                         this.numberPoints = this.evalVisProp('numberpointshigh');
819                     } else {
820                         this.numberPoints = this.evalVisProp('numberpointslow');
821                     }
822 
823                     // It is possible, that the array length has increased.
824                     this.allocatePoints();
825                     Plot.updateParametricCurveNaive(this, mi, ma, this.numberPoints);
826                 }
827                 len = this.numberPoints;
828 
829                 if (
830                     this.evalVisProp('useqdt') &&
831                     this.board.updateQuality === this.board.BOARD_QUALITY_HIGH
832                 ) {
833                     this.qdt = new QDT(this.board.getBoundingBox());
834                     for (i = 0; i < this.points.length; i++) {
835                         this.qdt.insert(this.points[i]);
836 
837                         if (i > 0) {
838                             this.points[i].prev = this.points[i - 1];
839                         }
840 
841                         if (i < len - 1) {
842                             this.points[i].next = this.points[i + 1];
843                         }
844                     }
845                 }
846             }
847 
848             if (
849                 this.bezierDegree === 1 &&
850                 // this.evalVisProp('curvetype') !== "plot" &&
851                 this.evalVisProp('rdpsmoothing')
852             ) {
853                 // console.time('rdp');
854                 // RDP in screen coords:
855                 // this.points = Numerics.RamerDouglasPeucker(this.points, 0.2);
856 
857                 // RDP in user coords:
858                 // Use a default size of 800 x 800 pixel and
859                 // maximum distance of 0.2 pixel:
860                 // Determine the geometric mean M of the horizontal and vertical box size in user coords, i.e.
861                 // 1 u = 1000 / M px => 1 px = M / 1000 u => eps := 0.2 * M / 800
862                 bb = this.board.getBoundingBox();
863                 eps = this.evalVisProp('rdpthreshold') * Math.sqrt((bb[2] - bb[0]) * (bb[1] - bb[3])) * 0.00125;
864                 this.points = Numerics.RamerDouglasPeucker(this.points, eps, true);
865 
866                 this.numberPoints = this.points.length;
867                 // console.timeEnd('rdp');
868                 // console.log(this.numberPoints);
869             }
870 
871             len = this.numberPoints;
872             for (i = 0; i < len; i++) {
873                 this.updateTransform(this.points[i]);
874             }
875 
876             return this;
877         },
878 
879         updateTransformMatrix: function () {
880             var t,
881                 i,
882                 len = this.transformations.length;
883 
884             this.transformMat = [
885                 [1, 0, 0],
886                 [0, 1, 0],
887                 [0, 0, 1]
888             ];
889 
890             for (i = 0; i < len; i++) {
891                 t = this.transformations[i];
892                 t.update();
893                 this.transformMat = Mat.matMatMult(t.matrix, this.transformMat);
894             }
895 
896             return this;
897         },
898 
899         /**
900          * Applies the transformations of the curve to the given point <tt>p</tt>.
901          * Before using it, {@link JXG.Curve#updateTransformMatrix} has to be called.
902          * @param {JXG.Point} p
903          * @returns {JXG.Point} The given point.
904          */
905         updateTransform: function (p) {
906             var c,
907                 len = this.transformations.length;
908 
909             if (len > 0) {
910                 c = Mat.matVecMult(this.transformMat, p.usrCoords);
911                 p.setCoordinates(Const.COORDS_BY_USER, c, false, true);
912             }
913 
914             return p;
915         },
916 
917         /**
918          * Add transformations to this curve.
919          * @param {JXG.Transformation|Array} transform Either one {@link JXG.Transformation} or an array of {@link JXG.Transformation}s.
920          * @returns {JXG.Curve} Reference to the curve object.
921          */
922         addTransform: function (transform) {
923             var i,
924                 list = Type.isArray(transform) ? transform : [transform],
925                 len = list.length;
926 
927             for (i = 0; i < len; i++) {
928                 this.transformations.push(list[i]);
929             }
930 
931             return this;
932         },
933 
934         removeTransform: function (transform) {
935             var i,
936                 list = Type.isArray(transform) ? transform : [transform],
937                 len = list.length;
938 
939             for (i = 0; i < len; i++) {
940                 Type.removeElementFromArray(this.transformations, list[i]);
941             }
942 
943             return this;
944         },
945 
946         clearTransforms: function () {
947             this.transformations = [];
948 
949             return this;
950         },
951 
952         /**
953          * Generate the method curve.X() in case curve.dataX is an array
954          * and generate the method curve.Y() in case curve.dataY is an array.
955          * @private
956          * @param {String} which Either 'X' or 'Y'
957          * @returns {function}
958          **/
959         interpolationFunctionFromArray: function (which) {
960             var data = "data" + which,
961                 that = this;
962 
963             return function (t, suspendedUpdate) {
964                 var i,
965                     j,
966                     t0,
967                     t1,
968                     arr = that[data],
969                     len = arr.length,
970                     last,
971                     f = [];
972 
973                 if (isNaN(t)) {
974                     return NaN;
975                 }
976 
977                 if (t < 0) {
978                     if (Type.isFunction(arr[0])) {
979                         return arr[0]();
980                     }
981 
982                     return arr[0];
983                 }
984 
985                 if (that.bezierDegree === 3) {
986                     last = (len - 1) / 3;
987 
988                     if (t >= last) {
989                         if (Type.isFunction(arr[arr.length - 1])) {
990                             return arr[arr.length - 1]();
991                         }
992 
993                         return arr[arr.length - 1];
994                     }
995 
996                     i = Math.floor(t) * 3;
997                     t0 = t % 1;
998                     t1 = 1 - t0;
999 
1000                     for (j = 0; j < 4; j++) {
1001                         if (Type.isFunction(arr[i + j])) {
1002                             f[j] = arr[i + j]();
1003                         } else {
1004                             f[j] = arr[i + j];
1005                         }
1006                     }
1007 
1008                     return (
1009                         t1 * t1 * (t1 * f[0] + 3 * t0 * f[1]) +
1010                         (3 * t1 * f[2] + t0 * f[3]) * t0 * t0
1011                     );
1012                 }
1013 
1014                 if (t > len - 2) {
1015                     i = len - 2;
1016                 } else {
1017                     i = parseInt(Math.floor(t), 10);
1018                 }
1019 
1020                 if (i === t) {
1021                     if (Type.isFunction(arr[i])) {
1022                         return arr[i]();
1023                     }
1024                     return arr[i];
1025                 }
1026 
1027                 for (j = 0; j < 2; j++) {
1028                     if (Type.isFunction(arr[i + j])) {
1029                         f[j] = arr[i + j]();
1030                     } else {
1031                         f[j] = arr[i + j];
1032                     }
1033                 }
1034                 return f[0] + (f[1] - f[0]) * (t - i);
1035             };
1036         },
1037 
1038         /**
1039          * Converts the JavaScript/JessieCode/GEONExT syntax of the defining function term into JavaScript.
1040          * New methods X() and Y() for the Curve object are generated, further
1041          * new methods for minX() and maxX().
1042          * If mi or ma are not supplied, default functions are set.
1043          *
1044          * @param {String} varname Name of the parameter in xterm and yterm, e.g. 'x' or 't'
1045          * @param {String|Number|Function|Array} xterm Term for the x coordinate. Can also be an array consisting of discrete values.
1046          * @param {String|Number|Function|Array} yterm Term for the y coordinate. Can also be an array consisting of discrete values.
1047          * @param {String|Number|Function} [mi] Lower bound on the parameter
1048          * @param {String|Number|Function} [ma] Upper bound on the parameter
1049          * @see JXG.GeonextParser.geonext2JS
1050          */
1051         generateTerm: function (varname, xterm, yterm, mi, ma) {
1052             var fx, fy, mat;
1053 
1054             // Generate the methods X() and Y()
1055             if (Type.isArray(xterm)) {
1056                 // Discrete data
1057                 this.dataX = xterm;
1058 
1059                 this.numberPoints = this.dataX.length;
1060                 this.X = this.interpolationFunctionFromArray.apply(this, ["X"]);
1061                 this.visProp.curvetype = 'plot';
1062                 this.isDraggable = true;
1063             } else {
1064                 // Continuous data
1065                 this.X = Type.createFunction(xterm, this.board, varname);
1066                 if (Type.isString(xterm)) {
1067                     this.visProp.curvetype = 'functiongraph';
1068                 } else if (Type.isFunction(xterm) || Type.isNumber(xterm)) {
1069                     this.visProp.curvetype = 'parameter';
1070                 }
1071 
1072                 this.isDraggable = true;
1073             }
1074 
1075             if (Type.isArray(yterm)) {
1076                 this.dataY = yterm;
1077                 this.Y = this.interpolationFunctionFromArray.apply(this, ["Y"]);
1078             } else if (!Type.exists(yterm)) {
1079                 // Discrete data as an array of coordinate pairs,
1080                 // i.e. transposed input
1081                 mat = Mat.transpose(xterm);
1082                 this.dataX = mat[0];
1083                 this.dataY = mat[1];
1084                 this.numberPoints = this.dataX.length;
1085                 this.Y = this.interpolationFunctionFromArray.apply(this, ["Y"]);
1086             } else {
1087                 this.Y = Type.createFunction(yterm, this.board, varname);
1088             }
1089 
1090             /**
1091              * Polar form
1092              * Input data is function xterm() and offset coordinates yterm
1093              */
1094             if (Type.isFunction(xterm) && Type.isArray(yterm)) {
1095                 // Xoffset, Yoffset
1096                 fx = Type.createFunction(yterm[0], this.board, "");
1097                 fy = Type.createFunction(yterm[1], this.board, "");
1098 
1099                 this.X = function (phi) {
1100                     return xterm(phi) * Math.cos(phi) + fx();
1101                 };
1102                 this.X.deps = fx.deps;
1103 
1104                 this.Y = function (phi) {
1105                     return xterm(phi) * Math.sin(phi) + fy();
1106                 };
1107                 this.Y.deps = fy.deps;
1108 
1109                 this.visProp.curvetype = 'polar';
1110             }
1111 
1112             // Set the upper and lower bounds for the parameter of the curve.
1113             // If not defined, reset the bounds to the default values
1114             // given in Curve.prototype.minX, Curve.prototype.maxX
1115             if (Type.exists(mi)) {
1116                 this.minX = Type.createFunction(mi, this.board, "");
1117             } else {
1118                 delete this.minX;
1119             }
1120             if (Type.exists(ma)) {
1121                 this.maxX = Type.createFunction(ma, this.board, "");
1122             } else {
1123                 delete this.maxX;
1124             }
1125 
1126             this.addParentsFromJCFunctions([this.X, this.Y, this.minX, this.maxX]);
1127         },
1128 
1129         /**
1130          * Finds dependencies in a given term and notifies the parents by adding the
1131          * dependent object to the found objects child elements.
1132          * @param {String} contentStr String containing dependencies for the given object.
1133          */
1134         notifyParents: function (contentStr) {
1135             var fstr,
1136                 dep,
1137                 isJessieCode = false,
1138                 obj;
1139 
1140             // Read dependencies found by the JessieCode parser
1141             obj = { xterm: 1, yterm: 1 };
1142             for (fstr in obj) {
1143                 if (
1144                     obj.hasOwnProperty(fstr) &&
1145                     this.hasOwnProperty(fstr) &&
1146                     this[fstr].origin
1147                 ) {
1148                     isJessieCode = true;
1149                     for (dep in this[fstr].origin.deps) {
1150                         if (this[fstr].origin.deps.hasOwnProperty(dep)) {
1151                             this[fstr].origin.deps[dep].addChild(this);
1152                         }
1153                     }
1154                 }
1155             }
1156 
1157             if (!isJessieCode) {
1158                 GeonextParser.findDependencies(this, contentStr, this.board);
1159             }
1160         },
1161 
1162         /**
1163          * Position a curve label according to the attributes "position" and distance.
1164          * This function is also used for angle, arc and sector.
1165          *
1166          * @param {String} pos
1167          * @param {Number} distance
1168          * @returns {JXG.Coords}
1169          */
1170         getLabelPosition: function(pos, distance) {
1171             var x, y, xy,
1172                 c, d, e,
1173                 c_t, c_te, c_ma, c_mi,
1174                 lbda,
1175                 mi, ma, ar,
1176                 t, dx, dy,
1177                 dist = 1.5;
1178 
1179             // Shrink domain if necessary
1180             mi = this.minX();
1181             ma = this.maxX();
1182             ar = Numerics.findDomain(this.X, [mi, ma], null, false);
1183             ar = Numerics.findDomain(this.Y, ar, null, false);
1184             mi = Math.max(ar[0], ar[0]); // ???
1185             ma = Math.min(ar[1], ar[1]); // ???
1186 
1187             xy = Type.parsePosition(pos);
1188             lbda = Type.parseNumber(xy.pos, ma - mi, 1);
1189 
1190             if (xy.pos.indexOf('fr') < 0 && xy.pos.indexOf('%') < 0) {
1191                 // The unit has to be 'fr' or '%'. 'px' or plain numbers are not supported
1192                 lbda = 0;
1193             }
1194 
1195             t = mi + lbda;
1196 
1197             // x = this.X(t);
1198             // y = this.Y(t);
1199             c_t = this.Ft(t); // Include transformations
1200             x = c_t[1];
1201             y = c_t[2];
1202             // If x or y are NaN, the label is set to the line
1203             // between the first and last point.
1204             if (isNaN(x + y)) {
1205                 lbda /= (ma - mi);
1206                 t = mi + lbda;
1207 
1208                 // x = this.X(mi) + lbda * (this.X(ma) - this.X(mi));
1209                 // y = this.Y(mi) + lbda * (this.Y(ma) - this.Y(mi));
1210                 c_mi = this.Ft(mi);
1211                 c_ma = this.Ft(ma);
1212                 x = c_mi[1] + lbda * (c_ma[1] - c_mi[1]);
1213                 y = c_mi[2] + lbda * (c_ma[2] - c_mi[2]);
1214             }
1215             c = (new Coords(Const.COORDS_BY_USER, [x, y], this.board)).scrCoords;
1216 
1217             e = Mat.eps;
1218             if (t < mi + e) {
1219                 // dx = (this.X(t + e) - this.X(t)) / e;
1220                 // dy = (this.Y(t + e) - this.Y(t)) / e;
1221                 c_te = this.Ft(t + e);
1222                 dx = (c_te[1] - c_t[1]) / e;
1223                 dy = (c_te[2] - c_t[2]) / e;
1224             } else if (t > ma - e) {
1225                 // dx = (this.X(t) - this.X(t - e)) / e;
1226                 // dy = (this.Y(t) - this.Y(t - e)) / e;
1227                 c_te = this.Ft(t - e);
1228                 dx = (c_t[1] - c_te[1]) / e;
1229                 dy = (c_t[2] - c_te[2]) / e;
1230             } else {
1231                 // dx = 0.5 * (this.X(t + e) - this.X(t - e)) / e;
1232                 // dy = 0.5 * (this.Y(t + e) - this.Y(t - e)) / e;
1233                 c_te = this.Ft(t + e);
1234                 c_t  = this.Ft(t - e);
1235                 dx = 0.5 * (c_te[1] - c_t[1]) / e;
1236                 dy = 0.5 * (c_te[2] - c_t[2]) / e;
1237             }
1238             dx = isNaN(dx) ? 1. : dx;
1239             dy = isNaN(dy) ? 1. : dy;
1240             d = Mat.hypot(dx, dy);
1241 
1242             if (xy.side === 'left') {
1243                 dy *= -1;
1244             } else {
1245                 dx *= -1;
1246             }
1247 
1248             // Position left or right
1249 
1250             if (Type.exists(this.label)) {
1251                 dist = 0.5 * distance / d;
1252             }
1253 
1254             x = c[1] + dy * this.label.size[0] * dist;
1255             y = c[2] - dx * this.label.size[1] * dist;
1256 
1257             return new Coords(Const.COORDS_BY_SCREEN, [x, y], this.board);
1258         },
1259 
1260         // documented in geometryElement
1261         getLabelAnchor: function () {
1262             var x, y, pos,
1263                 // xy, lbda, e,
1264                 // t, dx, dy, d,
1265                 // dist = 1.5,
1266                 c,
1267                 lo = 0.1,
1268                 up = 0.9,
1269                 ax = lo * this.board.canvasWidth,
1270                 ay = lo * this.board.canvasHeight,
1271                 bx = up * this.board.canvasWidth,
1272                 by = up * this.board.canvasHeight;
1273 
1274             if (!Type.exists(this.label)) {
1275                 return new Coords(Const.COORDS_BY_SCREEN, [NaN, NaN], this.board);
1276             }
1277             pos = this.label.evalVisProp('position');
1278             if (!Type.isString(pos)) {
1279                 return new Coords(Const.COORDS_BY_SCREEN, [NaN, NaN], this.board);
1280             }
1281 
1282             if (pos.indexOf('right') < 0 && pos.indexOf('left') < 0) {
1283                 // Old system
1284                 switch (this.evalVisProp('label.position')) {
1285                     case "ulft":
1286                         x = ax;
1287                         y = ay;
1288                         break;
1289                     case "llft":
1290                         x = ax;
1291                         y = by;
1292                         break;
1293                     case "rt":
1294                         x = bx;
1295                         y = 0.5 * by;
1296                         break;
1297                     case "lrt":
1298                         x = bx;
1299                         y = by;
1300                         break;
1301                     case "urt":
1302                         x = bx;
1303                         y = ay;
1304                         break;
1305                     case "top":
1306                         x = 0.5 * bx;
1307                         y = ay;
1308                         break;
1309                     case "bot":
1310                         x = 0.5 * bx;
1311                         y = by;
1312                         break;
1313                     default:
1314                         // includes case 'lft'
1315                         x = ax;
1316                         y = 0.5 * by;
1317                 }
1318             } else {
1319                 // New positioning, e.g. "25% left"
1320                 return this.getLabelPosition(pos, this.label.evalVisProp('distance'));
1321             }
1322             c = new Coords(Const.COORDS_BY_SCREEN, [x, y], this.board, false);
1323             return Geometry.projectCoordsToCurve(
1324                 c.usrCoords[1], c.usrCoords[2], 0, this, this.board
1325             )[0];
1326         },
1327 
1328         // documented in geometry element
1329         cloneToBackground: function () {
1330             var er,
1331                 copy = Type.getCloneObject(this);
1332 
1333             copy.points = this.points.slice(0);
1334             copy.bezierDegree = this.bezierDegree;
1335             copy.numberPoints = this.numberPoints;
1336 
1337             er = this.board.renderer.enhancedRendering;
1338             this.board.renderer.enhancedRendering = true;
1339             this.board.renderer.drawCurve(copy);
1340             this.board.renderer.enhancedRendering = er;
1341             this.traces[copy.id] = copy.rendNode;
1342 
1343             return this;
1344         },
1345 
1346         // Already documented in GeometryElement
1347         bounds: function () {
1348             var minX = Infinity,
1349                 maxX = -Infinity,
1350                 minY = Infinity,
1351                 maxY = -Infinity,
1352                 l = this.points.length,
1353                 i,
1354                 bezier,
1355                 up;
1356 
1357             if (this.bezierDegree === 3) {
1358                 // Add methods X(), Y()
1359                 for (i = 0; i < l; i++) {
1360                     this.points[i].X = Type.bind(function () {
1361                         return this.usrCoords[1];
1362                     }, this.points[i]);
1363                     this.points[i].Y = Type.bind(function () {
1364                         return this.usrCoords[2];
1365                     }, this.points[i]);
1366                 }
1367                 bezier = Numerics.bezier(this.points);
1368                 up = bezier[3]();
1369                 minX = Numerics.fminbr(
1370                     function (t) {
1371                         return bezier[0](t);
1372                     },
1373                     [0, up]
1374                 );
1375                 maxX = Numerics.fminbr(
1376                     function (t) {
1377                         return -bezier[0](t);
1378                     },
1379                     [0, up]
1380                 );
1381                 minY = Numerics.fminbr(
1382                     function (t) {
1383                         return bezier[1](t);
1384                     },
1385                     [0, up]
1386                 );
1387                 maxY = Numerics.fminbr(
1388                     function (t) {
1389                         return -bezier[1](t);
1390                     },
1391                     [0, up]
1392                 );
1393 
1394                 minX = bezier[0](minX);
1395                 maxX = bezier[0](maxX);
1396                 minY = bezier[1](minY);
1397                 maxY = bezier[1](maxY);
1398                 return [minX, maxY, maxX, minY];
1399             }
1400 
1401             // Linear segments
1402             for (i = 0; i < l; i++) {
1403                 if (minX > this.points[i].usrCoords[1]) {
1404                     minX = this.points[i].usrCoords[1];
1405                 }
1406 
1407                 if (maxX < this.points[i].usrCoords[1]) {
1408                     maxX = this.points[i].usrCoords[1];
1409                 }
1410 
1411                 if (minY > this.points[i].usrCoords[2]) {
1412                     minY = this.points[i].usrCoords[2];
1413                 }
1414 
1415                 if (maxY < this.points[i].usrCoords[2]) {
1416                     maxY = this.points[i].usrCoords[2];
1417                 }
1418             }
1419 
1420             return [minX, maxY, maxX, minY];
1421         },
1422 
1423         // documented in element.js
1424         getParents: function () {
1425             var p = [this.xterm, this.yterm, this.minX(), this.maxX()];
1426 
1427             if (this.parents.length !== 0) {
1428                 p = this.parents;
1429             }
1430 
1431             return p;
1432         },
1433 
1434         /**
1435          * Shift the curve by the vector 'where'.
1436          *
1437          * @param {Array} where Array containing the x and y coordinate of the target location.
1438          * @returns {JXG.Curve} Reference to itself.
1439          */
1440         moveTo: function (where) {
1441             // TODO add animation
1442             var delta = [],
1443                 p;
1444             if (this.points.length > 0 && !this.evalVisProp('fixed')) {
1445                 p = this.points[0];
1446                 if (where.length === 3) {
1447                     delta = [
1448                         where[0] - p.usrCoords[0],
1449                         where[1] - p.usrCoords[1],
1450                         where[2] - p.usrCoords[2]
1451                     ];
1452                 } else {
1453                     delta = [where[0] - p.usrCoords[1], where[1] - p.usrCoords[2]];
1454                 }
1455                 this.setPosition(Const.COORDS_BY_USER, delta);
1456                 return this.board.update(this);
1457             }
1458             return this;
1459         },
1460 
1461         /**
1462          * If the curve is the result of a transformation applied
1463          * to a continuous curve, the glider projection has to be done
1464          * on the original curve. Otherwise there will be problems
1465          * when changing between high and low precision plotting,
1466          * since there number of points changes.
1467          *
1468          * @private
1469          * @returns {Array} [Boolean, curve]: Array contining 'true' if curve is result of a transformation,
1470          *   and the source curve of the transformation.
1471          */
1472         getTransformationSource: function () {
1473             var isTransformed, curve_org;
1474             if (Type.exists(this._transformationSource)) {
1475                 curve_org = this._transformationSource;
1476                 if (
1477                     curve_org.elementClass === Const.OBJECT_CLASS_CURVE //&&
1478                     //curve_org.evalVisProp('curvetype') !== 'plot'
1479                 ) {
1480                     isTransformed = true;
1481                 }
1482             }
1483             return [isTransformed, curve_org];
1484         }
1485 
1486         // See JXG.Math.Geometry.pnpoly
1487         // pnpoly: function (x_in, y_in, coord_type) {
1488         //     var i,
1489         //         j,
1490         //         len,
1491         //         x,
1492         //         y,
1493         //         crds,
1494         //         v = this.points,
1495         //         isIn = false;
1496 
1497         //     if (coord_type === Const.COORDS_BY_USER) {
1498         //         crds = new Coords(Const.COORDS_BY_USER, [x_in, y_in], this.board);
1499         //         x = crds.scrCoords[1];
1500         //         y = crds.scrCoords[2];
1501         //     } else {
1502         //         x = x_in;
1503         //         y = y_in;
1504         //     }
1505 
1506         //     len = this.points.length;
1507         //     for (i = 0, j = len - 2; i < len - 1; j = i++) {
1508         //         if (
1509         //             v[i].scrCoords[2] > y !== v[j].scrCoords[2] > y &&
1510         //             x <
1511         //                 ((v[j].scrCoords[1] - v[i].scrCoords[1]) * (y - v[i].scrCoords[2])) /
1512         //                     (v[j].scrCoords[2] - v[i].scrCoords[2]) +
1513         //                     v[i].scrCoords[1]
1514         //         ) {
1515         //             isIn = !isIn;
1516         //         }
1517         //     }
1518 
1519         //     return isIn;
1520         // }
1521     }
1522 );
1523 
1524 /**
1525  * @class  Curves can be defined by mappings or by discrete data sets.
1526  * In general, a curve is a mapping from R to R^2, where t maps to (x(t),y(t)). The graph is drawn for t in the interval [a,b].
1527  * <p>
1528  * The following types of curves can be plotted:
1529  * <ul>
1530  *  <li> parametric curves: t mapsto (x(t),y(t)), where x() and y() are univariate functions.
1531  *  <li> polar curves: curves commonly written with polar equations like spirals and cardioids.
1532  *  <li> data plots: plot line segments through a given list of coordinates.
1533  * </ul>
1534  * @pseudo
1535  * @name Curve
1536  * @augments JXG.Curve
1537  * @constructor
1538  * @type Object
1539  * @description JXG.Curve
1540 
1541  * @param {function,number_function,number_function,number_function,number}  x,y,a_,b_ Parent elements for Parametric Curves.
1542  *                     <p>
1543  *                     x describes the x-coordinate of the curve. It may be a function term in one variable, e.g. x(t).
1544  *                     In case of x being of type number, x(t) is set to  a constant function.
1545  *                     this function at the values of the array.
1546  *                     </p>
1547  *                     <p>
1548  *                     y describes the y-coordinate of the curve. In case of a number, y(t) is set to the constant function
1549  *                     returning this number.
1550  *                     </p>
1551  *                     <p>
1552  *                     Further parameters are an optional number or function for the left interval border a,
1553  *                     and an optional number or function for the right interval border b.
1554  *                     </p>
1555  *                     <p>
1556  *                     Default values are a=-10 and b=10.
1557  *                     </p>
1558  *
1559  * @param {array_array,function,number}
1560  *
1561  * @description x,y Parent elements for Data Plots.
1562  *                     <p>
1563  *                     x and y are arrays contining the x and y coordinates of the data points which are connected by
1564  *                     line segments. The individual entries of x and y may also be functions.
1565  *                     In case of x being an array the curve type is data plot, regardless of the second parameter and
1566  *                     if additionally the second parameter y is a function term the data plot evaluates.
1567  *                     </p>
1568  * @param {function_array,function,number_function,number_function,number}
1569  * @description r,offset_,a_,b_ Parent elements for Polar Curves.
1570  *                     <p>
1571  *                     The first parameter is a function term r(phi) describing the polar curve.
1572  *                     </p>
1573  *                     <p>
1574  *                     The second parameter is the offset of the curve. It has to be
1575  *                     an array containing numbers or functions describing the offset. Default value is the origin [0,0].
1576  *                     </p>
1577  *                     <p>
1578  *                     Further parameters are an optional number or function for the left interval border a,
1579  *                     and an optional number or function for the right interval border b.
1580  *                     </p>
1581  *                     <p>
1582  *                     Default values are a=-10 and b=10.
1583  *                     </p>
1584  * <p>
1585  * Additionally, a curve can be created by providing a curve and a transformation (or an array of transformations).
1586  * The result is a curve which is the transformation of the supplied curve.
1587  *
1588  * @see JXG.Curve
1589  * @example
1590  * // Parametric curve
1591  * // Create a curve of the form (t-sin(t), 1-cos(t), i.e.
1592  * // the cycloid curve.
1593  *   var graph = board.create('curve',
1594  *                        [function(t){ return t-Math.sin(t);},
1595  *                         function(t){ return 1-Math.cos(t);},
1596  *                         0, 2*Math.PI]
1597  *                     );
1598  * </pre><div class="jxgbox" id="JXGaf9f818b-f3b6-4c4d-8c4c-e4a4078b726d" style="width: 300px; height: 300px;"></div>
1599  * <script type="text/javascript">
1600  *   var c1_board = JXG.JSXGraph.initBoard('JXGaf9f818b-f3b6-4c4d-8c4c-e4a4078b726d', {boundingbox: [-1, 5, 7, -1], axis: true, showcopyright: false, shownavigation: false});
1601  *   var graph1 = c1_board.create('curve', [function(t){ return t-Math.sin(t);},function(t){ return 1-Math.cos(t);},0, 2*Math.PI]);
1602  * </script><pre>
1603  * @example
1604  * // Data plots
1605  * // Connect a set of points given by coordinates with dashed line segments.
1606  * // The x- and y-coordinates of the points are given in two separate
1607  * // arrays.
1608  *   var x = [0,1,2,3,4,5,6,7,8,9];
1609  *   var y = [9.2,1.3,7.2,-1.2,4.0,5.3,0.2,6.5,1.1,0.0];
1610  *   var graph = board.create('curve', [x,y], {dash:2});
1611  * </pre><div class="jxgbox" id="JXG7dcbb00e-b6ff-481d-b4a8-887f5d8c6a83" style="width: 300px; height: 300px;"></div>
1612  * <script type="text/javascript">
1613  *   var c3_board = JXG.JSXGraph.initBoard('JXG7dcbb00e-b6ff-481d-b4a8-887f5d8c6a83', {boundingbox: [-1,10,10,-1], axis: true, showcopyright: false, shownavigation: false});
1614  *   var x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
1615  *   var y = [9.2, 1.3, 7.2, -1.2, 4.0, 5.3, 0.2, 6.5, 1.1, 0.0];
1616  *   var graph3 = c3_board.create('curve', [x,y], {dash:2});
1617  * </script><pre>
1618  * @example
1619  * // Polar plot
1620  * // Create a curve with the equation r(phi)= a*(1+phi), i.e.
1621  * // a cardioid.
1622  *   var a = board.create('slider',[[0,2],[2,2],[0,1,2]]);
1623  *   var graph = board.create('curve',
1624  *                        [function(phi){ return a.Value()*(1-Math.cos(phi));},
1625  *                         [1,0],
1626  *                         0, 2*Math.PI],
1627  *                         {curveType: 'polar'}
1628  *                     );
1629  * </pre><div class="jxgbox" id="JXGd0bc7a2a-8124-45ca-a6e7-142321a8f8c2" style="width: 300px; height: 300px;"></div>
1630  * <script type="text/javascript">
1631  *   var c2_board = JXG.JSXGraph.initBoard('JXGd0bc7a2a-8124-45ca-a6e7-142321a8f8c2', {boundingbox: [-3,3,3,-3], axis: true, showcopyright: false, shownavigation: false});
1632  *   var a = c2_board.create('slider',[[0,2],[2,2],[0,1,2]]);
1633  *   var graph2 = c2_board.create('curve', [function(phi){ return a.Value()*(1-Math.cos(phi));}, [1,0], 0, 2*Math.PI], {curveType: 'polar'});
1634  * </script><pre>
1635  *
1636  * @example
1637  *  // Draggable Bezier curve
1638  *  var col, p, c;
1639  *  col = 'blue';
1640  *  p = [];
1641  *  p.push(board.create('point',[-2, -1 ], {size: 5, strokeColor:col, fillColor:col}));
1642  *  p.push(board.create('point',[1, 2.5 ], {size: 5, strokeColor:col, fillColor:col}));
1643  *  p.push(board.create('point',[-1, -2.5 ], {size: 5, strokeColor:col, fillColor:col}));
1644  *  p.push(board.create('point',[2, -2], {size: 5, strokeColor:col, fillColor:col}));
1645  *
1646  *  c = board.create('curve', JXG.Math.Numerics.bezier(p),
1647  *              {strokeColor:'red', name:"curve", strokeWidth:5, fixed: false}); // Draggable curve
1648  *  c.addParents(p);
1649  * </pre><div class="jxgbox" id="JXG7bcc6280-f6eb-433e-8281-c837c3387849" style="width: 300px; height: 300px;"></div>
1650  * <script type="text/javascript">
1651  * (function(){
1652  *  var board, col, p, c;
1653  *  board = JXG.JSXGraph.initBoard('JXG7bcc6280-f6eb-433e-8281-c837c3387849', {boundingbox: [-3,3,3,-3], axis: true, showcopyright: false, shownavigation: false});
1654  *  col = 'blue';
1655  *  p = [];
1656  *  p.push(board.create('point',[-2, -1 ], {size: 5, strokeColor:col, fillColor:col}));
1657  *  p.push(board.create('point',[1, 2.5 ], {size: 5, strokeColor:col, fillColor:col}));
1658  *  p.push(board.create('point',[-1, -2.5 ], {size: 5, strokeColor:col, fillColor:col}));
1659  *  p.push(board.create('point',[2, -2], {size: 5, strokeColor:col, fillColor:col}));
1660  *
1661  *  c = board.create('curve', JXG.Math.Numerics.bezier(p),
1662  *              {strokeColor:'red', name:"curve", strokeWidth:5, fixed: false}); // Draggable curve
1663  *  c.addParents(p);
1664  * })();
1665  * </script><pre>
1666  *
1667  * @example
1668  *         // The curve cu2 is the reflection of cu1 against line li
1669  *         var li = board.create('line', [1,1,1], {strokeColor: '#aaaaaa'});
1670  *         var reflect = board.create('transform', [li], {type: 'reflect'});
1671  *         var cu1 = board.create('curve', [[-1, -1, -0.5, -1, -1, -0.5], [-3, -2, -2, -2, -2.5, -2.5]]);
1672  *         var cu2 = board.create('curve', [cu1, reflect], {strokeColor: 'red'});
1673  *
1674  * </pre><div id="JXG866dc7a2-d448-11e7-93b3-901b0e1b8723" class="jxgbox" style="width: 300px; height: 300px;"></div>
1675  * <script type="text/javascript">
1676  *     (function() {
1677  *         var board = JXG.JSXGraph.initBoard('JXG866dc7a2-d448-11e7-93b3-901b0e1b8723',
1678  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
1679  *             var li = board.create('line', [1,1,1], {strokeColor: '#aaaaaa'});
1680  *             var reflect = board.create('transform', [li], {type: 'reflect'});
1681  *             var cu1 = board.create('curve', [[-1, -1, -0.5, -1, -1, -0.5], [-3, -2, -2, -2, -2.5, -2.5]]);
1682  *             var cu2 = board.create('curve', [cu1, reflect], {strokeColor: 'red'});
1683  *
1684  *     })();
1685  *
1686  * </script><pre>
1687  */
1688 JXG.createCurve = function (board, parents, attributes) {
1689     var obj,
1690         cu,
1691         attr = Type.copyAttributes(attributes, board.options, 'curve');
1692 
1693     obj = board.select(parents[0], true);
1694     if (
1695         Type.isTransformationOrArray(parents[1]) &&
1696         Type.isObject(obj) &&
1697         (obj.type === Const.OBJECT_TYPE_CURVE ||
1698             obj.type === Const.OBJECT_TYPE_ANGLE ||
1699             obj.type === Const.OBJECT_TYPE_ARC ||
1700             obj.type === Const.OBJECT_TYPE_CONIC ||
1701             obj.type === Const.OBJECT_TYPE_SECTOR)
1702     ) {
1703         if (obj.type === Const.OBJECT_TYPE_SECTOR) {
1704             attr = Type.copyAttributes(attributes, board.options, 'sector');
1705         } else if (obj.type === Const.OBJECT_TYPE_ARC) {
1706             attr = Type.copyAttributes(attributes, board.options, 'arc');
1707         } else if (obj.type === Const.OBJECT_TYPE_ANGLE) {
1708             if (!Type.exists(attributes.withLabel)) {
1709                 attributes.withLabel = false;
1710             }
1711             attr = Type.copyAttributes(attributes, board.options, 'angle');
1712         } else {
1713             attr = Type.copyAttributes(attributes, board.options, 'curve');
1714         }
1715         attr = Type.copyAttributes(attr, board.options, 'curve');
1716 
1717         cu = new JXG.Curve(board, ["x", [], []], attr);
1718         /**
1719          * @class
1720          * @ignore
1721          */
1722         cu.updateDataArray = function () {
1723             var i,
1724                 le = obj.numberPoints;
1725             this.bezierDegree = obj.bezierDegree;
1726             this.dataX = [];
1727             this.dataY = [];
1728             for (i = 0; i < le; i++) {
1729                 this.dataX.push(obj.points[i].usrCoords[1]);
1730                 this.dataY.push(obj.points[i].usrCoords[2]);
1731             }
1732             return this;
1733         };
1734         cu.addTransform(parents[1]);
1735         obj.addChild(cu);
1736         cu.setParents([obj]);
1737         cu._transformationSource = obj;
1738 
1739         return cu;
1740     }
1741     attr = Type.copyAttributes(attributes, board.options, 'curve');
1742     return new JXG.Curve(board, ["x"].concat(parents), attr);
1743 };
1744 
1745 JXG.registerElement("curve", JXG.createCurve);
1746 
1747 /**
1748  * @class A functiongraph visualizes a map x → f(x).
1749  * The graph is displayed for x in the interval [a,b] and is a {@link Curve} element.
1750  * @pseudo
1751  * @name Functiongraph
1752  * @augments JXG.Curve
1753  * @constructor
1754  * @type JXG.Curve
1755  * @param {function_number,function_number,function} f,a_,b_ Parent elements are a function term f(x) describing the function graph.
1756  *         <p>
1757  *         Further, an optional number or function for the left interval border a,
1758  *         and an optional number or function for the right interval border b.
1759  *         <p>
1760  *         Default values are a=-10 and b=10.
1761  * @see JXG.Curve
1762  * @example
1763  * // Create a function graph for f(x) = 0.5*x*x-2*x
1764  *   var graph = board.create('functiongraph',
1765  *                        [function(x){ return 0.5*x*x-2*x;}, -2, 4]
1766  *                     );
1767  * </pre><div class="jxgbox" id="JXGefd432b5-23a3-4846-ac5b-b471e668b437" style="width: 300px; height: 300px;"></div>
1768  * <script type="text/javascript">
1769  *   var alex1_board = JXG.JSXGraph.initBoard('JXGefd432b5-23a3-4846-ac5b-b471e668b437', {boundingbox: [-3, 7, 5, -3], axis: true, showcopyright: false, shownavigation: false});
1770  *   var graph = alex1_board.create('functiongraph', [function(x){ return 0.5*x*x-2*x;}, -2, 4]);
1771  * </script><pre>
1772  * @example
1773  * // Create a function graph for f(x) = 0.5*x*x-2*x with variable interval
1774  *   var s = board.create('slider',[[0,4],[3,4],[-2,4,5]]);
1775  *   var graph = board.create('functiongraph',
1776  *                        [function(x){ return 0.5*x*x-2*x;},
1777  *                         -2,
1778  *                         function(){return s.Value();}]
1779  *                     );
1780  * </pre><div class="jxgbox" id="JXG4a203a84-bde5-4371-ad56-44619690bb50" style="width: 300px; height: 300px;"></div>
1781  * <script type="text/javascript">
1782  *   var alex2_board = JXG.JSXGraph.initBoard('JXG4a203a84-bde5-4371-ad56-44619690bb50', {boundingbox: [-3, 7, 5, -3], axis: true, showcopyright: false, shownavigation: false});
1783  *   var s = alex2_board.create('slider',[[0,4],[3,4],[-2,4,5]]);
1784  *   var graph = alex2_board.create('functiongraph', [function(x){ return 0.5*x*x-2*x;}, -2, function(){return s.Value();}]);
1785  * </script><pre>
1786  */
1787 JXG.createFunctiongraph = function (board, parents, attributes) {
1788     var attr,
1789         par = ["x", "x"].concat(parents); // variable name and identity function for x-coordinate
1790     // par = ["x", function(x) { return x; }].concat(parents);
1791 
1792     attr = Type.copyAttributes(attributes, board.options, 'functiongraph');
1793     attr = Type.copyAttributes(attr, board.options, 'curve');
1794     attr.curvetype = 'functiongraph';
1795     return new JXG.Curve(board, par, attr);
1796 };
1797 
1798 JXG.registerElement("functiongraph", JXG.createFunctiongraph);
1799 JXG.registerElement("plot", JXG.createFunctiongraph);
1800 
1801 /**
1802  * @class The (natural) cubic spline curves (function graph) interpolating a set of points.
1803  * Create a dynamic spline interpolated curve given by sample points p_1 to p_n.
1804  * @pseudo
1805  * @name Spline
1806  * @augments JXG.Curve
1807  * @constructor
1808  * @type JXG.Curve
1809  * @param {JXG.Board} board Reference to the board the spline is drawn on.
1810  * @param {Array} parents Array of points the spline interpolates. This can be
1811  *   <ul>
1812  *   <li> an array of JSXGraph points</li>
1813  *   <li> an array of coordinate pairs</li>
1814  *   <li> an array of functions returning coordinate pairs</li>
1815  *   <li> an array consisting of an array with x-coordinates and an array of y-coordinates</li>
1816  *   </ul>
1817  *   All individual entries of coordinates arrays may be numbers or functions returning numbers.
1818  * @param {Object} attributes Define color, width, ... of the spline
1819  * @returns {JXG.Curve} Returns reference to an object of type JXG.Curve.
1820  * @see JXG.Curve
1821  * @example
1822  *
1823  * var p = [];
1824  * p[0] = board.create('point', [-2,2], {size: 4, face: 'o'});
1825  * p[1] = board.create('point', [0,-1], {size: 4, face: 'o'});
1826  * p[2] = board.create('point', [2,0], {size: 4, face: 'o'});
1827  * p[3] = board.create('point', [4,1], {size: 4, face: 'o'});
1828  *
1829  * var c = board.create('spline', p, {strokeWidth:3});
1830  * </pre><div id="JXG6c197afc-e482-11e5-b1bf-901b0e1b8723" style="width: 300px; height: 300px;"></div>
1831  * <script type="text/javascript">
1832  *     (function() {
1833  *         var board = JXG.JSXGraph.initBoard('JXG6c197afc-e482-11e5-b1bf-901b0e1b8723',
1834  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
1835  *
1836  *     var p = [];
1837  *     p[0] = board.create('point', [-2,2], {size: 4, face: 'o'});
1838  *     p[1] = board.create('point', [0,-1], {size: 4, face: 'o'});
1839  *     p[2] = board.create('point', [2,0], {size: 4, face: 'o'});
1840  *     p[3] = board.create('point', [4,1], {size: 4, face: 'o'});
1841  *
1842  *     var c = board.create('spline', p, {strokeWidth:3});
1843  *     })();
1844  *
1845  * </script><pre>
1846  *
1847  */
1848 JXG.createSpline = function (board, parents, attributes) {
1849     var el, funcs, ret;
1850 
1851     funcs = function () {
1852         var D,
1853             x = [],
1854             y = [];
1855 
1856         return [
1857             function (t, suspended) {
1858                 // Function term
1859                 var i, j, c;
1860 
1861                 if (!suspended) {
1862                     x = [];
1863                     y = [];
1864 
1865                     // given as [x[], y[]]
1866                     if (
1867                         parents.length === 2 &&
1868                         Type.isArray(parents[0]) &&
1869                         Type.isArray(parents[1]) &&
1870                         parents[0].length === parents[1].length
1871                     ) {
1872                         for (i = 0; i < parents[0].length; i++) {
1873                             if (Type.isFunction(parents[0][i])) {
1874                                 x.push(parents[0][i]());
1875                             } else {
1876                                 x.push(parents[0][i]);
1877                             }
1878 
1879                             if (Type.isFunction(parents[1][i])) {
1880                                 y.push(parents[1][i]());
1881                             } else {
1882                                 y.push(parents[1][i]);
1883                             }
1884                         }
1885                     } else {
1886                         for (i = 0; i < parents.length; i++) {
1887                             if (Type.isPoint(parents[i])) {
1888                                 x.push(parents[i].X());
1889                                 y.push(parents[i].Y());
1890                                 // given as [[x1,y1], [x2, y2], ...]
1891                             } else if (Type.isArray(parents[i]) && parents[i].length === 2) {
1892                                 for (j = 0; j < parents.length; j++) {
1893                                     if (Type.isFunction(parents[j][0])) {
1894                                         x.push(parents[j][0]());
1895                                     } else {
1896                                         x.push(parents[j][0]);
1897                                     }
1898 
1899                                     if (Type.isFunction(parents[j][1])) {
1900                                         y.push(parents[j][1]());
1901                                     } else {
1902                                         y.push(parents[j][1]);
1903                                     }
1904                                 }
1905                             } else if (
1906                                 Type.isFunction(parents[i]) &&
1907                                 parents[i]().length === 2
1908                             ) {
1909                                 c = parents[i]();
1910                                 x.push(c[0]);
1911                                 y.push(c[1]);
1912                             }
1913                         }
1914                     }
1915 
1916                     // The array D has only to be calculated when the position of one or more sample points
1917                     // changes. Otherwise D is always the same for all points on the spline.
1918                     D = Numerics.splineDef(x, y);
1919                 }
1920 
1921                 return Numerics.splineEval(t, x, y, D);
1922             },
1923             // minX()
1924             function () {
1925                 return x[0];
1926             },
1927             //maxX()
1928             function () {
1929                 return x[x.length - 1];
1930             }
1931         ];
1932     };
1933 
1934     attributes = Type.copyAttributes(attributes, board.options, 'curve');
1935     attributes.curvetype = 'functiongraph';
1936     ret = funcs();
1937     el = new JXG.Curve(board, ["x", "x", ret[0], ret[1], ret[2]], attributes);
1938     el.setParents(parents);
1939     el.elType = 'spline';
1940 
1941     return el;
1942 };
1943 
1944 /**
1945  * Register the element type spline at JSXGraph
1946  * @private
1947  */
1948 JXG.registerElement("spline", JXG.createSpline);
1949 
1950 /**
1951  * @class Cardinal spline curve through a given data set.
1952  * Create a dynamic cardinal spline interpolated curve given by sample points p_1 to p_n.
1953  * @pseudo
1954  * @name Cardinalspline
1955  * @augments JXG.Curve
1956  * @constructor
1957  * @type JXG.Curve
1958  * @param {Array} points Points array defining the cardinal spline. This can be
1959  *   <ul>
1960  *   <li> an array of JSXGraph points</li>
1961  *   <li> an array of coordinate pairs</li>
1962  *   <li> an array of functions returning coordinate pairs</li>
1963  *   <li> an array consisting of an array with x-coordinates and an array of y-coordinates</li>
1964  *   </ul>
1965  *   All individual entries of coordinates arrays may be numbers or functions returning numbers.
1966  * @param {function,Number} tau Tension parameter
1967  * @param {String} [type='uniform'] Type of the cardinal spline, may be 'uniform' (default) or 'centripetal'
1968  * @see JXG.Curve
1969  * @example
1970  * //Create a cardinal spline out of an array of JXG points with adjustable tension
1971  *
1972  * //Create array of points
1973  * var p = [];
1974  * p.push(board.create('point',[0,0]));
1975  * p.push(board.create('point',[1,4]));
1976  * p.push(board.create('point',[4,5]));
1977  * p.push(board.create('point',[2,3]));
1978  * p.push(board.create('point',[3,0]));
1979  *
1980  * // tension
1981  * var tau = board.create('slider', [[-4,-5],[2,-5],[0.001,0.5,1]], {name:'tau'});
1982  * var c = board.create('cardinalspline', [p, function(){ return tau.Value();}], {strokeWidth:3});
1983  *
1984  * </pre><div id="JXG1537cb69-4d45-43aa-8fc3-c6d4f98b4cdd" class="jxgbox" style="width: 300px; height: 300px;"></div>
1985  * <script type="text/javascript">
1986  *     (function() {
1987  *         var board = JXG.JSXGraph.initBoard('JXG1537cb69-4d45-43aa-8fc3-c6d4f98b4cdd',
1988  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
1989  *     //Create a cardinal spline out of an array of JXG points with adjustable tension
1990  *
1991  *     //Create array of points
1992  *     var p = [];
1993  *     p.push(board.create('point',[0,0]));
1994  *     p.push(board.create('point',[1,4]));
1995  *     p.push(board.create('point',[4,5]));
1996  *     p.push(board.create('point',[2,3]));
1997  *     p.push(board.create('point',[3,0]));
1998  *
1999  *     // tension
2000  *     var tau = board.create('slider', [[-4,-5],[2,-5],[0.001,0.5,1]], {name:'tau'});
2001  *     var c = board.create('cardinalspline', [p, function(){ return tau.Value();}], {strokeWidth:3});
2002  *
2003  *     })();
2004  *
2005  * </script><pre>
2006  *
2007  */
2008 JXG.createCardinalSpline = function (board, parents, attributes) {
2009     var el,
2010         getPointLike,
2011         points,
2012         tau,
2013         type,
2014         p,
2015         q,
2016         i,
2017         le,
2018         splineArr,
2019         errStr = "\nPossible parent types: [points:array, tau:number|function, type:string]";
2020 
2021     if (!Type.exists(parents[0]) || !Type.isArray(parents[0])) {
2022         throw new Error(
2023             "JSXGraph: JXG.createCardinalSpline: argument 1 'points' has to be array of points or coordinate pairs" +
2024             errStr
2025         );
2026     }
2027     if (
2028         !Type.exists(parents[1]) ||
2029         (!Type.isNumber(parents[1]) && !Type.isFunction(parents[1]))
2030     ) {
2031         throw new Error(
2032             "JSXGraph: JXG.createCardinalSpline: argument 2 'tau' has to be number between [0,1] or function'" +
2033             errStr
2034         );
2035     }
2036     if (!Type.exists(parents[2]) || !Type.isString(parents[2])) {
2037         type = 'uniform';
2038         // throw new Error(
2039         //     "JSXGraph: JXG.createCardinalSpline: argument 3 'type' has to be string 'uniform' or 'centripetal'" +
2040         //     errStr
2041         // );
2042     } else {
2043         type = parents[2];
2044     }
2045 
2046     attributes = Type.copyAttributes(attributes, board.options, 'curve');
2047     attributes = Type.copyAttributes(attributes, board.options, 'cardinalspline');
2048     attributes.curvetype = 'parameter';
2049 
2050     p = parents[0];
2051     q = [];
2052 
2053     // Given as [x[], y[]]
2054     if (
2055         !attributes.isarrayofcoordinates &&
2056         p.length === 2 &&
2057         Type.isArray(p[0]) &&
2058         Type.isArray(p[1]) &&
2059         p[0].length === p[1].length
2060     ) {
2061         for (i = 0; i < p[0].length; i++) {
2062             q[i] = [];
2063             if (Type.isFunction(p[0][i])) {
2064                 q[i].push(p[0][i]());
2065             } else {
2066                 q[i].push(p[0][i]);
2067             }
2068 
2069             if (Type.isFunction(p[1][i])) {
2070                 q[i].push(p[1][i]());
2071             } else {
2072                 q[i].push(p[1][i]);
2073             }
2074         }
2075     } else {
2076         // given as [[x0, y0], [x1, y1], point, ...]
2077         for (i = 0; i < p.length; i++) {
2078             if (Type.isString(p[i])) {
2079                 q.push(board.select(p[i]));
2080             } else if (Type.isPoint(p[i])) {
2081                 q.push(p[i]);
2082                 // given as [[x0,y0], [x1, y2], ...]
2083             } else if (Type.isArray(p[i]) && p[i].length === 2) {
2084                 q[i] = [];
2085                 if (Type.isFunction(p[i][0])) {
2086                     q[i].push(p[i][0]());
2087                 } else {
2088                     q[i].push(p[i][0]);
2089                 }
2090 
2091                 if (Type.isFunction(p[i][1])) {
2092                     q[i].push(p[i][1]());
2093                 } else {
2094                     q[i].push(p[i][1]);
2095                 }
2096             } else if (Type.isFunction(p[i]) && p[i]().length === 2) {
2097                 q.push(parents[i]());
2098             }
2099         }
2100     }
2101 
2102     if (attributes.createpoints === true) {
2103         points = Type.providePoints(board, q, attributes, "cardinalspline", ["points"]);
2104     } else {
2105         points = [];
2106 
2107         /**
2108          * @ignore
2109          */
2110         getPointLike = function (ii) {
2111             return {
2112                 X: function () {
2113                     return q[ii][0];
2114                 },
2115                 Y: function () {
2116                     return q[ii][1];
2117                 },
2118                 Dist: function (p) {
2119                     var dx = this.X() - p.X(),
2120                         dy = this.Y() - p.Y();
2121 
2122                     return Mat.hypot(dx, dy);
2123                 }
2124             };
2125         };
2126 
2127         for (i = 0; i < q.length; i++) {
2128             if (Type.isPoint(q[i])) {
2129                 points.push(q[i]);
2130             } else {
2131                 points.push(getPointLike(i));
2132             }
2133         }
2134     }
2135 
2136     tau = parents[1];
2137     // type = parents[2];
2138 
2139     splineArr = ["x"].concat(Numerics.CardinalSpline(points, tau, type));
2140 
2141     el = new JXG.Curve(board, splineArr, attributes);
2142     le = points.length;
2143     el.setParents(points);
2144     for (i = 0; i < le; i++) {
2145         p = points[i];
2146         if (Type.isPoint(p)) {
2147             if (Type.exists(p._is_new)) {
2148                 el.addChild(p);
2149                 delete p._is_new;
2150             } else {
2151                 p.addChild(el);
2152             }
2153         }
2154     }
2155     el.elType = 'cardinalspline';
2156 
2157     return el;
2158 };
2159 
2160 /**
2161  * Register the element type cardinalspline at JSXGraph
2162  * @private
2163  */
2164 JXG.registerElement("cardinalspline", JXG.createCardinalSpline);
2165 
2166 /**
2167  * @class Interpolate data points by the spline curve from Metapost (by Donald Knuth and John Hobby).
2168  * Create a dynamic metapost spline interpolated curve given by sample points p_1 to p_n.
2169  * @pseudo
2170  * @name Metapostspline
2171  * @augments JXG.Curve
2172  * @constructor
2173  * @type JXG.Curve
2174  * @param {JXG.Board} board Reference to the board the metapost spline is drawn on.
2175  * @param {Array} parents Array with two entries.
2176  * <p>
2177  *   First entry: Array of points the spline interpolates. This can be
2178  *   <ul>
2179  *   <li> an array of JSXGraph points</li>
2180  *   <li> an object of coordinate pairs</li>
2181  *   <li> an array of functions returning coordinate pairs</li>
2182  *   <li> an array consisting of an array with x-coordinates and an array of y-coordinates</li>
2183  *   </ul>
2184  *   All individual entries of coordinates arrays may be numbers or functions returning numbers.
2185  *   <p>
2186  *   Second entry: JavaScript object containing the control values like tension, direction, curl.
2187  * @param {Object} attributes Define color, width, ... of the metapost spline
2188  * @returns {JXG.Curve} Returns reference to an object of type JXG.Curve.
2189  * @see JXG.Curve
2190  * @example
2191  *     var po = [],
2192  *         attr = {
2193  *             size: 5,
2194  *             color: 'red'
2195  *         },
2196  *         controls;
2197  *
2198  *     var tension = board.create('slider', [[-3, 6], [3, 6], [0, 1, 20]], {name: 'tension'});
2199  *     var curl = board.create('slider', [[-3, 5], [3, 5], [0, 1, 30]], {name: 'curl A, D'});
2200  *     var dir = board.create('slider', [[-3, 4], [3, 4], [-180, 0, 180]], {name: 'direction B'});
2201  *
2202  *     po.push(board.create('point', [-3, -3]));
2203  *     po.push(board.create('point', [0, -3]));
2204  *     po.push(board.create('point', [4, -5]));
2205  *     po.push(board.create('point', [6, -2]));
2206  *
2207  *     var controls = {
2208  *         tension: function() {return tension.Value(); },
2209  *         direction: { 1: function() {return dir.Value(); } },
2210  *         curl: { 0: function() {return curl.Value(); },
2211  *                 3: function() {return curl.Value(); }
2212  *             },
2213  *         isClosed: false
2214  *     };
2215  *
2216  *     // Plot a metapost curve
2217  *     var cu = board.create('metapostspline', [po, controls], {strokeColor: 'blue', strokeWidth: 2});
2218  *
2219  *
2220  * </pre><div id="JXGb8c6ffed-7419-41a3-9e55-3754b2327ae9" class="jxgbox" style="width: 300px; height: 300px;"></div>
2221  * <script type="text/javascript">
2222  *     (function() {
2223  *         var board = JXG.JSXGraph.initBoard('JXGb8c6ffed-7419-41a3-9e55-3754b2327ae9',
2224  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
2225  *         var po = [],
2226  *             attr = {
2227  *                 size: 5,
2228  *                 color: 'red'
2229  *             },
2230  *             controls;
2231  *
2232  *         var tension = board.create('slider', [[-3, 6], [3, 6], [0, 1, 20]], {name: 'tension'});
2233  *         var curl = board.create('slider', [[-3, 5], [3, 5], [0, 1, 30]], {name: 'curl A, D'});
2234  *         var dir = board.create('slider', [[-3, 4], [3, 4], [-180, 0, 180]], {name: 'direction B'});
2235  *
2236  *         po.push(board.create('point', [-3, -3]));
2237  *         po.push(board.create('point', [0, -3]));
2238  *         po.push(board.create('point', [4, -5]));
2239  *         po.push(board.create('point', [6, -2]));
2240  *
2241  *         var controls = {
2242  *             tension: function() {return tension.Value(); },
2243  *             direction: { 1: function() {return dir.Value(); } },
2244  *             curl: { 0: function() {return curl.Value(); },
2245  *                     3: function() {return curl.Value(); }
2246  *                 },
2247  *             isClosed: false
2248  *         };
2249  *
2250  *         // Plot a metapost curve
2251  *         var cu = board.create('metapostspline', [po, controls], {strokeColor: 'blue', strokeWidth: 2});
2252  *
2253  *
2254  *     })();
2255  *
2256  * </script><pre>
2257  *
2258  */
2259 JXG.createMetapostSpline = function (board, parents, attributes) {
2260     var el,
2261         getPointLike,
2262         points,
2263         controls,
2264         p,
2265         q,
2266         i,
2267         le,
2268         errStr = "\nPossible parent types: [points:array, controls:object";
2269 
2270     if (!Type.exists(parents[0]) || !Type.isArray(parents[0])) {
2271         throw new Error(
2272             "JSXGraph: JXG.createMetapostSpline: argument 1 'points' has to be array of points or coordinate pairs" +
2273             errStr
2274         );
2275     }
2276     if (!Type.exists(parents[1]) || !Type.isObject(parents[1])) {
2277         throw new Error(
2278             "JSXGraph: JXG.createMetapostSpline: argument 2 'controls' has to be a JavaScript object'" +
2279             errStr
2280         );
2281     }
2282 
2283     attributes = Type.copyAttributes(attributes, board.options, 'curve');
2284     attributes = Type.copyAttributes(attributes, board.options, 'metapostspline');
2285     attributes.curvetype = 'parameter';
2286 
2287     p = parents[0];
2288     q = [];
2289 
2290     // given as [x[], y[]]
2291     if (
2292         !attributes.isarrayofcoordinates &&
2293         p.length === 2 &&
2294         Type.isArray(p[0]) &&
2295         Type.isArray(p[1]) &&
2296         p[0].length === p[1].length
2297     ) {
2298         for (i = 0; i < p[0].length; i++) {
2299             q[i] = [];
2300             if (Type.isFunction(p[0][i])) {
2301                 q[i].push(p[0][i]());
2302             } else {
2303                 q[i].push(p[0][i]);
2304             }
2305 
2306             if (Type.isFunction(p[1][i])) {
2307                 q[i].push(p[1][i]());
2308             } else {
2309                 q[i].push(p[1][i]);
2310             }
2311         }
2312     } else {
2313         // given as [[x0, y0], [x1, y1], point, ...]
2314         for (i = 0; i < p.length; i++) {
2315             if (Type.isString(p[i])) {
2316                 q.push(board.select(p[i]));
2317             } else if (Type.isPoint(p[i])) {
2318                 q.push(p[i]);
2319                 // given as [[x0,y0], [x1, y2], ...]
2320             } else if (Type.isArray(p[i]) && p[i].length === 2) {
2321                 q[i] = [];
2322                 if (Type.isFunction(p[i][0])) {
2323                     q[i].push(p[i][0]());
2324                 } else {
2325                     q[i].push(p[i][0]);
2326                 }
2327 
2328                 if (Type.isFunction(p[i][1])) {
2329                     q[i].push(p[i][1]());
2330                 } else {
2331                     q[i].push(p[i][1]);
2332                 }
2333             } else if (Type.isFunction(p[i]) && p[i]().length === 2) {
2334                 q.push(parents[i]());
2335             }
2336         }
2337     }
2338 
2339     if (attributes.createpoints === true) {
2340         points = Type.providePoints(board, q, attributes, 'metapostspline', ['points']);
2341     } else {
2342         points = [];
2343 
2344         /**
2345          * @ignore
2346          */
2347         getPointLike = function (ii) {
2348             return {
2349                 X: function () {
2350                     return q[ii][0];
2351                 },
2352                 Y: function () {
2353                     return q[ii][1];
2354                 }
2355             };
2356         };
2357 
2358         for (i = 0; i < q.length; i++) {
2359             if (Type.isPoint(q[i])) {
2360                 points.push(q[i]);
2361             } else {
2362                 points.push(getPointLike);
2363             }
2364         }
2365     }
2366 
2367     controls = parents[1];
2368 
2369     el = new JXG.Curve(board, ["t", [], [], 0, p.length - 1], attributes);
2370     /**
2371      * @class
2372      * @ignore
2373      */
2374     el.updateDataArray = function () {
2375         var res,
2376             i,
2377             len = points.length,
2378             p = [];
2379 
2380         for (i = 0; i < len; i++) {
2381             p.push([points[i].X(), points[i].Y()]);
2382         }
2383 
2384         res = Metapost.curve(p, controls);
2385         this.dataX = res[0];
2386         this.dataY = res[1];
2387     };
2388     el.bezierDegree = 3;
2389 
2390     le = points.length;
2391     el.setParents(points);
2392     for (i = 0; i < le; i++) {
2393         if (Type.isPoint(points[i])) {
2394             points[i].addChild(el);
2395         }
2396     }
2397     el.elType = 'metapostspline';
2398 
2399     return el;
2400 };
2401 
2402 JXG.registerElement("metapostspline", JXG.createMetapostSpline);
2403 
2404 /**
2405  * @class Visualize the Riemann sum which is an approximation of an integral by a finite sum.
2406  * It is realized as a special curve.
2407  * The returned element has the method Value() which returns the sum of the areas of the bars.
2408  * <p>
2409  * In case of type "simpson" and "trapezoidal", the horizontal line approximating the function value
2410  * is replaced by a parabola or a secant. IN case of "simpson",
2411  * the parabola is approximated visually by a polygonal chain of fixed step width.
2412  *
2413  * @pseudo
2414  * @name Riemannsum
2415  * @augments JXG.Curve
2416  * @constructor
2417  * @type Curve
2418  * @param {function,array_number,function_string,function_function,number_function,number} f,n,type_,a_,b_ Parent elements of Riemannsum are a
2419  *         Either a function term f(x) describing the function graph which is filled by the Riemann bars, or
2420  *         an array consisting of two functions and the area between is filled by the Riemann bars.
2421  *         <p>
2422  *         n determines the number of bars, it is either a fixed number or a function.
2423  *         <p>
2424  *         type is a string or function returning one of the values:  'left', 'right', 'middle', 'lower', 'upper', 'random', 'simpson', or 'trapezoidal'.
2425  *         Default value is 'left'. "simpson" is Simpson's 1/3 rule.
2426  *         <p>
2427  *         Further parameters are an optional number or function for the left interval border a,
2428  *         and an optional number or function for the right interval border b.
2429  *         <p>
2430  *         Default values are a=-10 and b=10.
2431  * @see JXG.Curve
2432  * @example
2433  * // Create Riemann sums for f(x) = 0.5*x*x-2*x.
2434  *   var s = board.create('slider',[[0,4],[3,4],[0,4,10]],{snapWidth:1});
2435  *   var f = function(x) { return 0.5*x*x-2*x; };
2436  *   var r = board.create('riemannsum',
2437  *               [f, function(){return s.Value();}, 'upper', -2, 5],
2438  *               {fillOpacity:0.4}
2439  *               );
2440  *   var g = board.create('functiongraph',[f, -2, 5]);
2441  *   var t = board.create('text',[-2,-2, function(){ return 'Sum=' + JXG.toFixed(r.Value(), 4); }]);
2442  * </pre><div class="jxgbox" id="JXG940f40cc-2015-420d-9191-c5d83de988cf" style="width: 300px; height: 300px;"></div>
2443  * <script type="text/javascript">
2444  * (function(){
2445  *   var board = JXG.JSXGraph.initBoard('JXG940f40cc-2015-420d-9191-c5d83de988cf', {boundingbox: [-3, 7, 5, -3], axis: true, showcopyright: false, shownavigation: false});
2446  *   var f = function(x) { return 0.5*x*x-2*x; };
2447  *   var s = board.create('slider',[[0,4],[3,4],[0,4,10]],{snapWidth:1});
2448  *   var r = board.create('riemannsum', [f, function(){return s.Value();}, 'upper', -2, 5], {fillOpacity:0.4});
2449  *   var g = board.create('functiongraph', [f, -2, 5]);
2450  *   var t = board.create('text',[-2,-2, function(){ return 'Sum=' + JXG.toFixed(r.Value(), 4); }]);
2451  * })();
2452  * </script><pre>
2453  *
2454  * @example
2455  *   // Riemann sum between two functions
2456  *   var s = board.create('slider',[[0,4],[3,4],[0,4,10]],{snapWidth:1});
2457  *   var g = function(x) { return 0.5*x*x-2*x; };
2458  *   var f = function(x) { return -x*(x-4); };
2459  *   var r = board.create('riemannsum',
2460  *               [[g,f], function(){return s.Value();}, 'lower', 0, 4],
2461  *               {fillOpacity:0.4}
2462  *               );
2463  *   var f = board.create('functiongraph',[f, -2, 5]);
2464  *   var g = board.create('functiongraph',[g, -2, 5]);
2465  *   var t = board.create('text',[-2,-2, function(){ return 'Sum=' + JXG.toFixed(r.Value(), 4); }]);
2466  * </pre><div class="jxgbox" id="JXGf9a7ba38-b50f-4a32-a873-2f3bf9caee79" style="width: 300px; height: 300px;"></div>
2467  * <script type="text/javascript">
2468  * (function(){
2469  *   var board = JXG.JSXGraph.initBoard('JXGf9a7ba38-b50f-4a32-a873-2f3bf9caee79', {boundingbox: [-3, 7, 5, -3], axis: true, showcopyright: false, shownavigation: false});
2470  *   var s = board.create('slider',[[0,4],[3,4],[0,4,10]],{snapWidth:1});
2471  *   var g = function(x) { return 0.5*x*x-2*x; };
2472  *   var f = function(x) { return -x*(x-4); };
2473  *   var r = board.create('riemannsum',
2474  *               [[g,f], function(){return s.Value();}, 'lower', 0, 4],
2475  *               {fillOpacity:0.4}
2476  *               );
2477  *   var f = board.create('functiongraph',[f, -2, 5]);
2478  *   var g = board.create('functiongraph',[g, -2, 5]);
2479  *   var t = board.create('text',[-2,-2, function(){ return 'Sum=' + JXG.toFixed(r.Value(), 4); }]);
2480  * })();
2481  * </script><pre>
2482  */
2483 JXG.createRiemannsum = function (board, parents, attributes) {
2484     var n, type, f, par, c, attr;
2485 
2486     attr = Type.copyAttributes(attributes, board.options, 'riemannsum');
2487     attr.curvetype = 'plot';
2488 
2489     f = parents[0];
2490     n = Type.createFunction(parents[1], board, "");
2491 
2492     if (!Type.exists(n)) {
2493         throw new Error(
2494             "JSXGraph: JXG.createRiemannsum: argument '2' n has to be number or function." +
2495             "\nPossible parent types: [function,n:number|function,type,start:number|function,end:number|function]"
2496         );
2497     }
2498 
2499     if (typeof parents[2] === 'string') {
2500         parents[2] = '\'' + parents[2] + '\'';
2501     }
2502 
2503     type = Type.createFunction(parents[2], board, "");
2504     if (!Type.exists(type)) {
2505         throw new Error(
2506             "JSXGraph: JXG.createRiemannsum: argument 3 'type' has to be string or function." +
2507             "\nPossible parent types: [function,n:number|function,type,start:number|function,end:number|function]"
2508         );
2509     }
2510 
2511     par = [[0], [0]].concat(parents.slice(3));
2512 
2513     c = board.create("curve", par, attr);
2514 
2515     c.sum = 0.0;
2516     /**
2517      * Returns the value of the Riemann sum, i.e. the sum of the (signed) areas of the rectangles.
2518      * @name Value
2519      * @memberOf Riemannsum.prototype
2520      * @function
2521      * @returns {Number} value of Riemann sum.
2522      */
2523     c.Value = function () {
2524         return this.sum;
2525     };
2526 
2527     /**
2528      * @class
2529      * @ignore
2530      */
2531     c.updateDataArray = function () {
2532         var u = Numerics.riemann(f, n(), type(), this.minX(), this.maxX());
2533         this.dataX = u[0];
2534         this.dataY = u[1];
2535 
2536         // Update "Riemann sum"
2537         this.sum = u[2];
2538     };
2539 
2540     c.addParentsFromJCFunctions([n, type]);
2541 
2542     return c;
2543 };
2544 
2545 JXG.registerElement("riemannsum", JXG.createRiemannsum);
2546 
2547 /**
2548  * @class A trace curve is simple locus curve showing the orbit of a point that depends on a glider point.
2549  * @pseudo
2550  * @name Tracecurve
2551  * @augments JXG.Curve
2552  * @constructor
2553  * @type Object
2554  * @descript JXG.Curve
2555  * @param {Point} Parent elements of Tracecurve are a
2556  *         glider point and a point whose locus is traced.
2557  * @param {point}
2558  * @see JXG.Curve
2559  * @example
2560  * // Create trace curve.
2561  * var c1 = board.create('circle',[[0, 0], [2, 0]]),
2562  * p1 = board.create('point',[-3, 1]),
2563  * g1 = board.create('glider',[2, 1, c1]),
2564  * s1 = board.create('segment',[g1, p1]),
2565  * p2 = board.create('midpoint',[s1]),
2566  * curve = board.create('tracecurve', [g1, p2]);
2567  *
2568  * </pre><div class="jxgbox" id="JXG5749fb7d-04fc-44d2-973e-45c1951e29ad" style="width: 300px; height: 300px;"></div>
2569  * <script type="text/javascript">
2570  *   var tc1_board = JXG.JSXGraph.initBoard('JXG5749fb7d-04fc-44d2-973e-45c1951e29ad', {boundingbox: [-4, 4, 4, -4], axis: false, showcopyright: false, shownavigation: false});
2571  *   var c1 = tc1_board.create('circle',[[0, 0], [2, 0]]),
2572  *       p1 = tc1_board.create('point',[-3, 1]),
2573  *       g1 = tc1_board.create('glider',[2, 1, c1]),
2574  *       s1 = tc1_board.create('segment',[g1, p1]),
2575  *       p2 = tc1_board.create('midpoint',[s1]),
2576  *       curve = tc1_board.create('tracecurve', [g1, p2]);
2577  * </script><pre>
2578  */
2579 JXG.createTracecurve = function (board, parents, attributes) {
2580     var c, glider, tracepoint, attr;
2581 
2582     if (parents.length !== 2) {
2583         throw new Error(
2584             "JSXGraph: Can't create trace curve with given parent'" +
2585             "\nPossible parent types: [glider, point]"
2586         );
2587     }
2588 
2589     glider = board.select(parents[0]);
2590     tracepoint = board.select(parents[1]);
2591 
2592     if (glider.type !== Const.OBJECT_TYPE_GLIDER || !Type.isPoint(tracepoint)) {
2593         throw new Error(
2594             "JSXGraph: Can't create trace curve with parent types '" +
2595             typeof parents[0] +
2596             "' and '" +
2597             typeof parents[1] +
2598             "'." +
2599             "\nPossible parent types: [glider, point]"
2600         );
2601     }
2602 
2603     attr = Type.copyAttributes(attributes, board.options, 'tracecurve');
2604     attr.curvetype = 'plot';
2605     c = board.create("curve", [[0], [0]], attr);
2606 
2607     /**
2608      * @class
2609      * @ignore
2610      */
2611     c.updateDataArray = function () {
2612         var i, step, t, el, pEl, x, y, from,
2613             savetrace,
2614             le = this.visProp.numberpoints,
2615             savePos = glider.position,
2616             slideObj = glider.slideObject,
2617             mi = slideObj.minX(),
2618             ma = slideObj.maxX();
2619 
2620         // set step width
2621         step = (ma - mi) / le;
2622         this.dataX = [];
2623         this.dataY = [];
2624 
2625         /*
2626          * For gliders on circles and lines a closed curve is computed.
2627          * For gliders on curves the curve is not closed.
2628          */
2629         if (slideObj.elementClass !== Const.OBJECT_CLASS_CURVE) {
2630             le++;
2631         }
2632 
2633         // Loop over all steps
2634         for (i = 0; i < le; i++) {
2635             t = mi + i * step;
2636             x = slideObj.X(t) / slideObj.Z(t);
2637             y = slideObj.Y(t) / slideObj.Z(t);
2638 
2639             // Position the glider
2640             glider.setPositionDirectly(Const.COORDS_BY_USER, [x, y]);
2641             from = false;
2642 
2643             // Update all elements from the glider up to the trace element
2644             for (el in this.board.objects) {
2645                 if (this.board.objects.hasOwnProperty(el)) {
2646                     pEl = this.board.objects[el];
2647 
2648                     if (pEl === glider) {
2649                         from = true;
2650                     }
2651 
2652                     if (from && pEl.needsRegularUpdate) {
2653                         // Save the trace mode of the element
2654                         savetrace = pEl.visProp.trace;
2655                         pEl.visProp.trace = false;
2656                         pEl.needsUpdate = true;
2657                         pEl.update(true);
2658 
2659                         // Restore the trace mode
2660                         pEl.visProp.trace = savetrace;
2661                         if (pEl === tracepoint) {
2662                             break;
2663                         }
2664                     }
2665                 }
2666             }
2667 
2668             // Store the position of the trace point
2669             this.dataX[i] = tracepoint.X();
2670             this.dataY[i] = tracepoint.Y();
2671         }
2672 
2673         // Restore the original position of the glider
2674         glider.position = savePos;
2675         from = false;
2676 
2677         // Update all elements from the glider to the trace point
2678         for (el in this.board.objects) {
2679             if (this.board.objects.hasOwnProperty(el)) {
2680                 pEl = this.board.objects[el];
2681                 if (pEl === glider) {
2682                     from = true;
2683                 }
2684 
2685                 if (from && pEl.needsRegularUpdate) {
2686                     savetrace = pEl.visProp.trace;
2687                     pEl.visProp.trace = false;
2688                     pEl.needsUpdate = true;
2689                     pEl.update(true);
2690                     pEl.visProp.trace = savetrace;
2691 
2692                     if (pEl === tracepoint) {
2693                         break;
2694                     }
2695                 }
2696             }
2697         }
2698     };
2699 
2700     return c;
2701 };
2702 
2703 JXG.registerElement("tracecurve", JXG.createTracecurve);
2704 
2705 /**
2706      * @class A step function is a function graph that is piecewise constant.
2707      *
2708      * In case the data points should be updated after creation time,
2709      * they can be accessed by curve.xterm and curve.yterm.
2710      * @pseudo
2711      * @name Stepfunction
2712      * @augments JXG.Curve
2713      * @constructor
2714      * @type Curve
2715      * @description JXG.Curve
2716      * @param {Array|Function} Parent1 elements of Stepfunction are two arrays containing the coordinates.
2717      * @param {Array|Function} Parent2
2718      * @see JXG.Curve
2719      * @example
2720      * // Create step function.
2721      var curve = board.create('stepfunction', [[0,1,2,3,4,5], [1,3,0,2,2,1]]);
2722 
2723      * </pre><div class="jxgbox" id="JXG32342ec9-ad17-4339-8a97-ff23dc34f51a" style="width: 300px; height: 300px;"></div>
2724      * <script type="text/javascript">
2725      *   var sf1_board = JXG.JSXGraph.initBoard('JXG32342ec9-ad17-4339-8a97-ff23dc34f51a', {boundingbox: [-1, 5, 6, -2], axis: true, showcopyright: false, shownavigation: false});
2726      *   var curve = sf1_board.create('stepfunction', [[0,1,2,3,4,5], [1,3,0,2,2,1]]);
2727      * </script><pre>
2728      */
2729 JXG.createStepfunction = function (board, parents, attributes) {
2730     var c, attr;
2731     if (parents.length !== 2) {
2732         throw new Error(
2733             "JSXGraph: Can't create step function with given parent'" +
2734             "\nPossible parent types: [array, array|function]"
2735         );
2736     }
2737 
2738     attr = Type.copyAttributes(attributes, board.options, 'stepfunction');
2739     c = board.create("curve", parents, attr);
2740     /**
2741      * @class
2742      * @ignore
2743      */
2744     c.updateDataArray = function () {
2745         var i,
2746             j = 0,
2747             len = this.xterm.length;
2748 
2749         this.dataX = [];
2750         this.dataY = [];
2751 
2752         if (len === 0) {
2753             return;
2754         }
2755 
2756         this.dataX[j] = this.xterm[0];
2757         this.dataY[j] = this.yterm[0];
2758         ++j;
2759 
2760         for (i = 1; i < len; ++i) {
2761             this.dataX[j] = this.xterm[i];
2762             this.dataY[j] = this.dataY[j - 1];
2763             ++j;
2764             this.dataX[j] = this.xterm[i];
2765             this.dataY[j] = this.yterm[i];
2766             ++j;
2767         }
2768     };
2769 
2770     return c;
2771 };
2772 
2773 JXG.registerElement("stepfunction", JXG.createStepfunction);
2774 
2775 /**
2776  * @class A curve visualizing the function graph of the (numerical) derivative of a given curve.
2777  *
2778  * @pseudo
2779  * @name Derivative
2780  * @augments JXG.Curve
2781  * @constructor
2782  * @type JXG.Curve
2783  * @param {JXG.Curve} Parent Curve for which the derivative is generated.
2784  * @see JXG.Curve
2785  * @example
2786  * var cu = board.create('cardinalspline', [[[-3,0], [-1,2], [0,1], [2,0], [3,1]], 0.5, 'centripetal'], {createPoints: false});
2787  * var d = board.create('derivative', [cu], {dash: 2});
2788  *
2789  * </pre><div id="JXGb9600738-1656-11e8-8184-901b0e1b8723" class="jxgbox" style="width: 300px; height: 300px;"></div>
2790  * <script type="text/javascript">
2791  *     (function() {
2792  *         var board = JXG.JSXGraph.initBoard('JXGb9600738-1656-11e8-8184-901b0e1b8723',
2793  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
2794  *     var cu = board.create('cardinalspline', [[[-3,0], [-1,2], [0,1], [2,0], [3,1]], 0.5, 'centripetal'], {createPoints: false});
2795  *     var d = board.create('derivative', [cu], {dash: 2});
2796  *
2797  *     })();
2798  *
2799  * </script><pre>
2800  *
2801  */
2802 JXG.createDerivative = function (board, parents, attributes) {
2803     var c, curve, dx, dy, attr;
2804 
2805     if (parents.length !== 1 && parents[0].class !== Const.OBJECT_CLASS_CURVE) {
2806         throw new Error(
2807             "JSXGraph: Can't create derivative curve with given parent'" +
2808             "\nPossible parent types: [curve]"
2809         );
2810     }
2811 
2812     attr = Type.copyAttributes(attributes, board.options, 'curve');
2813 
2814     curve = parents[0];
2815     dx = Numerics.D(curve.X);
2816     dy = Numerics.D(curve.Y);
2817 
2818     c = board.create(
2819         "curve",
2820         [
2821             function (t) {
2822                 return curve.X(t);
2823             },
2824             function (t) {
2825                 return dy(t) / dx(t);
2826             },
2827             curve.minX(),
2828             curve.maxX()
2829         ],
2830         attr
2831     );
2832 
2833     c.setParents(curve);
2834 
2835     return c;
2836 };
2837 
2838 JXG.registerElement("derivative", JXG.createDerivative);
2839 
2840 /**
2841  * @class The path forming the intersection of two closed path elements.
2842  * The elements may be of type curve, circle, polygon, inequality.
2843  * If one element is a curve, it has to be closed.
2844  * The resulting element is of type curve.
2845  * @pseudo
2846  * @name CurveIntersection
2847  * @param {JXG.Curve|JXG.Polygon|JXG.Circle} curve1 First element which is intersected
2848  * @param {JXG.Curve|JXG.Polygon|JXG.Circle} curve2 Second element which is intersected
2849  * @augments JXG.Curve
2850  * @constructor
2851  * @type JXG.Curve
2852  *
2853  * @example
2854  * var f = board.create('functiongraph', ['cos(x)']);
2855  * var ineq = board.create('inequality', [f], {inverse: true, fillOpacity: 0.1});
2856  * var circ = board.create('circle', [[0,0], 4]);
2857  * var clip = board.create('curveintersection', [ineq, circ], {fillColor: 'yellow', fillOpacity: 0.6});
2858  *
2859  * </pre><div id="JXGe2948257-8835-4276-9164-8acccb48e8d4" class="jxgbox" style="width: 300px; height: 300px;"></div>
2860  * <script type="text/javascript">
2861  *     (function() {
2862  *         var board = JXG.JSXGraph.initBoard('JXGe2948257-8835-4276-9164-8acccb48e8d4',
2863  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
2864  *     var f = board.create('functiongraph', ['cos(x)']);
2865  *     var ineq = board.create('inequality', [f], {inverse: true, fillOpacity: 0.1});
2866  *     var circ = board.create('circle', [[0,0], 4]);
2867  *     var clip = board.create('curveintersection', [ineq, circ], {fillColor: 'yellow', fillOpacity: 0.6});
2868  *
2869  *     })();
2870  *
2871  * </script><pre>
2872  *
2873  */
2874 JXG.createCurveIntersection = function (board, parents, attributes) {
2875     var c;
2876 
2877     if (parents.length !== 2) {
2878         throw new Error(
2879             "JSXGraph: Can't create curve intersection with given parent'" +
2880             "\nPossible parent types: [array, array|function]"
2881         );
2882     }
2883 
2884     c = board.create("curve", [[], []], attributes);
2885     /**
2886      * @class
2887      * @ignore
2888      */
2889     c.updateDataArray = function () {
2890         var a = Clip.intersection(parents[0], parents[1], this.board);
2891         this.dataX = a[0];
2892         this.dataY = a[1];
2893     };
2894     return c;
2895 };
2896 
2897 /**
2898  * @class The path forming the union of two closed path elements.
2899  * The elements may be of type curve, circle, polygon, inequality.
2900  * If one element is a curve, it has to be closed.
2901  * The resulting element is of type curve.
2902  * @pseudo
2903  * @name CurveUnion
2904  * @param {JXG.Curve|JXG.Polygon|JXG.Circle} curve1 First element defining the union
2905  * @param {JXG.Curve|JXG.Polygon|JXG.Circle} curve2 Second element defining the union
2906  * @augments JXG.Curve
2907  * @constructor
2908  * @type JXG.Curve
2909  *
2910  * @example
2911  * var f = board.create('functiongraph', ['cos(x)']);
2912  * var ineq = board.create('inequality', [f], {inverse: true, fillOpacity: 0.1});
2913  * var circ = board.create('circle', [[0,0], 4]);
2914  * var clip = board.create('curveunion', [ineq, circ], {fillColor: 'yellow', fillOpacity: 0.6});
2915  *
2916  * </pre><div id="JXGe2948257-8835-4276-9164-8acccb48e8d4" class="jxgbox" style="width: 300px; height: 300px;"></div>
2917  * <script type="text/javascript">
2918  *     (function() {
2919  *         var board = JXG.JSXGraph.initBoard('JXGe2948257-8835-4276-9164-8acccb48e8d4',
2920  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
2921  *     var f = board.create('functiongraph', ['cos(x)']);
2922  *     var ineq = board.create('inequality', [f], {inverse: true, fillOpacity: 0.1});
2923  *     var circ = board.create('circle', [[0,0], 4]);
2924  *     var clip = board.create('curveunion', [ineq, circ], {fillColor: 'yellow', fillOpacity: 0.6});
2925  *
2926  *     })();
2927  *
2928  * </script><pre>
2929  *
2930  */
2931 JXG.createCurveUnion = function (board, parents, attributes) {
2932     var c;
2933 
2934     if (parents.length !== 2) {
2935         throw new Error(
2936             "JSXGraph: Can't create curve union with given parent'" +
2937             "\nPossible parent types: [array, array|function]"
2938         );
2939     }
2940 
2941     c = board.create("curve", [[], []], attributes);
2942     /**
2943      * @class
2944      * @ignore
2945      */
2946     c.updateDataArray = function () {
2947         var a = Clip.union(parents[0], parents[1], this.board);
2948         this.dataX = a[0];
2949         this.dataY = a[1];
2950     };
2951     return c;
2952 };
2953 
2954 /**
2955  * @class The path forming the difference of two closed path elements.
2956  * The elements may be of type curve, circle, polygon, inequality.
2957  * If one element is a curve, it has to be closed.
2958  * The resulting element is of type curve.
2959  * @pseudo
2960  * @name CurveDifference
2961  * @param {JXG.Curve|JXG.Polygon|JXG.Circle} curve1 First element from which the second element is "subtracted"
2962  * @param {JXG.Curve|JXG.Polygon|JXG.Circle} curve2 Second element which is subtracted from the first element
2963  * @augments JXG.Curve
2964  * @constructor
2965  * @type JXG.Curve
2966  *
2967  * @example
2968  * var f = board.create('functiongraph', ['cos(x)']);
2969  * var ineq = board.create('inequality', [f], {inverse: true, fillOpacity: 0.1});
2970  * var circ = board.create('circle', [[0,0], 4]);
2971  * var clip = board.create('curvedifference', [ineq, circ], {fillColor: 'yellow', fillOpacity: 0.6});
2972  *
2973  * </pre><div id="JXGe2948257-8835-4276-9164-8acccb48e8d4" class="jxgbox" style="width: 300px; height: 300px;"></div>
2974  * <script type="text/javascript">
2975  *     (function() {
2976  *         var board = JXG.JSXGraph.initBoard('JXGe2948257-8835-4276-9164-8acccb48e8d4',
2977  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
2978  *     var f = board.create('functiongraph', ['cos(x)']);
2979  *     var ineq = board.create('inequality', [f], {inverse: true, fillOpacity: 0.1});
2980  *     var circ = board.create('circle', [[0,0], 4]);
2981  *     var clip = board.create('curvedifference', [ineq, circ], {fillColor: 'yellow', fillOpacity: 0.6});
2982  *
2983  *     })();
2984  *
2985  * </script><pre>
2986  *
2987  */
2988 JXG.createCurveDifference = function (board, parents, attributes) {
2989     var c;
2990 
2991     if (parents.length !== 2) {
2992         throw new Error(
2993             "JSXGraph: Can't create curve difference with given parent'" +
2994             "\nPossible parent types: [array, array|function]"
2995         );
2996     }
2997 
2998     c = board.create("curve", [[], []], attributes);
2999     /**
3000      * @class
3001      * @ignore
3002      */
3003     c.updateDataArray = function () {
3004         var a = Clip.difference(parents[0], parents[1], this.board);
3005         this.dataX = a[0];
3006         this.dataY = a[1];
3007     };
3008     return c;
3009 };
3010 
3011 JXG.registerElement("curvedifference", JXG.createCurveDifference);
3012 JXG.registerElement("curveintersection", JXG.createCurveIntersection);
3013 JXG.registerElement("curveunion", JXG.createCurveUnion);
3014 
3015 // /**
3016 //  * @class Concat of two path elements, in general neither is a closed path. The parent elements have to be curves, too.
3017 //  * The resulting element is of type curve. The curve points are simply concatenated.
3018 //  * @pseudo
3019 //  * @name CurveConcat
3020 //  * @param {JXG.Curve} curve1 First curve element.
3021 //  * @param {JXG.Curve} curve2 Second curve element.
3022 //  * @augments JXG.Curve
3023 //  * @constructor
3024 //  * @type JXG.Curve
3025 //  */
3026 // JXG.createCurveConcat = function (board, parents, attributes) {
3027 //     var c;
3028 
3029 //     if (parents.length !== 2) {
3030 //         throw new Error(
3031 //             "JSXGraph: Can't create curve difference with given parent'" +
3032 //                 "\nPossible parent types: [array, array|function]"
3033 //         );
3034 //     }
3035 
3036 //     c = board.create("curve", [[], []], attributes);
3037 //     /**
3038 //      * @class
3039 //      * @ignore
3040 //      */
3041 //     c.updateCurve = function () {
3042 //         this.points = parents[0].points.concat(
3043 //                 [new JXG.Coords(Const.COORDS_BY_USER, [NaN, NaN], this.board)]
3044 //             ).concat(parents[1].points);
3045 //         this.numberPoints = this.points.length;
3046 //         return this;
3047 //     };
3048 
3049 //     return c;
3050 // };
3051 
3052 // JXG.registerElement("curveconcat", JXG.createCurveConcat);
3053 
3054 /**
3055  * @class Vertical or horizontal boxplot or also called box-and-whisker plot to present numerical data through their quartiles.
3056  * The direction of the boxplot is controlled by the attribute "dir". Internally, a boxplot is realized with a single JSXGraph curve.
3057  * <p>
3058  * Given a data set, the input array Q for the boxplot can be computed e.g. with the method {@link JXG.Math.Statistics.boxplot}.
3059  *
3060  * @example
3061  * 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];
3062  * var Q = JXG.Math.Statistics.boxplot(data);
3063  * var b = board.create('boxplot', [Q, 2, 4]);
3064  *
3065  * @pseudo
3066  * @name Boxplot
3067  * @param {Array} quantiles Array containing five quantiles (e.g. min, first quartile, median, third quartile, maximum) and an optional array with outlier values. The elements of this array can be of type number, function or string. The optional aub-array outlier is an array of numbers or a function returning an array of numbers.
3068  * @param {Number|Function} axis Axis position of the boxplot
3069  * @param {Number|Function} width Width of the rectangle part of the boxplot. The width of the first and 3th quartile
3070  * is relative to this width and can be controlled by the attribute "smallWidth".
3071  * @augments JXG.Curve
3072  * @constructor
3073  * @type JXG.Curve
3074  * @see JXG.Math.Statistics#boxplot
3075  *
3076  * @example
3077  * var Q = [ -1, 2, 3, 3.5, 5 ];
3078  *
3079  * var b = board.create('boxplot', [Q, 2, 4], {strokeWidth: 3});
3080  *
3081  * </pre><div id="JXG13eb23a1-a641-41a2-be11-8e03e400a947" class="jxgbox" style="width: 300px; height: 300px;"></div>
3082  * <script type="text/javascript">
3083  *     (function() {
3084  *         var board = JXG.JSXGraph.initBoard('JXG13eb23a1-a641-41a2-be11-8e03e400a947',
3085  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
3086  *     var Q = [ -1, 2, 3, 3.5, 5 ];
3087  *     var b = board.create('boxplot', [Q, 2, 4], {strokeWidth: 3});
3088  *
3089  *     })();
3090  *
3091  * </script><pre>
3092  *
3093  * @example
3094  * // With outliers
3095  * var Q = [ -1, 2, 3, 3.5, 5, [-4, -6] ];
3096  * var b = board.create('boxplot', [Q, 3, 4], {dir: 'horizontal', width: 2, smallWidth: 0.25, color:'red'});
3097  *
3098  * </pre><div id="JXG0deb9cb2-84bc-470d-a6db-8be9a5694813" class="jxgbox" style="width: 300px; height: 300px;"></div>
3099  * <script type="text/javascript">
3100  *     (function() {
3101  *         var board = JXG.JSXGraph.initBoard('JXG0deb9cb2-84bc-470d-a6db-8be9a5694813',
3102  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
3103  *     var Q = [ -1, 2, 3, 3.5, 5, [-4, -6] ];
3104  *     var b = board.create('boxplot', [Q, 3, 4], {dir: 'horizontal', width: 2, smallWidth: 0.25, color:'red'});
3105  *
3106  *     })();
3107  *
3108  * </script><pre>
3109  *
3110  * @example
3111  * 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];
3112  * var Q = JXG.Math.Statistics.boxplot(data);
3113  * var b = board.create('boxplot', [Q, 0, 3]);
3114  *
3115  * </pre><div id="JXGef079e76-ae99-41e4-af29-1d07d83bf85a" class="jxgbox" style="width: 300px; height: 300px;"></div>
3116  * <script type="text/javascript">
3117  *     (function() {
3118  *         var board = JXG.JSXGraph.initBoard('JXGef079e76-ae99-41e4-af29-1d07d83bf85a',
3119  *             {boundingbox: [-5,90,5,30], axis: true, showcopyright: false, shownavigation: false});
3120  *     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];
3121  *     var Q = JXG.Math.Statistics.boxplot(data, [25, 50, 75]);
3122  *     var b = board.create('boxplot', [Q, 0, 3]);
3123  *
3124  *     })();
3125  *
3126  * </script><pre>
3127  *
3128  * @example
3129  * var mi = board.create('glider', [0, -1, board.defaultAxes.y]);
3130  * var ma = board.create('glider', [0, 5, board.defaultAxes.y]);
3131  * var Q = [function() { return mi.Y(); }, 2, 3, 3.5, function() { return ma.Y(); }];
3132  *
3133  * var b = board.create('boxplot', [Q, 0, 2]);
3134  *
3135  * </pre><div id="JXG3b3225da-52f0-42fe-8396-be9016bf289b" class="jxgbox" style="width: 300px; height: 300px;"></div>
3136  * <script type="text/javascript">
3137  *     (function() {
3138  *         var board = JXG.JSXGraph.initBoard('JXG3b3225da-52f0-42fe-8396-be9016bf289b',
3139  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
3140  *     var mi = board.create('glider', [0, -1, board.defaultAxes.y]);
3141  *     var ma = board.create('glider', [0, 5, board.defaultAxes.y]);
3142  *     var Q = [function() { return mi.Y(); }, 2, 3, 3.5, function() { return ma.Y(); }];
3143  *
3144  *     var b = board.create('boxplot', [Q, 0, 2]);
3145  *
3146  *     })();
3147  *
3148  * </script><pre>
3149  *
3150  */
3151 JXG.createBoxPlot = function (board, parents, attributes) {
3152     var box, i, len,
3153         attr = Type.copyAttributes(attributes, board.options, 'boxplot');
3154 
3155     if (parents.length !== 3) {
3156         throw new Error(
3157             "JSXGraph: Can't create boxplot with given parent'" +
3158             "\nPossible parent types: [array, number|function, number|function] containing quantiles, axis, width"
3159         );
3160     }
3161     if (parents[0].length < 5) {
3162         throw new Error(
3163             "JSXGraph: Can't create boxplot with given parent[0]'" +
3164             "\nparent[0] has to contain at least 5 quantiles."
3165         );
3166     }
3167     box = board.create("curve", [[], []], attr);
3168 
3169     len = parents[0].length; // Quantiles
3170     box.Q = [];
3171     for (i = 0; i < len; i++) {
3172         box.Q[i] = Type.createFunction(parents[0][i], board);
3173     }
3174     box.x = Type.createFunction(parents[1], board);
3175     box.w = Type.createFunction(parents[2], board);
3176 
3177     /**
3178      * @class
3179      * @ignore
3180      */
3181     box.updateDataArray = function () {
3182         var v1, v2, l1, l2, r1, r2, w2, dir, x,
3183             i, le, q5, y, sx, sy, sx2, sy2, t, f;
3184 
3185         w2 = this.evalVisProp('smallwidth');
3186         dir = this.evalVisProp('dir');
3187         x = this.x();
3188         l1 = x - this.w() * 0.5;
3189         l2 = x - this.w() * 0.5 * w2;
3190         r1 = x + this.w() * 0.5;
3191         r2 = x + this.w() * 0.5 * w2;
3192         v1 = [x, l2, r2, x, x, l1, l1, r1, r1, x, NaN, l1, r1, NaN, x, x, l2, r2, x];
3193         v2 = [
3194             this.Q[0](),
3195             this.Q[0](),
3196             this.Q[0](),
3197             this.Q[0](),
3198             this.Q[1](),
3199             this.Q[1](),
3200             this.Q[3](),
3201             this.Q[3](),
3202             this.Q[1](),
3203             this.Q[1](),
3204             NaN,
3205             this.Q[2](),
3206             this.Q[2](),
3207             NaN,
3208             this.Q[3](),
3209             this.Q[4](),
3210             this.Q[4](),
3211             this.Q[4](),
3212             this.Q[4]()
3213         ];
3214 
3215         // Outliers
3216         if (this.Q.length > 5 && Type.isArray(this.Q[5]())) {
3217             v1.push(NaN);
3218             v2.push(NaN);
3219 
3220             f = this.evalVisProp('outlier.face');
3221 
3222             if (dir === 'vertical') {
3223                 sx = this.evalVisProp('outlier.size') / this.board.unitX;
3224                 sy = this.evalVisProp('outlier.size') / this.board.unitY;
3225             } else {
3226                 sy = this.evalVisProp('outlier.size') / this.board.unitX;
3227                 sx = this.evalVisProp('outlier.size') / this.board.unitY;
3228             }
3229             sx2 = sx * Math.sqrt(2);
3230             sy2 = sy * Math.sqrt(2);
3231 
3232             q5 = this.Q[5]();
3233             le = q5.length;
3234             for (i = 0; i < le; i++) {
3235                 y = q5[i];
3236                 switch (f) {
3237                     case 'x':
3238                     case 'cross':
3239                         v1.push(x - sx, x + sx, NaN, x - sx, x + sx, NaN);
3240                         v2.push(y + sy, y - sy, NaN, y - sy, y + sy, NaN);
3241                         break;
3242                     case '[]':
3243                     case 'square':
3244                         v1.push(x - sx, x + sx, x + sx, x - sx, x - sx, NaN);
3245                         v2.push(y + sy, y + sy, y - sy, y - sy, y + sy, NaN);
3246                         break;
3247                     case '<>':
3248                     case 'diamond':
3249                         v1.push(x, x + sx, x, x - sx, x, NaN);
3250                         v2.push(y + sy, y, y - sy, y, y + sy, NaN);
3251                         break;
3252                     case '<<>>':
3253                     case 'diamond2':
3254                         v1.push(x, x + sx2, x, x - sx2, x, NaN);
3255                         v2.push(y + sy2, y, y - sy2, y, y + sy2, NaN);
3256                         break;
3257                     case '+':
3258                     case 'plus':
3259                         v1.push(x - sx, x + sx, NaN, x, x, NaN);
3260                         v2.push(y, y, NaN, y - sy, y + sy, NaN);
3261                         break;
3262                     case '-':
3263                     case 'minus':
3264                         v1.push(x - sx, x + sx, NaN);
3265                         v2.push(y, y, NaN);
3266                         break;
3267                     case '|':
3268                     case 'divide':
3269                         v1.push(x, x, NaN);
3270                         v2.push(y - sy, y + sy, NaN);
3271                         break;
3272                     default:
3273                     case 'o':
3274                     case 'circle':
3275                         for (t = 0; t <= 2 * Math.PI; t += (2 * Math.PI) / 17) {
3276                             v1.push(x - sx * Math.sin(t));
3277                             v2.push(y - sy * Math.cos(t));
3278                         }
3279                         v1.push(NaN);
3280                         v2.push(NaN);
3281                 }
3282             }
3283         }
3284 
3285         if (dir === 'vertical') {
3286             this.dataX = v1;
3287             this.dataY = v2;
3288         } else {
3289             this.dataX = v2;
3290             this.dataY = v1;
3291         }
3292     };
3293 
3294     box.addParentsFromJCFunctions([box.Q, box.x, box.w]);
3295 
3296     return box;
3297 };
3298 
3299 JXG.registerElement("boxplot", JXG.createBoxPlot);
3300 
3301 /**
3302  * @class An implicit curve is a plane curve defined by an implicit equation
3303  * relating two coordinate variables, commonly <i>x</i> and <i>y</i>.
3304  * For example, the unit circle is defined by the implicit equation
3305  * x<sup>2</sup> + y<sup>2</sup> = 1.
3306  * In general, every implicit curve is defined by an equation of the form
3307  * <i>f(x, y) = 0</i>
3308  * for some function <i>f</i> of two variables. (<a href="https://en.wikipedia.org/wiki/Implicit_curve">Wikipedia</a>)
3309  * <p>
3310  * The partial derivatives for <i>f</i> are optional. If not given, numerical
3311  * derivatives are used instead. This is good enough for most practical use cases.
3312  * But if supplied, both partial derivatives must be supplied.
3313  * <p>
3314  * The most effective attributes to tinker with if the implicit curve algorithm fails are
3315  * {@link ImplicitCurve#resolution_outer},
3316  * {@link ImplicitCurve#resolution_inner},
3317  * {@link ImplicitCurve#alpha_0},
3318  * {@link ImplicitCurve#h_initial},
3319  * {@link ImplicitCurve#h_max}, and
3320  * {@link ImplicitCurve#qdt_box}.
3321  *
3322  * @pseudo
3323  * @name ImplicitCurve
3324  * @param {Function|String} f Function of two variables for the left side of the equation <i>f(x,y)=0</i>.
3325  * If f is supplied as string, it has to use the variables 'x' and 'y'.
3326  * @param {Function|String} [dfx=null] Optional partial derivative in respect to the first variable
3327  * If dfx is supplied as string, it has to use the variables 'x' and 'y'.
3328  * @param {Function|String} [dfy=null] Optional partial derivative in respect to the second variable
3329  * If dfy is supplied as string, it has to use the variables 'x' and 'y'.
3330  * @param {Array|Function} [rangex=boundingbox] Optional array of length 2
3331  * of the form [x_min, x_max] setting the domain of the x coordinate of the implicit curve.
3332  * If not supplied, the board's boundingbox (+ the attribute 'margin') is taken.
3333  * @param {Array|Function} [rangey=boundingbox] Optional array of length 2
3334  * of the form [y_min, y_max] setting the domain of the y coordinate of the implicit curve.
3335  * If not supplied, the board's boundingbox (+ the attribute 'margin') is taken.
3336  * @augments JXG.Curve
3337  * @constructor
3338  * @type JXG.Curve
3339  *
3340  * @example
3341  *   var f, c;
3342  *   f = (x, y) => 1 / 16 * x ** 2 + y ** 2 - 1;
3343  *   c = board.create('implicitcurve', [f], {
3344  *       strokeWidth: 3,
3345  *       strokeColor: JXG.palette.red,
3346  *       strokeOpacity: 0.8
3347  *   });
3348  *
3349  * </pre><div id="JXGa6e86701-1a82-48d0-b007-3a3d32075076" class="jxgbox" style="width: 300px; height: 300px;"></div>
3350  * <script type="text/javascript">
3351  *     (function() {
3352  *         var board = JXG.JSXGraph.initBoard('JXGa6e86701-1a82-48d0-b007-3a3d32075076',
3353  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
3354  *             var f, c;
3355  *             f = (x, y) => 1 / 16 * x ** 2 + y ** 2 - 1;
3356  *             c = board.create('implicitcurve', [f], {
3357  *                 strokeWidth: 3,
3358  *                 strokeColor: JXG.palette.red,
3359  *                 strokeOpacity: 0.8
3360  *             });
3361  *
3362  *     })();
3363  *
3364  * </script><pre>
3365  *
3366  * @example
3367  *  var a, c, f;
3368  *  a = board.create('slider', [[-3, 6], [3, 6], [-3, 1, 3]], {
3369  *      name: 'a', stepWidth: 0.1
3370  *  });
3371  *  f = (x, y) => x ** 2 - 2 * x * y - 2 * x + (a.Value() + 1) * y ** 2 + (4 * a.Value() + 2) * y + 4 * a.Value() - 3;
3372  *  c = board.create('implicitcurve', [f], {
3373  *      strokeWidth: 3,
3374  *      strokeColor: JXG.palette.red,
3375  *      strokeOpacity: 0.8,
3376  *      resolution_outer: 20,
3377  *      resolution_inner: 20
3378  *  });
3379  *
3380  * </pre><div id="JXG0b133a54-9509-4a65-9722-9c5145e23b40" class="jxgbox" style="width: 300px; height: 300px;"></div>
3381  * <script type="text/javascript">
3382  *     (function() {
3383  *         var board = JXG.JSXGraph.initBoard('JXG0b133a54-9509-4a65-9722-9c5145e23b40',
3384  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
3385  *             var a, c, f;
3386  *             a = board.create('slider', [[-3, 6], [3, 6], [-3, 1, 3]], {
3387  *                 name: 'a', stepWidth: 0.1
3388  *             });
3389  *             f = (x, y) => x ** 2 - 2 * x * y - 2 * x + (a.Value() + 1) * y ** 2 + (4 * a.Value() + 2) * y + 4 * a.Value() - 3;
3390  *             c = board.create('implicitcurve', [f], {
3391  *                 strokeWidth: 3,
3392  *                 strokeColor: JXG.palette.red,
3393  *                 strokeOpacity: 0.8,
3394  *                 resolution_outer: 20,
3395  *                 resolution_inner: 20
3396  *             });
3397  *
3398  *     })();
3399  *
3400  * </script><pre>
3401  *
3402  * @example
3403  *  var c = board.create('implicitcurve', ['abs(x * y) - 3'], {
3404  *      strokeWidth: 3,
3405  *      strokeColor: JXG.palette.red,
3406  *      strokeOpacity: 0.8
3407  *  });
3408  *
3409  * </pre><div id="JXG02802981-0abb-446b-86ea-ee588f02ed1a" class="jxgbox" style="width: 300px; height: 300px;"></div>
3410  * <script type="text/javascript">
3411  *     (function() {
3412  *         var board = JXG.JSXGraph.initBoard('JXG02802981-0abb-446b-86ea-ee588f02ed1a',
3413  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
3414  *             var c = board.create('implicitcurve', ['abs(x * y) - 3'], {
3415  *                 strokeWidth: 3,
3416  *                 strokeColor: JXG.palette.red,
3417  *                 strokeOpacity: 0.8
3418  *             });
3419  *
3420  *     })();
3421  *
3422  * </script><pre>
3423  *
3424  * @example
3425  * var niveauline = [];
3426  * niveauline = [0.5, 1, 1.5, 2];
3427  * for (let i = 0; i < niveauline.length; i++) {
3428  *     board.create("implicitcurve", [
3429  *         (x, y) => x ** .5 * y ** .5 - niveauline[i],
3430            [0.25, 3], [0.5, 4] // Domain
3431  *     ], {
3432  *         strokeWidth: 2,
3433  *         strokeColor: JXG.palette.red,
3434  *         strokeOpacity: (1 + i) / niveauline.length,
3435  *         needsRegularUpdate: false
3436  *     });
3437  * }
3438  *
3439  * </pre><div id="JXGccee9aab-6dd9-4a79-827d-3164f70cc6a1" class="jxgbox" style="width: 300px; height: 300px;"></div>
3440  * <script type="text/javascript">
3441  *     (function() {
3442  *         var board = JXG.JSXGraph.initBoard('JXGccee9aab-6dd9-4a79-827d-3164f70cc6a1',
3443  *             {boundingbox: [-1, 5, 5,-1], axis: true, showcopyright: false, shownavigation: false});
3444  *         var niveauline = [];
3445  *         niveauline = [0.5, 1, 1.5, 2];
3446  *         for (let i = 0; i < niveauline.length; i++) {
3447  *             board.create("implicitcurve", [
3448  *                 (x, y) => x ** .5 * y ** .5 - niveauline[i],
3449  *                 [0.25, 3], [0.5, 4]
3450  *             ], {
3451  *                 strokeWidth: 2,
3452  *                 strokeColor: JXG.palette.red,
3453  *                 strokeOpacity: (1 + i) / niveauline.length,
3454  *                 needsRegularUpdate: false
3455  *             });
3456  *         }
3457  *
3458  *     })();
3459  *
3460  * </script><pre>
3461  *
3462  */
3463 JXG.createImplicitCurve = function (board, parents, attributes) {
3464     var c, attr;
3465 
3466     if ([1, 3, 5].indexOf(parents.length) < 0) {
3467         throw new Error(
3468             "JSXGraph: Can't create curve implicitCurve with given parent'" +
3469             "\nPossible parent types: [f], [f, rangex, rangey], [f, dfx, dfy] or [f, dfx, dfy, rangex, rangey]" +
3470             "\nwith functions f, dfx, dfy and arrays of length 2 rangex, rangey."
3471         );
3472     }
3473 
3474     // if (parents.length === 3) {
3475     //     if (!Type.isArray(parents[1]) && !Type.isArray(parents[2])) {
3476     //         throw new Error(
3477     //             "JSXGraph: Can't create curve implicitCurve with given parent'" +
3478     //             "\nPossible parent types: [f], [f, rangex, rangey], [f, dfx, dfy] or [f, dfx, dfy, rangex, rangey]" +
3479     //             "\nwith functions f, dfx, dfy and arrays of length 2 rangex, rangey."
3480     //         );
3481     //     }
3482     // }
3483     // if (parents.length === 5) {
3484     //     if (!Type.isArray(parents[3]) && !Type.isArray(parents[4])) {
3485     //         throw new Error(
3486     //             "JSXGraph: Can't create curve implicitCurve with given parent'" +
3487     //             "\nPossible parent types: [f], [f, rangex, rangey], [f, dfx, dfy] or [f, dfx, dfy, rangex, rangey]" +
3488     //             "\nwith functions f, dfx, dfy and arrays of length 2 rangex, rangey."
3489     //         );
3490     //     }
3491     // }
3492 
3493     attr = Type.copyAttributes(attributes, board.options, 'implicitcurve');
3494     c = board.create("curve", [[], []], attr);
3495 
3496     /**
3497      * Function of two variables for the left side of the equation <i>f(x,y)=0</i>.
3498      *
3499      * @name f
3500      * @memberOf ImplicitCurve.prototype
3501      * @function
3502      * @returns {Number}
3503      */
3504     c.f = Type.createFunction(parents[0], board, 'x, y');
3505 
3506     /**
3507      * Partial derivative in the first variable of
3508      * the left side of the equation <i>f(x,y)=0</i>.
3509      * If null, then numerical derivative is used.
3510      *
3511      * @name dfx
3512      * @memberOf ImplicitCurve.prototype
3513      * @function
3514      * @returns {Number}
3515      */
3516     if (parents.length === 5 || Type.isString(parents[1]) || Type.isFunction(parents[1])) {
3517         c.dfx = Type.createFunction(parents[1], board, 'x, y');
3518     } else {
3519         c.dfx = null;
3520     }
3521 
3522     /**
3523      * Partial derivative in the second variable of
3524      * the left side of the equation <i>f(x,y)=0</i>.
3525      * If null, then numerical derivative is used.
3526      *
3527      * @name dfy
3528      * @memberOf ImplicitCurve.prototype
3529      * @function
3530      * @returns {Number}
3531      */
3532     if (parents.length === 5 || Type.isString(parents[2]) || Type.isFunction(parents[2])) {
3533         c.dfy = Type.createFunction(parents[2], board, 'x, y');
3534     } else {
3535         c.dfy = null;
3536     }
3537 
3538     /**
3539      * Defines a domain for searching f(x,y)=0. Default is null, meaning
3540      * the bounding box of the board is used.
3541      * Using domain, visProp.margin is ignored.
3542      * @name domain
3543      * @memberOf ImplicitCurve.prototype
3544      * @param {Array} of length 4 defining the domain used to compute the implict curve.
3545      * Syntax: [x_min, y_max, x_max, y_min]
3546      */
3547     // c.domain = board.getBoundingBox();
3548     c.domain = null;
3549     if (parents.length === 5) {
3550         c.domain = [parents[3], parents[4]];
3551         //     [Math.min(parents[3][0], parents[3][1]), Math.max(parents[3][0], parents[3][1])],
3552         //     [Math.min(parents[4][0], parents[4][1]), Math.max(parents[4][0], parents[4][1])]
3553         // ];
3554     } else if (parents.length === 3) {
3555         c.domain = [parents[1], parents[2]];
3556         //     [Math.min(parents[1][0], parents[1][1]), Math.max(parents[1][0], parents[1][1])],
3557         //     [Math.min(parents[2][0], parents[2][1]), Math.max(parents[2][0], parents[2][1])]
3558         // ];
3559     }
3560 
3561     /**
3562      * @class
3563      * @ignore
3564      */
3565     c.updateDataArray = function () {
3566         var bbox, rx, ry,
3567             ip, cfg,
3568             ret = [],
3569             mgn;
3570 
3571         if (this.domain === null) {
3572             mgn = this.evalVisProp('margin');
3573             bbox = this.board.getBoundingBox();
3574             bbox[0] -= mgn;
3575             bbox[1] += mgn;
3576             bbox[2] += mgn;
3577             bbox[3] -= mgn;
3578         } else {
3579             rx = Type.evaluate(this.domain[0]);
3580             ry = Type.evaluate(this.domain[1]);
3581             bbox = [
3582                 Math.min(rx[0], rx[1]),
3583                 Math.max(ry[0], ry[1]),
3584                 Math.max(rx[0], rx[1]),
3585                 Math.min(ry[0], ry[1])
3586                 // rx[0], ry[1], rx[1], ry[0]
3587             ];
3588         }
3589 
3590         cfg = {
3591             resolution_out: Math.max(0.01, this.evalVisProp('resolution_outer')),
3592             resolution_in: Math.max(0.01, this.evalVisProp('resolution_inner')),
3593             max_steps: this.evalVisProp('max_steps'),
3594             alpha_0: this.evalVisProp('alpha_0'),
3595             tol_u0: this.evalVisProp('tol_u0'),
3596             tol_newton: this.evalVisProp('tol_newton'),
3597             tol_cusp: this.evalVisProp('tol_cusp'),
3598             tol_progress: this.evalVisProp('tol_progress'),
3599             qdt_box: this.evalVisProp('qdt_box'),
3600             kappa_0: this.evalVisProp('kappa_0'),
3601             delta_0: this.evalVisProp('delta_0'),
3602             h_initial: this.evalVisProp('h_initial'),
3603             h_critical: this.evalVisProp('h_critical'),
3604             h_max: this.evalVisProp('h_max'),
3605             loop_dist: this.evalVisProp('loop_dist'),
3606             loop_dir: this.evalVisProp('loop_dir'),
3607             loop_detection: this.evalVisProp('loop_detection'),
3608             unitX: this.board.unitX,
3609             unitY: this.board.unitY
3610         };
3611         this.dataX = [];
3612         this.dataY = [];
3613 
3614         // console.time("implicit plot");
3615         ip = new ImplicitPlot(bbox, cfg, this.f, this.dfx, this.dfy);
3616         this.qdt = ip.qdt;
3617 
3618         ret = ip.plot();
3619         // console.timeEnd("implicit plot");
3620 
3621         this.dataX = ret[0];
3622         this.dataY = ret[1];
3623     };
3624 
3625     c.elType = 'implicitcurve';
3626 
3627     return c;
3628 };
3629 
3630 JXG.registerElement("implicitcurve", JXG.createImplicitCurve);
3631 
3632 /**
3633  * @class Sketch a curve by dragging the pointer device on the board.
3634  * If enabled:true, it is always done even if the curve is invisible.
3635  * A JSXGraph borad contains a length two array board.sketches
3636  * with two sketchcurves.
3637  *
3638  * @pseudo
3639  * @name SketchCurve
3640  * @augments JXG.Curve
3641  * @constructor
3642  * @type JXG.Curve
3643  * @see JXG.Board#sketches
3644  * @see JXG.Board#sketch
3645  * @private
3646  */
3647 JXG.createSketchCurve = function (board, parents, attributes) {
3648     var c, attr;
3649 
3650     attr = Type.copyAttributes(attributes, board.options, 'sketchcurve');
3651     c = board.create("curve", [[], []], attr);
3652 
3653     c.elType = 'sketchcurve';
3654 
3655     return c;
3656 };
3657 
3658 JXG.registerElement("sketchcurve", JXG.createSketchCurve);
3659 
3660 export default JXG.Curve;
3661 
3662 // export default {
3663 //     Curve: JXG.Curve,
3664 //     createCardinalSpline: JXG.createCardinalSpline,
3665 //     createCurve: JXG.createCurve,
3666 //     createCurveDifference: JXG.createCurveDifference,
3667 //     createCurveIntersection: JXG.createCurveIntersection,
3668 //     createCurveUnion: JXG.createCurveUnion,
3669 //     createDerivative: JXG.createDerivative,
3670 //     createFunctiongraph: JXG.createFunctiongraph,
3671 //     createMetapostSpline: JXG.createMetapostSpline,
3672 //     createPlot: JXG.createFunctiongraph,
3673 //     createSpline: JXG.createSpline,
3674 //     createRiemannsum: JXG.createRiemannsum,
3675 //     createStepfunction: JXG.createStepfunction,
3676 //     createTracecurve: JXG.createTracecurve
3677 // };
3678 
3679 // const Curve = JXG.Curve;
3680 // export { Curve as default, Curve};
3681