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