1 /*
  2     Copyright 2008-2011,
  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: you can redistribute it and/or modify
 13     it under the terms of the GNU Lesser General Public License as published by
 14     the Free Software Foundation, either version 3 of the License, or
 15     (at your option) any later version.
 16 
 17     JSXGraph is distributed in the hope that it will be useful,
 18     but WITHOUT ANY WARRANTY; without even the implied warranty of
 19     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 20     GNU Lesser General Public License for more details.
 21 
 22     You should have received a copy of the GNU Lesser General Public License
 23     along with JSXGraph.  If not, see <http://www.gnu.org/licenses/>.
 24 */
 25 
 26 /**
 27  * @fileoverview In this file the geometry element Curve is defined.
 28  */
 29 
 30 /**
 31  * Curves are the common object for function graphs, parametric curves, polar curves, adn data plots.
 32  * @class Creates a new curve object. Do not use this constructor to create a curve. Use {@link JXG.Board#create} with
 33  * type {@link Curve}, or {@link Functiongraph} instead.  
 34  * @augments JXG.GeometryElement
 35  * @param {string,JXG.Board} board The board the new curve is drawn on.
 36  * @param {Array} defining terms An array with the functon terms, data points of the curve.
 37  * @param {String} id Unique identifier for the point. If null or an empty string is given,
 38  *  an unique id will be generated by Board
 39  * @param {String} name Not necessarily unique name for the point. If null or an
 40  *  empty string is given, an unique name will be generated
 41  * @param {boolean} show False if the point is invisible, True otherwise
 42  * @see JXG.Board#generateName
 43  * @see JXG.Board#addCurve
 44   */
 45 JXG.Curve = function (board, parents, id, name, withLabel, layer) {
 46     this.constructor();
 47  
 48     this.points = []; 
 49 
 50     this.type = JXG.OBJECT_TYPE_CURVE;
 51     this.elementClass = JXG.OBJECT_CLASS_CURVE;                
 52     
 53     this.init(board, id, name);
 54 
 55     /**
 56      * Set the display layer.
 57      */
 58     if (layer == null) layer = board.options.layer['curve'];
 59     this.layer = layer;
 60 
 61     /** Use the algorithm by Gillam and Hohenwarter for plotting.
 62       * If false the naive algorithm is used.
 63       * It is much slower, but the result is better.
 64       */
 65     this.doAdvancedPlot = this.board.options.curve.doAdvancedPlot;
 66     
 67     /** 
 68       * Number of points on curves after mouseUp, i.e. high quality output.
 69       * Only used if this.doAdvancedPlot==false
 70       * May be overwritten.
 71       **/
 72     this.numberPointsHigh = this.board.options.curve.numberPointsHigh;
 73     /** 
 74       * Number of points on curves after mousemove, i.e. low quality output.
 75       * Only used if this.doAdvancedPlot==false
 76       * May be overwritten.
 77       **/
 78     this.numberPointsLow = this.board.options.curve.numberPointsLow;
 79     /** 
 80       * Number of points on curves. This value changes
 81       * between numberPointsLow and numberPointsHigh.
 82       * It is set in {@link #updateCurve}.
 83       */
 84     this.numberPoints = this.numberPointsHigh; 
 85 
 86     this.visProp['strokeWidth'] = this.board.options.curve.strokeWidth;
 87     this.visProp['highlightStrokeWidth'] = this.visProp['strokeWidth'];
 88 
 89     this.visProp['visible'] = true;
 90     this.dataX = null;
 91     this.dataY = null;
 92 
 93     /**
 94      * This is just for the hasPoint() method.
 95      * @type int
 96      */
 97     //this.r = this.board.options.precision.hasPoint;
 98     
 99     /**
100      * The curveType is set in @see generateTerm and used in 
101      * {@link updateCurve}
102      * Possible values are:
103      * 'none'
104      * 'plot': Data plot
105      * 'parameter': we can not distinguish function graphs and parameter curves
106      * 'functiongraph': function graph
107      * 'polar'
108      * 'implicit' (not yet)
109      *
110      * Only parameter and plot are set directly.
111      * polar is set with setProperties only.
112      **/
113     // this.curveType = 'none';
114     this.curveType = null;
115 
116     if (parents[0]!=null) {
117         this.varname = parents[0];
118     } else {
119         this.varname = 'x';
120     }
121     this.xterm = parents[1];  // function graphs: "x"
122     this.yterm = parents[2];  // function graphs: e.g. "x^2"
123     this.generateTerm(this.varname,this.xterm,this.yterm,parents[3],parents[4]);  // Converts GEONExT syntax into JavaScript syntax
124     this.updateCurve();                        // First evaluation of the curve
125     
126     this.createLabel(withLabel);
127     this.id = this.board.setId(this,'G');
128     this.board.renderer.drawCurve(this);
129     this.board.finalizeAdding(this);
130     
131     if (typeof this.xterm=='string') {
132         this.notifyParents(this.xterm);
133     }
134     if (typeof this.yterm=='string') {
135         this.notifyParents(this.yterm);
136     }
137 };
138 JXG.Curve.prototype = new JXG.GeometryElement;
139 
140 /**
141  * Gives the default value of the left bound for the curve.
142  * May be overwritten in @see generateTerm.
143  */
144 JXG.Curve.prototype.minX = function () {
145     if (this.curveType=='polar') {
146         return 0.0;
147     } else {
148         var leftCoords = new JXG.Coords(JXG.COORDS_BY_SCREEN, [0, 0], this.board);
149         return leftCoords.usrCoords[1];
150     }
151 };
152 
153 /**
154  * Gives the default value of the right bound for the curve.
155  * May be overwritten in @see generateTerm.
156  */
157 JXG.Curve.prototype.maxX = function () {
158     var rightCoords;
159     if (this.curveType=='polar') {
160         return 2.0*Math.PI;
161     } else {
162         rightCoords = new JXG.Coords(JXG.COORDS_BY_SCREEN, [this.board.canvasWidth, 0], this.board);
163         return rightCoords.usrCoords[1];
164     }
165 };
166 
167 /**
168  * Checks whether (x,y) is near the curve.
169  * @param {int} x Coordinate in x direction, screen coordinates.
170  * @param {int} y Coordinate in y direction, screen coordinates.
171  * @param {y} Find closest point on the curve to (x,y)
172  * @return {bool} True if (x,y) is near the curve, False otherwise.
173  */
174 JXG.Curve.prototype.hasPoint = function (x,y) {
175     var t, dist = Infinity, 
176         c, trans, i, j, tX, tY,
177         xi, xi1, yi, yi1,
178         lbda, x0, y0, x1, y1, xy, den,
179         steps = this.numberPointsLow, 
180         d = (this.maxX()-this.minX())/steps,
181         prec = this.board.options.precision.hasPoint/(this.board.unitX*this.board.zoomX),
182         checkPoint, len,
183         suspendUpdate = true;
184 
185     prec = prec*prec;
186     checkPoint = new JXG.Coords(JXG.COORDS_BY_SCREEN, [x,y], this.board);
187     x = checkPoint.usrCoords[1];
188     y = checkPoint.usrCoords[2];
189     if (this.curveType=='parameter' || this.curveType=='polar' || this.curveType=='functiongraph') { 
190         // Brute fore search for a point on the curve close to the mouse pointer
191         len = this.transformations.length;
192         for (i=0,t=this.minX(); i<steps; i++) {
193             tX = this.X(t,suspendUpdate);
194             tY = this.Y(t,suspendUpdate);
195             for (j=0; j<len; j++) {
196                 trans = this.transformations[j];
197                 trans.update();
198                 c = JXG.Math.matVecMult(trans.matrix,[1,tX,tY]);
199                 tX = c[1];
200                 tY = c[2];
201             }
202             dist = (x-tX)*(x-tX)+(y-tY)*(y-tY);
203             if (dist<prec) { return true; }
204             t+=d;
205         }  
206     } else if (this.curveType == 'plot') {
207         //$('debug').innerHTML +='. ';
208         len = this.numberPoints; // Rough search quality
209         for (i=0;i<len-1;i++) {
210             xi = this.X(i);
211             xi1 = this.X(i+1);
212             //if (i!=xi) {
213             //    yi = this.Y(xi);
214             //    yi1 = this.Y(xi1);
215             //} else {
216                 yi = this.Y(i);
217                 yi1 = this.Y(i+1);
218                // $('debug').innerHTML = this.Y.toString();
219             //}
220             x1 = xi1 - xi;
221             y1 = yi1-yi;
222             
223             x0 = x-xi; //this.X(i);
224             y0 = y-yi; //this.Y(i);
225             den = x1*x1+y1*y1;
226             
227             if (den>=JXG.Math.eps) {
228                 xy = x0*x1+y0*y1;
229                 lbda = xy/den;
230                 dist = x0*x0+y0*y0 - lbda*xy;
231             } else {
232                 lbda = 0.0;
233                 dist = x0*x0+y0*y0;
234             }
235             if (lbda>=0.0 && lbda<=1.0 && dist<prec) { 
236                 return true; 
237             } 
238         }
239         return false;
240     } 
241     return (dist<prec);
242 };
243 
244 /**
245   * Allocate points in the Coords array this.points
246   */
247 JXG.Curve.prototype.allocatePoints = function () {
248     var i, len;
249     len = this.numberPoints;
250     if (this.points.length<this.numberPoints) {
251         for (i=this.points.length; i<len; i++) {
252             this.points[i] = new JXG.Coords(JXG.COORDS_BY_USER, [0,0], this.board);
253         }
254     }
255 };
256 
257 /**
258  * Computes for equidistant points on the x-axis the values
259  * of the function, {@link #updateCurve}
260  * Then, the update function of the renderer
261  * is called. 
262  */
263 JXG.Curve.prototype.update = function () {
264     if (this.needsUpdate) {
265         this.updateCurve();
266     }
267     if(this.traced) {
268         this.cloneToBackground(true);
269     }    
270     return this;
271 };
272 
273 /**
274  * Then, the update function of the renderer
275  * is called. 
276  */
277 JXG.Curve.prototype.updateRenderer = function () {
278     if (this.needsUpdate) {
279         this.board.renderer.updateCurve(this);
280         this.needsUpdate = false;
281     }
282     
283     /* Update the label if visible. */
284     if(this.hasLabel && this.label.content.visProp['visible']) {
285         //this.label.setCoordinates(this.coords);
286         this.label.content.update();
287         //this.board.renderer.updateLabel(this.label);
288         this.board.renderer.updateText(this.label.content);
289     }       
290     return this;
291 };
292 
293 /**
294   * For dynamic dataplots updateCurve
295   * can be used to compute new entries
296   * for the arrays this.dataX and
297   * this.dataY. It is used in @see updateCurve.
298   * Default is an empty method, can be overwritten
299   * by the user.
300   */
301 JXG.Curve.prototype.updateDataArray = function () { return this; };
302 
303 /**
304  * Computes for equidistant points on the x-axis the values
305  * of the function. @see #update
306  * If the mousemove event triggers this update, we use only few
307  * points. Otherwise, e.g. on mouseup, many points are used.
308  */
309 JXG.Curve.prototype.updateCurve = function () {
310     var len, mi, ma, x, y, i,
311         suspendUpdate = false;
312     
313     this.updateDataArray();
314     mi = this.minX();
315     ma = this.maxX();
316 
317     // Discrete data points
318     if (this.dataX!=null) { // x-coordinates are in an array
319         this.numberPoints = this.dataX.length;
320         len = this.numberPoints;
321         this.allocatePoints();  // It is possible, that the array length has increased.
322         for (i=0; i<len; i++) {
323             x = i;
324             if (this.dataY!=null) { // y-coordinates are in an array
325                 y = i;
326             } else {
327                 y = this.X(x); // discrete x data, continuous y data
328             }
329             this.points[i].setCoordinates(JXG.COORDS_BY_USER, [this.X(x,suspendUpdate),this.Y(y,suspendUpdate)], false); // The last parameter prevents rounding in usr2screen().
330             this.updateTransform(this.points[i]);
331             suspendUpdate = true;
332         }
333     } else { // continuous x data
334         if (this.doAdvancedPlot) {
335             this.updateParametricCurve(mi,ma,len);
336         } else {
337             if (this.board.updateQuality==this.board.BOARD_QUALITY_HIGH) {
338                 this.numberPoints = this.numberPointsHigh;
339             } else {
340                 this.numberPoints = this.numberPointsLow;
341             }
342             len = this.numberPoints;
343             this.allocatePoints();  // It is possible, that the array length has increased.
344             this.updateParametricCurveNaive(mi,ma,len);
345         }
346     }
347     this.getLabelAnchor();
348     return this;
349 };
350 
351 JXG.Curve.prototype.updateParametricCurveNaive = function(mi,ma,len) {
352     var i, t,
353         suspendUpdate = false,
354         stepSize = (ma-mi)/len;
355         
356     for (i=0; i<len; i++) {
357         t = mi+i*stepSize;
358         this.points[i].setCoordinates(JXG.COORDS_BY_USER, [this.X(t,suspendUpdate),this.Y(t,suspendUpdate)], false); // The last parameter prevents rounding in usr2screen().
359         this.updateTransform(this.points[i]);
360         suspendUpdate = true;
361     }
362     return this;
363 };
364 
365 JXG.Curve.prototype.updateParametricCurve = function(mi,ma,len) {
366     var i, t, t0,
367         suspendUpdate = false,
368         po = new JXG.Coords(JXG.COORDS_BY_USER, [0,0], this.board),
369         x, y, x0, y0, top, depth,
370         MAX_DEPTH,
371         MAX_XDIST,
372         MAX_YDIST,
373         dyadicStack = [],
374         depthStack = [],
375         pointStack = [],
376         divisors = [], 
377         //xd_ = NaN, yd_ = NaN,
378         distOK = false,
379         j = 0;
380 
381     
382     if (this.board.updateQuality==this.board.BOARD_QUALITY_LOW) {
383         MAX_DEPTH = 12;
384         MAX_XDIST = 12;
385         MAX_YDIST = 12;
386     } else {
387         MAX_DEPTH = 20;
388         MAX_XDIST = 2;
389         MAX_YDIST = 2;
390     }
391     
392     divisors[0] = ma-mi;
393     for (i=1;i<MAX_DEPTH;i++) {
394         divisors[i] = divisors[i-1]*0.5;
395     }
396     
397     i = 1;
398     dyadicStack[0] = 1;
399     depthStack[0] = 0;
400     t = mi;
401     po.setCoordinates(JXG.COORDS_BY_USER, [this.X(t,suspendUpdate),this.Y(t,suspendUpdate)], false);
402     suspendUpdate = true;
403     x0 = po.scrCoords[1];
404     y0 = po.scrCoords[2];
405     t0 = t;
406     
407     t = ma;
408     po.setCoordinates(JXG.COORDS_BY_USER, [this.X(t,suspendUpdate),this.Y(t,suspendUpdate)], false);
409     x = po.scrCoords[1];
410     y = po.scrCoords[2];
411     
412     pointStack[0] = [x,y];
413     
414     top = 1;
415     depth = 0;
416 
417     this.points = [];
418     this.points[j++] = new JXG.Coords(JXG.COORDS_BY_SCREEN, [x0, y0], this.board);
419     
420     do {
421         distOK = this.isDistOK(x0,y0,x,y,MAX_XDIST,MAX_YDIST)||this.isSegmentOutside(x0,y0,x,y);
422         while ( depth<MAX_DEPTH &&
423                (!distOK || depth<3 /*|| (j>1 &&!this.bendOK(xd_,yd_,x-x0,y-y0))*/) &&
424                !(!this.isSegmentDefined(x0,y0,x,y) && depth>8)
425             ) {
426             dyadicStack[top] = i;
427             depthStack[top] = depth;
428             pointStack[top] = [x,y];
429             top++;
430             
431             i = 2*i-1;
432             depth++;
433             t = mi+i*divisors[depth];
434             po.setCoordinates(JXG.COORDS_BY_USER, [this.X(t,suspendUpdate),this.Y(t,suspendUpdate)], false);
435             x = po.scrCoords[1];
436             y = po.scrCoords[2];
437             distOK = this.isDistOK(x0,y0,x,y,MAX_XDIST,MAX_YDIST)||this.isSegmentOutside(x0,y0,x,y);
438         }
439         /*
440         if (this.board.updateQuality==this.board.BOARD_QUALITY_HIGH && !this.isContinuous(t0,t,MAX_DEPTH)) {
441             //$('debug').innerHTML += 'x ';
442             this.points[j] = new JXG.Coords(JXG.COORDS_BY_SCREEN, [NaN, NaN], this.board);
443             //this.points[j] = new JXG.Coords(JXG.COORDS_BY_SCREEN, [1, 1], this.board);
444             j++;
445         }
446         */
447         this.points[j] = new JXG.Coords(JXG.COORDS_BY_SCREEN, [x, y], this.board);
448         this.updateTransform(this.points[j]);
449         j++;
450         //xd_ = x-x0;
451         //yd_ = x-y0;
452         x0 = x;
453         y0 = y;
454         t0 = t;
455         
456         top--;
457         x = pointStack[top][0];
458         y = pointStack[top][1];
459         depth = depthStack[top]+1;
460         i = dyadicStack[top]*2;
461         
462     } while (top != 0);
463     this.numberPoints = this.points.length;
464     //$('debug').innerHTML = ' '+this.numberPoints;
465     return this;
466         
467 };
468 
469 JXG.Curve.prototype.isSegmentOutside = function (x0,y0,x1,y1) {
470     if (y0<0 && y1<0) { return true; }
471     else if (y0>this.board.canvasHeight && y1>this.board.canvasHeight) { return true; }
472     else if (x0<0 && x1<0) { return true; }
473     else if (x0>this.board.canvasWidth && x1>this.board.canvasWidth) { return true; }
474     return false;
475 };
476 
477 JXG.Curve.prototype.isDistOK = function (x0,y0,x1,y1,MAXX,MAXY) {
478     if (isNaN(x0+y0+x1+y1)) { return false; }
479     return (Math.abs(x1-x0)<MAXY && Math.abs(y1-y0)<MAXY);
480 };
481 
482 JXG.Curve.prototype.isSegmentDefined = function (x0,y0,x1,y1) {
483     return !(isNaN(x0 + y0) && isNaN(x1 + y1));
484 
485 };
486 /*
487 JXG.Curve.prototype.isContinuous = function (t0, t1, MAX_ITER) {
488     var left, middle, right, tm,
489         iter = 0,
490         initDist, dist = Infinity,
491         dl, dr; 
492 
493     if (Math.abs(t0-t1)<JXG.Math.eps) { return true; }
494     left = new JXG.Coords(JXG.COORDS_BY_USER, [0,0], this.board);
495     middle = new JXG.Coords(JXG.COORDS_BY_USER, [0,0], this.board);
496     right = new JXG.Coords(JXG.COORDS_BY_USER, [0,0], this.board);
497     
498     left.setCoordinates(JXG.COORDS_BY_USER, [this.X(t0,true),this.Y(t0,true)], false);
499     right.setCoordinates(JXG.COORDS_BY_USER, [this.X(t1,true),this.Y(t1,true)], false);
500     
501     initDist = Math.max(Math.abs(left.scrCoords[1]-right.scrCoords[1]),Math.abs(left.scrCoords[2]-right.scrCoords[2]));
502     while (iter++<MAX_ITER && dist>initDist*0.9) {
503         tm = (t0+t1)*0.5;
504         middle.setCoordinates(JXG.COORDS_BY_USER, [this.X(tm,true),this.Y(tm,true)], false);
505         dl = Math.max(Math.abs(left.scrCoords[1]-middle.scrCoords[1]),Math.abs(left.scrCoords[2]-middle.scrCoords[2]));
506         dr = Math.max(Math.abs(middle.scrCoords[1]-right.scrCoords[1]),Math.abs(middle.scrCoords[2]-right.scrCoords[2]));
507         
508         if (dl>dr) {
509             dist = dl;
510             t1 = tm;
511         } else {
512             dist = dr;
513             t0 = tm;
514         }
515         if (Math.abs(t0-t1)<JXG.Math.eps) { return true;}
516     }
517     if (dist>initDist*0.9) {
518         return false;
519     } else {
520         return true;
521     }
522 };
523 */
524 
525 /*
526 JXG.Curve.prototype.bendOK = function (xd_,yd_,xd,yd) {
527     var ip = xd_*xd+yd_*yd,
528         MAX_BEND = Math.tan(45*Math.PI/180.0);
529 
530     if (isNaN(ip)) {
531         return true;
532     } else if (ip<=0.0) {
533         return false;
534     } else {
535         return Math.abs(xd_*yd-yd_*xd)<MAX_BEND*ip;
536     }
537 };
538 */
539 
540 JXG.Curve.prototype.updateTransform = function (p) {
541     var t, c, i, 
542         len = this.transformations.length;
543     if (len==0) {
544         return p;
545     }
546     for (i=0; i<len; i++) {
547         t = this.transformations[i];
548         t.update();
549         c = JXG.Math.matVecMult(t.matrix,p.usrCoords);
550         p.setCoordinates(JXG.COORDS_BY_USER,[c[1],c[2]]);
551     }
552     return p;
553 };
554 
555 JXG.Curve.prototype.addTransform = function (transform) {
556     var list, i, len;
557     if (JXG.isArray(transform)) {
558         list = transform;
559     } else {
560         list = [transform];
561     }
562     len = list.length;
563     for (i=0; i<len; i++) {
564         this.transformations.push(list[i]);
565     }
566     return this;
567 };
568 
569 JXG.Curve.prototype.setPosition = function (method, x, y) {
570     //if(this.group.length != 0) {
571     // AW: Do we need this for lines?
572     //} else {
573     var t = this.board.create('transform',[x,y],{type:'translate'});
574     if (this.transformations.length>0 && this.transformations[this.transformations.length-1].isNumericMatrix) {
575         this.transformations[this.transformations.length-1].melt(t);
576     } else {
577         this.addTransform(t);
578     }
579     //this.update();
580     //}
581     return this;
582 };
583 
584 /**
585  * Converts the GEONExT syntax of the defining function term into JavaScript.
586  * New methods X() and Y() for the Curve object are generated, further
587  * new methods for minX() and maxX().
588  *
589  * Also, all objects whose name appears in the term are searched and
590  * the curve is added as child to these objects. (Commented out!!!!)
591  * @see Algebra
592  * @see #geonext2JS.
593  */
594 JXG.Curve.prototype.generateTerm = function (varname, xterm, yterm, mi, ma) {
595     var fx, fy;
596 
597     // Generate the methods X() and Y()
598     if (JXG.isArray(xterm)) {
599         this.dataX = xterm;
600         this.X = function(i) { return this.dataX[i]; };
601         this.curveType = 'plot';
602         this.numberPoints = this.dataX.length;
603     } else {
604         this.X = JXG.createFunction(xterm,this.board,varname);
605         if (JXG.isString(xterm)) { 
606             this.curveType = 'functiongraph'; 
607         } else if (JXG.isFunction(xterm) || JXG.isNumber(xterm)) {
608             this.curveType = 'parameter';
609         }
610     }
611 
612     if (JXG.isArray(yterm)) {
613         this.dataY = yterm;
614         this.Y = function(i) { 
615                 if (JXG.isFunction(this.dataY[i])) { 
616                     return this.dataY[i](); 
617                 } else {
618                     return this.dataY[i]; 
619                 }
620             };
621     } else {
622         this.Y = JXG.createFunction(yterm,this.board,varname);
623     }
624 
625     // polar form
626     if (JXG.isFunction(xterm) && JXG.isArray(yterm)) {
627         // Xoffset, Yoffset
628         fx = JXG.createFunction(yterm[0],this.board,'');
629         fy = JXG.createFunction(yterm[1],this.board,'');
630         this.X = function(phi){return (xterm)(phi)*Math.cos(phi)+fx();};
631         this.Y = function(phi){return (xterm)(phi)*Math.sin(phi)+fy();};
632         this.curveType = 'polar';
633     }
634 
635     // Set the bounds
636     // lower bound
637     if (mi!=null) this.minX = JXG.createFunction(mi,this.board,'');
638     if (ma!=null) this.maxX = JXG.createFunction(ma,this.board,'');
639 
640 /*    
641     // Find dependencies
642     var elements = this.board.elementsByName;
643     for (el in elements) {
644         if (el != this.name) {
645             var s1 = "X(" + el + ")";
646             var s2 = "Y(" + el + ")";
647             if (xterm.indexOf(s1)>=0 || xterm.indexOf(s2)>=0 ||
648                 yterm.indexOf(s1)>=0 || yterm.indexOf(s2)>=0) {
649                 elements[el].addChild(this);
650             }
651         }
652     }
653 */    
654 };
655 
656 /**
657  * Finds dependencies in a given term and notifies the parents by adding the
658  * dependent object to the found objects child elements.
659  * @param {String} term String containing dependencies for the given object.
660  */
661 JXG.Curve.prototype.notifyParents = function (contentStr) {
662     //var res = null;
663     //var elements = this.board.elementsByName;
664     JXG.GeonextParser.findDependencies(this,contentStr,this.board);
665 };
666 
667 /**
668  * Calculates LabelAnchor.
669  * @type JXG.Coords
670  * @return Text anchor coordinates as JXG.Coords object.
671  */
672 JXG.Curve.prototype.getLabelAnchor = function() {
673     var c = new JXG.Coords(JXG.COORDS_BY_SCREEN, [0, this.board.canvasHeight*0.5], this.board);
674     c = JXG.Math.Geometry.projectCoordsToCurve(c.usrCoords[1],c.usrCoords[2],0.0,this,this.board)[0];
675     return c;
676 };
677 
678 /**
679  * Clone curve to the background.
680  * @param addToTrace Not used yet. Always true.
681  */
682 JXG.Curve.prototype.cloneToBackground = function(addToTrace) {
683     var copy = {}, r, s, i, er;
684 
685     copy.id = this.id + 'T' + this.numTraces;
686     copy.elementClass = JXG.OBJECT_CLASS_CURVE;
687     this.numTraces++;
688     
689     copy.points = this.points.slice(0); 
690     copy.numberPoints = this.numberPoints;
691     copy.curveType = this.curveType;
692 
693     copy.board = {};
694     copy.board.unitX = this.board.unitX;
695     copy.board.unitY = this.board.unitY;
696     copy.board.zoomX = this.board.zoomX;
697     copy.board.zoomY = this.board.zoomY;
698     copy.board.stretchX = this.board.stretchX;
699     copy.board.stretchY = this.board.stretchY;
700     copy.board.origin = this.board.origin;
701     copy.board.canvasHeight = this.board.canvasHeight;
702     copy.board.canvasWidth = this.board.canvasWidth;
703     copy.board.dimension = this.board.dimension;
704     //copy.board.algebra = this.board.algebra;
705     copy.board.options = this.board.options;
706 
707     copy.visProp = this.visProp;
708     JXG.clearVisPropOld(copy);
709     er = this.board.renderer.enhancedRendering;
710     this.board.renderer.enhancedRendering = true;
711     this.board.renderer.drawCurve(copy);
712     this.board.renderer.enhancedRendering = er;
713     this.traces[copy.id] = copy.rendNode; //this.board.renderer.getElementById(copy.id);
714 
715     delete copy;
716 };
717 
718 /**
719  * @class This element is used to provide a constructor for curve, which is just a wrapper for element {@link Curve}. 
720  * 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]. 
721  * <p>
722  * The following types of curves can be plotted:
723  * <ul>
724  *  <li> parametric curves: t mapsto (x(t),y(t)), where x() and y() are univariate functions.
725  *  <li> polar curves: curves commonly written with polar equations like spirals and cardioids.
726  *  <li> data plots: plot linbe segments through a given list of coordinates.
727  * </ul>
728  * @pseudo
729  * @description
730  * @name Curve
731  * @augments JXG.Curve
732  * @constructor
733  * @type JXG.Curve
734  *
735  * @param {function,number_function,number_function,number_function,number} x,y,a_,b_ Parent elements for Parametric Curves. 
736  *                     <p>
737  *                     x describes the x-coordinate of the curve. It may be a function term in one variable, e.g. x(t). 
738  *                     In case of x being of type number, x(t) is set to  a constant function.
739  *                     this function at the values of the array.
740  *                     <p>
741  *                     y describes the y-coordinate of the curve. In case of a number, y(t) is set to the constant function
742  *                     returning this number. 
743  *                     <p>
744  *                     Further parameters are an optional number or function for the left interval border a, 
745  *                     and an optional number or function for the right interval border b. 
746  *                     <p>
747  *                     Default values are a=-10 and b=10.
748  * @param {array_array,function,number} x,y Parent elements for Data Plots. 
749  *                     <p>
750  *                     x and y are arrays contining the x and y coordinates of the data points which are connected by
751  *                     line segments. The individual entries of x and y may also be functions.
752  *                     In case of x being an array the curve type is data plot, regardless of the second parameter and 
753  *                     if additionally the second parameter y is a function term the data plot evaluates.
754  * @param {function_array,function,number_function,number_function,number} r,offset_,a_,b_ Parent elements for Polar Curves. 
755  *                     <p>
756  *                     The first parameter is a function term r(phi) describing the polar curve.
757  *                     <p>
758  *                     The second parameter is the offset of the curve. It has to be
759  *                     an array containing numbers or functions describing the offset. Default value is the origin [0,0].
760  *                     <p>
761  *                     Further parameters are an optional number or function for the left interval border a, 
762  *                     and an optional number or function for the right interval border b. 
763  *                     <p>
764  *                     Default values are a=-10 and b=10.
765  * @see JXG.Curve
766  * @example
767  * // Parametric curve
768  * // Create a curve of the form (t-sin(t), 1-cos(t), i.e.
769  * // the cycloid curve.
770  *   var graph = board.create('curve', 
771  *                        [function(t){ return t-Math.sin(t);}, 
772  *                         function(t){ return 1-Math.cos(t);},
773  *                         0, 2*Math.PI]
774  *                     );
775  * </pre><div id="af9f818b-f3b6-4c4d-8c4c-e4a4078b726d" style="width: 300px; height: 300px;"></div>
776  * <script type="text/javascript">
777  *   var c1_board = JXG.JSXGraph.initBoard('af9f818b-f3b6-4c4d-8c4c-e4a4078b726d', {boundingbox: [-1, 5, 7, -1], axis: true, showcopyright: false, shownavigation: false});
778  *   var graph1 = c1_board.create('curve', [function(t){ return t-Math.sin(t);},function(t){ return 1-Math.cos(t);},0, 2*Math.PI]);
779  * </script><pre>
780  * @example
781  * // Data plots
782  * // Connect a set of points given by coordinates with dashed line segments.
783  * // The x- and y-coordinates of the points are given in two separate 
784  * // arrays.
785  *   var x = [0,1,2,3,4,5,6,7,8,9];
786  *   var y = [9.2,1.3,7.2,-1.2,4.0,5.3,0.2,6.5,1.1,0.0];
787  *   var graph = board.create('curve', [x,y], {dash:2});
788  * </pre><div id="7dcbb00e-b6ff-481d-b4a8-887f5d8c6a83" style="width: 300px; height: 300px;"></div>
789  * <script type="text/javascript">
790  *   var c3_board = JXG.JSXGraph.initBoard('7dcbb00e-b6ff-481d-b4a8-887f5d8c6a83', {boundingbox: [-1,10,10,-1], axis: true, showcopyright: false, shownavigation: false});
791  *   var x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
792  *   var y = [9.2, 1.3, 7.2, -1.2, 4.0, 5.3, 0.2, 6.5, 1.1, 0.0];
793  *   var graph3 = c3_board.create('curve', [x,y], {dash:2});
794  * </script><pre>
795  * @example
796  * // Polar plot
797  * // Create a curve with the equation r(phi)= a*(1+phi), i.e.
798  * // a cardioid.
799  *   var a = board.create('slider',[[0,2],[2,2],[0,1,2]]);
800  *   var graph = board.create('curve', 
801  *                        [function(phi){ return a.Value()*(1-Math.cos(phi));}, 
802  *                         [1,0], 
803  *                         0, 2*Math.PI]
804  *                     );
805  * </pre><div id="d0bc7a2a-8124-45ca-a6e7-142321a8f8c2" style="width: 300px; height: 300px;"></div>
806  * <script type="text/javascript">
807  *   var c2_board = JXG.JSXGraph.initBoard('d0bc7a2a-8124-45ca-a6e7-142321a8f8c2', {boundingbox: [-3,3,3,-3], axis: true, showcopyright: false, shownavigation: false});
808  *   var a = c2_board.create('slider',[[0,2],[2,2],[0,1,2]]);
809  *   var graph2 = c2_board.create('curve', [function(phi){ return a.Value()*(1-Math.cos(phi));}, [1,0], 0, 2*Math.PI]);
810  * </script><pre>
811  */
812 JXG.createCurve = function(board, parents, attributes) {
813     attributes = JXG.checkAttributes(attributes,{withLabel:JXG.readOption(board.options,'curve','withLabel'), layer:null});
814     return new JXG.Curve(board, ['x'].concat(parents), attributes['id'], attributes['name'],   
815                          attributes['withLabel'],attributes['layer']);
816 };
817 
818 JXG.JSXGraph.registerElement('curve', JXG.createCurve);
819 
820 /**
821  * @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()}
822  * set to x. The graph is drawn for x in the interval [a,b].
823  * @pseudo
824  * @description
825  * @name Functiongraph
826  * @augments JXG.Curve
827  * @constructor
828  * @type JXG.Curve
829  * @param {function_number,function_number,function} f,a_,b_ Parent elements are a function term f(x) describing the function graph. 
830  *         <p>
831  *         Further, an optional number or function for the left interval border a, 
832  *         and an optional number or function for the right interval border b. 
833  *         <p>
834  *         Default values are a=-10 and b=10.
835  * @see JXG.Curve
836  * @example
837  * // Create a function graph for f(x) = 0.5*x*x-2*x
838  *   var graph = board.create('functiongraph', 
839  *                        [function(x){ return 0.5*x*x-2*x;}, -2, 4]
840  *                     );
841  * </pre><div id="efd432b5-23a3-4846-ac5b-b471e668b437" style="width: 300px; height: 300px;"></div>
842  * <script type="text/javascript">
843  *   var alex1_board = JXG.JSXGraph.initBoard('efd432b5-23a3-4846-ac5b-b471e668b437', {boundingbox: [-3, 7, 5, -3], axis: true, showcopyright: false, shownavigation: false});
844  *   var graph = alex1_board.create('functiongraph', [function(x){ return 0.5*x*x-2*x;}, -2, 4]);
845  * </script><pre>
846  * @example
847  * // Create a function graph for f(x) = 0.5*x*x-2*x with variable interval
848  *   var s = board.create('slider',[[0,4],[3,4],[-2,4,5]]);
849  *   var graph = board.create('functiongraph', 
850  *                        [function(x){ return 0.5*x*x-2*x;}, 
851  *                         -2, 
852  *                         function(){return s.Value();}]
853  *                     );
854  * </pre><div id="4a203a84-bde5-4371-ad56-44619690bb50" style="width: 300px; height: 300px;"></div>
855  * <script type="text/javascript">
856  *   var alex2_board = JXG.JSXGraph.initBoard('4a203a84-bde5-4371-ad56-44619690bb50', {boundingbox: [-3, 7, 5, -3], axis: true, showcopyright: false, shownavigation: false});
857  *   var s = alex2_board.create('slider',[[0,4],[3,4],[-2,4,5]]);
858  *   var graph = alex2_board.create('functiongraph', [function(x){ return 0.5*x*x-2*x;}, -2, function(){return s.Value();}]);
859  * </script><pre>
860  */
861 JXG.createFunctiongraph = function(board, parents, attributes) {
862     var par = ["x","x"].concat(parents);
863     attributes = JXG.checkAttributes(attributes,{withLabel:JXG.readOption(board.options,'curve','withLabel'), layer:null});
864     attributes['curveType'] = 'functiongraph';
865     return new JXG.Curve(board, par, attributes['id'], attributes['name'],attributes['withLabel'],attributes['layer']);
866 };
867 
868 JXG.JSXGraph.registerElement('functiongraph', JXG.createFunctiongraph);
869 
870 
871 /**
872  * TODO
873  * Create a dynamic spline interpolated curve given by sample points p_1 to p_n.
874  * @param {JXG.Board} board Reference to the board the spline is drawn on.
875  * @param {Array} parents Array of points the spline interpolates
876  * @param {Object} attributes Define color, width, ... of the spline
877  * @type JXG.Curve
878  * @return Returns reference to an object of type JXG.Curve.
879  */
880 JXG.createSpline = function(board, parents, attributes) {
881     var F;
882     attributes = JXG.checkAttributes(attributes,{withLabel:JXG.readOption(board.options,'curve','withLabel'), layer:null});
883     F = function() {
884         var D, x=[], y=[];
885         
886         var fct = function (t,suspended) {
887             var i, j;
888         
889             if (!suspended) {
890                 x = [];
891                 y = [];
892 
893                 // given as [x[], y[]]
894                 if(parents.length == 2 && JXG.isArray(parents[0]) && JXG.isArray(parents[1]) && parents[0].length == parents[1].length) {
895                     for(i=0; i<parents[0].length; i++) {
896                         if(typeof parents[0][i] == 'function')
897                             x.push(parents[0][i]());
898                         else
899                             x.push(parents[0][i]);
900                         if(typeof parents[1][i] == 'function')
901                             y.push(parents[1][i]());
902                         else
903                             y.push(parents[1][i]);
904                     }
905                 } else {
906                     for(i=0; i<parents.length; i++) {
907                         if(JXG.isPoint(parents[i])) {
908                             //throw new Error("JSXGraph: JXG.createSpline: Parents has to be an array of JXG.Point.");
909                             x.push(parents[i].X());
910                             y.push(parents[i].Y());
911                         } else if (JXG.isArray(parents[i]) && parents[i].length == 2) {     // given as [[x1,y1], [x2, y2], ...]
912                             for(i=0; i<parents.length; i++) {
913                                 if(typeof parents[i][0] == 'function')
914                                     x.push(parents[i][0]());
915                                 else
916                                     x.push(parents[i][0]);
917                                 if(typeof parents[i][1] == 'function')
918                                     y.push(parents[i][1]());
919                                 else
920                                     y.push(parents[i][1]);
921                             }
922                         }
923                     }
924                 }
925         
926                 // The array D has only to be calculated when the position of one or more sample point
927                 // changes. otherwise D is always the same for all points on the spline.
928                 D = JXG.Math.Numerics.splineDef(x, y);
929             }
930             return JXG.Math.Numerics.splineEval(t, x, y, D);
931         };
932         return fct;
933     };
934     return new JXG.Curve(board, ["x","x", F()], attributes["id"], attributes["name"],
935                         attributes['withLabel'],attributes['layer']);
936 };
937 
938 /**
939  * Register the element type spline at JSXGraph
940  * @private
941  */
942 JXG.JSXGraph.registerElement('spline', JXG.createSpline);
943 
944 /**
945  * @class This element is used to provide a constructor for Riemann sums, which is relaized as a special curve. 
946  * @pseudo
947  * @description
948  * @name Riemannsum
949  * @augments JXG.Curve
950  * @constructor
951  * @type JXG.Curve
952  * @param {function_number,function_string,function_function,number_function,number} f,n,type_,a_,b_ Parent elements of Riemannsum are a 
953  *         function term f(x) describing the function graph which is filled by the Riemann rectangles.
954  *         <p>
955  *         n determines the number of rectangles, it is either a fixed number or a function.
956  *         <p>
957  *         type is a string or function returning one of the values:  'left', 'right', 'middle', 'lower', 'upper', or 'trapezodial'.
958  *         Default value is 'left'.
959  *         <p>
960  *         Further parameters are an optional number or function for the left interval border a, 
961  *         and an optional number or function for the right interval border b. 
962  *         <p>
963  *         Default values are a=-10 and b=10.
964  * @see JXG.Curve
965  * @example
966  * // Create Riemann sums for f(x) = 0.5*x*x-2*x.
967  *   var s = board.create('slider',[[0,4],[3,4],[0,4,10]],{snapWidth:1});
968  *   var f = function(x) { return 0.5*x*x-2*x; };
969  *   var r = board.create('riemannsum', 
970  *               [f, function(){return s.Value();}, 'upper', -2, 5],
971  *               {fillOpacity:0.4}
972  *               );
973  *   var g = board.create('functiongraph',[f, -2, 5]);
974  * </pre><div id="940f40cc-2015-420d-9191-c5d83de988cf" style="width: 300px; height: 300px;"></div>
975  * <script type="text/javascript">
976  *   var rs1_board = JXG.JSXGraph.initBoard('940f40cc-2015-420d-9191-c5d83de988cf', {boundingbox: [-3, 7, 5, -3], axis: true, showcopyright: false, shownavigation: false});
977  *   var f = function(x) { return 0.5*x*x-2*x; };
978  *   var s = rs1_board.create('slider',[[0,4],[3,4],[0,4,10]],{snapWidth:1});
979  *   var r = rs1_board.create('riemannsum', [f, function(){return s.Value();}, 'upper', -2, 5], {fillOpacity:0.4});
980  *   var g = rs1_board.create('functiongraph', [f, -2, 5]);
981  * </script><pre>
982  */
983 JXG.createRiemannsum = function(board, parents, attributes) {
984     var n, type, f, par, c;
985     
986     attributes = JXG.checkAttributes(attributes,
987                     {withLabel:JXG.readOption(board.options,'curve','withLabel'),layer:null,fillOpacity:0.3,fillColor:'#ffff00', curveType:'plot'});
988 
989     f = parents[0]; 
990     n = JXG.createFunction(parents[1],board,'');
991     if (n==null) {
992         throw new Error("JSXGraph: JXG.createRiemannsum: argument '2' n has to be number or function." +
993                         "\nPossible parent types: [function,n:number|function,type,start:number|function,end:number|function]");
994     }
995     type = JXG.createFunction(parents[2],board,'',false);
996     if (type==null) {
997         throw new Error("JSXGraph: JXG.createRiemannsum: argument 3 'type' has to be string or function." +
998                         "\nPossible parent types: [function,n:number|function,type,start:number|function,end:number|function]");
999     }
1000 
1001     par = ['x', [0], [0]].concat(parents.slice(3));
1002     /**
1003      * @private
1004      */
1005     c = new JXG.Curve(board, par, attributes['id'], attributes['name'], attributes['withLabel'],attributes['layer']);
1006     /**
1007      * @private
1008      */
1009     c.updateDataArray = function() {
1010             var u = JXG.Math.Numerics.riemann(f,n(),type(),this.minX(),this.maxX());
1011             this.dataX = u[0];
1012             this.dataY = u[1];
1013         };
1014     return c;
1015 };
1016 
1017 JXG.JSXGraph.registerElement('riemannsum', JXG.createRiemannsum);
1018