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