1 /* 
  2     Copyright 2008,2009
  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 /** 
 28  * @fileoverview JSXGraph can use various technologies to render the contents of a construction, e.g.
 29  * SVG, VML, and HTML5 Canvas. To accomplish this, The rendering and the logic and control mechanisms
 30  * are completely separated from each other. Every rendering technology has it's own class, called
 31  * Renderer, e.g. SVGRenderer for SVG, the same for VML and Canvas. The common base for all available
 32  * renderers is the class AbstractRenderer defined in this file.
 33  */
 34 
 35 /**
 36  * This function returns the AbstractRenderer object which defines the interface between the renderer
 37  * objects and the logical parts of JSXGraph.
 38  * @returns {Object} An instance of the AbstractRenderer class.
 39  * @see JXG.SVGRenderer
 40  * @see JXG.VMLRenderer
 41  * @see JXG.CanvasRenderer
 42  */
 43 JXG.AbstractRenderer = function() {
 44 
 45 return {
 46 	/**
 47 	 * The vertical offset for {@link Text} elements. Every {@link Text} element will
 48 	 * be placed this amount of pixels below the user given coordinates.
 49 	 * @type number
 50 	 * @default 8
 51 	 */
 52     vOffsetText: 8,
 53     
 54     /**
 55      * If this property is set to <tt>true</tt> the visual properties of the elements are updated
 56      * on every update. Visual properties means: All the stuff stored in the
 57      * {@link JXG.GeometryElement#visProp} property won't be set if enhancedRendering is <tt>false</tt>
 58      * @type Boolean
 59      * @default true 
 60      */
 61     enhancedRendering: true,
 62 
 63     /**
 64      * Update visual properties, but only if JXG.AbstractRenderer#enhancedRendering is set to true.
 65      * @param {JXG.GeometryElement} el The element to update
 66      * @param {Object} not Select properties you don't want to be updated: {fill: true, dash: true} updates
 67      * everything except for fill and dash. Possible values are stroke, fill, dash, shadow.
 68      * @param {Boolean} enhanced If true JXG.AbstractRenderer#enhancedRendering is assumed to be true.
 69      */
 70     updateVisual: function(el, not, enhanced) {
 71         not = not || {};
 72         
 73         if (enhanced || this.enhancedRendering) {
 74             if (!el.visProp['draft']) {
 75                 if(!not.stroke) {
 76                     this.setObjectStrokeWidth(el, el.visProp['strokeWidth']);
 77                     this.setObjectStrokeColor(el, el.visProp['strokeColor'], el.visProp['strokeOpacity']);
 78                 }
 79 
 80                 if(!not.fill)
 81                     this.setObjectFillColor(el, el.visProp['fillColor'], el.visProp['fillOpacity']);
 82 
 83                 if(!not.dash)
 84                     this.setDashStyle(el, el.visProp);
 85 
 86                 if(!not.shadow)
 87                     this.setShadow(el);
 88             } else {
 89                 this.setDraft(el);
 90             }
 91         }
 92     },
 93 
 94 
 95     /* ******************************** *
 96      *    Point drawing and updating    *
 97      * ******************************** */
 98 
 99     /**
100      * Draws a point on the {@link JXG.Board}.
101      * @param {JXG.Point} el Reference to a {@link JXG.Point} object that has to be drawn.
102      * @see JXG.Point
103      * @see #updatePoint
104      * @see #changePointStyle
105      */
106     drawPoint: function(el) {
107         var prim,
108             face = JXG.Point.prototype.normalizeFace.call(this, el.visProp['face']);//el.normalizeFace(el.visProp['face']);
109 
110         // determine how the point looks like
111         if (face === 'o') {
112             prim = 'circle';
113         } else if (face === '[]') {
114             prim = 'rect';
115         } else {
116             // cross/x, diamond/<>, triangleup/a/^, triangledown/v, triangleleft/<,
117             // triangleright/>, plus/+,
118             prim = 'path';
119         }
120 
121         this.appendChildPrim(this.createPrim(prim, el.id), el.layer);
122         this.appendNodesToElement(el, prim);
123 
124         // adjust visual propertys
125         this.updateVisual(el, {dash: true, shadow: true}, true);
126 
127         // By now we only created the xml nodes and set some styles, in updatePoint
128         // the attributes are filled with data.
129         this.updatePoint(el);
130     },
131 
132     // continue revision here
133 
134     /**
135      * Updates visual appearance of the renderer element assigned to the given {@link JXG.Point}.
136      * @param {JXG.Point} el Reference to a {@link JXG.Point} object, that has to be updated.
137      * @see JXG.Point
138      * @see #drawPoint
139      * @see #changePointStyle
140      */
141     updatePoint: function(el) {
142         var size = el.visProp['size'],
143             face = JXG.Point.prototype.normalizeFace.call(this, el.visProp['face']);//el.normalizeFace(el.visProp['face']);
144 
145         if (isNaN(el.coords.scrCoords[2]) || isNaN(el.coords.scrCoords[1])) return;
146 
147         this.updateVisual(el, {dash: false, shadow: false});
148 
149         // Zoom does not work for traces.
150         size *= ((!el.board || !el.board.options.point.zoom) ? 1.0 : Math.sqrt(el.board.zoomX * el.board.zoomY));
151 
152         if (face === 'o') { // circle
153             this.updateCirclePrim(el.rendNode, el.coords.scrCoords[1], el.coords.scrCoords[2], size + 1);
154         } else if (face === '[]') { // rectangle
155             this.updateRectPrim(el.rendNode, el.coords.scrCoords[1] - size, el.coords.scrCoords[2] - size, size * 2, size * 2);
156         } else { // x, +, <>, ^, v, <, >
157             this.updatePathPrim(el.rendNode, this.updatePathStringPoint(el, size, face), el.board);
158         }
159         this.setShadow(el);
160     },
161 
162     /**
163      * Changes the style of a {@link JXG.Point}. This is required because the point styles differ in what
164      * elements have to be drawn, e.g. if the point is marked by a "x" or a "+" two lines are drawn, if
165      * it's marked by spot a circle is drawn. This method removes the old renderer element(s) and creates
166      * the new one(s).
167      * @param {JXG.Point} el Reference to a {@link JXG.Point} object, that's style is changed.
168      * @see Point
169      * @see JXG.Point
170      * @see #updatePoint
171      * @see #drawPoint
172      */
173     changePointStyle: function(el) {
174         var node = this.getElementById(el.id);
175 
176         // remove the existing point rendering node
177         if (JXG.exists(node)) {
178             this.remove(node);
179         }
180 
181         // and make a new one
182         this.drawPoint(el);
183         JXG.clearVisPropOld(el);
184 
185         if (!el.visProp['visible']) {
186             this.hide(el);
187         }
188         
189         if (el.visProp['draft']) {
190             this.setDraft(el);
191         }
192     },
193 
194 
195 
196     /* ******************************** *
197      *           Lines                  *
198      * ******************************** */
199 
200     /**
201      * Draws a line on the {@link JXG.Board}.
202      * @param {JXG.Line} el Reference to a line object, that has to be drawn.
203      * @see Line
204      * @see JXG.Line
205      * @see #updateLine
206      * @see #calcStraight
207      */
208     drawLine: function(el) {
209         this.appendChildPrim(this.createPrim('line', el.id), el.layer);
210         this.appendNodesToElement(el, 'lines');
211         this.updateLine(el);
212     },
213 
214     /**
215      * Updates visual appearance of the renderer element assigned to the given {@link JXG.Line}.
216      * @param {JXG.Line} el Reference to the {@link JXG.Line} object that has to be updated.
217      * @see Line
218      * @see JXG.Line
219      * @see #drawLine
220      * @see #calcStraight
221      */
222     updateLine: function(el) {
223         var screenCoords1 = new JXG.Coords(JXG.COORDS_BY_USER, el.point1.coords.usrCoords, el.board),
224             screenCoords2 = new JXG.Coords(JXG.COORDS_BY_USER, el.point2.coords.usrCoords, el.board),
225             ax, ay, bx, by, beta, x, y;
226 
227         // a line can be a segment, straight, or ray. so it's not always delimited by point1 and point2
228         // calcstraight calculates the visual start point and end point of the line.
229         this.calcStraight(el, screenCoords1, screenCoords2);
230         this.updateLinePrim(el.rendNode, screenCoords1.scrCoords[1], screenCoords1.scrCoords[2],
231                                          screenCoords2.scrCoords[1], screenCoords2.scrCoords[2], el.board);
232         // if this line has arrows attached, update them, too.
233         this.makeArrows(el);
234         this.updateVisual(el, {fill: true});
235     },
236 
237     /**
238      * Calculates drawing start and end point for a line. A segment is only drawn from start to end point, a straight line
239      * is drawn until it meets the boards boundaries.
240      * @param {JXG.Line} el Reference to a line object, that needs calculation of start and end point.
241      * @param {JXG.Coords} point1 Coordinates of the point where line drawing begins. This value is calculated and set by this method.
242      * @param {JXG.Coords} point2 Coordinates of the point where line drawing ends. This value is calculated and set by this method.
243      * @see Line
244      * @see JXG.Line
245      * @see #drawLine
246      * @see #updateLine
247      */
248     calcStraight: function(el, point1, point2) {
249         var takePoint1, takePoint2, intersect1, intersect2, straightFirst, straightLast,
250             c, s, i, j, p1, p2;
251 
252         straightFirst = el.visProp['straightFirst'];
253         straightLast = el.visProp['straightLast'];
254 
255         // If one of the point is an ideal point in homogeneous coordinates
256         // drawing of line segments or rays are not possible.
257         if (Math.abs(point1.scrCoords[0]) < JXG.Math.eps) {
258             straightFirst = true;
259         }
260         if (Math.abs(point2.scrCoords[0]) < JXG.Math.eps) {
261             straightLast = true;
262         }
263 
264         if (!straightFirst && !straightLast) {  // Do nothing in case of line segments (inside or outside of the board)
265             return;
266         }
267 
268         // Compute the stdform of the line in screen coordinates.
269         c = [];
270         c[0] = el.stdform[0] -
271                 el.stdform[1] * el.board.origin.scrCoords[1] / el.board.stretchX +
272                 el.stdform[2] * el.board.origin.scrCoords[2] / el.board.stretchY;
273         c[1] = el.stdform[1] / el.board.stretchX;
274         c[2] = el.stdform[2] / (-el.board.stretchY);
275 
276         if (isNaN(c[0] + c[1] + c[2])) return; // p1=p2
277 
278         // Intersect the line with the four borders of the board.
279         s = [];
280         s[0] = JXG.Math.crossProduct(c, [0,0,1]);  // top
281         s[1] = JXG.Math.crossProduct(c, [0,1,0]);  // left
282         s[2] = JXG.Math.crossProduct(c, [-el.board.canvasHeight,0,1]);  // bottom
283         s[3] = JXG.Math.crossProduct(c, [-el.board.canvasWidth,1,0]);   // right
284 
285         // Normalize the intersections
286         for (i = 0; i < 4; i++) {
287             if (Math.abs(s[i][0]) > JXG.Math.eps) {
288                 for (j = 2; j > 0; j--) {
289                     s[i][j] /= s[i][0];
290                 }
291                 s[i][0] = 1.0;
292             }
293         }
294 
295         takePoint1 = false;
296         takePoint2 = false;
297         if (!straightFirst && // Line starts at point1 and point2 is inside the board
298                 point1.scrCoords[1] >= 0.0 && point1.scrCoords[1] <= el.board.canvasWidth &&
299                 point1.scrCoords[2] >= 0.0 && point1.scrCoords[2] <= el.board.canvasHeight) {
300             takePoint1 = true;
301         }
302         if (!straightLast && // Line ends at point2 and point2 is inside the board
303                 point2.scrCoords[1] >= 0.0 && point2.scrCoords[1] <= el.board.canvasWidth &&
304                 point2.scrCoords[2] >= 0.0 && point2.scrCoords[2] <= el.board.canvasHeight) {
305             takePoint2 = true;
306         }
307 
308         if (Math.abs(s[1][0]) < JXG.Math.eps) {           // line is parallel to "left", take "top" and "bottom"
309             intersect1 = s[0];                          // top
310             intersect2 = s[2];                          // bottom
311         } else if (Math.abs(s[0][0]) < JXG.Math.eps) {           // line is parallel to "top", take "left" and "right"
312             intersect1 = s[1];                          // left
313             intersect2 = s[3];                          // right
314         } else if (s[1][2] < 0) {                         // left intersection out of board (above)
315             intersect1 = s[0];                          // top
316             if (s[3][2] > el.board.canvasHeight) {        // right intersection out of board (below)
317                 intersect2 = s[2];                      // bottom
318             } else {
319                 intersect2 = s[3];                      // right
320             }
321         } else if (s[1][2] > el.board.canvasHeight) {     // left intersection out of board (below)
322             intersect1 = s[2];                          // bottom
323             if (s[3][2] < 0) {                            // right intersection out of board (above)
324                 intersect2 = s[0];                      // top
325             } else {
326                 intersect2 = s[3];                      // right
327             }
328         } else {
329             intersect1 = s[1];                          // left
330             if (s[3][2] < 0) {                            // right intersection out of board (above)
331                 intersect2 = s[0];                      // top
332             } else if (s[3][2] > el.board.canvasHeight) { // right intersection out of board (below)
333                 intersect2 = s[2];                      // bottom
334             } else {
335                 intersect2 = s[3];                      // right
336             }
337         }
338 
339         intersect1 = new JXG.Coords(JXG.COORDS_BY_SCREEN, intersect1.slice(1), el.board);
340         intersect2 = new JXG.Coords(JXG.COORDS_BY_SCREEN, intersect2.slice(1), el.board);
341 
342         if (!takePoint1 && !takePoint2) {              // If both points are outside and the complete ray is outside we do nothing
343             if (!straightFirst && straightLast && // Ray starting at point 1
344                     !this.isSameDirection(point1, point2, intersect1) && !this.isSameDirection(point1, point2, intersect2)) {
345                 return;
346             } else if (straightFirst && !straightLast && // Ray starting at point 2
347                     !this.isSameDirection(point2, point1, intersect1) && !this.isSameDirection(point2, point1, intersect2)) {
348                 return;
349             }
350         }
351 
352         if (!takePoint1) {
353             if (!takePoint2) {                // Two border intersection points are used
354                 if (this.isSameDirection(point1, point2, intersect1)) {
355                     if (!this.isSameDirection(point1, point2, intersect2)) {
356                         p2 = intersect1;
357                         p1 = intersect2;
358                     } else {
359                         if (JXG.Math.Geometry.affineDistance(point2.usrCoords, intersect1.usrCoords) < JXG.Math.Geometry.affineDistance(point2.usrCoords, intersect2.usrCoords)) {
360                             p1 = intersect1;
361                             p2 = intersect2;
362                         } else {
363                             p2 = intersect1;
364                             p1 = intersect2;
365                         }
366                     }
367                 } else {
368                     if (this.isSameDirection(point1, point2, intersect2)) {
369                         p1 = intersect1;
370                         p2 = intersect2;
371                     } else {
372                         if (JXG.Math.Geometry.affineDistance(point2.usrCoords, intersect1.usrCoords) < JXG.Math.Geometry.affineDistance(point2.usrCoords, intersect2.usrCoords)) {
373                             p2 = intersect1;
374                             p1 = intersect2;
375                         } else {
376                             p1 = intersect1;
377                             p2 = intersect2;
378                         }
379                     }
380                 }
381             } else {                          // Instead of point1 the border intersection is taken
382                 if (this.isSameDirection(point2, point1, intersect1)) {
383                     p1 = intersect1;
384                 } else {
385                     p1 = intersect2;
386                 }
387             }
388         } else {
389             if (!takePoint2) {                // Instead of point2 the border intersection is taken
390                 if (this.isSameDirection(point1, point2, intersect1)) {
391                     p2 = intersect1;
392                 } else {
393                     p2 = intersect2;
394                 }
395             }
396         }
397 
398         if (p1) point1.setCoordinates(JXG.COORDS_BY_USER, p1.usrCoords.slice(1));
399         if (p2) point2.setCoordinates(JXG.COORDS_BY_USER, p2.usrCoords.slice(1));
400     },
401 
402     /**
403      * If you're looking from point "start" towards point "s" and can see the point "p", true is returned. Otherwise false.
404      * @param {JXG.Coords} start The point you're standing on.
405      * @param {JXG.Coords} p The point in which direction you're looking.
406      * @param {JXG.Coords} s The point that should be visible.
407      * @returns {Boolean} True, if from start the point p is in the same direction as s is, that means s-start = k*(p-start) with k>=0.
408      */
409     isSameDirection: function(start, p, s) {
410         var dx, dy, sx, sy, r = false;
411 
412         dx = p.usrCoords[1] - start.usrCoords[1];
413         dy = p.usrCoords[2] - start.usrCoords[2];
414 
415         sx = s.usrCoords[1] - start.usrCoords[1];
416         sy = s.usrCoords[2] - start.usrCoords[2];
417 
418         if (Math.abs(dx) < JXG.Math.eps) dx = 0;
419         if (Math.abs(dy) < JXG.Math.eps) dy = 0;
420         if (Math.abs(sx) < JXG.Math.eps) sx = 0;
421         if (Math.abs(sy) < JXG.Math.eps) sy = 0;
422 
423         if (dx >= 0 && sx >= 0) {
424             if ((dy >= 0 && sy >= 0) || (dy <= 0 && sy <= 0)) {
425                 r = true;
426             }
427         } else if (dx <= 0 && sx <= 0) {
428             if ((dy >= 0 && sy >= 0) || (dy <= 0 && sy <= 0)) {
429                 r = true;
430             }
431         }
432 
433         return r;
434     },
435 
436     /**
437      * Update {@link Ticks} on a {@link JXG.Line}. This method is only a stub and is implemented only in the special renderers.
438      * @param {JXG.Line} axis Reference of an line object, thats ticks have to be updated.
439      * @param {Number} dxMaj Number of pixels a major tick counts in x direction.
440      * @param {Number} dyMaj Number of pixels a major tick counts in y direction.
441      * @param {Number} dxMin Number of pixels a minor tick counts in x direction.
442      * @param {Number} dyMin Number of pixels a minor tick counts in y direction.
443      * @see Line
444      * @see Ticks
445      * @see JXG.Line
446      * @see JXG.Ticks
447      * @see #removeTicks
448      */
449     updateTicks: function(axis, dxMaj, dyMaj, dxMin, dyMin) {
450     },
451 
452     /**
453      * Removes all ticks from an {@link JXG.Line}.
454      * @param {JXG.Line} axis Reference of an {@link JXG.Line} object, that's ticks have to be removed.
455      * @deprecated
456      * @see Line
457      * @see Ticks
458      * @see Axis
459      * @see JXG.Line
460      * @see JXG.Ticks
461      */
462     removeTicks: function(axis) {
463         this.remove(this.getElementById(axis.id + '_ticks'));
464     },
465 
466     /* **************************
467      *    Curves
468      * **************************/
469 
470     /**
471      * Draws a {@link JXG.Curve} on the {@link JXG.Board}.
472      * @param {JXG.Curve} el Reference to a graph object, that has to be plotted.
473      * @see Curve
474      * @see JXG.Curve
475      * @see #updateCurve
476      */
477     drawCurve: function(el) {
478         this.appendChildPrim(this.createPrim('path', el.id), el.layer);
479         this.appendNodesToElement(el, 'path');
480         this.updateVisual(el, {shadow: true}, true);
481         this.updateCurve(el);
482     },
483 
484     /**
485      * Updates visual appearance of the renderer element assigned to the given {@link JXG.Curve}.
486      * @param {JXG.Curve} el Reference to a {@link JXG.Curve} object, that has to be updated.
487      * @see Curve
488      * @see JXG.Curve
489      * @see #drawCurve
490      */
491     updateCurve: function(el) {
492         this.updateVisual(el);
493         this.updatePathPrim(el.rendNode, this.updatePathStringPrim(el), el.board);
494         this.makeArrows(el);
495     },
496 
497 
498     /* **************************
499      *    Circle related stuff
500      * **************************/
501 
502     /**
503      * Draws a {@link JXG.Circle} on the {@link JXG.Board}.
504      * @param {JXG.Circle} el Reference to a {@link JXG.Circle} object, that has to be drawn.
505      * @see Circle
506      * @see JXG.Circle
507      * @see #updateCircle
508      */
509     drawCircle: function(el) {
510         this.appendChildPrim(this.createPrim('ellipse', el.id), el.layer);
511         this.appendNodesToElement(el, 'ellipse');
512         this.updateCircle(el);
513     },
514 
515     /**
516      * Updates visual appearance of a given {@link JXG.Circle} on the {@link JXG.Board}.
517      * @param {JXG.Circle} el Reference to a {@link JXG.Circle} object, that has to be updated.
518      * @see Circle
519      * @see JXG.Circle
520      * @see #drawCircle
521      */
522     updateCircle: function(el) {
523         this.updateVisual(el);
524 
525         // Radius umrechnen:
526         var radius = el.Radius();
527         if (radius > 0.0 && !isNaN(radius + el.midpoint.coords.scrCoords[1] + el.midpoint.coords.scrCoords[2]) && radius*el.board.stretchX<20000) {
528             this.updateEllipsePrim(el.rendNode, el.midpoint.coords.scrCoords[1], el.midpoint.coords.scrCoords[2],
529                     (radius * el.board.stretchX), (radius * el.board.stretchY));
530         }
531     },
532 
533 
534     /* **************************
535      *   Polygon related stuff
536      * **************************/
537 
538     /**
539      * Draws a {@link JXG.Polygon} on the {@link JXG.Board}.
540      * @param {JXG.Polygon} el Reference to a Polygon object, that is to be drawn.
541      * @see Polygon
542      * @see JXG.Polygon
543      * @see #updatePolygon
544      */
545     drawPolygon: function(el) {
546         this.appendChildPrim(this.createPrim('polygon', el.id), el.layer);
547         this.appendNodesToElement(el, 'polygon');
548         this.updatePolygon(el);
549     },
550 
551     /**
552      * Updates properties of a {@link JXG.Polygon}'s rendering node.
553      * @param {JXG.Polygon} el Reference to a {@link JXG.Polygon} object, that has to be updated.
554      * @see Polygon
555      * @see JXG.Polygon
556      * @see #drawPolygon
557      */
558     updatePolygon: function(el) {
559         // here originally strokecolor wasn't updated but strokewidth was
560         // but if there's no strokecolor i don't see why we should update strokewidth.
561         this.updateVisual(el, {stroke: true, dash: true});
562         this.updatePolygonPrim(el.rendNode, el);
563     },
564 
565     /* **************************
566      *    Text related stuff
567      * **************************/
568 
569     /**
570      * Displays a {@link JXG.Text} on the {@link JXG.Board} by putting a HTML div over it.
571      * @param {JXG.Text} el Reference to an {@link JXG.Text} object, that has to be displayed
572      * @see Text
573      * @see JXG.Text
574      * @see #drawInternalText
575      * @see #updateText
576      * @see #updateInternalText
577      * @see #updateTextStyle
578      */
579     drawText: function(el) {
580         var node;
581         
582         if (el.display == 'html') {
583             node = this.container.ownerDocument.createElement('div');
584             node.style.position = 'absolute';
585             node.style.color = el.visProp['strokeColor'];
586             node.className = 'JXGtext';
587             node.style.zIndex = '10';
588             this.container.appendChild(node);
589             node.setAttribute('id', this.container.id + '_' + el.id);
590         } else {
591             node = this.drawInternalText(el);
592         }
593         node.style.fontSize = el.board.options.text.fontSize + 'px';
594         el.rendNode = node;
595         el.htmlStr = '';
596         this.updateText(el);
597     },
598 
599     /**
600      * An internal text is a {@link JXG.Text} element which is drawn using only
601      * the given renderer but no HTML. This method is only a stub, the drawing
602      * is done in the special renderers.
603      * @param {JXG.Text} el Reference to a {@link JXG.Text} object
604      * @see Text
605      * @see JXG.Text
606      * @see #updateInternalText
607      * @see #drawText
608      * @see #updateText
609      * @see #updateTextStyle
610      */
611     drawInternalText: function(el) {
612     },
613 
614 
615     /**
616      * Updates visual properties of an already existing {@link JXG.Text} element.
617      * @param {JXG.Text} el Reference to an {@link JXG.Text} object, that has to be updated.
618      * @see Text
619      * @see JXG.Text
620      * @see #drawText
621      * @see #drawInternalText
622      * @see #updateInternalText
623      * @see #updateTextStyle
624      */
625     updateText: function(el) {
626         // Update only objects that are visible.
627         if (!el.visProp['visible']) return;
628         if (isNaN(el.coords.scrCoords[1] + el.coords.scrCoords[2])) return;
629 
630         this.updateTextStyle(el);
631         if (el.display == 'html') {
632             el.rendNode.style.left = (el.coords.scrCoords[1]) + 'px';
633             el.rendNode.style.top = (el.coords.scrCoords[2] - this.vOffsetText) + 'px';
634             el.updateText();
635             if (el.htmlStr != el.plaintextStr) {
636                 el.rendNode.innerHTML = el.plaintextStr;
637                 if (el.board.options.text.useASCIIMathML) {
638                     AMprocessNode(el.rendNode, false);
639                 }
640                 el.htmlStr = el.plaintextStr;
641                 if (el.board.options.text.useMathJax) {
642                     MathJax.Hub.Typeset(el.rendNode);
643                 }
644             }
645             this.transformImage(el, el.transformations);
646         } else {
647             this.updateInternalText(el);
648         }
649     },
650 
651     /**
652      * Updates visual properties of an already existing {@link JXG.Text} element.
653      * @param {JXG.Text} el Reference to an {@link JXG.Text} object, that has to be updated.
654      * @see Text
655      * @see JXG.Text
656      * @see #drawInternalText
657      * @see #drawText
658      * @see #updateText
659      * @see #updateTextStyle
660      */
661     updateInternalText: function(el) {
662     },
663 
664     /**
665      * Updates CSS style properties of a {@link JXG.Text} node.
666      * @param {JXG.Text} el Reference to the {@link JXG.Text} object, that has to be updated.
667      * @see Text
668      * @see JXG.Text
669      * @see #drawText
670      * @see #drawInternalText
671      * @see #updateText
672      * @see #updateInternalText
673      */
674     updateTextStyle: function(el) {
675         var fs;
676         
677         if (el.visProp['fontSize']) {
678             if (typeof el.visProp['fontSize'] == 'function') {
679                 fs = el.visProp['fontSize']();
680                 el.rendNode.style.fontSize = (fs > 0 ? fs : 0);
681             } else {
682                 el.rendNode.style.fontSize = (el.visProp['fontSize']);
683             }
684         }
685     },
686 
687     /* **************************
688      *    Image related stuff
689      * **************************/
690 
691     /**
692      * Draws an {@link JXG.Image} on the {@link JXG.Board}; This is just a template, has to be implemented by special renderers.
693      * @param {JXG.Image} el Reference to an image object, that has to be drawn.
694      * @see Image
695      * @see JXG.Image
696      * @see #updateImage
697      */
698     drawImage: function(el) {
699     },
700 
701     /**
702      * If the URL of the image is proveided by a function, i.e. dynamic URL
703      * the URL has to be updated during updateImage()
704      * @param {JXG.Image} el Reference to an image object.
705      * @see #updateImage
706      */
707     updateImageURL: function(el) {
708     },
709 
710     /**
711      * Updates the properties of an {@link JXG.Image} element.
712      * @param {JXG.Image} el Reference to an {@link JXG.Image} object, that has to be updated.
713      * @see JXG.Image
714      * @see #drawImage
715      */
716     updateImage: function(el) {
717         this.updateRectPrim(el.rendNode, el.coords.scrCoords[1], el.coords.scrCoords[2] - el.size[1],
718                 el.size[0], el.size[1]);
719          
720         this.updateImageURL(el);
721         this.transformImage(el, el.transformations);
722         this.updateVisual(el, {stroke: true, dash: true}, true);
723     },
724     
725     /**
726     * Multiplication of transformations without updating.
727     * That means, at that point it is expected that the matrices
728     * contain numbers only.
729     * First, the origin in user coords is translated to (0,0) in screen coords.
730     * Then, the stretch factors are divided out.
731     * After the transformations in user coords, the  strech factors are multiplied in again,
732     * and the origin in user coords is translated back to its position.
733     * @see #transformImage
734     */
735     joinTransforms: function(el,t) {
736         var m = [[1,0,0],[0,1,0],[0,0,1]], 
737             mpre1 =  [[1, 0, 0], [-el.board.origin.scrCoords[1], 1, 0], [-el.board.origin.scrCoords[2], 0, 1]], 
738             mpre2 =  [[1, 0, 0], [0, 1/el.board.stretchX, 0], [0, 0, -1/el.board.stretchY]],
739             mpost2 = [[1, 0, 0], [0, el.board.stretchX, 0], [0, 0, -el.board.stretchY]],
740             mpost1 = [[1, 0, 0], [el.board.origin.scrCoords[1], 1, 0], [el.board.origin.scrCoords[2], 0, 1]],
741             i, len = t.length;
742             
743         for (i=0;i<len;i++) {
744             m = JXG.Math.matMatMult(mpre1,m);
745             m = JXG.Math.matMatMult(mpre2,m);
746             m = JXG.Math.matMatMult(t[i].matrix,m);
747             m = JXG.Math.matMatMult(mpost2,m);
748             m = JXG.Math.matMatMult(mpost1,m);
749         }
750         return m;
751     },
752     
753 
754     /* **************************
755      *    Grid stuff
756      * **************************/
757 
758     /**
759      * Creates a grid on the board, i.e. light helper lines to support the user on creating and manipulating a construction.
760      * @param {JXG.Board} board Board on which the grid is drawn.
761      * @see #removeGrid
762      */
763     drawGrid: function(board) {
764         var gridX = board.options.grid.gridX,
765             gridY = board.options.grid.gridY,
766             k = new JXG.Coords(JXG.COORDS_BY_SCREEN, [0,0], board),
767             k2 = new JXG.Coords(JXG.COORDS_BY_SCREEN, [board.canvasWidth, board.canvasHeight], board),
768             tmp = Math.ceil(k.usrCoords[1]),
769             j = 0,
770             i, j2, l, l2,
771             gx, gy, topLeft, bottomRight, node2, el;
772 
773         board.options.grid.hasGrid = true;
774 
775         for (i = 0; i <= gridX + 1; i++) {
776             if (tmp - i / gridX < k.usrCoords[1]) {
777                 j = i - 1;
778                 break;
779             }
780         }
781 
782         tmp = Math.floor(k2.usrCoords[1]);
783         j2 = 0;
784         for (i = 0; i <= gridX + 1; i++) {
785             if (tmp + i / gridX > k2.usrCoords[1]) {
786                 j2 = i - 1;
787                 break;
788             }
789         }
790 
791         tmp = Math.ceil(k2.usrCoords[2]);
792         l2 = 0;
793         for (i = 0; i <= gridY + 1; i++) {
794             if (tmp - i / gridY < k2.usrCoords[2]) {
795                 l2 = i - 1;
796                 break;
797             }
798         }
799 
800         tmp = Math.floor(k.usrCoords[2]);
801         l = 0;
802         for (i = 0; i <= gridY + 1; i++) {
803             if (tmp + i / gridY > k.usrCoords[2]) {
804                 l = i - 1;
805                 break;
806             }
807         }
808 
809         gx = Math.round((1.0 / gridX) * board.stretchX);
810         gy = Math.round((1.0 / gridY) * board.stretchY);
811 
812         topLeft = new JXG.Coords(JXG.COORDS_BY_USER,
813                 [Math.ceil(k.usrCoords[1]) - j / gridX, Math.floor(k.usrCoords[2]) + l / gridY],
814                 board);
815         bottomRight = new JXG.Coords(JXG.COORDS_BY_USER,
816                 [Math.floor(k2.usrCoords[1]) + j2 / gridX, Math.ceil(k2.usrCoords[2]) - l2 / gridY],
817                 board);
818 
819         node2 = this.drawVerticalGrid(topLeft, bottomRight, gx, board);
820         this.appendChildPrim(node2, board.options.layer['grid']);
821         if (!board.options.grid.snapToGrid) {
822             el = new Object();
823             el.visProp = {};
824             el.rendNode = node2;
825             el.elementClass = JXG.OBJECT_CLASS_LINE;
826             el.id = "gridx";
827             JXG.clearVisPropOld(el);
828             this.setObjectStrokeColor(el, board.options.grid.gridColor, board.options.grid.gridOpacity);
829         }
830         else {
831             el = new Object();
832             el.visProp = {};
833             el.rendNode = node2;
834             el.elementClass = JXG.OBJECT_CLASS_LINE;
835             el.id = "gridx";
836             JXG.clearVisPropOld(el);
837             this.setObjectStrokeColor(el, '#FF8080', 0.5); //board.gridOpacity);
838         }
839         this.setPropertyPrim(node2, 'stroke-width', '0.4px');
840         if (board.options.grid.gridDash) {
841             this.setGridDash("gridx");
842         }
843 
844         node2 = this.drawHorizontalGrid(topLeft, bottomRight, gy, board);
845         this.appendChildPrim(node2, board.options.layer['grid']); // Attention layer=1
846         if (!board.options.grid.snapToGrid) {
847             el = new Object();
848             el.visProp = {};
849             el.rendNode = node2;
850             el.elementClass = JXG.OBJECT_CLASS_LINE;
851             el.id = "gridy";
852             JXG.clearVisPropOld(el);
853             this.setObjectStrokeColor(el, board.options.grid.gridColor, board.options.grid.gridOpacity);
854         }
855         else {
856             el = new Object();
857             el.visProp = {};
858             el.rendNode = node2;
859             el.elementClass = JXG.OBJECT_CLASS_LINE;
860             el.id = "gridy";
861             JXG.clearVisPropOld(el);
862             this.setObjectStrokeColor(el, '#FF8080', 0.5); //board.gridOpacity);
863         }
864         this.setPropertyPrim(node2, 'stroke-width', '0.4px');
865         if (board.options.grid.gridDash) {
866             this.setGridDash("gridy");
867         }
868 
869     },
870 
871     /**
872      * Remove the grid from the given board.
873      * @param {JXG.Board} board Board from which the grid is removed.
874      * @see #drawGrid
875      */
876     removeGrid: function(board) {
877         // var getElementById = this.getElementById; // Does not work, because 
878                                                      // this in getElementById would point to "window"
879 
880         this.remove(this.getElementById('gridx'));
881         this.remove(this.getElementById('gridy'));
882 
883         board.options.grid.hasGrid = false;
884     },
885 
886 
887     /* **************************
888      *  general element helpers
889      * **************************/
890 
891     /**
892      * Hides an element on the canvas; Only a stub, requires implementation in the derived renderer.
893      * @param {JXG.GeometryElement} obj Reference to the geometry element that has to disappear.
894      * @see #show
895      */
896     hide: function(obj) {
897     },
898 
899     /**
900      * Shows a hidden element on the canvas; Only a stub, requires implementation in the derived renderer.
901      * @param {JXG.GeometryElement} obj Reference to the object that has to appear.
902      * @see #hide
903      */
904     show: function(obj) {
905     },
906 
907     /**
908      * Sets an element's stroke width.
909      * @param {JXG.GeometryElement} el Reference to the geometry element.
910      * @param {Number} width The new stroke width to be assigned to the element.
911      */
912     setObjectStrokeWidth: function(el, width) {
913     },
914 
915     /**
916      * Changes an objects stroke color to the given color.
917      * @param {JXG.GeometryElement} obj Reference of the {@link JXG.GeometryElement} that gets a new stroke color.
918      * @param {String} color Color in a HTML/CSS compatible format, e.g. <strong>#00ff00</strong> or <strong>green</strong> for green.
919      * @param {Number} opacity Opacity of the fill color. Must be between 0 and 1.
920      */
921     setObjectStrokeColor: function(obj, color, opacity) {
922     },
923 
924     /**
925      * Sets an objects fill color.
926      * @param {JXG.GeometryElement} obj Reference of the object that wants a new fill color.
927      * @param {String} color Color in a HTML/CSS compatible format. If you don't want any fill color at all, choose 'none'.
928      * @param {Number} opacity Opacity of the fill color. Must be between 0 and 1.
929      */
930     setObjectFillColor: function(obj, color, opacity) {
931     },
932 
933     /**
934      * Puts an object into draft mode, i.e. it's visual appearance will be changed. For GEONE<sub>x</sub>T backwards compatibility.
935      * @param {JXG.GeometryElement} obj Reference of the object that is in draft mode.
936      */
937     setDraft: function (obj) {
938         if (!obj.visProp['draft']) {
939             return;
940         }
941         var draftColor = obj.board.options.elements.draft.color,
942             draftOpacity = obj.board.options.elements.draft.opacity;
943 
944         if (obj.type == JXG.OBJECT_TYPE_POLYGON) {
945             this.setObjectFillColor(obj, draftColor, draftOpacity);
946         }
947         else {
948             if (obj.elementClass == JXG.OBJECT_CLASS_POINT) {
949                 this.setObjectFillColor(obj, draftColor, draftOpacity);
950             }
951             else {
952                 this.setObjectFillColor(obj, 'none', 0);
953             }
954             this.setObjectStrokeColor(obj, draftColor, draftOpacity);
955             this.setObjectStrokeWidth(obj, obj.board.options.elements.draft.strokeWidth);
956         }
957     },
958 
959     /**
960      * Puts an object from draft mode back into normal mode.
961      * @param {JXG.GeometryElement} obj Reference of the object that no longer is in draft mode.
962      */
963     removeDraft: function (obj) {
964         if (obj.type == JXG.OBJECT_TYPE_POLYGON) {
965             this.setObjectFillColor(obj, obj.visProp['fillColor'], obj.visProp['fillColorOpacity']);
966         }
967         else {
968             if (obj.type == JXG.OBJECT_CLASS_POINT) {
969                 this.setObjectFillColor(obj, obj.visProp['fillColor'], obj.visProp['fillColorOpacity']);
970             }
971             this.setObjectStrokeColor(obj, obj.visProp['strokeColor'], obj.visProp['strokeColorOpacity']);
972             this.setObjectStrokeWidth(obj, obj.visProp['strokeWidth']);
973         }
974     },
975 
976     /**
977      * Highlights an object, i.e. changes the current colors of the object to its highlighting colors
978      * @param {JXG.GeometryElement} obj Reference of the object that will be highlighted.
979      */
980     highlight: function(obj) {
981         var i;
982 
983         if (!obj.visProp['draft']) {
984             if (obj.type == JXG.OBJECT_CLASS_POINT) {
985                 this.setObjectStrokeColor(obj, obj.visProp['highlightStrokeColor'], obj.visProp['highlightStrokeOpacity']);
986                 this.setObjectFillColor(obj, obj.visProp['highlightStrokeColor'], obj.visProp['highlightStrokeOpacity']);
987             }
988             else if (obj.type == JXG.OBJECT_TYPE_POLYGON) {
989                 this.setObjectFillColor(obj, obj.visProp['highlightFillColor'], obj.visProp['highlightFillOpacity']);
990                 for (i = 0; i < obj.borders.length; i++) {
991                     this.setObjectStrokeColor(obj.borders[i], obj.borders[i].visProp['highlightStrokeColor'], obj.visProp['highlightStrokeOpacity']);
992                 }
993             }
994             else {
995                 this.setObjectStrokeColor(obj, obj.visProp['highlightStrokeColor'], obj.visProp['highlightStrokeOpacity']);
996                 this.setObjectFillColor(obj, obj.visProp['highlightFillColor'], obj.visProp['highlightFillOpacity']);
997             }
998             if (obj.visProp['highlightStrokeWidth']) {
999                 this.setObjectStrokeWidth(obj, obj.visProp['highlightStrokeWidth']);
1000             }
1001         }
1002     },
1003 
1004     /**
1005      * Uses the "normal" colors of an object, i.e. the opposite of {@link #highlight}.
1006      * @param {JXG.GeometryElement} obj Reference of the object that will get its normal colors.
1007      */
1008     noHighlight: function(obj) {
1009         var i;
1010         
1011         if (!obj.visProp['draft']) {
1012             if (obj.type == JXG.OBJECT_CLASS_POINT) {
1013                 this.setObjectStrokeColor(obj, obj.visProp['strokeColor'], obj.visProp['strokeOpacity']);
1014                 this.setObjectFillColor(obj, obj.visProp['strokeColor'], obj.visProp['strokeOpacity']);
1015             }
1016             else if (obj.type == JXG.OBJECT_TYPE_POLYGON) {
1017                 this.setObjectFillColor(obj, obj.visProp['fillColor'], obj.visProp['fillOpacity']);
1018                 for (i = 0; i < obj.borders.length; i++) {
1019                     this.setObjectStrokeColor(obj.borders[i], obj.borders[i].visProp['strokeColor'], obj.visProp['strokeOpacity']);
1020                 }
1021             }
1022             else {
1023                 this.setObjectStrokeColor(obj, obj.visProp['strokeColor'], obj.visProp['strokeOpacity']);
1024                 this.setObjectFillColor(obj, obj.visProp['fillColor'], obj.visProp['fillOpacity']);
1025             }
1026             this.setObjectStrokeWidth(obj, obj.visProp['strokeWidth']);
1027         }
1028     },
1029 
1030     /**
1031      * Removes an HTML-Element from Canvas. Just a stub.
1032      * @param {HTMLElement} node The HTMLElement to remove.
1033      */
1034     remove: function(node) {
1035     },
1036 
1037 
1038     /* **************************
1039      * general renderer related methods
1040      * **************************/
1041 
1042     /**
1043      * Stop redraw. This method is called before every update, so a non-vector-graphics based renderer
1044      * can delete the contents of the drawing panel.
1045      * @see #unsuspendRedraw
1046      */
1047     suspendRedraw: function() {
1048     },
1049 
1050     /**
1051      * Restart redraw. This method is called after updating all the rendering node attributes.
1052      * @see #suspendRedraw
1053      */
1054     unsuspendRedraw: function() {
1055     },
1056 
1057     /**
1058      * The tiny zoom bar shown on the bottom of a board (if {@link JXG.Board#showNavigation} is true).
1059      * @param {JXG.Board} board Reference to a JSXGraph board.
1060      */
1061     drawZoomBar: function(board) {
1062         var doc,
1063             node,
1064             createButton = function(label, handler) {
1065                 var button;
1066 
1067                 button = doc.createElement('span');
1068                 node.appendChild(button);
1069                 button.innerHTML = label;
1070                 JXG.addEvent(button, 'click', handler, board);
1071             };
1072 
1073         doc = this.container.ownerDocument;
1074         node = doc.createElement('div');
1075 
1076         node.setAttribute('id', this.container.id + '_navigationbar');
1077         node.className = 'JXGtext';
1078         node.style.color = '#aaaaaa';
1079         node.style.backgroundColor = '#f5f5f5';
1080         node.style.padding = '2px';
1081         node.style.position = 'absolute';
1082         node.style.fontSize = '10px';
1083         node.style.cursor = 'pointer';
1084         node.style.zIndex = '100';
1085         this.container.appendChild(node);
1086         node.style.right = '5px'; //(board.canvasWidth-100)+ 'px';
1087         node.style.bottom = '5px';
1088 
1089         createButton(' – ', board.zoomOut);
1090         createButton(' o ', board.zoom100);
1091         createButton(' + ', board.zoomIn);
1092         createButton(' ← ', board.clickLeftArrow);
1093         createButton(' ↑ ', board.clickUpArrow);
1094         createButton(' ↓ ', board.clickDownArrow);
1095         createButton(' → ', board.clickRightArrow);
1096     },
1097 
1098     /**
1099      * Wrapper for getElementById for maybe other renderers which elements are not directly accessible by DOM methods like document.getElementById().
1100      * @param {String} id Unique identifier for element.
1101      * @returns {Object} Reference to a JavaScript object. In case of SVG/VMLRenderer it's a reference to a SVG/VML node.
1102      */
1103     getElementById: function(id) {
1104         return document.getElementById(this.container.id + '_' + id);
1105     },
1106 
1107     /**
1108      * findSplit() is a subroutine for {@link #RamenDouglasPeuker}.
1109      * It searches for the point between index i and j which
1110      * has the largest distance from the line between the points i and j.
1111      * @param {Array} pts Array of {@link JXG.Point}
1112      * @param {Number} i Index of a point in pts
1113      * @param {Number} j Index of a point in pts
1114      **/
1115     findSplit: function(pts, i, j) {
1116         var dist = 0,
1117             f = i,
1118             d, k, ci, cj, ck,
1119             x0, y0, x1, y1,
1120             den, lbda;
1121 
1122         if (j - i < 2) return [-1.0,0];
1123 
1124         ci = pts[i].scrCoords;
1125         cj = pts[j].scrCoords;
1126         if (isNaN(ci[1] + ci[2] + cj[1] + cj[2])) return [NaN,j];
1127 
1128         for (k = i + 1; k < j; k++) {
1129             ck = pts[k].scrCoords;
1130             x0 = ck[1] - ci[1];
1131             y0 = ck[2] - ci[2];
1132             x1 = cj[1] - ci[1];
1133             y1 = cj[2] - ci[2];
1134             den = x1 * x1 + y1 * y1;
1135             if (den >= JXG.Math.eps) {
1136                 lbda = (x0 * x1 + y0 * y1) / den;
1137                 d = x0 * x0 + y0 * y0 - lbda * (x0 * x1 + y0 * y1);
1138             } else {
1139                 lbda = 0.0;
1140                 d = x0 * x0 + y0 * y0;
1141             }
1142             if (lbda < 0.0) {
1143                 d = x0 * x0 + y0 * y0;
1144             } else if (lbda > 1.0) {
1145                 x0 = ck[1] - cj[1];
1146                 y0 = ck[2] - cj[2];
1147                 d = x0 * x0 + y0 * y0;
1148             }
1149             if (d > dist) {
1150                 dist = d;
1151                 f = k;
1152             }
1153         }
1154         return [Math.sqrt(dist),f];
1155     },
1156 
1157     /**
1158      * RDB() is a subroutine for {@link #RamenDouglasPeuker}.
1159      * It runs recursively through the point set and searches the
1160      * point which has the largest distance from the line between the first point and
1161      * the last point. If the distance from the line is greater than eps, this point is
1162      * included in our new point set otherwise it is discarded.
1163      * If it is taken, we recursively apply the subroutine to the point set before
1164      * and after the chosen point.
1165      * @param {Array} pts Array of {@link JXG.Point}s
1166      * @param {Number} i Index of an element of pts
1167      * @param {Number} j Index of an element of pts
1168      * @param {Number} eps If the absolute value of a given number <tt>x</tt> is smaller than <tt>eps</tt> it is considered to be equal <tt>0</tt>.
1169      * @param {Array} newPts Array of {@link JXG.Point}s
1170      */
1171     RDP: function(pts, i, j, eps, newPts) {
1172         var result = this.findSplit(pts, i, j);
1173 
1174         if (result[0] > eps) {
1175             this.RDP(pts, i, result[1], eps, newPts);
1176             this.RDP(pts, result[1], j, eps, newPts);
1177         } else {
1178             newPts.push(pts[j]);
1179         }
1180     },
1181 
1182     /**
1183      * Ramen-Douglas-Peuker algorithm.
1184      * It discards points which are not necessary from the polygonal line defined by the point array
1185      * pts. The computation is done in screen coordinates.
1186      * Average runtime is O(nlog(n)), worst case runtime is O(n^2), where n is the number of points.
1187      * @param {Array} pts Array of {@link JXG.Point}s
1188      * @param {Number} eps If the absolute value of a given number <tt>x</tt> is smaller than <tt>eps</tt> it is considered to be equal <tt>0</tt>.
1189      * @returns {Array} An array containing points which represent an apparently identical curve as the points of pts do, but contains fewer points.
1190      */
1191     RamenDouglasPeuker: function(pts, eps) {
1192         var newPts = [], i, k, len;
1193 
1194         len = pts.length;
1195 
1196         // Search for the left most point woithout NaN coordinates
1197         i = 0;
1198         while (i < len && isNaN(pts[i].scrCoords[1] + pts[i].scrCoords[2])) {
1199             i++;
1200         }
1201         // Search for the right most point woithout NaN coordinates
1202         k = len - 1;
1203         while (k > i && isNaN(pts[k].scrCoords[1] + pts[k].scrCoords[2])) {
1204             k--;
1205         }
1206 
1207         // Only proceed if something is left
1208         if (!(i > k || i == len)) {
1209             newPts[0] = pts[i];
1210             this.RDP(pts, i, k, eps, newPts);
1211         }
1212 
1213         return newPts;
1214     },
1215 
1216     /**
1217      * Sets the shadow properties to a geometry element. This method is only a stub, it is implemented in the actual renderers.
1218      * @param {JXG.GeometryElement} element Reference to a geometry object, that should get a shadow
1219      */
1220     setShadow: function(element) {
1221     },
1222 
1223 
1224     /**
1225      * @TODO Description of parameters
1226      * Updates a path element.
1227      */
1228     updatePathStringPoint: function(el, size, type) {
1229     },
1230 
1231     /**
1232      * If <tt>val</tt> is a function, it will be evaluated without giving any parameters, else the input value is just returned.
1233      * @param val Could be anything.
1234      */
1235     evaluate: function(val) {
1236         if (JXG.isFunction(val)) {
1237             return val();
1238         } else {
1239             return val;
1240         }
1241     },
1242 
1243     /**
1244      * This is just a stub. Usage and implementation may differ between the different renderers.
1245      */
1246     setBuffering: function() {
1247 
1248     }
1249 };
1250 };
1251