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