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