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