1 /*
  2     Copyright 2008-2013
  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/statistics', 'math/numerics',
 57     'math/geometry', 'parser/geonext', 'utils/type', 'base/transformation'
 58 ], function (JXG, Const, Coords, GeometryElement, Mat, Statistics, Numerics, Geometry, GeonextParser, Type, Transform) {
 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 = this.visProp.numberpointshigh;
 83 
 84         this.bezierDegree = 1;
 85 
 86         this.dataX = null;
 87         this.dataY = null;
 88 
 89         if (Type.exists(parents[0])) {
 90             this.varname = parents[0];
 91         } else {
 92             this.varname = 'x';
 93         }
 94 
 95         // function graphs: "x"
 96         this.xterm = parents[1];
 97         // function graphs: e.g. "x^2"
 98         this.yterm = parents[2];
 99 
100         // Converts GEONExT syntax into JavaScript syntax
101         this.generateTerm(this.varname, this.xterm, this.yterm, parents[3], parents[4]);
102         // First evaluation of the curve
103         this.updateCurve();
104 
105         this.id = this.board.setId(this, 'G');
106         this.board.renderer.drawCurve(this);
107 
108         this.board.finalizeAdding(this);
109 
110         this.createGradient();
111         this.elType = 'curve';
112         this.createLabel();
113 
114         if (typeof this.xterm === 'string') {
115             this.notifyParents(this.xterm);
116         }
117         if (typeof this.yterm === 'string') {
118             this.notifyParents(this.yterm);
119         }
120 
121         this.methodMap = Type.deepCopy(this.methodMap, {
122             generateTerm: 'generateTerm',
123             setTerm: 'generateTerm'
124         });
125     };
126 
127     JXG.Curve.prototype = new GeometryElement();
128 
129 
130     JXG.extend(JXG.Curve.prototype, /** @lends JXG.Curve.prototype */ {
131 
132         /**
133          * Gives the default value of the left bound for the curve.
134          * May be overwritten in {@link JXG.Curve#generateTerm}.
135          * @returns {Number} Left bound for the curve.
136          */
137         minX: function () {
138             var leftCoords;
139 
140             if (this.visProp.curvetype === 'polar') {
141                 return 0;
142             }
143 
144             leftCoords = new Coords(Const.COORDS_BY_SCREEN, [0, 0], this.board, false);
145             return leftCoords.usrCoords[1];
146         },
147 
148         /**
149          * Gives the default value of the right bound for the curve.
150          * May be overwritten in {@link JXG.Curve#generateTerm}.
151          * @returns {Number} Right bound for the curve.
152          */
153         maxX: function () {
154             var rightCoords;
155 
156             if (this.visProp.curvetype === 'polar') {
157                 return 2 * Math.PI;
158             }
159             rightCoords = new Coords(Const.COORDS_BY_SCREEN, [this.board.canvasWidth, 0], this.board, false);
160 
161             return rightCoords.usrCoords[1];
162         },
163 
164         /**
165          * Treat the curve as curve with homogeneous coordinates.
166          * @param {Number} t A number between 0.0 and 1.0.
167          * @return {Number} Always 1.0
168          */
169         Z: function (t) {
170             return 1;
171         },
172 
173         /**
174          * Checks whether (x,y) is near the curve.
175          * @param {Number} x Coordinate in x direction, screen coordinates.
176          * @param {Number} y Coordinate in y direction, screen coordinates.
177          * @param {Number} start Optional start index for search on data plots.
178          * @return {Boolean} True if (x,y) is near the curve, False otherwise.
179          */
180         hasPoint: function (x, y, start) {
181             var t, checkPoint, len, invMat, c,
182                 i, tX, tY, res,
183                 steps = this.visProp.numberpointslow,
184                 d = (this.maxX() - this.minX()) / steps,
185                 prec = this.board.options.precision.hasPoint / this.board.unitX,
186                 dist = Infinity,
187                 suspendUpdate = true;
188 
189             checkPoint = new Coords(Const.COORDS_BY_SCREEN, [x, y], this.board, false);
190             x = checkPoint.usrCoords[1];
191             y = checkPoint.usrCoords[2];
192 
193             if (this.transformations.length > 0) {
194                 /**
195                  * Transform the mouse/touch coordinates
196                  * back to the original position of the curve.
197                  */
198                 this.updateTransformMatrix();
199                 invMat = Mat.inverse(this.transformMat);
200                 c = Mat.matVecMult(invMat, [1, x, y]);
201                 x = c[1];
202                 y = c[2];
203             }
204 
205             if (this.visProp.curvetype === 'parameter' ||
206                     this.visProp.curvetype === 'polar' ||
207                     this.visProp.curvetype === 'functiongraph') {
208 
209                 prec = prec * prec;
210 
211                 // Brute force search for a point on the curve close to the mouse pointer
212                 for (i = 0, t = this.minX(); i < steps; i++) {
213                     tX = this.X(t, suspendUpdate);
214                     tY = this.Y(t, suspendUpdate);
215 
216                     dist = (x - tX) * (x - tX) + (y - tY) * (y - tY);
217 
218                     if (dist < prec) {
219                         return true;
220                     }
221 
222                     t += d;
223                 }
224             } else if (this.visProp.curvetype === 'plot') {
225                 if (!Type.exists(start) || start < 0) {
226                     start = 0;
227                 }
228 
229                 len = this.numberPoints;
230                 for (i = start; i < len - 1; i++) {
231 
232                     if (this.bezierDegree === 3) {
233                         res = Geometry.projectCoordsToBeziersegment([1, x, y], this, i);
234                         //i += 2;
235                     } else {
236                         res = Geometry.projectCoordsToSegment(
237                             [1, x, y],
238                             [1, this.X(i), this.Y(i)],
239                             [1, this.X(i + 1), this.Y(i + 1)]
240                         );
241                     }
242 
243                     if (res[1] >= 0 && res[1] <= 1 &&
244                             Geometry.distance([1, x, y], res[0], 3) <= prec) {
245                         return true;
246                     }
247                 }
248                 return false;
249             }
250             return (dist < prec);
251         },
252 
253         /**
254          * Allocate points in the Coords array this.points
255          */
256         allocatePoints: function () {
257             var i, len;
258 
259             len = this.numberPoints;
260 
261             if (this.points.length < this.numberPoints) {
262                 for (i = this.points.length; i < len; i++) {
263                     this.points[i] = new Coords(Const.COORDS_BY_USER, [0, 0], this.board, false);
264                 }
265             }
266         },
267 
268         /**
269          * Computes for equidistant points on the x-axis the values of the function
270          * @returns {JXG.Curve} Reference to the curve object.
271          * @see JXG.Curve#updateCurve
272          */
273         update: function () {
274             if (this.needsUpdate) {
275                 if (this.visProp.trace) {
276                     this.cloneToBackground(true);
277                 }
278                 this.updateCurve();
279             }
280 
281             return this;
282         },
283 
284         /**
285          * Updates the visual contents of the curve.
286          * @returns {JXG.Curve} Reference to the curve object.
287          */
288         updateRenderer: function () {
289             var wasReal;
290 
291             if (this.needsUpdate && this.visProp.visible) {
292                 wasReal = this.isReal;
293 
294                 this.checkReal();
295 
296                 if (this.isReal || wasReal) {
297                     this.board.renderer.updateCurve(this);
298                 }
299 
300                 if (this.isReal) {
301                     if (wasReal !== this.isReal) {
302                         this.board.renderer.show(this);
303                         if (this.hasLabel && this.label.visProp.visible) {
304                             this.board.renderer.show(this.label.content);
305                         }
306                     }
307                 } else {
308                     if (wasReal !== this.isReal) {
309                         this.board.renderer.hide(this);
310                         if (this.hasLabel && this.label.visProp.visible) {
311                             this.board.renderer.hide(this.label);
312                         }
313                     }
314                 }
315 
316                 // Update the label if visible.
317                 if (this.hasLabel && Type.exists(this.label.visProp) && this.label.visProp.visible) {
318                     this.label.update();
319                     this.board.renderer.updateText(this.label);
320                 }
321             }
322             return this;
323         },
324 
325         /**
326          * For dynamic dataplots updateCurve can be used to compute new entries
327          * for the arrays {@link JXG.Curve#dataX} and {@link JXG.Curve#dataY}. It
328          * is used in {@link JXG.Curve#updateCurve}. Default is an empty method, can
329          * be overwritten by the user.
330          */
331         updateDataArray: function () {
332             // this used to return this, but we shouldn't rely on the user to implement it.
333         },
334 
335         /**
336          * Computes for equidistant points on the x-axis the values
337          * of the function.
338          * If the mousemove event triggers this update, we use only few
339          * points. Otherwise, e.g. on mouseup, many points are used.
340          * @see JXG.Curve#update
341          * @returns {JXG.Curve} Reference to the curve object.
342          */
343         updateCurve: function () {
344             var len, mi, ma, x, y, i,
345                 suspendUpdate = false;
346 
347             this.updateTransformMatrix();
348             this.updateDataArray();
349             mi = this.minX();
350             ma = this.maxX();
351 
352             // Discrete data points
353             // x-coordinates are in an array
354             if (Type.exists(this.dataX)) {
355                 this.numberPoints = this.dataX.length;
356                 len = this.numberPoints;
357 
358                 // It is possible, that the array length has increased.
359                 this.allocatePoints();
360 
361                 for (i = 0; i < len; i++) {
362                     x = i;
363 
364                     // y-coordinates are in an array
365                     if (Type.exists(this.dataY)) {
366                         y = i;
367                         // The last parameter prevents rounding in usr2screen().
368                         this.points[i].setCoordinates(Const.COORDS_BY_USER, [this.dataX[i], this.dataY[i]], false);
369                     } else {
370                         // discrete x data, continuous y data
371                         y = this.X(x);
372                         // The last parameter prevents rounding in usr2screen().
373                         this.points[i].setCoordinates(Const.COORDS_BY_USER, [this.dataX[i], this.Y(y, suspendUpdate)], false);
374                     }
375 
376                     this.updateTransform(this.points[i]);
377                     suspendUpdate = true;
378                 }
379             // continuous x data
380             } else {
381                 if (this.visProp.doadvancedplot) {
382                     this.updateParametricCurve(mi, ma, len);
383                 } else {
384                     if (this.board.updateQuality === this.board.BOARD_QUALITY_HIGH) {
385                         this.numberPoints = this.visProp.numberpointshigh;
386                     } else {
387                         this.numberPoints = this.visProp.numberpointslow;
388                     }
389 
390                     // It is possible, that the array length has increased.
391                     this.allocatePoints();
392                     this.updateParametricCurveNaive(mi, ma, this.numberPoints);
393                 }
394                 len = this.numberPoints;
395 
396                 for (i = 0; i < len; i++) {
397                     this.updateTransform(this.points[i]);
398                 }
399             }
400 
401             return this;
402         },
403 
404         updateTransformMatrix: function () {
405             var t, c, i,
406                 len = this.transformations.length;
407 
408             this.transformMat = [[1, 0, 0], [0, 1, 0], [0, 0, 1]];
409 
410             for (i = 0; i < len; i++) {
411                 t = this.transformations[i];
412                 t.update();
413                 this.transformMat = Mat.matMatMult(t.matrix, this.transformMat);
414             }
415 
416             return this;
417         },
418 
419         /**
420          * Check if at least one point on the curve is finite and real.
421          **/
422         checkReal: function () {
423             var b = false, i, p,
424                 len = this.numberPoints;
425 
426             for (i = 0; i < len; i++) {
427                 p = this.points[i].usrCoords;
428                 if (!isNaN(p[1]) && !isNaN(p[2]) && Math.abs(p[0]) > Mat.eps) {
429                     b = true;
430                     break;
431                 }
432             }
433             this.isReal = b;
434         },
435 
436         /**
437          * Updates the data points of a parametric curve. This version is used if {@link JXG.Curve#doadvancedplot} is <tt>false</tt>.
438          * @param {Number} mi Left bound of curve
439          * @param {Number} ma Right bound of curve
440          * @param {Number} len Number of data points
441          * @returns {JXG.Curve} Reference to the curve object.
442          */
443         updateParametricCurveNaive: function (mi, ma, len) {
444             var i, t,
445                 suspendUpdate = false,
446                 stepSize = (ma - mi) / len;
447 
448             for (i = 0; i < len; i++) {
449                 t = mi + i * stepSize;
450                 // The last parameter prevents rounding in usr2screen().
451                 this.points[i].setCoordinates(Const.COORDS_BY_USER, [this.X(t, suspendUpdate), this.Y(t, suspendUpdate)], false);
452                 suspendUpdate = true;
453             }
454             return this;
455         },
456 
457         /**
458          * Updates the data points of a parametric curve. This version is used if {@link JXG.Curve#doadvancedplot} is <tt>true</tt>.
459          * @param {Number} mi Left bound of curve
460          * @param {Number} ma Right bound of curve
461          * @returns {JXG.Curve} Reference to the curve object.
462          */
463         updateParametricCurve: function (mi, ma) {
464             var i, t, t0, d,
465                 x, y, x0, y0, top, depth,
466                 MAX_DEPTH, MAX_XDIST, MAX_YDIST,
467                 suspendUpdate = false,
468                 po = new Coords(Const.COORDS_BY_USER, [0, 0], this.board, false),
469                 dyadicStack = [],
470                 depthStack = [],
471                 pointStack = [],
472                 divisors = [],
473                 distOK = false,
474                 j = 0,
475                 distFromLine = function (p1, p2, p0) {
476                     var lbda, d,
477                         x0 = p0[1] - p1[1],
478                         y0 = p0[2] - p1[2],
479                         x1 = p2[0] - p1[1],
480                         y1 = p2[1] - p1[2],
481                         den = x1 * x1 + y1 * y1;
482 
483                     if (den >= Mat.eps) {
484                         lbda = (x0 * x1 + y0 * y1) / den;
485                         if (lbda > 0) {
486                             if (lbda <= 1) {
487                                 x0 -= lbda * x1;
488                                 y0 -= lbda * y1;
489                             // lbda = 1.0;
490                             } else {
491                                 x0 -= x1;
492                                 y0 -= y1;
493                             }
494                         }
495                     }
496                     d = x0 * x0 + y0 * y0;
497                     return Math.sqrt(d);
498                 };
499 
500             if (this.board.updateQuality === this.board.BOARD_QUALITY_LOW) {
501                 MAX_DEPTH = 15;
502                 MAX_XDIST = 10;
503                 MAX_YDIST = 10;
504             } else {
505                 MAX_DEPTH = 21;
506                 MAX_XDIST = 0.7;
507                 MAX_YDIST = 0.7;
508             }
509 
510             divisors[0] = ma - mi;
511             for (i = 1; i < MAX_DEPTH; i++) {
512                 divisors[i] = divisors[i - 1] * 0.5;
513             }
514 
515             i = 1;
516             dyadicStack[0] = 1;
517             depthStack[0] = 0;
518 
519             t = mi;
520             po.setCoordinates(Const.COORDS_BY_USER, [this.X(t, suspendUpdate), this.Y(t, suspendUpdate)], false);
521 
522             // Now, there was a first call to the functions defining the curve.
523             // Defining elements like sliders have been evaluated.
524             // Therefore, we can set suspendUpdate to false, so that these defining elements
525             // need not be evaluated anymore for the rest of the plotting.
526             suspendUpdate = true;
527             x0 = po.scrCoords[1];
528             y0 = po.scrCoords[2];
529             t0 = t;
530 
531             t = ma;
532             po.setCoordinates(Const.COORDS_BY_USER, [this.X(t, suspendUpdate), this.Y(t, suspendUpdate)], false);
533             x = po.scrCoords[1];
534             y = po.scrCoords[2];
535 
536             pointStack[0] = [x, y];
537 
538             top = 1;
539             depth = 0;
540 
541             this.points = [];
542             this.points[j++] = new Coords(Const.COORDS_BY_SCREEN, [x0, y0], this.board, false);
543 
544             do {
545                 distOK = this.isDistOK(x - x0, y - y0, MAX_XDIST, MAX_YDIST) || this.isSegmentOutside(x0, y0, x, y);
546                 while (depth < MAX_DEPTH && (!distOK || depth < 6) && (depth <= 7 || this.isSegmentDefined(x0, y0, x, y))) {
547                     // We jump out of the loop if
548                     // * depth>=MAX_DEPTH or
549                     // * (depth>=6 and distOK) or
550                     // * (depth>7 and segment is not defined)
551 
552                     dyadicStack[top] = i;
553                     depthStack[top] = depth;
554                     pointStack[top] = [x, y];
555                     top += 1;
556 
557                     i = 2 * i - 1;
558                     // Here, depth is increased and may reach MAX_DEPTH
559                     depth++;
560                     // In that case, t is undefined and we will see a jump in the curve.
561                     t = mi + i * divisors[depth];
562 
563                     po.setCoordinates(Const.COORDS_BY_USER, [this.X(t, suspendUpdate), this.Y(t, suspendUpdate)], false, true);
564                     x = po.scrCoords[1];
565                     y = po.scrCoords[2];
566                     distOK = this.isDistOK(x - x0, y - y0, MAX_XDIST, MAX_YDIST) || this.isSegmentOutside(x0, y0, x, y);
567                 }
568 
569                 if (j > 1) {
570                     d = distFromLine(this.points[j - 2].scrCoords, [x, y], this.points[j - 1].scrCoords);
571                     if (d < 0.015) {
572                         j -= 1;
573                     }
574                 }
575 
576                 this.points[j] = new Coords(Const.COORDS_BY_SCREEN, [x, y], this.board, false);
577                 j += 1;
578 
579                 x0 = x;
580                 y0 = y;
581                 t0 = t;
582 
583                 top -= 1;
584                 x = pointStack[top][0];
585                 y = pointStack[top][1];
586                 depth = depthStack[top] + 1;
587                 i = dyadicStack[top] * 2;
588 
589             } while (top > 0 && j < 500000);
590 
591             this.numberPoints = this.points.length;
592 
593             return this;
594         },
595 
596         /**
597          * Crude and cheap test if the segment defined by the two points <tt>(x0, y0)</tt> and <tt>(x1, y1)</tt> is
598          * outside the viewport of the board. All parameters have to be given in screen coordinates.
599          * @param {Number} x0
600          * @param {Number} y0
601          * @param {Number} x1
602          * @param {Number} y1
603          * @returns {Boolean} <tt>true</tt> if the given segment is outside the visible area.
604          */
605         isSegmentOutside: function (x0, y0, x1, y1) {
606             return (y0 < 0 && y1 < 0) || (y0 > this.board.canvasHeight && y1 > this.board.canvasHeight) ||
607                 (x0 < 0 && x1 < 0) || (x0 > this.board.canvasWidth && x1 > this.board.canvasWidth);
608         },
609 
610         /**
611          * Compares the absolute value of <tt>dx</tt> with <tt>MAXX</tt> and the absolute value of <tt>dy</tt>
612          * with <tt>MAXY</tt>.
613          * @param {Number} dx
614          * @param {Number} dy
615          * @param {Number} MAXX
616          * @param {Number} MAXY
617          * @returns {Boolean} <tt>true</tt>, if <tt>|dx| < MAXX</tt> and <tt>|dy| < MAXY</tt>.
618          */
619         isDistOK: function (dx, dy, MAXX, MAXY) {
620             return (Math.abs(dx) < MAXX && Math.abs(dy) < MAXY) && !isNaN(dx + dy);
621         },
622 
623         isSegmentDefined: function (x0, y0, x1, y1) {
624             return !(isNaN(x0 + y0) && isNaN(x1 + y1));
625         },
626 
627         /**
628          * Applies the transformations of the curve to the given point <tt>p</tt>.
629          * Before using it, {@link JXG.Curve#updateTransformMatrix} has to be called.
630          * @param {JXG.Point} p
631          * @returns {JXG.Point} The given point.
632          */
633         updateTransform: function (p) {
634             var c,
635                 len = this.transformations.length;
636 
637             if (len > 0) {
638                 c = Mat.matVecMult(this.transformMat, p.usrCoords);
639                 p.setPosition(Const.COORDS_BY_USER, [c[1], c[2]]);
640             }
641 
642             return p;
643         },
644 
645         /**
646          * Add transformations to this curve.
647          * @param {JXG.Transformation|Array} transform Either one {@link JXG.Transformation} or an array of {@link JXG.Transformation}s.
648          * @returns {JXG.Curve} Reference to the curve object.
649          */
650         addTransform: function (transform) {
651             var i,
652                 list = Type.isArray(transform) ? transform : [transform],
653                 len = list.length;
654 
655             for (i = 0; i < len; i++) {
656                 this.transformations.push(list[i]);
657             }
658 
659             return this;
660         },
661 
662         /**
663          * Translates the object by <tt>(x, y)</tt>.
664          * @param {Number} method The type of coordinates used here. Possible values are {@link JXG.COORDS_BY_USER} and {@link JXG.COORDS_BY_SCREEN}.
665          * @param {Array} coords array of translation vector.
666          * @returns {JXG.Curve} Reference to the curve object.
667          */
668         setPosition: function (method, coords) {
669             var t, obj, i,
670                 len = 0;
671 
672             if (Type.exists(this.parents)) {
673                 len = this.parents.length;
674             }
675 
676             for (i = 0; i < len; i++) {
677                 obj = this.board.select(this.parents[i]);
678 
679                 if (!obj.draggable()) {
680                     return this;
681                 }
682             }
683 
684             // We distinguish two cases:
685             // 1) curves which depend on free elements, i.e. arcs and sectors
686             // 2) other curves
687             //
688             // In the first case we simply transform the parents elements
689             // In the second case we add a transform to the curve.
690             //
691             coords = new Coords(method, coords, this.board, false);
692             t = this.board.create('transform', coords.usrCoords.slice(1), {type: 'translate'});
693 
694             if (len > 0) {
695                 for (i = 0; i < len; i++) {
696                     obj = this.board.select(this.parents[i]);
697                     t.applyOnce(obj);
698                 }
699             } else {
700                 if (this.transformations.length > 0 &&
701                         this.transformations[this.transformations.length - 1].isNumericMatrix) {
702                     this.transformations[this.transformations.length - 1].melt(t);
703                 } else {
704                     this.addTransform(t);
705                 }
706             }
707             return this;
708         },
709 
710         /**
711          * Moves the cuvre by the difference of two coordinates.
712          * @param {Number} method The type of coordinates used here. Possible values are {@link JXG.COORDS_BY_USER} and {@link JXG.COORDS_BY_SCREEN}.
713          * @param {Array} coords coordinates in screen/user units
714          * @param {Array} oldcoords previous coordinates in screen/user units
715          * @returns {JXG.Curve} this element
716          */
717         setPositionDirectly: function (method, coords, oldcoords) {
718             var c = new Coords(method, coords, this.board, false),
719                 oldc = new Coords(method, oldcoords, this.board, false),
720                 dc = Statistics.subtract(c.usrCoords, oldc.usrCoords);
721 
722             this.setPosition(Const.COORDS_BY_USER, dc);
723 
724             return this;
725         },
726 
727         /**
728          * Generate the method curve.X() in case curve.dataX is an array
729          * and generate the method curve.Y() in case curve.dataY is an array.
730          * @private
731          * @param {String} which Either 'X' or 'Y'
732          * @returns {function}
733          **/
734         interpolationFunctionFromArray: function (which) {
735             var data = 'data' + which;
736 
737             return function (t, suspendedUpdate) {
738                 var i, j, f1, f2, z, t0, t1,
739                     arr = this[data],
740                     len = arr.length,
741                     f = [];
742 
743                 if (isNaN(t)) {
744                     return NaN;
745                 }
746 
747                 if (t < 0) {
748                     if (Type.isFunction(arr[0])) {
749                         return arr[0]();
750                     }
751 
752                     return arr[0];
753                 }
754 
755                 if (this.bezierDegree === 3) {
756                     len /= 3;
757                     if (t >= len) {
758                         if (Type.isFunction(arr[arr.length - 1])) {
759                             return arr[arr.length - 1]();
760                         }
761 
762                         return arr[arr.length - 1];
763                     }
764 
765                     i = Math.floor(t) * 3;
766                     t0 = t % 1;
767                     t1 = 1 - t0;
768 
769                     for (j = 0; j < 4; j++) {
770                         if (Type.isFunction(arr[i + j])) {
771                             f[j] = arr[i + j]();
772                         } else {
773                             f[j] = arr[i + j];
774                         }
775                     }
776 
777                     return t1 * t1 * (t1 * f[0] + 3 * t0 * f[1]) + (3 * t1 * f[2] + t0 * f[3]) * t0 * t0;
778                 }
779 
780                 if (t > len - 2) {
781                     i = len - 2;
782                 } else {
783                     i = parseInt(Math.floor(t), 10);
784                 }
785 
786                 if (i === t) {
787                     if (Type.isFunction(arr[i])) {
788                         return arr[i]();
789                     }
790                     return arr[i];
791                 }
792 
793                 for (j = 0; j < 2; j++) {
794                     if (Type.isFunction(arr[i + j])) {
795                         f[j] = arr[i + j]();
796                     } else {
797                         f[j] = arr[i + j];
798                     }
799                 }
800                 return f[0] + (f[1] - f[0]) * (t - i);
801             };
802         },
803         /**
804          * Converts the GEONExT syntax of the defining function term into JavaScript.
805          * New methods X() and Y() for the Curve object are generated, further
806          * new methods for minX() and maxX().
807          * @see JXG.GeonextParser.geonext2JS.
808          */
809         generateTerm: function (varname, xterm, yterm, mi, ma) {
810             var fx, fy;
811 
812             // Generate the methods X() and Y()
813             if (Type.isArray(xterm)) {
814                 // Discrete data
815                 this.dataX = xterm;
816 
817                 this.numberPoints = this.dataX.length;
818                 this.X = this.interpolationFunctionFromArray('X');
819                 this.visProp.curvetype = 'plot';
820                 this.isDraggable = true;
821             } else {
822                 // Continuous data
823                 this.X = Type.createFunction(xterm, this.board, varname);
824                 if (Type.isString(xterm)) {
825                     this.visProp.curvetype = 'functiongraph';
826                 } else if (Type.isFunction(xterm) || Type.isNumber(xterm)) {
827                     this.visProp.curvetype = 'parameter';
828                 }
829 
830                 this.isDraggable = true;
831             }
832 
833             if (Type.isArray(yterm)) {
834                 this.dataY = yterm;
835                 this.Y = this.interpolationFunctionFromArray('Y');
836             } else {
837                 this.Y = Type.createFunction(yterm, this.board, varname);
838             }
839 
840             /**
841              * Polar form
842              * Input data is function xterm() and offset coordinates yterm
843              */
844             if (Type.isFunction(xterm) && Type.isArray(yterm)) {
845                 // Xoffset, Yoffset
846                 fx = Type.createFunction(yterm[0], this.board, '');
847                 fy = Type.createFunction(yterm[1], this.board, '');
848 
849                 this.X = function (phi) {
850                     return xterm(phi) * Math.cos(phi) + fx();
851                 };
852 
853                 this.Y = function (phi) {
854                     return xterm(phi) * Math.sin(phi) + fy();
855                 };
856 
857                 this.visProp.curvetype = 'polar';
858             }
859 
860             // Set the bounds lower bound
861             if (Type.exists(mi)) {
862                 this.minX = Type.createFunction(mi, this.board, '');
863             }
864             if (Type.exists(ma)) {
865                 this.maxX = Type.createFunction(ma, this.board, '');
866             }
867         },
868 
869         /**
870          * Finds dependencies in a given term and notifies the parents by adding the
871          * dependent object to the found objects child elements.
872          * @param {String} contentStr String containing dependencies for the given object.
873          */
874         notifyParents: function (contentStr) {
875             GeonextParser.findDependencies(this, contentStr, this.board);
876         },
877 
878         // documented in geometry element
879         getLabelAnchor: function () {
880             var c, x, y,
881                 ax = 0.05 * this.board.canvasWidth,
882                 ay = 0.05 * this.board.canvasHeight,
883                 bx = 0.95 * this.board.canvasWidth,
884                 by = 0.95 * this.board.canvasHeight;
885 
886             switch (this.visProp.label.position) {
887             case 'ulft':
888                 x = ax;
889                 y = ay;
890                 break;
891             case 'llft':
892                 x = ax;
893                 y = by;
894                 break;
895             case 'rt':
896                 x = bx;
897                 y = 0.5 * by;
898                 break;
899             case 'lrt':
900                 x = bx;
901                 y = by;
902                 break;
903             case 'urt':
904                 x = bx;
905                 y = ay;
906                 break;
907             case 'top':
908                 x = 0.5 * bx;
909                 y = ay;
910                 break;
911             case 'bot':
912                 x = 0.5 * bx;
913                 y = by;
914                 break;
915             default:
916                 // includes case 'lft'
917                 x = ax;
918                 y = 0.5 * by;
919             }
920 
921             c = new Coords(Const.COORDS_BY_SCREEN, [x, y], this.board, false);
922             return Geometry.projectCoordsToCurve(c.usrCoords[1], c.usrCoords[2], 0, this, this.board)[0];
923         },
924 
925         // documented in geometry element
926         cloneToBackground: function () {
927             var er,
928                 copy = {
929                     id: this.id + 'T' + this.numTraces,
930                     elementClass: Const.OBJECT_CLASS_CURVE,
931 
932                     points: this.points.slice(0),
933                     bezierDegree: this.bezierDegree,
934                     numberPoints: this.numberPoints,
935                     board: this.board,
936                     visProp: Type.deepCopy(this.visProp, this.visProp.traceattributes, true)
937                 };
938 
939             copy.visProp.layer = this.board.options.layer.trace;
940             copy.visProp.curvetype = this.visProp.curvetype;
941             this.numTraces++;
942 
943             Type.clearVisPropOld(copy);
944 
945             er = this.board.renderer.enhancedRendering;
946             this.board.renderer.enhancedRendering = true;
947             this.board.renderer.drawCurve(copy);
948             this.board.renderer.enhancedRendering = er;
949             this.traces[copy.id] = copy.rendNode;
950 
951             return this;
952         },
953 
954         // already documented in GeometryElement
955         bounds: function () {
956             var minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity,
957                 l = this.points.length, i;
958 
959             for (i = 0; i < l; i++) {
960                 if (minX > this.points[i].usrCoords[1]) {
961                     minX = this.points[i].usrCoords[1];
962                 }
963 
964                 if (maxX < this.points[i].usrCoords[1]) {
965                     maxX = this.points[i].usrCoords[1];
966                 }
967 
968                 if (minY > this.points[i].usrCoords[2]) {
969                     minY = this.points[i].usrCoords[2];
970                 }
971 
972                 if (maxY < this.points[i].usrCoords[2]) {
973                     maxY = this.points[i].usrCoords[2];
974                 }
975             }
976 
977             return [minX, maxY, maxX, minY];
978         }
979     });
980 
981 
982     /**
983      * @class This element is used to provide a constructor for curve, which is just a wrapper for element {@link Curve}.
984      * 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].
985      * <p>
986      * The following types of curves can be plotted:
987      * <ul>
988      *  <li> parametric curves: t mapsto (x(t),y(t)), where x() and y() are univariate functions.
989      *  <li> polar curves: curves commonly written with polar equations like spirals and cardioids.
990      *  <li> data plots: plot linbe segments through a given list of coordinates.
991      * </ul>
992      * @pseudo
993      * @description
994      * @name Curve
995      * @augments JXG.Curve
996      * @constructor
997      * @type JXG.Curve
998      *
999      * @param {function,number_function,number_function,number_function,number} x,y,a_,b_ Parent elements for Parametric Curves.
1000      *                     <p>
1001      *                     x describes the x-coordinate of the curve. It may be a function term in one variable, e.g. x(t).
1002      *                     In case of x being of type number, x(t) is set to  a constant function.
1003      *                     this function at the values of the array.
1004      *                     </p>
1005      *                     <p>
1006      *                     y describes the y-coordinate of the curve. In case of a number, y(t) is set to the constant function
1007      *                     returning this number.
1008      *                     </p>
1009      *                     <p>
1010      *                     Further parameters are an optional number or function for the left interval border a,
1011      *                     and an optional number or function for the right interval border b.
1012      *                     </p>
1013      *                     <p>
1014      *                     Default values are a=-10 and b=10.
1015      *                     </p>
1016      * @param {array_array,function,number} x,y Parent elements for Data Plots.
1017      *                     <p>
1018      *                     x and y are arrays contining the x and y coordinates of the data points which are connected by
1019      *                     line segments. The individual entries of x and y may also be functions.
1020      *                     In case of x being an array the curve type is data plot, regardless of the second parameter and
1021      *                     if additionally the second parameter y is a function term the data plot evaluates.
1022      *                     </p>
1023      * @param {function_array,function,number_function,number_function,number} r,offset_,a_,b_ Parent elements for Polar Curves.
1024      *                     <p>
1025      *                     The first parameter is a function term r(phi) describing the polar curve.
1026      *                     </p>
1027      *                     <p>
1028      *                     The second parameter is the offset of the curve. It has to be
1029      *                     an array containing numbers or functions describing the offset. Default value is the origin [0,0].
1030      *                     </p>
1031      *                     <p>
1032      *                     Further parameters are an optional number or function for the left interval border a,
1033      *                     and an optional number or function for the right interval border b.
1034      *                     </p>
1035      *                     <p>
1036      *                     Default values are a=-10 and b=10.
1037      *                     </p>
1038      * @see JXG.Curve
1039      * @example
1040      * // Parametric curve
1041      * // Create a curve of the form (t-sin(t), 1-cos(t), i.e.
1042      * // the cycloid curve.
1043      *   var graph = board.create('curve',
1044      *                        [function(t){ return t-Math.sin(t);},
1045      *                         function(t){ return 1-Math.cos(t);},
1046      *                         0, 2*Math.PI]
1047      *                     );
1048      * </pre><div id="af9f818b-f3b6-4c4d-8c4c-e4a4078b726d" style="width: 300px; height: 300px;"></div>
1049      * <script type="text/javascript">
1050      *   var c1_board = JXG.JSXGraph.initBoard('af9f818b-f3b6-4c4d-8c4c-e4a4078b726d', {boundingbox: [-1, 5, 7, -1], axis: true, showcopyright: false, shownavigation: false});
1051      *   var graph1 = c1_board.create('curve', [function(t){ return t-Math.sin(t);},function(t){ return 1-Math.cos(t);},0, 2*Math.PI]);
1052      * </script><pre>
1053      * @example
1054      * // Data plots
1055      * // Connect a set of points given by coordinates with dashed line segments.
1056      * // The x- and y-coordinates of the points are given in two separate
1057      * // arrays.
1058      *   var x = [0,1,2,3,4,5,6,7,8,9];
1059      *   var y = [9.2,1.3,7.2,-1.2,4.0,5.3,0.2,6.5,1.1,0.0];
1060      *   var graph = board.create('curve', [x,y], {dash:2});
1061      * </pre><div id="7dcbb00e-b6ff-481d-b4a8-887f5d8c6a83" style="width: 300px; height: 300px;"></div>
1062      * <script type="text/javascript">
1063      *   var c3_board = JXG.JSXGraph.initBoard('7dcbb00e-b6ff-481d-b4a8-887f5d8c6a83', {boundingbox: [-1,10,10,-1], axis: true, showcopyright: false, shownavigation: false});
1064      *   var x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
1065      *   var y = [9.2, 1.3, 7.2, -1.2, 4.0, 5.3, 0.2, 6.5, 1.1, 0.0];
1066      *   var graph3 = c3_board.create('curve', [x,y], {dash:2});
1067      * </script><pre>
1068      * @example
1069      * // Polar plot
1070      * // Create a curve with the equation r(phi)= a*(1+phi), i.e.
1071      * // a cardioid.
1072      *   var a = board.create('slider',[[0,2],[2,2],[0,1,2]]);
1073      *   var graph = board.create('curve',
1074      *                        [function(phi){ return a.Value()*(1-Math.cos(phi));},
1075      *                         [1,0],
1076      *                         0, 2*Math.PI]
1077      *                     );
1078      * </pre><div id="d0bc7a2a-8124-45ca-a6e7-142321a8f8c2" style="width: 300px; height: 300px;"></div>
1079      * <script type="text/javascript">
1080      *   var c2_board = JXG.JSXGraph.initBoard('d0bc7a2a-8124-45ca-a6e7-142321a8f8c2', {boundingbox: [-3,3,3,-3], axis: true, showcopyright: false, shownavigation: false});
1081      *   var a = c2_board.create('slider',[[0,2],[2,2],[0,1,2]]);
1082      *   var graph2 = c2_board.create('curve', [function(phi){ return a.Value()*(1-Math.cos(phi));}, [1,0], 0, 2*Math.PI]);
1083      * </script><pre>
1084      */
1085     JXG.createCurve = function (board, parents, attributes) {
1086         var attr = Type.copyAttributes(attributes, board.options, 'curve');
1087         return new JXG.Curve(board, ['x'].concat(parents), attr);
1088     };
1089 
1090     JXG.registerElement('curve', JXG.createCurve);
1091 
1092     /**
1093      * @class This element is used to provide a constructor for functiongraph, which is just a wrapper for element {@link Curve} with {@link JXG.Curve#X()}
1094      * set to x. The graph is drawn for x in the interval [a,b].
1095      * @pseudo
1096      * @description
1097      * @name Functiongraph
1098      * @augments JXG.Curve
1099      * @constructor
1100      * @type JXG.Curve
1101      * @param {function_number,function_number,function} f,a_,b_ Parent elements are a function term f(x) describing the function graph.
1102      *         <p>
1103      *         Further, an optional number or function for the left interval border a,
1104      *         and an optional number or function for the right interval border b.
1105      *         <p>
1106      *         Default values are a=-10 and b=10.
1107      * @see JXG.Curve
1108      * @example
1109      * // Create a function graph for f(x) = 0.5*x*x-2*x
1110      *   var graph = board.create('functiongraph',
1111      *                        [function(x){ return 0.5*x*x-2*x;}, -2, 4]
1112      *                     );
1113      * </pre><div id="efd432b5-23a3-4846-ac5b-b471e668b437" style="width: 300px; height: 300px;"></div>
1114      * <script type="text/javascript">
1115      *   var alex1_board = JXG.JSXGraph.initBoard('efd432b5-23a3-4846-ac5b-b471e668b437', {boundingbox: [-3, 7, 5, -3], axis: true, showcopyright: false, shownavigation: false});
1116      *   var graph = alex1_board.create('functiongraph', [function(x){ return 0.5*x*x-2*x;}, -2, 4]);
1117      * </script><pre>
1118      * @example
1119      * // Create a function graph for f(x) = 0.5*x*x-2*x with variable interval
1120      *   var s = board.create('slider',[[0,4],[3,4],[-2,4,5]]);
1121      *   var graph = board.create('functiongraph',
1122      *                        [function(x){ return 0.5*x*x-2*x;},
1123      *                         -2,
1124      *                         function(){return s.Value();}]
1125      *                     );
1126      * </pre><div id="4a203a84-bde5-4371-ad56-44619690bb50" style="width: 300px; height: 300px;"></div>
1127      * <script type="text/javascript">
1128      *   var alex2_board = JXG.JSXGraph.initBoard('4a203a84-bde5-4371-ad56-44619690bb50', {boundingbox: [-3, 7, 5, -3], axis: true, showcopyright: false, shownavigation: false});
1129      *   var s = alex2_board.create('slider',[[0,4],[3,4],[-2,4,5]]);
1130      *   var graph = alex2_board.create('functiongraph', [function(x){ return 0.5*x*x-2*x;}, -2, function(){return s.Value();}]);
1131      * </script><pre>
1132      */
1133     JXG.createFunctiongraph = function (board, parents, attributes) {
1134         var attr,
1135             par = ['x', 'x'].concat(parents);
1136 
1137         attr = Type.copyAttributes(attributes, board.options, 'curve');
1138         attr.curvetype = 'functiongraph';
1139         return new JXG.Curve(board, par, attr);
1140     };
1141 
1142     JXG.registerElement('functiongraph', JXG.createFunctiongraph);
1143     JXG.registerElement('plot', JXG.createFunctiongraph);
1144 
1145 
1146     /**
1147      * TODO
1148      * Create a dynamic spline interpolated curve given by sample points p_1 to p_n.
1149      * @param {JXG.Board} board Reference to the board the spline is drawn on.
1150      * @param {Array} parents Array of points the spline interpolates
1151      * @param {Object} attributes Define color, width, ... of the spline
1152      * @returns {JXG.Curve} Returns reference to an object of type JXG.Curve.
1153      */
1154     JXG.createSpline = function (board, parents, attributes) {
1155         var f;
1156 
1157         f = function () {
1158             var D, x = [], y = [];
1159 
1160             return function (t, suspended) {
1161                 var i, j;
1162 
1163                 if (!suspended) {
1164                     x = [];
1165                     y = [];
1166 
1167                     // given as [x[], y[]]
1168                     if (parents.length === 2 && Type.isArray(parents[0]) && Type.isArray(parents[1]) && parents[0].length === parents[1].length) {
1169                         for (i = 0; i < parents[0].length; i++) {
1170                             if (typeof parents[0][i] === 'function') {
1171                                 x.push(parents[0][i]());
1172                             } else {
1173                                 x.push(parents[0][i]);
1174                             }
1175 
1176                             if (typeof parents[1][i] === 'function') {
1177                                 y.push(parents[1][i]());
1178                             } else {
1179                                 y.push(parents[1][i]);
1180                             }
1181                         }
1182                     } else {
1183                         for (i = 0; i < parents.length; i++) {
1184                             if (Type.isPoint(parents[i])) {
1185                                 x.push(parents[i].X());
1186                                 y.push(parents[i].Y());
1187                             // given as [[x1,y1], [x2, y2], ...]
1188                             } else if (Type.isArray(parents[i]) && parents[i].length === 2) {
1189                                 for (i = 0; i < parents.length; i++) {
1190                                     if (typeof parents[i][0] === 'function') {
1191                                         x.push(parents[i][0]());
1192                                     } else {
1193                                         x.push(parents[i][0]);
1194                                     }
1195 
1196                                     if (typeof parents[i][1] === 'function') {
1197                                         y.push(parents[i][1]());
1198                                     } else {
1199                                         y.push(parents[i][1]);
1200                                     }
1201                                 }
1202                             }
1203                         }
1204                     }
1205 
1206                     // The array D has only to be calculated when the position of one or more sample point
1207                     // changes. otherwise D is always the same for all points on the spline.
1208                     D = Numerics.splineDef(x, y);
1209                 }
1210                 return Numerics.splineEval(t, x, y, D);
1211             };
1212         };
1213         return board.create('curve', ["x", f()], attributes);
1214     };
1215 
1216     /**
1217      * Register the element type spline at JSXGraph
1218      * @private
1219      */
1220     JXG.registerElement('spline', JXG.createSpline);
1221 
1222     /**
1223      * @class This element is used to provide a constructor for Riemann sums, which is realized as a special curve.
1224      * The returned element has the method Value() which returns the sum of the areas of the rectangles.
1225      * @pseudo
1226      * @description
1227      * @name Riemannsum
1228      * @augments JXG.Curve
1229      * @constructor
1230      * @type JXG.Curve
1231      * @param {function_number,function_string,function_function,number_function,number} f,n,type_,a_,b_ Parent elements of Riemannsum are a
1232      *         function term f(x) describing the function graph which is filled by the Riemann rectangles.
1233      *         <p>
1234      *         n determines the number of rectangles, it is either a fixed number or a function.
1235      *         <p>
1236      *         type is a string or function returning one of the values:  'left', 'right', 'middle', 'lower', 'upper', 'random', 'simpson', or 'trapezodial'.
1237      *         Default value is 'left'.
1238      *         <p>
1239      *         Further parameters are an optional number or function for the left interval border a,
1240      *         and an optional number or function for the right interval border b.
1241      *         <p>
1242      *         Default values are a=-10 and b=10.
1243      * @see JXG.Curve
1244      * @example
1245      * // Create Riemann sums for f(x) = 0.5*x*x-2*x.
1246      *   var s = board.create('slider',[[0,4],[3,4],[0,4,10]],{snapWidth:1});
1247      *   var f = function(x) { return 0.5*x*x-2*x; };
1248      *   var r = board.create('riemannsum',
1249      *               [f, function(){return s.Value();}, 'upper', -2, 5],
1250      *               {fillOpacity:0.4}
1251      *               );
1252      *   var g = board.create('functiongraph',[f, -2, 5]);
1253      *   var t = board.create('text',[-1,-1, function(){ return 'Sum=' + r.Value().toFixed(4); }]);
1254      * </pre><div id="940f40cc-2015-420d-9191-c5d83de988cf" style="width: 300px; height: 300px;"></div>
1255      * <script type="text/javascript">
1256      *   var rs1_board = JXG.JSXGraph.initBoard('940f40cc-2015-420d-9191-c5d83de988cf', {boundingbox: [-3, 7, 5, -3], axis: true, showcopyright: false, shownavigation: false});
1257      *   var f = function(x) { return 0.5*x*x-2*x; };
1258      *   var s = rs1_board.create('slider',[[0,4],[3,4],[0,4,10]],{snapWidth:1});
1259      *   var r = rs1_board.create('riemannsum', [f, function(){return s.Value();}, 'upper', -2, 5], {fillOpacity:0.4});
1260      *   var g = rs1_board.create('functiongraph', [f, -2, 5]);
1261      *   var t = board.create('text',[-1,-1, function(){ return 'Sum=' + r.Value().toFixed(4); }]);
1262      * </script><pre>
1263      */
1264     JXG.createRiemannsum = function (board, parents, attributes) {
1265         var n, type, f, par, c, attr;
1266 
1267         attr = Type.copyAttributes(attributes, board.options, 'riemannsum');
1268         attr.curvetype = 'plot';
1269 
1270         f = parents[0];
1271         n = Type.createFunction(parents[1], board, '');
1272 
1273         if (!Type.exists(n)) {
1274             throw new Error("JSXGraph: JXG.createRiemannsum: argument '2' n has to be number or function." +
1275                 "\nPossible parent types: [function,n:number|function,type,start:number|function,end:number|function]");
1276         }
1277 
1278         type = Type.createFunction(parents[2], board, '', false);
1279         if (!Type.exists(type)) {
1280             throw new Error("JSXGraph: JXG.createRiemannsum: argument 3 'type' has to be string or function." +
1281                 "\nPossible parent types: [function,n:number|function,type,start:number|function,end:number|function]");
1282         }
1283 
1284         par = [[0], [0]].concat(parents.slice(3));
1285 
1286         c = board.create('curve', par, attr);
1287 
1288         c.sum = 0.0;
1289         c.Value = function () {
1290             return this.sum;
1291         };
1292 
1293         c.updateDataArray = function () {
1294             var u = Numerics.riemann(f, n(), type(), this.minX(), this.maxX());
1295             this.dataX = u[0];
1296             this.dataY = u[1];
1297 
1298             // Update "Riemann sum"
1299             this.sum = u[2];
1300         };
1301 
1302         return c;
1303     };
1304 
1305     JXG.registerElement('riemannsum', JXG.createRiemannsum);
1306 
1307     /**
1308      * @class This element is used to provide a constructor for travce curve (simple locus curve), which is realized as a special curve.
1309      * @pseudo
1310      * @description
1311      * @name Tracecurve
1312      * @augments JXG.Curve
1313      * @constructor
1314      * @type JXG.Curve
1315      * @param {Point,Point} Parent elements of Tracecurve are a
1316      *         glider point and a point whose locus is traced.
1317      * @see JXG.Curve
1318      * @example
1319      * // Create trace curve.
1320      var c1 = board.create('circle',[[0, 0], [2, 0]]),
1321      p1 = board.create('point',[-3, 1]),
1322      g1 = board.create('glider',[2, 1, c1]),
1323      s1 = board.create('segment',[g1, p1]),
1324      p2 = board.create('midpoint',[s1]),
1325      curve = board.create('tracecurve', [g1, p2]);
1326 
1327      * </pre><div id="5749fb7d-04fc-44d2-973e-45c1951e29ad" style="width: 300px; height: 300px;"></div>
1328      * <script type="text/javascript">
1329      *   var tc1_board = JXG.JSXGraph.initBoard('5749fb7d-04fc-44d2-973e-45c1951e29ad', {boundingbox: [-4, 4, 4, -4], axis: false, showcopyright: false, shownavigation: false});
1330      *   var c1 = tc1_board.create('circle',[[0, 0], [2, 0]]),
1331      *       p1 = tc1_board.create('point',[-3, 1]),
1332      *       g1 = tc1_board.create('glider',[2, 1, c1]),
1333      *       s1 = tc1_board.create('segment',[g1, p1]),
1334      *       p2 = tc1_board.create('midpoint',[s1]),
1335      *       curve = tc1_board.create('tracecurve', [g1, p2]);
1336      * </script><pre>
1337      */
1338     JXG.createTracecurve = function (board, parents, attributes) {
1339         var c, glider, tracepoint, attr;
1340 
1341         if (parents.length !== 2) {
1342             throw new Error("JSXGraph: Can't create trace curve with given parent'" +
1343                 "\nPossible parent types: [glider, point]");
1344         }
1345 
1346         glider = board.select(parents[0]);
1347         tracepoint = board.select(parents[1]);
1348 
1349         if (glider.type !== Const.OBJECT_TYPE_GLIDER || !Type.isPoint(tracepoint)) {
1350             throw new Error("JSXGraph: Can't create trace curve with parent types '" +
1351                 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." +
1352                 "\nPossible parent types: [glider, point]");
1353         }
1354 
1355         attr = Type.copyAttributes(attributes, board.options, 'tracecurve');
1356         attr.curvetype = 'plot';
1357         c = board.create('curve', [[0], [0]], attr);
1358 
1359         c.updateDataArray = function () {
1360             var i, step, t, el, pEl, x, y, v, from, savetrace,
1361                 le = attr.numberpoints,
1362                 savePos = glider.position,
1363                 slideObj = glider.slideObject,
1364                 mi = slideObj.minX(),
1365                 ma = slideObj.maxX();
1366 
1367             // set step width
1368             step = (ma - mi) / le;
1369             this.dataX = [];
1370             this.dataY = [];
1371 
1372             /*
1373              * For gliders on circles and lines a closed curve is computed.
1374              * For gliders on curves the curve is not closed.
1375              */
1376             if (slideObj.elementClass !== Const.OBJECT_CLASS_CURVE) {
1377                 le++;
1378             }
1379 
1380             // Loop over all steps
1381             for (i = 0; i < le; i++) {
1382                 t = mi + i * step;
1383                 x = slideObj.X(t) / slideObj.Z(t);
1384                 y = slideObj.Y(t) / slideObj.Z(t);
1385 
1386                 // Position the glider
1387                 glider.setPositionDirectly(Const.COORDS_BY_USER, [x, y]);
1388                 from = false;
1389 
1390                 // Update all elements from the glider up to the trace element
1391                 for (el in this.board.objects) {
1392                     if (this.board.objects.hasOwnProperty(el)) {
1393                         pEl = this.board.objects[el];
1394 
1395                         if (pEl === glider) {
1396                             from = true;
1397                         }
1398 
1399                         if (from && pEl.needsRegularUpdate) {
1400                             // Save the trace mode of the element
1401                             savetrace = pEl.visProp.trace;
1402                             pEl.visProp.trace = false;
1403                             pEl.needsUpdate = true;
1404                             pEl.update(true);
1405 
1406                             // Restore the trace mode
1407                             pEl.visProp.trace = savetrace;
1408                             if (pEl === tracepoint) {
1409                                 break;
1410                             }
1411                         }
1412                     }
1413                 }
1414 
1415                 // Store the position of the trace point
1416                 this.dataX[i] = tracepoint.X();
1417                 this.dataY[i] = tracepoint.Y();
1418             }
1419 
1420             // Restore the original position of the glider
1421             glider.position = savePos;
1422             from = false;
1423 
1424             // Update all elements from the glider to the trace point
1425             for (el in this.board.objects) {
1426                 if (this.board.objects.hasOwnProperty(el)) {
1427                     pEl = this.board.objects[el];
1428                     if (pEl === glider) {
1429                         from = true;
1430                     }
1431 
1432                     if (from && pEl.needsRegularUpdate) {
1433                         savetrace = pEl.visProp.trace;
1434                         pEl.visProp.trace = false;
1435                         pEl.needsUpdate = true;
1436                         pEl.update(true);
1437                         pEl.visProp.trace = savetrace;
1438 
1439                         if (pEl === tracepoint) {
1440                             break;
1441                         }
1442                     }
1443                 }
1444             }
1445         };
1446 
1447         return c;
1448     };
1449 
1450     JXG.registerElement('tracecurve', JXG.createTracecurve);
1451 
1452     return {
1453         Curve: JXG.Curve,
1454         createCurve: JXG.createCurve,
1455         createFunctiongraph: JXG.createFunctiongraph,
1456         createPlot: JXG.createPlot,
1457         createSpline: JXG.createSpline,
1458         createRiemannsum: JXG.createRiemannsum,
1459         createTracecurve: JXG.createTracecurve
1460     };
1461 });
1462