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, i;
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                 // Ignore first cooordinate if given as [z, x, y]
1083                 i = (mat.length > 2) ? 1 : 0;
1084                 this.dataX = mat[i];
1085                 this.dataY = mat[i + 1];
1086                 this.numberPoints = this.dataX.length;
1087                 this.Y = this.interpolationFunctionFromArray.apply(this, ["Y"]);
1088             } else {
1089                 this.Y = Type.createFunction(yterm, this.board, varname);
1090             }
1091 
1092             /**
1093              * Polar form
1094              * Input data is function xterm() and offset coordinates yterm
1095              */
1096             if (Type.isFunction(xterm) && Type.isArray(yterm)) {
1097                 // Xoffset, Yoffset
1098                 fx = Type.createFunction(yterm[0], this.board, "");
1099                 fy = Type.createFunction(yterm[1], this.board, "");
1100 
1101                 this.X = function (phi) {
1102                     return xterm(phi) * Math.cos(phi) + fx();
1103                 };
1104                 this.X.deps = fx.deps;
1105 
1106                 this.Y = function (phi) {
1107                     return xterm(phi) * Math.sin(phi) + fy();
1108                 };
1109                 this.Y.deps = fy.deps;
1110 
1111                 this.visProp.curvetype = 'polar';
1112             }
1113 
1114             // Set the upper and lower bounds for the parameter of the curve.
1115             // If not defined, reset the bounds to the default values
1116             // given in Curve.prototype.minX, Curve.prototype.maxX
1117             if (Type.exists(mi)) {
1118                 this.minX = Type.createFunction(mi, this.board, "");
1119             } else {
1120                 delete this.minX;
1121             }
1122             if (Type.exists(ma)) {
1123                 this.maxX = Type.createFunction(ma, this.board, "");
1124             } else {
1125                 delete this.maxX;
1126             }
1127 
1128             this.addParentsFromJCFunctions([this.X, this.Y, this.minX, this.maxX]);
1129         },
1130 
1131         /**
1132          * Finds dependencies in a given term and notifies the parents by adding the
1133          * dependent object to the found objects child elements.
1134          * @param {String} contentStr String containing dependencies for the given object.
1135          */
1136         notifyParents: function (contentStr) {
1137             var fstr,
1138                 dep,
1139                 isJessieCode = false,
1140                 obj;
1141 
1142             // Read dependencies found by the JessieCode parser
1143             obj = { xterm: 1, yterm: 1 };
1144             for (fstr in obj) {
1145                 if (
1146                     obj.hasOwnProperty(fstr) &&
1147                     this.hasOwnProperty(fstr) &&
1148                     this[fstr].origin
1149                 ) {
1150                     isJessieCode = true;
1151                     for (dep in this[fstr].origin.deps) {
1152                         if (this[fstr].origin.deps.hasOwnProperty(dep)) {
1153                             this[fstr].origin.deps[dep].addChild(this);
1154                         }
1155                     }
1156                 }
1157             }
1158 
1159             if (!isJessieCode) {
1160                 GeonextParser.findDependencies(this, contentStr, this.board);
1161             }
1162         },
1163 
1164         /**
1165          * Position a curve label according to the attributes "position" and distance.
1166          * This function is also used for angle, arc and sector.
1167          *
1168          * @param {String} pos
1169          * @param {Number} distance
1170          * @returns {JXG.Coords}
1171          */
1172         getLabelPosition: function(pos, distance) {
1173             var x, y, xy,
1174                 c, d, e,
1175                 c_t, c_te, c_ma, c_mi,
1176                 lbda,
1177                 mi, ma, ar,
1178                 t, dx, dy,
1179                 dist = 1.5;
1180 
1181             // Shrink domain if necessary
1182             mi = this.minX();
1183             ma = this.maxX();
1184             ar = Numerics.findDomain(this.X, [mi, ma], null, false);
1185             ar = Numerics.findDomain(this.Y, ar, null, false);
1186             mi = Math.max(ar[0], ar[0]); // ???
1187             ma = Math.min(ar[1], ar[1]); // ???
1188 
1189             xy = Type.parsePosition(pos);
1190             lbda = Type.parseNumber(xy.pos, ma - mi, 1);
1191 
1192             if (xy.pos.indexOf('fr') < 0 && xy.pos.indexOf('%') < 0) {
1193                 // The unit has to be 'fr' or '%'. 'px' or plain numbers are not supported
1194                 lbda = 0;
1195             }
1196 
1197             t = mi + lbda;
1198 
1199             // x = this.X(t);
1200             // y = this.Y(t);
1201             c_t = this.Ft(t); // Include transformations
1202             x = c_t[1];
1203             y = c_t[2];
1204             // If x or y are NaN, the label is set to the line
1205             // between the first and last point.
1206             if (isNaN(x + y)) {
1207                 lbda /= (ma - mi);
1208                 t = mi + lbda;
1209 
1210                 // x = this.X(mi) + lbda * (this.X(ma) - this.X(mi));
1211                 // y = this.Y(mi) + lbda * (this.Y(ma) - this.Y(mi));
1212                 c_mi = this.Ft(mi);
1213                 c_ma = this.Ft(ma);
1214                 x = c_mi[1] + lbda * (c_ma[1] - c_mi[1]);
1215                 y = c_mi[2] + lbda * (c_ma[2] - c_mi[2]);
1216             }
1217             c = (new Coords(Const.COORDS_BY_USER, [x, y], this.board)).scrCoords;
1218 
1219             e = Mat.eps;
1220             if (t < mi + e) {
1221                 // dx = (this.X(t + e) - this.X(t)) / e;
1222                 // dy = (this.Y(t + e) - this.Y(t)) / e;
1223                 c_te = this.Ft(t + e);
1224                 dx = (c_te[1] - c_t[1]) / e;
1225                 dy = (c_te[2] - c_t[2]) / e;
1226             } else if (t > ma - e) {
1227                 // dx = (this.X(t) - this.X(t - e)) / e;
1228                 // dy = (this.Y(t) - this.Y(t - e)) / e;
1229                 c_te = this.Ft(t - e);
1230                 dx = (c_t[1] - c_te[1]) / e;
1231                 dy = (c_t[2] - c_te[2]) / e;
1232             } else {
1233                 // dx = 0.5 * (this.X(t + e) - this.X(t - e)) / e;
1234                 // dy = 0.5 * (this.Y(t + e) - this.Y(t - e)) / e;
1235                 c_te = this.Ft(t + e);
1236                 c_t  = this.Ft(t - e);
1237                 dx = 0.5 * (c_te[1] - c_t[1]) / e;
1238                 dy = 0.5 * (c_te[2] - c_t[2]) / e;
1239             }
1240             dx = isNaN(dx) ? 1. : dx;
1241             dy = isNaN(dy) ? 1. : dy;
1242             d = Mat.hypot(dx, dy);
1243 
1244             if (xy.side === 'left') {
1245                 dy *= -1;
1246             } else {
1247                 dx *= -1;
1248             }
1249 
1250             // Position left or right
1251 
1252             if (Type.exists(this.label)) {
1253                 dist = 0.5 * distance / d;
1254             }
1255 
1256             x = c[1] + dy * this.label.size[0] * dist;
1257             y = c[2] - dx * this.label.size[1] * dist;
1258 
1259             return new Coords(Const.COORDS_BY_SCREEN, [x, y], this.board);
1260         },
1261 
1262         // documented in geometryElement
1263         getLabelAnchor: function () {
1264             var x, y, pos,
1265                 // xy, lbda, e,
1266                 // t, dx, dy, d,
1267                 // dist = 1.5,
1268                 c,
1269                 lo = 0.1,
1270                 up = 0.9,
1271                 ax = lo * this.board.canvasWidth,
1272                 ay = lo * this.board.canvasHeight,
1273                 bx = up * this.board.canvasWidth,
1274                 by = up * this.board.canvasHeight;
1275 
1276             if (!Type.exists(this.label)) {
1277                 return new Coords(Const.COORDS_BY_SCREEN, [NaN, NaN], this.board);
1278             }
1279             pos = this.label.evalVisProp('position');
1280             if (!Type.isString(pos)) {
1281                 return new Coords(Const.COORDS_BY_SCREEN, [NaN, NaN], this.board);
1282             }
1283 
1284             if (pos.indexOf('right') < 0 && pos.indexOf('left') < 0) {
1285                 // Old system
1286                 switch (this.evalVisProp('label.position')) {
1287                     case "ulft":
1288                         x = ax;
1289                         y = ay;
1290                         break;
1291                     case "llft":
1292                         x = ax;
1293                         y = by;
1294                         break;
1295                     case "rt":
1296                         x = bx;
1297                         y = 0.5 * by;
1298                         break;
1299                     case "lrt":
1300                         x = bx;
1301                         y = by;
1302                         break;
1303                     case "urt":
1304                         x = bx;
1305                         y = ay;
1306                         break;
1307                     case "top":
1308                         x = 0.5 * bx;
1309                         y = ay;
1310                         break;
1311                     case "bot":
1312                         x = 0.5 * bx;
1313                         y = by;
1314                         break;
1315                     default:
1316                         // includes case 'lft'
1317                         x = ax;
1318                         y = 0.5 * by;
1319                 }
1320             } else {
1321                 // New positioning, e.g. "25% left"
1322                 return this.getLabelPosition(pos, this.label.evalVisProp('distance'));
1323             }
1324             c = new Coords(Const.COORDS_BY_SCREEN, [x, y], this.board, false);
1325             return Geometry.projectCoordsToCurve(
1326                 c.usrCoords[1], c.usrCoords[2], 0, this, this.board
1327             )[0];
1328         },
1329 
1330         // documented in geometry element
1331         cloneToBackground: function () {
1332             var er,
1333                 copy = Type.getCloneObject(this);
1334 
1335             copy.points = this.points.slice(0);
1336             copy.bezierDegree = this.bezierDegree;
1337             copy.numberPoints = this.numberPoints;
1338 
1339             er = this.board.renderer.enhancedRendering;
1340             this.board.renderer.enhancedRendering = true;
1341             this.board.renderer.drawCurve(copy);
1342             this.board.renderer.enhancedRendering = er;
1343             this.traces[copy.id] = copy.rendNode;
1344 
1345             return this;
1346         },
1347 
1348         // Already documented in GeometryElement
1349         bounds: function () {
1350             var minX = Infinity,
1351                 maxX = -Infinity,
1352                 minY = Infinity,
1353                 maxY = -Infinity,
1354                 l = this.points.length,
1355                 i,
1356                 bezier,
1357                 up;
1358 
1359             if (this.bezierDegree === 3) {
1360                 // Add methods X(), Y()
1361                 for (i = 0; i < l; i++) {
1362                     this.points[i].X = Type.bind(function () {
1363                         return this.usrCoords[1];
1364                     }, this.points[i]);
1365                     this.points[i].Y = Type.bind(function () {
1366                         return this.usrCoords[2];
1367                     }, this.points[i]);
1368                 }
1369                 bezier = Numerics.bezier(this.points);
1370                 up = bezier[3]();
1371                 minX = Numerics.fminbr(
1372                     function (t) {
1373                         return bezier[0](t);
1374                     },
1375                     [0, up]
1376                 );
1377                 maxX = Numerics.fminbr(
1378                     function (t) {
1379                         return -bezier[0](t);
1380                     },
1381                     [0, up]
1382                 );
1383                 minY = Numerics.fminbr(
1384                     function (t) {
1385                         return bezier[1](t);
1386                     },
1387                     [0, up]
1388                 );
1389                 maxY = Numerics.fminbr(
1390                     function (t) {
1391                         return -bezier[1](t);
1392                     },
1393                     [0, up]
1394                 );
1395 
1396                 minX = bezier[0](minX);
1397                 maxX = bezier[0](maxX);
1398                 minY = bezier[1](minY);
1399                 maxY = bezier[1](maxY);
1400                 return [minX, maxY, maxX, minY];
1401             }
1402 
1403             // Linear segments
1404             for (i = 0; i < l; i++) {
1405                 if (minX > this.points[i].usrCoords[1]) {
1406                     minX = this.points[i].usrCoords[1];
1407                 }
1408 
1409                 if (maxX < this.points[i].usrCoords[1]) {
1410                     maxX = this.points[i].usrCoords[1];
1411                 }
1412 
1413                 if (minY > this.points[i].usrCoords[2]) {
1414                     minY = this.points[i].usrCoords[2];
1415                 }
1416 
1417                 if (maxY < this.points[i].usrCoords[2]) {
1418                     maxY = this.points[i].usrCoords[2];
1419                 }
1420             }
1421 
1422             return [minX, maxY, maxX, minY];
1423         },
1424 
1425         // documented in element.js
1426         getParents: function () {
1427             var p = [this.xterm, this.yterm, this.minX(), this.maxX()];
1428 
1429             if (this.parents.length !== 0) {
1430                 p = this.parents;
1431             }
1432 
1433             return p;
1434         },
1435 
1436         /**
1437          * Shift the curve by the vector 'where'.
1438          *
1439          * @param {Array} where Array containing the x and y coordinate of the target location.
1440          * @returns {JXG.Curve} Reference to itself.
1441          */
1442         moveTo: function (where) {
1443             // TODO add animation
1444             var delta = [],
1445                 p;
1446             if (this.points.length > 0 && !this.evalVisProp('fixed')) {
1447                 p = this.points[0];
1448                 if (where.length === 3) {
1449                     delta = [
1450                         where[0] - p.usrCoords[0],
1451                         where[1] - p.usrCoords[1],
1452                         where[2] - p.usrCoords[2]
1453                     ];
1454                 } else {
1455                     delta = [where[0] - p.usrCoords[1], where[1] - p.usrCoords[2]];
1456                 }
1457                 this.setPosition(Const.COORDS_BY_USER, delta);
1458                 return this.board.update(this);
1459             }
1460             return this;
1461         },
1462 
1463         /**
1464          * If the curve is the result of a transformation applied
1465          * to a continuous curve, the glider projection has to be done
1466          * on the original curve. Otherwise there will be problems
1467          * when changing between high and low precision plotting,
1468          * since there number of points changes.
1469          *
1470          * @private
1471          * @returns {Array} [Boolean, curve]: Array contining 'true' if curve is result of a transformation,
1472          *   and the source curve of the transformation.
1473          */
1474         getTransformationSource: function () {
1475             var isTransformed, curve_org;
1476             if (Type.exists(this._transformationSource)) {
1477                 curve_org = this._transformationSource;
1478                 if (
1479                     curve_org.elementClass === Const.OBJECT_CLASS_CURVE //&&
1480                     //curve_org.evalVisProp('curvetype') !== 'plot'
1481                 ) {
1482                     isTransformed = true;
1483                 }
1484             }
1485             return [isTransformed, curve_org];
1486         },
1487 
1488         /**
1489          * Return the points of the curve as array of length-three-arrays [z, x, y], i.e.
1490          * return an array of homogeneous coordinates.
1491          * The returned coordinates are in user coordinates. Finite homogeneous coordinates have the first value set to 1,
1492          * i.e. it can be ignored.
1493          * <p>
1494          * The points of the curve are either the elements of the properties dataX and dataY or
1495          * the result of the plotting algorithm. In any case, the points are stored in the private
1496          * property "points".
1497          * @returns {Array}
1498          */
1499         getCoords: function() {
1500             var len, i,
1501                 arr = [];
1502 
1503             len = this.numberPoints;
1504             for (i = 0; i < len; i++) {
1505                 arr.push(this.points[i].usrCoords.slice());
1506             }
1507             return arr;
1508         }
1509 
1510 
1511     }
1512 );
1513 
1514 /**
1515  * @class  Curves can be defined by mappings or by discrete data sets.
1516  * 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].
1517  * <p>
1518  * The following types of curves can be plotted:
1519  * <ul>
1520  *  <li> parametric curves: t mapsto (x(t),y(t)), where x() and y() are univariate functions.
1521  *  <li> polar curves: curves commonly written with polar equations like spirals and cardioids.
1522  *  <li> data plots: plot line segments through a given list of coordinates.
1523  * </ul>
1524  * @pseudo
1525  * @name Curve
1526  * @augments JXG.Curve
1527  * @constructor
1528  * @type Object
1529  * @description JXG.Curve
1530 
1531  * @param {function,number_function,number_function,number_function,number}  x,y,a_,b_ Parent elements for Parametric Curves.
1532  *                     <p>
1533  *                     x describes the x-coordinate of the curve. It may be a function term in one variable, e.g. x(t).
1534  *                     In case of x being of type number, x(t) is set to  a constant function.
1535  *                     this function at the values of the array.
1536  *                     </p>
1537  *                     <p>
1538  *                     y describes the y-coordinate of the curve. In case of a number, y(t) is set to the constant function
1539  *                     returning this number.
1540  *                     </p>
1541  *                     <p>
1542  *                     Further parameters are an optional number or function for the left interval border a,
1543  *                     and an optional number or function for the right interval border b.
1544  *                     </p>
1545  *                     <p>
1546  *                     Default values are a=-10 and b=10.
1547  *                     </p>
1548  *
1549  * @param {array_array,function,number}
1550  *
1551  * @description x,y Parent elements for Data Plots.
1552  *                     <p>
1553  *                     x and y are arrays contining the x and y coordinates of the data points which are connected by
1554  *                     line segments. The individual entries of x and y may also be functions.
1555  *                     In case of x being an array the curve type is data plot, regardless of the second parameter and
1556  *                     if additionally the second parameter y is a function term the data plot evaluates.
1557  *                     </p>
1558  * @param {function_array,function,number_function,number_function,number}
1559  * @description r,offset_,a_,b_ Parent elements for Polar Curves.
1560  *                     <p>
1561  *                     The first parameter is a function term r(phi) describing the polar curve.
1562  *                     </p>
1563  *                     <p>
1564  *                     The second parameter is the offset of the curve. It has to be
1565  *                     an array containing numbers or functions describing the offset. Default value is the origin [0,0].
1566  *                     </p>
1567  *                     <p>
1568  *                     Further parameters are an optional number or function for the left interval border a,
1569  *                     and an optional number or function for the right interval border b.
1570  *                     </p>
1571  *                     <p>
1572  *                     Default values are a=-10 and b=10.
1573  *                     </p>
1574  * <p>
1575  * Additionally, a curve can be created by providing a curve and a transformation (or an array of transformations).
1576  * The result is a curve which is the transformation of the supplied curve.
1577  *
1578  * @see JXG.Curve
1579  * @example
1580  * // Parametric curve
1581  * // Create a curve of the form (t-sin(t), 1-cos(t), i.e.
1582  * // the cycloid curve.
1583  *   var graph = board.create('curve',
1584  *                        [function(t){ return t-Math.sin(t);},
1585  *                         function(t){ return 1-Math.cos(t);},
1586  *                         0, 2*Math.PI]
1587  *                     );
1588  * </pre><div class="jxgbox" id="JXGaf9f818b-f3b6-4c4d-8c4c-e4a4078b726d" style="width: 300px; height: 300px;"></div>
1589  * <script type="text/javascript">
1590  *   var c1_board = JXG.JSXGraph.initBoard('JXGaf9f818b-f3b6-4c4d-8c4c-e4a4078b726d', {boundingbox: [-1, 5, 7, -1], axis: true, showcopyright: false, shownavigation: false});
1591  *   var graph1 = c1_board.create('curve', [function(t){ return t-Math.sin(t);},function(t){ return 1-Math.cos(t);},0, 2*Math.PI]);
1592  * </script><pre>
1593  * @example
1594  * // Data plots
1595  * // Connect a set of points given by coordinates with dashed line segments.
1596  * // The x- and y-coordinates of the points are given in two separate
1597  * // arrays.
1598  *   var x = [0,1,2,3,4,5,6,7,8,9];
1599  *   var y = [9.2,1.3,7.2,-1.2,4.0,5.3,0.2,6.5,1.1,0.0];
1600  *   var graph = board.create('curve', [x,y], {dash:2});
1601  * </pre><div class="jxgbox" id="JXG7dcbb00e-b6ff-481d-b4a8-887f5d8c6a83" style="width: 300px; height: 300px;"></div>
1602  * <script type="text/javascript">
1603  *   var c3_board = JXG.JSXGraph.initBoard('JXG7dcbb00e-b6ff-481d-b4a8-887f5d8c6a83', {boundingbox: [-1,10,10,-1], axis: true, showcopyright: false, shownavigation: false});
1604  *   var x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
1605  *   var y = [9.2, 1.3, 7.2, -1.2, 4.0, 5.3, 0.2, 6.5, 1.1, 0.0];
1606  *   var graph3 = c3_board.create('curve', [x,y], {dash:2});
1607  * </script><pre>
1608  * @example
1609  * // Polar plot
1610  * // Create a curve with the equation r(phi)= a*(1+phi), i.e.
1611  * // a cardioid.
1612  *   var a = board.create('slider',[[0,2],[2,2],[0,1,2]]);
1613  *   var graph = board.create('curve',
1614  *                        [function(phi){ return a.Value()*(1-Math.cos(phi));},
1615  *                         [1,0],
1616  *                         0, 2*Math.PI],
1617  *                         {curveType: 'polar'}
1618  *                     );
1619  * </pre><div class="jxgbox" id="JXGd0bc7a2a-8124-45ca-a6e7-142321a8f8c2" style="width: 300px; height: 300px;"></div>
1620  * <script type="text/javascript">
1621  *   var c2_board = JXG.JSXGraph.initBoard('JXGd0bc7a2a-8124-45ca-a6e7-142321a8f8c2', {boundingbox: [-3,3,3,-3], axis: true, showcopyright: false, shownavigation: false});
1622  *   var a = c2_board.create('slider',[[0,2],[2,2],[0,1,2]]);
1623  *   var graph2 = c2_board.create('curve', [function(phi){ return a.Value()*(1-Math.cos(phi));}, [1,0], 0, 2*Math.PI], {curveType: 'polar'});
1624  * </script><pre>
1625  *
1626  * @example
1627  *  // Draggable Bezier curve
1628  *  var col, p, c;
1629  *  col = 'blue';
1630  *  p = [];
1631  *  p.push(board.create('point',[-2, -1 ], {size: 5, strokeColor:col, fillColor:col}));
1632  *  p.push(board.create('point',[1, 2.5 ], {size: 5, strokeColor:col, fillColor:col}));
1633  *  p.push(board.create('point',[-1, -2.5 ], {size: 5, strokeColor:col, fillColor:col}));
1634  *  p.push(board.create('point',[2, -2], {size: 5, strokeColor:col, fillColor:col}));
1635  *
1636  *  c = board.create('curve', JXG.Math.Numerics.bezier(p),
1637  *              {strokeColor:'red', name:"curve", strokeWidth:5, fixed: false}); // Draggable curve
1638  *  c.addParents(p);
1639  * </pre><div class="jxgbox" id="JXG7bcc6280-f6eb-433e-8281-c837c3387849" style="width: 300px; height: 300px;"></div>
1640  * <script type="text/javascript">
1641  * (function(){
1642  *  var board, col, p, c;
1643  *  board = JXG.JSXGraph.initBoard('JXG7bcc6280-f6eb-433e-8281-c837c3387849', {boundingbox: [-3,3,3,-3], axis: true, showcopyright: false, shownavigation: false});
1644  *  col = 'blue';
1645  *  p = [];
1646  *  p.push(board.create('point',[-2, -1 ], {size: 5, strokeColor:col, fillColor:col}));
1647  *  p.push(board.create('point',[1, 2.5 ], {size: 5, strokeColor:col, fillColor:col}));
1648  *  p.push(board.create('point',[-1, -2.5 ], {size: 5, strokeColor:col, fillColor:col}));
1649  *  p.push(board.create('point',[2, -2], {size: 5, strokeColor:col, fillColor:col}));
1650  *
1651  *  c = board.create('curve', JXG.Math.Numerics.bezier(p),
1652  *              {strokeColor:'red', name:"curve", strokeWidth:5, fixed: false}); // Draggable curve
1653  *  c.addParents(p);
1654  * })();
1655  * </script><pre>
1656  *
1657  * @example
1658  *         // The curve cu2 is the reflection of cu1 against line li
1659  *         var li = board.create('line', [1,1,1], {strokeColor: '#aaaaaa'});
1660  *         var reflect = board.create('transform', [li], {type: 'reflect'});
1661  *         var cu1 = board.create('curve', [[-1, -1, -0.5, -1, -1, -0.5], [-3, -2, -2, -2, -2.5, -2.5]]);
1662  *         var cu2 = board.create('curve', [cu1, reflect], {strokeColor: 'red'});
1663  *
1664  * </pre><div id="JXG866dc7a2-d448-11e7-93b3-901b0e1b8723" class="jxgbox" style="width: 300px; height: 300px;"></div>
1665  * <script type="text/javascript">
1666  *     (function() {
1667  *         var board = JXG.JSXGraph.initBoard('JXG866dc7a2-d448-11e7-93b3-901b0e1b8723',
1668  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
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  *     })();
1675  *
1676  * </script><pre>
1677  */
1678 JXG.createCurve = function (board, parents, attributes) {
1679     var obj,
1680         cu,
1681         attr = Type.copyAttributes(attributes, board.options, 'curve');
1682 
1683     obj = board.select(parents[0], true);
1684     if (
1685         Type.isTransformationOrArray(parents[1]) &&
1686         Type.isObject(obj) &&
1687         (obj.type === Const.OBJECT_TYPE_CURVE ||
1688             obj.type === Const.OBJECT_TYPE_ANGLE ||
1689             obj.type === Const.OBJECT_TYPE_ARC ||
1690             obj.type === Const.OBJECT_TYPE_CONIC ||
1691             obj.type === Const.OBJECT_TYPE_SECTOR)
1692     ) {
1693         if (obj.type === Const.OBJECT_TYPE_SECTOR) {
1694             attr = Type.copyAttributes(attributes, board.options, 'sector');
1695         } else if (obj.type === Const.OBJECT_TYPE_ARC) {
1696             attr = Type.copyAttributes(attributes, board.options, 'arc');
1697         } else if (obj.type === Const.OBJECT_TYPE_ANGLE) {
1698             if (!Type.exists(attributes.withLabel)) {
1699                 attributes.withLabel = false;
1700             }
1701             attr = Type.copyAttributes(attributes, board.options, 'angle');
1702         } else {
1703             attr = Type.copyAttributes(attributes, board.options, 'curve');
1704         }
1705         attr = Type.copyAttributes(attr, board.options, 'curve');
1706 
1707         cu = new JXG.Curve(board, ["x", [], []], attr);
1708         /**
1709          * @class
1710          * @ignore
1711          */
1712         cu.updateDataArray = function () {
1713             var i,
1714                 le = obj.numberPoints;
1715             this.bezierDegree = obj.bezierDegree;
1716             this.dataX = [];
1717             this.dataY = [];
1718             for (i = 0; i < le; i++) {
1719                 this.dataX.push(obj.points[i].usrCoords[1]);
1720                 this.dataY.push(obj.points[i].usrCoords[2]);
1721             }
1722             return this;
1723         };
1724         cu.addTransform(parents[1]);
1725         obj.addChild(cu);
1726         cu.setParents([obj]);
1727         cu._transformationSource = obj;
1728 
1729         return cu;
1730     }
1731     attr = Type.copyAttributes(attributes, board.options, 'curve');
1732     return new JXG.Curve(board, ["x"].concat(parents), attr);
1733 };
1734 
1735 JXG.registerElement("curve", JXG.createCurve);
1736 
1737 /**
1738  * @class A functiongraph visualizes a map x → f(x).
1739  * The graph is displayed for x in the interval [a,b] and is a {@link Curve} element.
1740  * @pseudo
1741  * @name Functiongraph
1742  * @augments JXG.Curve
1743  * @constructor
1744  * @type JXG.Curve
1745  * @param {function_number,function_number,function} f,a_,b_ Parent elements are a function term f(x) describing the function graph.
1746  *         <p>
1747  *         Further, an optional number or function for the left interval border a,
1748  *         and an optional number or function for the right interval border b.
1749  *         <p>
1750  *         Default values are a=-10 and b=10.
1751  * @see JXG.Curve
1752  * @example
1753  * // Create a function graph for f(x) = 0.5*x*x-2*x
1754  *   var graph = board.create('functiongraph',
1755  *                        [function(x){ return 0.5*x*x-2*x;}, -2, 4]
1756  *                     );
1757  * </pre><div class="jxgbox" id="JXGefd432b5-23a3-4846-ac5b-b471e668b437" style="width: 300px; height: 300px;"></div>
1758  * <script type="text/javascript">
1759  *   var alex1_board = JXG.JSXGraph.initBoard('JXGefd432b5-23a3-4846-ac5b-b471e668b437', {boundingbox: [-3, 7, 5, -3], axis: true, showcopyright: false, shownavigation: false});
1760  *   var graph = alex1_board.create('functiongraph', [function(x){ return 0.5*x*x-2*x;}, -2, 4]);
1761  * </script><pre>
1762  * @example
1763  * // Create a function graph for f(x) = 0.5*x*x-2*x with variable interval
1764  *   var s = board.create('slider',[[0,4],[3,4],[-2,4,5]]);
1765  *   var graph = board.create('functiongraph',
1766  *                        [function(x){ return 0.5*x*x-2*x;},
1767  *                         -2,
1768  *                         function(){return s.Value();}]
1769  *                     );
1770  * </pre><div class="jxgbox" id="JXG4a203a84-bde5-4371-ad56-44619690bb50" style="width: 300px; height: 300px;"></div>
1771  * <script type="text/javascript">
1772  *   var alex2_board = JXG.JSXGraph.initBoard('JXG4a203a84-bde5-4371-ad56-44619690bb50', {boundingbox: [-3, 7, 5, -3], axis: true, showcopyright: false, shownavigation: false});
1773  *   var s = alex2_board.create('slider',[[0,4],[3,4],[-2,4,5]]);
1774  *   var graph = alex2_board.create('functiongraph', [function(x){ return 0.5*x*x-2*x;}, -2, function(){return s.Value();}]);
1775  * </script><pre>
1776  */
1777 JXG.createFunctiongraph = function (board, parents, attributes) {
1778     var attr,
1779         par = ["x", "x"].concat(parents); // variable name and identity function for x-coordinate
1780     // par = ["x", function(x) { return x; }].concat(parents);
1781 
1782     attr = Type.copyAttributes(attributes, board.options, 'functiongraph');
1783     attr = Type.copyAttributes(attr, board.options, 'curve');
1784     attr.curvetype = 'functiongraph';
1785     return new JXG.Curve(board, par, attr);
1786 };
1787 
1788 JXG.registerElement("functiongraph", JXG.createFunctiongraph);
1789 JXG.registerElement("plot", JXG.createFunctiongraph);
1790 
1791 /**
1792  * @class The (natural) cubic spline curves (function graph) interpolating a set of points.
1793  * Create a dynamic spline interpolated curve given by sample points p_1 to p_n.
1794  * @pseudo
1795  * @name Spline
1796  * @augments JXG.Curve
1797  * @constructor
1798  * @type JXG.Curve
1799  * @param {JXG.Board} board Reference to the board the spline is drawn on.
1800  * @param {Array} parents Array of points the spline interpolates. This can be
1801  *   <ul>
1802  *   <li> an array of JSXGraph points</li>
1803  *   <li> an array of coordinate pairs</li>
1804  *   <li> an array of functions returning coordinate pairs</li>
1805  *   <li> an array consisting of an array with x-coordinates and an array of y-coordinates</li>
1806  *   </ul>
1807  *   All individual entries of coordinates arrays may be numbers or functions returning numbers.
1808  * @param {Object} attributes Define color, width, ... of the spline
1809  * @returns {JXG.Curve} Returns reference to an object of type JXG.Curve.
1810  * @see JXG.Curve
1811  * @example
1812  *
1813  * var p = [];
1814  * p[0] = board.create('point', [-2,2], {size: 4, face: 'o'});
1815  * p[1] = board.create('point', [0,-1], {size: 4, face: 'o'});
1816  * p[2] = board.create('point', [2,0], {size: 4, face: 'o'});
1817  * p[3] = board.create('point', [4,1], {size: 4, face: 'o'});
1818  *
1819  * var c = board.create('spline', p, {strokeWidth:3});
1820  * </pre><div id="JXG6c197afc-e482-11e5-b1bf-901b0e1b8723" style="width: 300px; height: 300px;"></div>
1821  * <script type="text/javascript">
1822  *     (function() {
1823  *         var board = JXG.JSXGraph.initBoard('JXG6c197afc-e482-11e5-b1bf-901b0e1b8723',
1824  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
1825  *
1826  *     var p = [];
1827  *     p[0] = board.create('point', [-2,2], {size: 4, face: 'o'});
1828  *     p[1] = board.create('point', [0,-1], {size: 4, face: 'o'});
1829  *     p[2] = board.create('point', [2,0], {size: 4, face: 'o'});
1830  *     p[3] = board.create('point', [4,1], {size: 4, face: 'o'});
1831  *
1832  *     var c = board.create('spline', p, {strokeWidth:3});
1833  *     })();
1834  *
1835  * </script><pre>
1836  *
1837  */
1838 JXG.createSpline = function (board, parents, attributes) {
1839     var el, funcs, ret;
1840 
1841     funcs = function () {
1842         var D,
1843             x = [],
1844             y = [];
1845 
1846         return [
1847             function (t, suspended) {
1848                 // Function term
1849                 var i, j, c;
1850 
1851                 if (!suspended) {
1852                     x = [];
1853                     y = [];
1854 
1855                     // given as [x[], y[]]
1856                     if (
1857                         parents.length === 2 &&
1858                         Type.isArray(parents[0]) &&
1859                         Type.isArray(parents[1]) &&
1860                         parents[0].length === parents[1].length
1861                     ) {
1862                         for (i = 0; i < parents[0].length; i++) {
1863                             if (Type.isFunction(parents[0][i])) {
1864                                 x.push(parents[0][i]());
1865                             } else {
1866                                 x.push(parents[0][i]);
1867                             }
1868 
1869                             if (Type.isFunction(parents[1][i])) {
1870                                 y.push(parents[1][i]());
1871                             } else {
1872                                 y.push(parents[1][i]);
1873                             }
1874                         }
1875                     } else {
1876                         for (i = 0; i < parents.length; i++) {
1877                             if (Type.isPoint(parents[i])) {
1878                                 x.push(parents[i].X());
1879                                 y.push(parents[i].Y());
1880                                 // given as [[x1,y1], [x2, y2], ...]
1881                             } else if (Type.isArray(parents[i]) && parents[i].length === 2) {
1882                                 for (j = 0; j < parents.length; j++) {
1883                                     if (Type.isFunction(parents[j][0])) {
1884                                         x.push(parents[j][0]());
1885                                     } else {
1886                                         x.push(parents[j][0]);
1887                                     }
1888 
1889                                     if (Type.isFunction(parents[j][1])) {
1890                                         y.push(parents[j][1]());
1891                                     } else {
1892                                         y.push(parents[j][1]);
1893                                     }
1894                                 }
1895                             } else if (
1896                                 Type.isFunction(parents[i]) &&
1897                                 parents[i]().length === 2
1898                             ) {
1899                                 c = parents[i]();
1900                                 x.push(c[0]);
1901                                 y.push(c[1]);
1902                             }
1903                         }
1904                     }
1905 
1906                     // The array D has only to be calculated when the position of one or more sample points
1907                     // changes. Otherwise D is always the same for all points on the spline.
1908                     D = Numerics.splineDef(x, y);
1909                 }
1910 
1911                 return Numerics.splineEval(t, x, y, D);
1912             },
1913             // minX()
1914             function () {
1915                 return x[0];
1916             },
1917             //maxX()
1918             function () {
1919                 return x[x.length - 1];
1920             }
1921         ];
1922     };
1923 
1924     attributes = Type.copyAttributes(attributes, board.options, 'curve');
1925     attributes.curvetype = 'functiongraph';
1926     ret = funcs();
1927     el = new JXG.Curve(board, ["x", "x", ret[0], ret[1], ret[2]], attributes);
1928     el.setParents(parents);
1929     el.elType = 'spline';
1930 
1931     return el;
1932 };
1933 
1934 /**
1935  * Register the element type spline at JSXGraph
1936  * @private
1937  */
1938 JXG.registerElement("spline", JXG.createSpline);
1939 
1940 /**
1941  * @class Cardinal spline curve through a given data set.
1942  * Create a dynamic cardinal spline interpolated curve given by sample points p_1 to p_n.
1943  * @pseudo
1944  * @name Cardinalspline
1945  * @augments JXG.Curve
1946  * @constructor
1947  * @type JXG.Curve
1948  * @param {Array} points Points array defining the cardinal spline. This can be
1949  *   <ul>
1950  *   <li> an array of JSXGraph points</li>
1951  *   <li> an array of coordinate pairs</li>
1952  *   <li> an array of functions returning coordinate pairs</li>
1953  *   <li> an array consisting of an array with x-coordinates and an array of y-coordinates</li>
1954  *   </ul>
1955  *   All individual entries of coordinates arrays may be numbers or functions returning numbers.
1956  * @param {function,Number} tau Tension parameter
1957  * @param {String} [type='uniform'] Type of the cardinal spline, may be 'uniform' (default) or 'centripetal'
1958  * @see JXG.Curve
1959  * @example
1960  * //Create a cardinal spline out of an array of JXG points with adjustable tension
1961  *
1962  * //Create array of points
1963  * var p = [];
1964  * p.push(board.create('point',[0,0]));
1965  * p.push(board.create('point',[1,4]));
1966  * p.push(board.create('point',[4,5]));
1967  * p.push(board.create('point',[2,3]));
1968  * p.push(board.create('point',[3,0]));
1969  *
1970  * // tension
1971  * var tau = board.create('slider', [[-4,-5],[2,-5],[0.001,0.5,1]], {name:'tau'});
1972  * var c = board.create('cardinalspline', [p, function(){ return tau.Value();}], {strokeWidth:3});
1973  *
1974  * </pre><div id="JXG1537cb69-4d45-43aa-8fc3-c6d4f98b4cdd" class="jxgbox" style="width: 300px; height: 300px;"></div>
1975  * <script type="text/javascript">
1976  *     (function() {
1977  *         var board = JXG.JSXGraph.initBoard('JXG1537cb69-4d45-43aa-8fc3-c6d4f98b4cdd',
1978  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
1979  *     //Create a cardinal spline out of an array of JXG points with adjustable tension
1980  *
1981  *     //Create array of points
1982  *     var p = [];
1983  *     p.push(board.create('point',[0,0]));
1984  *     p.push(board.create('point',[1,4]));
1985  *     p.push(board.create('point',[4,5]));
1986  *     p.push(board.create('point',[2,3]));
1987  *     p.push(board.create('point',[3,0]));
1988  *
1989  *     // tension
1990  *     var tau = board.create('slider', [[-4,-5],[2,-5],[0.001,0.5,1]], {name:'tau'});
1991  *     var c = board.create('cardinalspline', [p, function(){ return tau.Value();}], {strokeWidth:3});
1992  *
1993  *     })();
1994  *
1995  * </script><pre>
1996  *
1997  */
1998 JXG.createCardinalSpline = function (board, parents, attributes) {
1999     var el,
2000         getPointLike,
2001         points,
2002         tau,
2003         type,
2004         p,
2005         q,
2006         i,
2007         le,
2008         splineArr,
2009         errStr = "\nPossible parent types: [points:array, tau:number|function, type:string]";
2010 
2011     if (!Type.exists(parents[0]) || !Type.isArray(parents[0])) {
2012         throw new Error(
2013             "JSXGraph: JXG.createCardinalSpline: argument 1 'points' has to be array of points or coordinate pairs" +
2014             errStr
2015         );
2016     }
2017     if (
2018         !Type.exists(parents[1]) ||
2019         (!Type.isNumber(parents[1]) && !Type.isFunction(parents[1]))
2020     ) {
2021         throw new Error(
2022             "JSXGraph: JXG.createCardinalSpline: argument 2 'tau' has to be number between [0,1] or function'" +
2023             errStr
2024         );
2025     }
2026     if (!Type.exists(parents[2]) || !Type.isString(parents[2])) {
2027         type = 'uniform';
2028         // throw new Error(
2029         //     "JSXGraph: JXG.createCardinalSpline: argument 3 'type' has to be string 'uniform' or 'centripetal'" +
2030         //     errStr
2031         // );
2032     } else {
2033         type = parents[2];
2034     }
2035 
2036     attributes = Type.copyAttributes(attributes, board.options, 'curve');
2037     attributes = Type.copyAttributes(attributes, board.options, 'cardinalspline');
2038     attributes.curvetype = 'parameter';
2039 
2040     p = parents[0];
2041     q = [];
2042 
2043     // Given as [x[], y[]]
2044     if (
2045         !attributes.isarrayofcoordinates &&
2046         p.length === 2 &&
2047         Type.isArray(p[0]) &&
2048         Type.isArray(p[1]) &&
2049         p[0].length === p[1].length
2050     ) {
2051         for (i = 0; i < p[0].length; i++) {
2052             q[i] = [];
2053             if (Type.isFunction(p[0][i])) {
2054                 q[i].push(p[0][i]());
2055             } else {
2056                 q[i].push(p[0][i]);
2057             }
2058 
2059             if (Type.isFunction(p[1][i])) {
2060                 q[i].push(p[1][i]());
2061             } else {
2062                 q[i].push(p[1][i]);
2063             }
2064         }
2065     } else {
2066         // given as [[x0, y0], [x1, y1], point, ...]
2067         for (i = 0; i < p.length; i++) {
2068             if (Type.isString(p[i])) {
2069                 q.push(board.select(p[i]));
2070             } else if (Type.isPoint(p[i])) {
2071                 q.push(p[i]);
2072                 // given as [[x0,y0], [x1, y2], ...]
2073             } else if (Type.isArray(p[i]) && p[i].length === 2) {
2074                 q[i] = [];
2075                 if (Type.isFunction(p[i][0])) {
2076                     q[i].push(p[i][0]());
2077                 } else {
2078                     q[i].push(p[i][0]);
2079                 }
2080 
2081                 if (Type.isFunction(p[i][1])) {
2082                     q[i].push(p[i][1]());
2083                 } else {
2084                     q[i].push(p[i][1]);
2085                 }
2086             } else if (Type.isFunction(p[i]) && p[i]().length === 2) {
2087                 q.push(parents[i]());
2088             }
2089         }
2090     }
2091 
2092     if (attributes.createpoints === true) {
2093         points = Type.providePoints(board, q, attributes, "cardinalspline", ["points"]);
2094     } else {
2095         points = [];
2096 
2097         /**
2098          * @ignore
2099          */
2100         getPointLike = function (ii) {
2101             return {
2102                 X: function () {
2103                     return q[ii][0];
2104                 },
2105                 Y: function () {
2106                     return q[ii][1];
2107                 },
2108                 Dist: function (p) {
2109                     var dx = this.X() - p.X(),
2110                         dy = this.Y() - p.Y();
2111 
2112                     return Mat.hypot(dx, dy);
2113                 }
2114             };
2115         };
2116 
2117         for (i = 0; i < q.length; i++) {
2118             if (Type.isPoint(q[i])) {
2119                 points.push(q[i]);
2120             } else {
2121                 points.push(getPointLike(i));
2122             }
2123         }
2124     }
2125 
2126     tau = parents[1];
2127     // type = parents[2];
2128 
2129     splineArr = ["x"].concat(Numerics.CardinalSpline(points, tau, type));
2130 
2131     el = new JXG.Curve(board, splineArr, attributes);
2132     le = points.length;
2133     el.setParents(points);
2134     for (i = 0; i < le; i++) {
2135         p = points[i];
2136         if (Type.isPoint(p)) {
2137             if (Type.exists(p._is_new)) {
2138                 el.addChild(p);
2139                 delete p._is_new;
2140             } else {
2141                 p.addChild(el);
2142             }
2143         }
2144     }
2145     el.elType = 'cardinalspline';
2146 
2147     return el;
2148 };
2149 
2150 /**
2151  * Register the element type cardinalspline at JSXGraph
2152  * @private
2153  */
2154 JXG.registerElement("cardinalspline", JXG.createCardinalSpline);
2155 
2156 /**
2157  * @class Interpolate data points by the spline curve from Metapost (by Donald Knuth and John Hobby).
2158  * Create a dynamic metapost spline interpolated curve given by sample points p_1 to p_n.
2159  * @pseudo
2160  * @name Metapostspline
2161  * @augments JXG.Curve
2162  * @constructor
2163  * @type JXG.Curve
2164  * @param {JXG.Board} board Reference to the board the metapost spline is drawn on.
2165  * @param {Array} parents Array with two entries.
2166  * <p>
2167  *   First entry: Array of points the spline interpolates. This can be
2168  *   <ul>
2169  *   <li> an array of JSXGraph points</li>
2170  *   <li> an object of coordinate pairs</li>
2171  *   <li> an array of functions returning coordinate pairs</li>
2172  *   <li> an array consisting of an array with x-coordinates and an array of y-coordinates</li>
2173  *   </ul>
2174  *   All individual entries of coordinates arrays may be numbers or functions returning numbers.
2175  *   <p>
2176  *   Second entry: JavaScript object containing the control values like tension, direction, curl.
2177  * @param {Object} attributes Define color, width, ... of the metapost spline
2178  * @returns {JXG.Curve} Returns reference to an object of type JXG.Curve.
2179  * @see JXG.Curve
2180  * @example
2181  *     var po = [],
2182  *         attr = {
2183  *             size: 5,
2184  *             color: 'red'
2185  *         },
2186  *         controls;
2187  *
2188  *     var tension = board.create('slider', [[-3, 6], [3, 6], [0, 1, 20]], {name: 'tension'});
2189  *     var curl = board.create('slider', [[-3, 5], [3, 5], [0, 1, 30]], {name: 'curl A, D'});
2190  *     var dir = board.create('slider', [[-3, 4], [3, 4], [-180, 0, 180]], {name: 'direction B'});
2191  *
2192  *     po.push(board.create('point', [-3, -3]));
2193  *     po.push(board.create('point', [0, -3]));
2194  *     po.push(board.create('point', [4, -5]));
2195  *     po.push(board.create('point', [6, -2]));
2196  *
2197  *     var controls = {
2198  *         tension: function() {return tension.Value(); },
2199  *         direction: { 1: function() {return dir.Value(); } },
2200  *         curl: { 0: function() {return curl.Value(); },
2201  *                 3: function() {return curl.Value(); }
2202  *             },
2203  *         isClosed: false
2204  *     };
2205  *
2206  *     // Plot a metapost curve
2207  *     var cu = board.create('metapostspline', [po, controls], {strokeColor: 'blue', strokeWidth: 2});
2208  *
2209  *
2210  * </pre><div id="JXGb8c6ffed-7419-41a3-9e55-3754b2327ae9" class="jxgbox" style="width: 300px; height: 300px;"></div>
2211  * <script type="text/javascript">
2212  *     (function() {
2213  *         var board = JXG.JSXGraph.initBoard('JXGb8c6ffed-7419-41a3-9e55-3754b2327ae9',
2214  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
2215  *         var po = [],
2216  *             attr = {
2217  *                 size: 5,
2218  *                 color: 'red'
2219  *             },
2220  *             controls;
2221  *
2222  *         var tension = board.create('slider', [[-3, 6], [3, 6], [0, 1, 20]], {name: 'tension'});
2223  *         var curl = board.create('slider', [[-3, 5], [3, 5], [0, 1, 30]], {name: 'curl A, D'});
2224  *         var dir = board.create('slider', [[-3, 4], [3, 4], [-180, 0, 180]], {name: 'direction B'});
2225  *
2226  *         po.push(board.create('point', [-3, -3]));
2227  *         po.push(board.create('point', [0, -3]));
2228  *         po.push(board.create('point', [4, -5]));
2229  *         po.push(board.create('point', [6, -2]));
2230  *
2231  *         var controls = {
2232  *             tension: function() {return tension.Value(); },
2233  *             direction: { 1: function() {return dir.Value(); } },
2234  *             curl: { 0: function() {return curl.Value(); },
2235  *                     3: function() {return curl.Value(); }
2236  *                 },
2237  *             isClosed: false
2238  *         };
2239  *
2240  *         // Plot a metapost curve
2241  *         var cu = board.create('metapostspline', [po, controls], {strokeColor: 'blue', strokeWidth: 2});
2242  *
2243  *
2244  *     })();
2245  *
2246  * </script><pre>
2247  *
2248  */
2249 JXG.createMetapostSpline = function (board, parents, attributes) {
2250     var el,
2251         getPointLike,
2252         points,
2253         controls,
2254         p,
2255         q,
2256         i,
2257         le,
2258         errStr = "\nPossible parent types: [points:array, controls:object";
2259 
2260     if (!Type.exists(parents[0]) || !Type.isArray(parents[0])) {
2261         throw new Error(
2262             "JSXGraph: JXG.createMetapostSpline: argument 1 'points' has to be array of points or coordinate pairs" +
2263             errStr
2264         );
2265     }
2266     if (!Type.exists(parents[1]) || !Type.isObject(parents[1])) {
2267         throw new Error(
2268             "JSXGraph: JXG.createMetapostSpline: argument 2 'controls' has to be a JavaScript object'" +
2269             errStr
2270         );
2271     }
2272 
2273     attributes = Type.copyAttributes(attributes, board.options, 'curve');
2274     attributes = Type.copyAttributes(attributes, board.options, 'metapostspline');
2275     attributes.curvetype = 'parameter';
2276 
2277     p = parents[0];
2278     q = [];
2279 
2280     // given as [x[], y[]]
2281     if (
2282         !attributes.isarrayofcoordinates &&
2283         p.length === 2 &&
2284         Type.isArray(p[0]) &&
2285         Type.isArray(p[1]) &&
2286         p[0].length === p[1].length
2287     ) {
2288         for (i = 0; i < p[0].length; i++) {
2289             q[i] = [];
2290             if (Type.isFunction(p[0][i])) {
2291                 q[i].push(p[0][i]());
2292             } else {
2293                 q[i].push(p[0][i]);
2294             }
2295 
2296             if (Type.isFunction(p[1][i])) {
2297                 q[i].push(p[1][i]());
2298             } else {
2299                 q[i].push(p[1][i]);
2300             }
2301         }
2302     } else {
2303         // given as [[x0, y0], [x1, y1], point, ...]
2304         for (i = 0; i < p.length; i++) {
2305             if (Type.isString(p[i])) {
2306                 q.push(board.select(p[i]));
2307             } else if (Type.isPoint(p[i])) {
2308                 q.push(p[i]);
2309                 // given as [[x0,y0], [x1, y2], ...]
2310             } else if (Type.isArray(p[i]) && p[i].length === 2) {
2311                 q[i] = [];
2312                 if (Type.isFunction(p[i][0])) {
2313                     q[i].push(p[i][0]());
2314                 } else {
2315                     q[i].push(p[i][0]);
2316                 }
2317 
2318                 if (Type.isFunction(p[i][1])) {
2319                     q[i].push(p[i][1]());
2320                 } else {
2321                     q[i].push(p[i][1]);
2322                 }
2323             } else if (Type.isFunction(p[i]) && p[i]().length === 2) {
2324                 q.push(parents[i]());
2325             }
2326         }
2327     }
2328 
2329     if (attributes.createpoints === true) {
2330         points = Type.providePoints(board, q, attributes, 'metapostspline', ['points']);
2331     } else {
2332         points = [];
2333 
2334         /**
2335          * @ignore
2336          */
2337         getPointLike = function (ii) {
2338             return {
2339                 X: function () {
2340                     return q[ii][0];
2341                 },
2342                 Y: function () {
2343                     return q[ii][1];
2344                 }
2345             };
2346         };
2347 
2348         for (i = 0; i < q.length; i++) {
2349             if (Type.isPoint(q[i])) {
2350                 points.push(q[i]);
2351             } else {
2352                 points.push(getPointLike);
2353             }
2354         }
2355     }
2356 
2357     controls = parents[1];
2358 
2359     el = new JXG.Curve(board, ["t", [], [], 0, p.length - 1], attributes);
2360     /**
2361      * @class
2362      * @ignore
2363      */
2364     el.updateDataArray = function () {
2365         var res,
2366             i,
2367             len = points.length,
2368             p = [];
2369 
2370         for (i = 0; i < len; i++) {
2371             p.push([points[i].X(), points[i].Y()]);
2372         }
2373 
2374         res = Metapost.curve(p, controls);
2375         this.dataX = res[0];
2376         this.dataY = res[1];
2377     };
2378     el.bezierDegree = 3;
2379 
2380     le = points.length;
2381     el.setParents(points);
2382     for (i = 0; i < le; i++) {
2383         if (Type.isPoint(points[i])) {
2384             points[i].addChild(el);
2385         }
2386     }
2387     el.elType = 'metapostspline';
2388 
2389     return el;
2390 };
2391 
2392 JXG.registerElement("metapostspline", JXG.createMetapostSpline);
2393 
2394 /**
2395  * @class Visualize the Riemann sum which is an approximation of an integral by a finite sum.
2396  * It is realized as a special curve.
2397  * The returned element has the method Value() which returns the sum of the areas of the bars.
2398  * <p>
2399  * In case of type "simpson" and "trapezoidal", the horizontal line approximating the function value
2400  * is replaced by a parabola or a secant. IN case of "simpson",
2401  * the parabola is approximated visually by a polygonal chain of fixed step width.
2402  *
2403  * @pseudo
2404  * @name Riemannsum
2405  * @augments JXG.Curve
2406  * @constructor
2407  * @type Curve
2408  * @param {function,array_number,function_string,function_function,number_function,number} f,n,type_,a_,b_ Parent elements of Riemannsum are a
2409  *         Either a function term f(x) describing the function graph which is filled by the Riemann bars, or
2410  *         an array consisting of two functions and the area between is filled by the Riemann bars.
2411  *         <p>
2412  *         n determines the number of bars, it is either a fixed number or a function.
2413  *         <p>
2414  *         type is a string or function returning one of the values:  'left', 'right', 'middle', 'lower', 'upper', 'random', 'simpson', or 'trapezoidal'.
2415  *         Default value is 'left'. "simpson" is Simpson's 1/3 rule.
2416  *         <p>
2417  *         Further parameters are an optional number or function for the left interval border a,
2418  *         and an optional number or function for the right interval border b.
2419  *         <p>
2420  *         Default values are a=-10 and b=10.
2421  * @see JXG.Curve
2422  * @example
2423  * // Create Riemann sums for f(x) = 0.5*x*x-2*x.
2424  *   var s = board.create('slider',[[0,4],[3,4],[0,4,10]],{snapWidth:1});
2425  *   var f = function(x) { return 0.5*x*x-2*x; };
2426  *   var r = board.create('riemannsum',
2427  *               [f, function(){return s.Value();}, 'upper', -2, 5],
2428  *               {fillOpacity:0.4}
2429  *               );
2430  *   var g = board.create('functiongraph',[f, -2, 5]);
2431  *   var t = board.create('text',[-2,-2, function(){ return 'Sum=' + JXG.toFixed(r.Value(), 4); }]);
2432  * </pre><div class="jxgbox" id="JXG940f40cc-2015-420d-9191-c5d83de988cf" style="width: 300px; height: 300px;"></div>
2433  * <script type="text/javascript">
2434  * (function(){
2435  *   var board = JXG.JSXGraph.initBoard('JXG940f40cc-2015-420d-9191-c5d83de988cf', {boundingbox: [-3, 7, 5, -3], axis: true, showcopyright: false, shownavigation: false});
2436  *   var f = function(x) { return 0.5*x*x-2*x; };
2437  *   var s = board.create('slider',[[0,4],[3,4],[0,4,10]],{snapWidth:1});
2438  *   var r = board.create('riemannsum', [f, function(){return s.Value();}, 'upper', -2, 5], {fillOpacity:0.4});
2439  *   var g = board.create('functiongraph', [f, -2, 5]);
2440  *   var t = board.create('text',[-2,-2, function(){ return 'Sum=' + JXG.toFixed(r.Value(), 4); }]);
2441  * })();
2442  * </script><pre>
2443  *
2444  * @example
2445  *   // Riemann sum between two functions
2446  *   var s = board.create('slider',[[0,4],[3,4],[0,4,10]],{snapWidth:1});
2447  *   var g = function(x) { return 0.5*x*x-2*x; };
2448  *   var f = function(x) { return -x*(x-4); };
2449  *   var r = board.create('riemannsum',
2450  *               [[g,f], function(){return s.Value();}, 'lower', 0, 4],
2451  *               {fillOpacity:0.4}
2452  *               );
2453  *   var f = board.create('functiongraph',[f, -2, 5]);
2454  *   var g = board.create('functiongraph',[g, -2, 5]);
2455  *   var t = board.create('text',[-2,-2, function(){ return 'Sum=' + JXG.toFixed(r.Value(), 4); }]);
2456  * </pre><div class="jxgbox" id="JXGf9a7ba38-b50f-4a32-a873-2f3bf9caee79" style="width: 300px; height: 300px;"></div>
2457  * <script type="text/javascript">
2458  * (function(){
2459  *   var board = JXG.JSXGraph.initBoard('JXGf9a7ba38-b50f-4a32-a873-2f3bf9caee79', {boundingbox: [-3, 7, 5, -3], axis: true, showcopyright: false, shownavigation: false});
2460  *   var s = board.create('slider',[[0,4],[3,4],[0,4,10]],{snapWidth:1});
2461  *   var g = function(x) { return 0.5*x*x-2*x; };
2462  *   var f = function(x) { return -x*(x-4); };
2463  *   var r = board.create('riemannsum',
2464  *               [[g,f], function(){return s.Value();}, 'lower', 0, 4],
2465  *               {fillOpacity:0.4}
2466  *               );
2467  *   var f = board.create('functiongraph',[f, -2, 5]);
2468  *   var g = board.create('functiongraph',[g, -2, 5]);
2469  *   var t = board.create('text',[-2,-2, function(){ return 'Sum=' + JXG.toFixed(r.Value(), 4); }]);
2470  * })();
2471  * </script><pre>
2472  */
2473 JXG.createRiemannsum = function (board, parents, attributes) {
2474     var n, type, f, par, c, attr;
2475 
2476     attr = Type.copyAttributes(attributes, board.options, 'riemannsum');
2477     attr.curvetype = 'plot';
2478 
2479     f = parents[0];
2480     n = Type.createFunction(parents[1], board, "");
2481 
2482     if (!Type.exists(n)) {
2483         throw new Error(
2484             "JSXGraph: JXG.createRiemannsum: argument '2' n has to be number or function." +
2485             "\nPossible parent types: [function,n:number|function,type,start:number|function,end:number|function]"
2486         );
2487     }
2488 
2489     if (typeof parents[2] === 'string') {
2490         parents[2] = '\'' + parents[2] + '\'';
2491     }
2492 
2493     type = Type.createFunction(parents[2], board, "");
2494     if (!Type.exists(type)) {
2495         throw new Error(
2496             "JSXGraph: JXG.createRiemannsum: argument 3 'type' has to be string or function." +
2497             "\nPossible parent types: [function,n:number|function,type,start:number|function,end:number|function]"
2498         );
2499     }
2500 
2501     par = [[0], [0]].concat(parents.slice(3));
2502 
2503     c = board.create("curve", par, attr);
2504 
2505     c.sum = 0.0;
2506     /**
2507      * Returns the value of the Riemann sum, i.e. the sum of the (signed) areas of the rectangles.
2508      * @name Value
2509      * @memberOf Riemannsum.prototype
2510      * @function
2511      * @returns {Number} value of Riemann sum.
2512      */
2513     c.Value = function () {
2514         return this.sum;
2515     };
2516 
2517     /**
2518      * @class
2519      * @ignore
2520      */
2521     c.updateDataArray = function () {
2522         var u = Numerics.riemann(f, n(), type(), this.minX(), this.maxX());
2523         this.dataX = u[0];
2524         this.dataY = u[1];
2525 
2526         // Update "Riemann sum"
2527         this.sum = u[2];
2528     };
2529 
2530     c.addParentsFromJCFunctions([n, type]);
2531 
2532     return c;
2533 };
2534 
2535 JXG.registerElement("riemannsum", JXG.createRiemannsum);
2536 
2537 /**
2538  * @class A trace curve is simple locus curve showing the orbit of a point that depends on a glider point.
2539  * @pseudo
2540  * @name Tracecurve
2541  * @augments JXG.Curve
2542  * @constructor
2543  * @type Object
2544  * @descript JXG.Curve
2545  * @param {Point} Parent elements of Tracecurve are a
2546  *         glider point and a point whose locus is traced.
2547  * @param {point}
2548  * @see JXG.Curve
2549  * @example
2550  * // Create trace curve.
2551  * var c1 = board.create('circle',[[0, 0], [2, 0]]),
2552  * p1 = board.create('point',[-3, 1]),
2553  * g1 = board.create('glider',[2, 1, c1]),
2554  * s1 = board.create('segment',[g1, p1]),
2555  * p2 = board.create('midpoint',[s1]),
2556  * curve = board.create('tracecurve', [g1, p2]);
2557  *
2558  * </pre><div class="jxgbox" id="JXG5749fb7d-04fc-44d2-973e-45c1951e29ad" style="width: 300px; height: 300px;"></div>
2559  * <script type="text/javascript">
2560  *   var tc1_board = JXG.JSXGraph.initBoard('JXG5749fb7d-04fc-44d2-973e-45c1951e29ad', {boundingbox: [-4, 4, 4, -4], axis: false, showcopyright: false, shownavigation: false});
2561  *   var c1 = tc1_board.create('circle',[[0, 0], [2, 0]]),
2562  *       p1 = tc1_board.create('point',[-3, 1]),
2563  *       g1 = tc1_board.create('glider',[2, 1, c1]),
2564  *       s1 = tc1_board.create('segment',[g1, p1]),
2565  *       p2 = tc1_board.create('midpoint',[s1]),
2566  *       curve = tc1_board.create('tracecurve', [g1, p2]);
2567  * </script><pre>
2568  */
2569 JXG.createTracecurve = function (board, parents, attributes) {
2570     var c, glider, tracepoint, attr;
2571 
2572     if (parents.length !== 2) {
2573         throw new Error(
2574             "JSXGraph: Can't create trace curve with given parent'" +
2575             "\nPossible parent types: [glider, point]"
2576         );
2577     }
2578 
2579     glider = board.select(parents[0]);
2580     tracepoint = board.select(parents[1]);
2581 
2582     if (glider.type !== Const.OBJECT_TYPE_GLIDER || !Type.isPoint(tracepoint)) {
2583         throw new Error(
2584             "JSXGraph: Can't create trace curve with parent types '" +
2585             typeof parents[0] +
2586             "' and '" +
2587             typeof parents[1] +
2588             "'." +
2589             "\nPossible parent types: [glider, point]"
2590         );
2591     }
2592 
2593     attr = Type.copyAttributes(attributes, board.options, 'tracecurve');
2594     attr.curvetype = 'plot';
2595     c = board.create("curve", [[0], [0]], attr);
2596 
2597     /**
2598      * @class
2599      * @ignore
2600      */
2601     c.updateDataArray = function () {
2602         var i, step, t, el, pEl, x, y, from,
2603             savetrace,
2604             le = this.visProp.numberpoints,
2605             savePos = glider.position,
2606             slideObj = glider.slideObject,
2607             mi = slideObj.minX(),
2608             ma = slideObj.maxX();
2609 
2610         // set step width
2611         step = (ma - mi) / le;
2612         this.dataX = [];
2613         this.dataY = [];
2614 
2615         /*
2616          * For gliders on circles and lines a closed curve is computed.
2617          * For gliders on curves the curve is not closed.
2618          */
2619         if (slideObj.elementClass !== Const.OBJECT_CLASS_CURVE) {
2620             le++;
2621         }
2622 
2623         // Loop over all steps
2624         for (i = 0; i < le; i++) {
2625             t = mi + i * step;
2626             x = slideObj.X(t) / slideObj.Z(t);
2627             y = slideObj.Y(t) / slideObj.Z(t);
2628 
2629             // Position the glider
2630             glider.setPositionDirectly(Const.COORDS_BY_USER, [x, y]);
2631             from = false;
2632 
2633             // Update all elements from the glider up to the trace element
2634             for (el in this.board.objects) {
2635                 if (this.board.objects.hasOwnProperty(el)) {
2636                     pEl = this.board.objects[el];
2637 
2638                     if (pEl === glider) {
2639                         from = true;
2640                     }
2641 
2642                     if (from && pEl.needsRegularUpdate) {
2643                         // Save the trace mode of the element
2644                         savetrace = pEl.visProp.trace;
2645                         pEl.visProp.trace = false;
2646                         pEl.needsUpdate = true;
2647                         pEl.update(true);
2648 
2649                         // Restore the trace mode
2650                         pEl.visProp.trace = savetrace;
2651                         if (pEl === tracepoint) {
2652                             break;
2653                         }
2654                     }
2655                 }
2656             }
2657 
2658             // Store the position of the trace point
2659             this.dataX[i] = tracepoint.X();
2660             this.dataY[i] = tracepoint.Y();
2661         }
2662 
2663         // Restore the original position of the glider
2664         glider.position = savePos;
2665         from = false;
2666 
2667         // Update all elements from the glider to the trace point
2668         for (el in this.board.objects) {
2669             if (this.board.objects.hasOwnProperty(el)) {
2670                 pEl = this.board.objects[el];
2671                 if (pEl === glider) {
2672                     from = true;
2673                 }
2674 
2675                 if (from && pEl.needsRegularUpdate) {
2676                     savetrace = pEl.visProp.trace;
2677                     pEl.visProp.trace = false;
2678                     pEl.needsUpdate = true;
2679                     pEl.update(true);
2680                     pEl.visProp.trace = savetrace;
2681 
2682                     if (pEl === tracepoint) {
2683                         break;
2684                     }
2685                 }
2686             }
2687         }
2688     };
2689 
2690     return c;
2691 };
2692 
2693 JXG.registerElement("tracecurve", JXG.createTracecurve);
2694 
2695 /**
2696      * @class A step function is a function graph that is piecewise constant.
2697      *
2698      * In case the data points should be updated after creation time,
2699      * they can be accessed by curve.xterm and curve.yterm.
2700      * @pseudo
2701      * @name Stepfunction
2702      * @augments JXG.Curve
2703      * @constructor
2704      * @type Curve
2705      * @description JXG.Curve
2706      * @param {Array|Function} Parent1 elements of Stepfunction are two arrays containing the coordinates.
2707      * @param {Array|Function} Parent2
2708      * @see JXG.Curve
2709      * @example
2710      * // Create step function.
2711      var curve = board.create('stepfunction', [[0,1,2,3,4,5], [1,3,0,2,2,1]]);
2712 
2713      * </pre><div class="jxgbox" id="JXG32342ec9-ad17-4339-8a97-ff23dc34f51a" style="width: 300px; height: 300px;"></div>
2714      * <script type="text/javascript">
2715      *   var sf1_board = JXG.JSXGraph.initBoard('JXG32342ec9-ad17-4339-8a97-ff23dc34f51a', {boundingbox: [-1, 5, 6, -2], axis: true, showcopyright: false, shownavigation: false});
2716      *   var curve = sf1_board.create('stepfunction', [[0,1,2,3,4,5], [1,3,0,2,2,1]]);
2717      * </script><pre>
2718      */
2719 JXG.createStepfunction = function (board, parents, attributes) {
2720     var c, attr;
2721     if (parents.length !== 2) {
2722         throw new Error(
2723             "JSXGraph: Can't create step function with given parent'" +
2724             "\nPossible parent types: [array, array|function]"
2725         );
2726     }
2727 
2728     attr = Type.copyAttributes(attributes, board.options, 'stepfunction');
2729     c = board.create("curve", parents, attr);
2730     /**
2731      * @class
2732      * @ignore
2733      */
2734     c.updateDataArray = function () {
2735         var i,
2736             j = 0,
2737             len = this.xterm.length;
2738 
2739         this.dataX = [];
2740         this.dataY = [];
2741 
2742         if (len === 0) {
2743             return;
2744         }
2745 
2746         this.dataX[j] = this.xterm[0];
2747         this.dataY[j] = this.yterm[0];
2748         ++j;
2749 
2750         for (i = 1; i < len; ++i) {
2751             this.dataX[j] = this.xterm[i];
2752             this.dataY[j] = this.dataY[j - 1];
2753             ++j;
2754             this.dataX[j] = this.xterm[i];
2755             this.dataY[j] = this.yterm[i];
2756             ++j;
2757         }
2758     };
2759 
2760     return c;
2761 };
2762 
2763 JXG.registerElement("stepfunction", JXG.createStepfunction);
2764 
2765 /**
2766  * @class A curve visualizing the function graph of the (numerical) derivative of a given curve.
2767  *
2768  * @pseudo
2769  * @name Derivative
2770  * @augments JXG.Curve
2771  * @constructor
2772  * @type JXG.Curve
2773  * @param {JXG.Curve} Parent Curve for which the derivative is generated.
2774  * @see JXG.Curve
2775  * @example
2776  * var cu = board.create('cardinalspline', [[[-3,0], [-1,2], [0,1], [2,0], [3,1]], 0.5, 'centripetal'], {createPoints: false});
2777  * var d = board.create('derivative', [cu], {dash: 2});
2778  *
2779  * </pre><div id="JXGb9600738-1656-11e8-8184-901b0e1b8723" class="jxgbox" style="width: 300px; height: 300px;"></div>
2780  * <script type="text/javascript">
2781  *     (function() {
2782  *         var board = JXG.JSXGraph.initBoard('JXGb9600738-1656-11e8-8184-901b0e1b8723',
2783  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
2784  *     var cu = board.create('cardinalspline', [[[-3,0], [-1,2], [0,1], [2,0], [3,1]], 0.5, 'centripetal'], {createPoints: false});
2785  *     var d = board.create('derivative', [cu], {dash: 2});
2786  *
2787  *     })();
2788  *
2789  * </script><pre>
2790  *
2791  */
2792 JXG.createDerivative = function (board, parents, attributes) {
2793     var c, curve, dx, dy, attr;
2794 
2795     if (parents.length !== 1 && parents[0].class !== Const.OBJECT_CLASS_CURVE) {
2796         throw new Error(
2797             "JSXGraph: Can't create derivative curve with given parent'" +
2798             "\nPossible parent types: [curve]"
2799         );
2800     }
2801 
2802     attr = Type.copyAttributes(attributes, board.options, 'curve');
2803 
2804     curve = parents[0];
2805     dx = Numerics.D(curve.X);
2806     dy = Numerics.D(curve.Y);
2807 
2808     c = board.create(
2809         "curve",
2810         [
2811             function (t) {
2812                 return curve.X(t);
2813             },
2814             function (t) {
2815                 return dy(t) / dx(t);
2816             },
2817             curve.minX(),
2818             curve.maxX()
2819         ],
2820         attr
2821     );
2822 
2823     c.setParents(curve);
2824 
2825     return c;
2826 };
2827 
2828 JXG.registerElement("derivative", JXG.createDerivative);
2829 
2830 /**
2831  * @class The path forming the intersection of two closed path elements.
2832  * The elements may be of type curve, circle, polygon, inequality.
2833  * If one element is a curve, it has to be closed.
2834  * The resulting element is of type curve.
2835  * @pseudo
2836  * @name CurveIntersection
2837  * @param {JXG.Curve|JXG.Polygon|JXG.Circle} curve1 First element which is intersected
2838  * @param {JXG.Curve|JXG.Polygon|JXG.Circle} curve2 Second element which is intersected
2839  * @augments JXG.Curve
2840  * @constructor
2841  * @type JXG.Curve
2842  *
2843  * @example
2844  * var f = board.create('functiongraph', ['cos(x)']);
2845  * var ineq = board.create('inequality', [f], {inverse: true, fillOpacity: 0.1});
2846  * var circ = board.create('circle', [[0,0], 4]);
2847  * var clip = board.create('curveintersection', [ineq, circ], {fillColor: 'yellow', fillOpacity: 0.6});
2848  *
2849  * </pre><div id="JXGe2948257-8835-4276-9164-8acccb48e8d4" class="jxgbox" style="width: 300px; height: 300px;"></div>
2850  * <script type="text/javascript">
2851  *     (function() {
2852  *         var board = JXG.JSXGraph.initBoard('JXGe2948257-8835-4276-9164-8acccb48e8d4',
2853  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
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  *     })();
2860  *
2861  * </script><pre>
2862  *
2863  */
2864 JXG.createCurveIntersection = function (board, parents, attributes) {
2865     var c;
2866 
2867     if (parents.length !== 2) {
2868         throw new Error(
2869             "JSXGraph: Can't create curve intersection with given parent'" +
2870             "\nPossible parent types: [array, array|function]"
2871         );
2872     }
2873 
2874     c = board.create("curve", [[], []], attributes);
2875     /**
2876      * @class
2877      * @ignore
2878      */
2879     c.updateDataArray = function () {
2880         var a = Clip.intersection(parents[0], parents[1], this.board);
2881         this.dataX = a[0];
2882         this.dataY = a[1];
2883     };
2884     return c;
2885 };
2886 
2887 /**
2888  * @class The path forming the union of two closed path elements.
2889  * The elements may be of type curve, circle, polygon, inequality.
2890  * If one element is a curve, it has to be closed.
2891  * The resulting element is of type curve.
2892  * @pseudo
2893  * @name CurveUnion
2894  * @param {JXG.Curve|JXG.Polygon|JXG.Circle} curve1 First element defining the union
2895  * @param {JXG.Curve|JXG.Polygon|JXG.Circle} curve2 Second element defining the union
2896  * @augments JXG.Curve
2897  * @constructor
2898  * @type JXG.Curve
2899  *
2900  * @example
2901  * var f = board.create('functiongraph', ['cos(x)']);
2902  * var ineq = board.create('inequality', [f], {inverse: true, fillOpacity: 0.1});
2903  * var circ = board.create('circle', [[0,0], 4]);
2904  * var clip = board.create('curveunion', [ineq, circ], {fillColor: 'yellow', fillOpacity: 0.6});
2905  *
2906  * </pre><div id="JXGe2948257-8835-4276-9164-8acccb48e8d4" class="jxgbox" style="width: 300px; height: 300px;"></div>
2907  * <script type="text/javascript">
2908  *     (function() {
2909  *         var board = JXG.JSXGraph.initBoard('JXGe2948257-8835-4276-9164-8acccb48e8d4',
2910  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
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  *     })();
2917  *
2918  * </script><pre>
2919  *
2920  */
2921 JXG.createCurveUnion = function (board, parents, attributes) {
2922     var c;
2923 
2924     if (parents.length !== 2) {
2925         throw new Error(
2926             "JSXGraph: Can't create curve union with given parent'" +
2927             "\nPossible parent types: [array, array|function]"
2928         );
2929     }
2930 
2931     c = board.create("curve", [[], []], attributes);
2932     /**
2933      * @class
2934      * @ignore
2935      */
2936     c.updateDataArray = function () {
2937         var a = Clip.union(parents[0], parents[1], this.board);
2938         this.dataX = a[0];
2939         this.dataY = a[1];
2940     };
2941     return c;
2942 };
2943 
2944 /**
2945  * @class The path forming the difference of two closed path elements.
2946  * The elements may be of type curve, circle, polygon, inequality.
2947  * If one element is a curve, it has to be closed.
2948  * The resulting element is of type curve.
2949  * @pseudo
2950  * @name CurveDifference
2951  * @param {JXG.Curve|JXG.Polygon|JXG.Circle} curve1 First element from which the second element is "subtracted"
2952  * @param {JXG.Curve|JXG.Polygon|JXG.Circle} curve2 Second element which is subtracted from the first element
2953  * @augments JXG.Curve
2954  * @constructor
2955  * @type JXG.Curve
2956  *
2957  * @example
2958  * var f = board.create('functiongraph', ['cos(x)']);
2959  * var ineq = board.create('inequality', [f], {inverse: true, fillOpacity: 0.1});
2960  * var circ = board.create('circle', [[0,0], 4]);
2961  * var clip = board.create('curvedifference', [ineq, circ], {fillColor: 'yellow', fillOpacity: 0.6});
2962  *
2963  * </pre><div id="JXGe2948257-8835-4276-9164-8acccb48e8d4" class="jxgbox" style="width: 300px; height: 300px;"></div>
2964  * <script type="text/javascript">
2965  *     (function() {
2966  *         var board = JXG.JSXGraph.initBoard('JXGe2948257-8835-4276-9164-8acccb48e8d4',
2967  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
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  *     })();
2974  *
2975  * </script><pre>
2976  *
2977  */
2978 JXG.createCurveDifference = function (board, parents, attributes) {
2979     var c;
2980 
2981     if (parents.length !== 2) {
2982         throw new Error(
2983             "JSXGraph: Can't create curve difference with given parent'" +
2984             "\nPossible parent types: [array, array|function]"
2985         );
2986     }
2987 
2988     c = board.create("curve", [[], []], attributes);
2989     /**
2990      * @class
2991      * @ignore
2992      */
2993     c.updateDataArray = function () {
2994         var a = Clip.difference(parents[0], parents[1], this.board);
2995         this.dataX = a[0];
2996         this.dataY = a[1];
2997     };
2998     return c;
2999 };
3000 
3001 JXG.registerElement("curvedifference", JXG.createCurveDifference);
3002 JXG.registerElement("curveintersection", JXG.createCurveIntersection);
3003 JXG.registerElement("curveunion", JXG.createCurveUnion);
3004 
3005 // /**
3006 //  * @class Concat of two path elements, in general neither is a closed path. The parent elements have to be curves, too.
3007 //  * The resulting element is of type curve. The curve points are simply concatenated.
3008 //  * @pseudo
3009 //  * @name CurveConcat
3010 //  * @param {JXG.Curve} curve1 First curve element.
3011 //  * @param {JXG.Curve} curve2 Second curve element.
3012 //  * @augments JXG.Curve
3013 //  * @constructor
3014 //  * @type JXG.Curve
3015 //  */
3016 // JXG.createCurveConcat = function (board, parents, attributes) {
3017 //     var c;
3018 
3019 //     if (parents.length !== 2) {
3020 //         throw new Error(
3021 //             "JSXGraph: Can't create curve difference with given parent'" +
3022 //                 "\nPossible parent types: [array, array|function]"
3023 //         );
3024 //     }
3025 
3026 //     c = board.create("curve", [[], []], attributes);
3027 //     /**
3028 //      * @class
3029 //      * @ignore
3030 //      */
3031 //     c.updateCurve = function () {
3032 //         this.points = parents[0].points.concat(
3033 //                 [new JXG.Coords(Const.COORDS_BY_USER, [NaN, NaN], this.board)]
3034 //             ).concat(parents[1].points);
3035 //         this.numberPoints = this.points.length;
3036 //         return this;
3037 //     };
3038 
3039 //     return c;
3040 // };
3041 
3042 // JXG.registerElement("curveconcat", JXG.createCurveConcat);
3043 
3044 /**
3045  * @class Vertical or horizontal boxplot or also called box-and-whisker plot to present numerical data through their quartiles.
3046  * The direction of the boxplot is controlled by the attribute "dir". Internally, a boxplot is realized with a single JSXGraph curve.
3047  * <p>
3048  * Given a data set, the input array Q for the boxplot can be computed e.g. with the method {@link JXG.Math.Statistics.boxplot}.
3049  *
3050  * @example
3051  * 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];
3052  * var Q = JXG.Math.Statistics.boxplot(data);
3053  * var b = board.create('boxplot', [Q, 2, 4]);
3054  *
3055  * @pseudo
3056  * @name Boxplot
3057  * @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.
3058  * @param {Number|Function} axis Axis position of the boxplot
3059  * @param {Number|Function} width Width of the rectangle part of the boxplot. The width of the first and 3th quartile
3060  * is relative to this width and can be controlled by the attribute "smallWidth".
3061  * @augments JXG.Curve
3062  * @constructor
3063  * @type JXG.Curve
3064  * @see JXG.Math.Statistics#boxplot
3065  *
3066  * @example
3067  * var Q = [ -1, 2, 3, 3.5, 5 ];
3068  *
3069  * var b = board.create('boxplot', [Q, 2, 4], {strokeWidth: 3});
3070  *
3071  * </pre><div id="JXG13eb23a1-a641-41a2-be11-8e03e400a947" class="jxgbox" style="width: 300px; height: 300px;"></div>
3072  * <script type="text/javascript">
3073  *     (function() {
3074  *         var board = JXG.JSXGraph.initBoard('JXG13eb23a1-a641-41a2-be11-8e03e400a947',
3075  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
3076  *     var Q = [ -1, 2, 3, 3.5, 5 ];
3077  *     var b = board.create('boxplot', [Q, 2, 4], {strokeWidth: 3});
3078  *
3079  *     })();
3080  *
3081  * </script><pre>
3082  *
3083  * @example
3084  * // With outliers
3085  * var Q = [ -1, 2, 3, 3.5, 5, [-4, -6] ];
3086  * var b = board.create('boxplot', [Q, 3, 4], {dir: 'horizontal', width: 2, smallWidth: 0.25, color:'red'});
3087  *
3088  * </pre><div id="JXG0deb9cb2-84bc-470d-a6db-8be9a5694813" class="jxgbox" style="width: 300px; height: 300px;"></div>
3089  * <script type="text/javascript">
3090  *     (function() {
3091  *         var board = JXG.JSXGraph.initBoard('JXG0deb9cb2-84bc-470d-a6db-8be9a5694813',
3092  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
3093  *     var Q = [ -1, 2, 3, 3.5, 5, [-4, -6] ];
3094  *     var b = board.create('boxplot', [Q, 3, 4], {dir: 'horizontal', width: 2, smallWidth: 0.25, color:'red'});
3095  *
3096  *     })();
3097  *
3098  * </script><pre>
3099  *
3100  * @example
3101  * 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];
3102  * var Q = JXG.Math.Statistics.boxplot(data);
3103  * var b = board.create('boxplot', [Q, 0, 3]);
3104  *
3105  * </pre><div id="JXGef079e76-ae99-41e4-af29-1d07d83bf85a" class="jxgbox" style="width: 300px; height: 300px;"></div>
3106  * <script type="text/javascript">
3107  *     (function() {
3108  *         var board = JXG.JSXGraph.initBoard('JXGef079e76-ae99-41e4-af29-1d07d83bf85a',
3109  *             {boundingbox: [-5,90,5,30], axis: true, showcopyright: false, shownavigation: false});
3110  *     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];
3111  *     var Q = JXG.Math.Statistics.boxplot(data, [25, 50, 75]);
3112  *     var b = board.create('boxplot', [Q, 0, 3]);
3113  *
3114  *     })();
3115  *
3116  * </script><pre>
3117  *
3118  * @example
3119  * var mi = board.create('glider', [0, -1, board.defaultAxes.y]);
3120  * var ma = board.create('glider', [0, 5, board.defaultAxes.y]);
3121  * var Q = [function() { return mi.Y(); }, 2, 3, 3.5, function() { return ma.Y(); }];
3122  *
3123  * var b = board.create('boxplot', [Q, 0, 2]);
3124  *
3125  * </pre><div id="JXG3b3225da-52f0-42fe-8396-be9016bf289b" class="jxgbox" style="width: 300px; height: 300px;"></div>
3126  * <script type="text/javascript">
3127  *     (function() {
3128  *         var board = JXG.JSXGraph.initBoard('JXG3b3225da-52f0-42fe-8396-be9016bf289b',
3129  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
3130  *     var mi = board.create('glider', [0, -1, board.defaultAxes.y]);
3131  *     var ma = board.create('glider', [0, 5, board.defaultAxes.y]);
3132  *     var Q = [function() { return mi.Y(); }, 2, 3, 3.5, function() { return ma.Y(); }];
3133  *
3134  *     var b = board.create('boxplot', [Q, 0, 2]);
3135  *
3136  *     })();
3137  *
3138  * </script><pre>
3139  *
3140  */
3141 JXG.createBoxPlot = function (board, parents, attributes) {
3142     var box, i, len,
3143         attr = Type.copyAttributes(attributes, board.options, 'boxplot');
3144 
3145     if (parents.length !== 3) {
3146         throw new Error(
3147             "JSXGraph: Can't create boxplot with given parent'" +
3148             "\nPossible parent types: [array, number|function, number|function] containing quantiles, axis, width"
3149         );
3150     }
3151     if (parents[0].length < 5) {
3152         throw new Error(
3153             "JSXGraph: Can't create boxplot with given parent[0]'" +
3154             "\nparent[0] has to contain at least 5 quantiles."
3155         );
3156     }
3157     box = board.create("curve", [[], []], attr);
3158 
3159     len = parents[0].length; // Quantiles
3160     box.Q = [];
3161     for (i = 0; i < len; i++) {
3162         box.Q[i] = Type.createFunction(parents[0][i], board);
3163     }
3164     box.x = Type.createFunction(parents[1], board);
3165     box.w = Type.createFunction(parents[2], board);
3166 
3167     /**
3168      * @class
3169      * @ignore
3170      */
3171     box.updateDataArray = function () {
3172         var v1, v2, l1, l2, r1, r2, w2, dir, x,
3173             i, le, q5, y, sx, sy, sx2, sy2, t, f;
3174 
3175         w2 = this.evalVisProp('smallwidth');
3176         dir = this.evalVisProp('dir');
3177         x = this.x();
3178         l1 = x - this.w() * 0.5;
3179         l2 = x - this.w() * 0.5 * w2;
3180         r1 = x + this.w() * 0.5;
3181         r2 = x + this.w() * 0.5 * w2;
3182         v1 = [x, l2, r2, x, x, l1, l1, r1, r1, x, NaN, l1, r1, NaN, x, x, l2, r2, x];
3183         v2 = [
3184             this.Q[0](),
3185             this.Q[0](),
3186             this.Q[0](),
3187             this.Q[0](),
3188             this.Q[1](),
3189             this.Q[1](),
3190             this.Q[3](),
3191             this.Q[3](),
3192             this.Q[1](),
3193             this.Q[1](),
3194             NaN,
3195             this.Q[2](),
3196             this.Q[2](),
3197             NaN,
3198             this.Q[3](),
3199             this.Q[4](),
3200             this.Q[4](),
3201             this.Q[4](),
3202             this.Q[4]()
3203         ];
3204 
3205         // Outliers
3206         if (this.Q.length > 5 && Type.isArray(this.Q[5]())) {
3207             v1.push(NaN);
3208             v2.push(NaN);
3209 
3210             f = this.evalVisProp('outlier.face');
3211 
3212             if (dir === 'vertical') {
3213                 sx = this.evalVisProp('outlier.size') / this.board.unitX;
3214                 sy = this.evalVisProp('outlier.size') / this.board.unitY;
3215             } else {
3216                 sy = this.evalVisProp('outlier.size') / this.board.unitX;
3217                 sx = this.evalVisProp('outlier.size') / this.board.unitY;
3218             }
3219             sx2 = sx * Math.sqrt(2);
3220             sy2 = sy * Math.sqrt(2);
3221 
3222             q5 = this.Q[5]();
3223             le = q5.length;
3224             for (i = 0; i < le; i++) {
3225                 y = q5[i];
3226                 switch (f) {
3227                     case 'x':
3228                     case 'cross':
3229                         v1.push(x - sx, x + sx, NaN, x - sx, x + sx, NaN);
3230                         v2.push(y + sy, y - sy, NaN, y - sy, y + sy, NaN);
3231                         break;
3232                     case '[]':
3233                     case 'square':
3234                         v1.push(x - sx, x + sx, x + sx, x - sx, x - sx, NaN);
3235                         v2.push(y + sy, y + sy, y - sy, y - sy, y + sy, NaN);
3236                         break;
3237                     case '<>':
3238                     case 'diamond':
3239                         v1.push(x, x + sx, x, x - sx, x, NaN);
3240                         v2.push(y + sy, y, y - sy, y, y + sy, NaN);
3241                         break;
3242                     case '<<>>':
3243                     case 'diamond2':
3244                         v1.push(x, x + sx2, x, x - sx2, x, NaN);
3245                         v2.push(y + sy2, y, y - sy2, y, y + sy2, NaN);
3246                         break;
3247                     case '+':
3248                     case 'plus':
3249                         v1.push(x - sx, x + sx, NaN, x, x, NaN);
3250                         v2.push(y, y, NaN, y - sy, y + sy, NaN);
3251                         break;
3252                     case '-':
3253                     case 'minus':
3254                         v1.push(x - sx, x + sx, NaN);
3255                         v2.push(y, y, NaN);
3256                         break;
3257                     case '|':
3258                     case 'divide':
3259                         v1.push(x, x, NaN);
3260                         v2.push(y - sy, y + sy, NaN);
3261                         break;
3262                     default:
3263                     case 'o':
3264                     case 'circle':
3265                         for (t = 0; t <= 2 * Math.PI; t += (2 * Math.PI) / 17) {
3266                             v1.push(x - sx * Math.sin(t));
3267                             v2.push(y - sy * Math.cos(t));
3268                         }
3269                         v1.push(NaN);
3270                         v2.push(NaN);
3271                 }
3272             }
3273         }
3274 
3275         if (dir === 'vertical') {
3276             this.dataX = v1;
3277             this.dataY = v2;
3278         } else {
3279             this.dataX = v2;
3280             this.dataY = v1;
3281         }
3282     };
3283 
3284     box.addParentsFromJCFunctions([box.Q, box.x, box.w]);
3285 
3286     return box;
3287 };
3288 
3289 JXG.registerElement("boxplot", JXG.createBoxPlot);
3290 
3291 /**
3292  * @class An implicit curve is a plane curve defined by an implicit equation
3293  * relating two coordinate variables, commonly <i>x</i> and <i>y</i>.
3294  * For example, the unit circle is defined by the implicit equation
3295  * x<sup>2</sup> + y<sup>2</sup> = 1.
3296  * In general, every implicit curve is defined by an equation of the form
3297  * <i>f(x, y) = 0</i>
3298  * for some function <i>f</i> of two variables. (<a href="https://en.wikipedia.org/wiki/Implicit_curve">Wikipedia</a>)
3299  * <p>
3300  * The partial derivatives for <i>f</i> are optional. If not given, numerical
3301  * derivatives are used instead. This is good enough for most practical use cases.
3302  * But if supplied, both partial derivatives must be supplied.
3303  * <p>
3304  * The most effective attributes to tinker with if the implicit curve algorithm fails are
3305  * {@link ImplicitCurve#resolution_outer},
3306  * {@link ImplicitCurve#resolution_inner},
3307  * {@link ImplicitCurve#alpha_0},
3308  * {@link ImplicitCurve#h_initial},
3309  * {@link ImplicitCurve#h_max}, and
3310  * {@link ImplicitCurve#qdt_box}.
3311  *
3312  * @pseudo
3313  * @name ImplicitCurve
3314  * @param {Function|String} f Function of two variables for the left side of the equation <i>f(x,y)=0</i>.
3315  * If f is supplied as string, it has to use the variables 'x' and 'y'.
3316  * @param {Function|String} [dfx=null] Optional partial derivative in respect to the first variable
3317  * If dfx is supplied as string, it has to use the variables 'x' and 'y'.
3318  * @param {Function|String} [dfy=null] Optional partial derivative in respect to the second variable
3319  * If dfy is supplied as string, it has to use the variables 'x' and 'y'.
3320  * @param {Array|Function} [rangex=boundingbox] Optional array of length 2
3321  * of the form [x_min, x_max] setting the domain of the x coordinate of the implicit curve.
3322  * If not supplied, the board's boundingbox (+ the attribute 'margin') is taken.
3323  * @param {Array|Function} [rangey=boundingbox] Optional array of length 2
3324  * of the form [y_min, y_max] setting the domain of the y coordinate of the implicit curve.
3325  * If not supplied, the board's boundingbox (+ the attribute 'margin') is taken.
3326  * @augments JXG.Curve
3327  * @constructor
3328  * @type JXG.Curve
3329  *
3330  * @example
3331  *   var f, c;
3332  *   f = (x, y) => 1 / 16 * x ** 2 + y ** 2 - 1;
3333  *   c = board.create('implicitcurve', [f], {
3334  *       strokeWidth: 3,
3335  *       strokeColor: JXG.palette.red,
3336  *       strokeOpacity: 0.8
3337  *   });
3338  *
3339  * </pre><div id="JXGa6e86701-1a82-48d0-b007-3a3d32075076" class="jxgbox" style="width: 300px; height: 300px;"></div>
3340  * <script type="text/javascript">
3341  *     (function() {
3342  *         var board = JXG.JSXGraph.initBoard('JXGa6e86701-1a82-48d0-b007-3a3d32075076',
3343  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
3344  *             var f, c;
3345  *             f = (x, y) => 1 / 16 * x ** 2 + y ** 2 - 1;
3346  *             c = board.create('implicitcurve', [f], {
3347  *                 strokeWidth: 3,
3348  *                 strokeColor: JXG.palette.red,
3349  *                 strokeOpacity: 0.8
3350  *             });
3351  *
3352  *     })();
3353  *
3354  * </script><pre>
3355  *
3356  * @example
3357  *  var a, c, f;
3358  *  a = board.create('slider', [[-3, 6], [3, 6], [-3, 1, 3]], {
3359  *      name: 'a', stepWidth: 0.1
3360  *  });
3361  *  f = (x, y) => x ** 2 - 2 * x * y - 2 * x + (a.Value() + 1) * y ** 2 + (4 * a.Value() + 2) * y + 4 * a.Value() - 3;
3362  *  c = board.create('implicitcurve', [f], {
3363  *      strokeWidth: 3,
3364  *      strokeColor: JXG.palette.red,
3365  *      strokeOpacity: 0.8,
3366  *      resolution_outer: 20,
3367  *      resolution_inner: 20
3368  *  });
3369  *
3370  * </pre><div id="JXG0b133a54-9509-4a65-9722-9c5145e23b40" class="jxgbox" style="width: 300px; height: 300px;"></div>
3371  * <script type="text/javascript">
3372  *     (function() {
3373  *         var board = JXG.JSXGraph.initBoard('JXG0b133a54-9509-4a65-9722-9c5145e23b40',
3374  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
3375  *             var a, c, f;
3376  *             a = board.create('slider', [[-3, 6], [3, 6], [-3, 1, 3]], {
3377  *                 name: 'a', stepWidth: 0.1
3378  *             });
3379  *             f = (x, y) => x ** 2 - 2 * x * y - 2 * x + (a.Value() + 1) * y ** 2 + (4 * a.Value() + 2) * y + 4 * a.Value() - 3;
3380  *             c = board.create('implicitcurve', [f], {
3381  *                 strokeWidth: 3,
3382  *                 strokeColor: JXG.palette.red,
3383  *                 strokeOpacity: 0.8,
3384  *                 resolution_outer: 20,
3385  *                 resolution_inner: 20
3386  *             });
3387  *
3388  *     })();
3389  *
3390  * </script><pre>
3391  *
3392  * @example
3393  *  var c = board.create('implicitcurve', ['abs(x * y) - 3'], {
3394  *      strokeWidth: 3,
3395  *      strokeColor: JXG.palette.red,
3396  *      strokeOpacity: 0.8
3397  *  });
3398  *
3399  * </pre><div id="JXG02802981-0abb-446b-86ea-ee588f02ed1a" class="jxgbox" style="width: 300px; height: 300px;"></div>
3400  * <script type="text/javascript">
3401  *     (function() {
3402  *         var board = JXG.JSXGraph.initBoard('JXG02802981-0abb-446b-86ea-ee588f02ed1a',
3403  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
3404  *             var c = board.create('implicitcurve', ['abs(x * y) - 3'], {
3405  *                 strokeWidth: 3,
3406  *                 strokeColor: JXG.palette.red,
3407  *                 strokeOpacity: 0.8
3408  *             });
3409  *
3410  *     })();
3411  *
3412  * </script><pre>
3413  *
3414  * @example
3415  * var niveauline = [];
3416  * niveauline = [0.5, 1, 1.5, 2];
3417  * for (let i = 0; i < niveauline.length; i++) {
3418  *     board.create("implicitcurve", [
3419  *         (x, y) => x ** .5 * y ** .5 - niveauline[i],
3420            [0.25, 3], [0.5, 4] // Domain
3421  *     ], {
3422  *         strokeWidth: 2,
3423  *         strokeColor: JXG.palette.red,
3424  *         strokeOpacity: (1 + i) / niveauline.length,
3425  *         needsRegularUpdate: false
3426  *     });
3427  * }
3428  *
3429  * </pre><div id="JXGccee9aab-6dd9-4a79-827d-3164f70cc6a1" class="jxgbox" style="width: 300px; height: 300px;"></div>
3430  * <script type="text/javascript">
3431  *     (function() {
3432  *         var board = JXG.JSXGraph.initBoard('JXGccee9aab-6dd9-4a79-827d-3164f70cc6a1',
3433  *             {boundingbox: [-1, 5, 5,-1], axis: true, showcopyright: false, shownavigation: false});
3434  *         var niveauline = [];
3435  *         niveauline = [0.5, 1, 1.5, 2];
3436  *         for (let i = 0; i < niveauline.length; i++) {
3437  *             board.create("implicitcurve", [
3438  *                 (x, y) => x ** .5 * y ** .5 - niveauline[i],
3439  *                 [0.25, 3], [0.5, 4]
3440  *             ], {
3441  *                 strokeWidth: 2,
3442  *                 strokeColor: JXG.palette.red,
3443  *                 strokeOpacity: (1 + i) / niveauline.length,
3444  *                 needsRegularUpdate: false
3445  *             });
3446  *         }
3447  *
3448  *     })();
3449  *
3450  * </script><pre>
3451  *
3452  */
3453 JXG.createImplicitCurve = function (board, parents, attributes) {
3454     var c, attr;
3455 
3456     if ([1, 3, 5].indexOf(parents.length) < 0) {
3457         throw new Error(
3458             "JSXGraph: Can't create curve implicitCurve with given parent'" +
3459             "\nPossible parent types: [f], [f, rangex, rangey], [f, dfx, dfy] or [f, dfx, dfy, rangex, rangey]" +
3460             "\nwith functions f, dfx, dfy and arrays of length 2 rangex, rangey."
3461         );
3462     }
3463 
3464     // if (parents.length === 3) {
3465     //     if (!Type.isArray(parents[1]) && !Type.isArray(parents[2])) {
3466     //         throw new Error(
3467     //             "JSXGraph: Can't create curve implicitCurve with given parent'" +
3468     //             "\nPossible parent types: [f], [f, rangex, rangey], [f, dfx, dfy] or [f, dfx, dfy, rangex, rangey]" +
3469     //             "\nwith functions f, dfx, dfy and arrays of length 2 rangex, rangey."
3470     //         );
3471     //     }
3472     // }
3473     // if (parents.length === 5) {
3474     //     if (!Type.isArray(parents[3]) && !Type.isArray(parents[4])) {
3475     //         throw new Error(
3476     //             "JSXGraph: Can't create curve implicitCurve with given parent'" +
3477     //             "\nPossible parent types: [f], [f, rangex, rangey], [f, dfx, dfy] or [f, dfx, dfy, rangex, rangey]" +
3478     //             "\nwith functions f, dfx, dfy and arrays of length 2 rangex, rangey."
3479     //         );
3480     //     }
3481     // }
3482 
3483     attr = Type.copyAttributes(attributes, board.options, 'implicitcurve');
3484     c = board.create("curve", [[], []], attr);
3485 
3486     /**
3487      * Function of two variables for the left side of the equation <i>f(x,y)=0</i>.
3488      *
3489      * @name f
3490      * @memberOf ImplicitCurve.prototype
3491      * @function
3492      * @returns {Number}
3493      */
3494     c.f = Type.createFunction(parents[0], board, 'x, y');
3495 
3496     /**
3497      * Partial derivative in the first variable of
3498      * the left side of the equation <i>f(x,y)=0</i>.
3499      * If null, then numerical derivative is used.
3500      *
3501      * @name dfx
3502      * @memberOf ImplicitCurve.prototype
3503      * @function
3504      * @returns {Number}
3505      */
3506     if (parents.length === 5 || Type.isString(parents[1]) || Type.isFunction(parents[1])) {
3507         c.dfx = Type.createFunction(parents[1], board, 'x, y');
3508     } else {
3509         c.dfx = null;
3510     }
3511 
3512     /**
3513      * Partial derivative in the second variable of
3514      * the left side of the equation <i>f(x,y)=0</i>.
3515      * If null, then numerical derivative is used.
3516      *
3517      * @name dfy
3518      * @memberOf ImplicitCurve.prototype
3519      * @function
3520      * @returns {Number}
3521      */
3522     if (parents.length === 5 || Type.isString(parents[2]) || Type.isFunction(parents[2])) {
3523         c.dfy = Type.createFunction(parents[2], board, 'x, y');
3524     } else {
3525         c.dfy = null;
3526     }
3527 
3528     /**
3529      * Defines a domain for searching f(x,y)=0. Default is null, meaning
3530      * the bounding box of the board is used.
3531      * Using domain, visProp.margin is ignored.
3532      * @name domain
3533      * @memberOf ImplicitCurve.prototype
3534      * @param {Array} of length 4 defining the domain used to compute the implict curve.
3535      * Syntax: [x_min, y_max, x_max, y_min]
3536      */
3537     // c.domain = board.getBoundingBox();
3538     c.domain = null;
3539     if (parents.length === 5) {
3540         c.domain = [parents[3], parents[4]];
3541         //     [Math.min(parents[3][0], parents[3][1]), Math.max(parents[3][0], parents[3][1])],
3542         //     [Math.min(parents[4][0], parents[4][1]), Math.max(parents[4][0], parents[4][1])]
3543         // ];
3544     } else if (parents.length === 3) {
3545         c.domain = [parents[1], parents[2]];
3546         //     [Math.min(parents[1][0], parents[1][1]), Math.max(parents[1][0], parents[1][1])],
3547         //     [Math.min(parents[2][0], parents[2][1]), Math.max(parents[2][0], parents[2][1])]
3548         // ];
3549     }
3550 
3551     /**
3552      * @class
3553      * @ignore
3554      */
3555     c.updateDataArray = function () {
3556         var bbox, rx, ry,
3557             ip, cfg,
3558             ret = [],
3559             mgn;
3560 
3561         if (this.domain === null) {
3562             mgn = this.evalVisProp('margin');
3563             bbox = this.board.getBoundingBox();
3564             bbox[0] -= mgn;
3565             bbox[1] += mgn;
3566             bbox[2] += mgn;
3567             bbox[3] -= mgn;
3568         } else {
3569             rx = Type.evaluate(this.domain[0]);
3570             ry = Type.evaluate(this.domain[1]);
3571             bbox = [
3572                 Math.min(rx[0], rx[1]),
3573                 Math.max(ry[0], ry[1]),
3574                 Math.max(rx[0], rx[1]),
3575                 Math.min(ry[0], ry[1])
3576                 // rx[0], ry[1], rx[1], ry[0]
3577             ];
3578         }
3579 
3580         cfg = {
3581             resolution_out: Math.max(0.01, this.evalVisProp('resolution_outer')),
3582             resolution_in: Math.max(0.01, this.evalVisProp('resolution_inner')),
3583             max_steps: this.evalVisProp('max_steps'),
3584             alpha_0: this.evalVisProp('alpha_0'),
3585             tol_u0: this.evalVisProp('tol_u0'),
3586             tol_newton: this.evalVisProp('tol_newton'),
3587             tol_cusp: this.evalVisProp('tol_cusp'),
3588             tol_progress: this.evalVisProp('tol_progress'),
3589             qdt_box: this.evalVisProp('qdt_box'),
3590             kappa_0: this.evalVisProp('kappa_0'),
3591             delta_0: this.evalVisProp('delta_0'),
3592             h_initial: this.evalVisProp('h_initial'),
3593             h_critical: this.evalVisProp('h_critical'),
3594             h_max: this.evalVisProp('h_max'),
3595             loop_dist: this.evalVisProp('loop_dist'),
3596             loop_dir: this.evalVisProp('loop_dir'),
3597             loop_detection: this.evalVisProp('loop_detection'),
3598             unitX: this.board.unitX,
3599             unitY: this.board.unitY
3600         };
3601         this.dataX = [];
3602         this.dataY = [];
3603 
3604         // console.time("implicit plot");
3605         ip = new ImplicitPlot(bbox, cfg, this.f, this.dfx, this.dfy);
3606         this.qdt = ip.qdt;
3607 
3608         ret = ip.plot();
3609         // console.timeEnd("implicit plot");
3610 
3611         this.dataX = ret[0];
3612         this.dataY = ret[1];
3613     };
3614 
3615     c.elType = 'implicitcurve';
3616 
3617     return c;
3618 };
3619 
3620 JXG.registerElement("implicitcurve", JXG.createImplicitCurve);
3621 
3622 /**
3623  * @class Sketch a curve by dragging the pointer device on the board.
3624  * If enabled:true, it is always done even if the curve is invisible.
3625  * A JSXGraph borad contains a length two array board.sketches
3626  * with two sketchcurves.
3627  *
3628  * @pseudo
3629  * @name SketchCurve
3630  * @augments JXG.Curve
3631  * @constructor
3632  * @type JXG.Curve
3633  * @see JXG.Board#sketches
3634  * @see JXG.Board#sketch
3635  * @private
3636  */
3637 JXG.createSketchCurve = function (board, parents, attributes) {
3638     var c, attr;
3639 
3640     attr = Type.copyAttributes(attributes, board.options, 'sketchcurve');
3641     c = board.create("curve", [[], []], attr);
3642 
3643     c.elType = 'sketchcurve';
3644 
3645     return c;
3646 };
3647 
3648 JXG.registerElement("sketchcurve", JXG.createSketchCurve);
3649 
3650 export default JXG.Curve;
3651 
3652 // export default {
3653 //     Curve: JXG.Curve,
3654 //     createCardinalSpline: JXG.createCardinalSpline,
3655 //     createCurve: JXG.createCurve,
3656 //     createCurveDifference: JXG.createCurveDifference,
3657 //     createCurveIntersection: JXG.createCurveIntersection,
3658 //     createCurveUnion: JXG.createCurveUnion,
3659 //     createDerivative: JXG.createDerivative,
3660 //     createFunctiongraph: JXG.createFunctiongraph,
3661 //     createMetapostSpline: JXG.createMetapostSpline,
3662 //     createPlot: JXG.createFunctiongraph,
3663 //     createSpline: JXG.createSpline,
3664 //     createRiemannsum: JXG.createRiemannsum,
3665 //     createStepfunction: JXG.createStepfunction,
3666 //     createTracecurve: JXG.createTracecurve
3667 // };
3668 
3669 // const Curve = JXG.Curve;
3670 // export { Curve as default, Curve};
3671