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