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 /*jshint bitwise: false, curly: true, debug: false, eqeqeq: true, devel: false, evil: false, 28 forin: false, immed: true, laxbreak: false, newcap: false, noarg: true, nonew: true, onevar: true, 29 undef: true, white: true, sub: false*/ 30 /*global JXG: true, AMprocessNode: true, MathJax: true, document: true */ 31 32 /** 33 * @fileoverview JSXGraph can use various technologies to render the contents of a construction, e.g. 34 * SVG, VML, and HTML5 Canvas. To accomplish this, The rendering and the logic and control mechanisms 35 * are completely separated from each other. Every rendering technology has it's own class, called 36 * Renderer, e.g. SVGRenderer for SVG, the same for VML and Canvas. The common base for all available 37 * renderers is the class AbstractRenderer defined in this file. 38 */ 39 40 /** 41 * <p>This class defines the interface to the graphics part of JSXGraph. This class is an abstract class, it 42 * actually does not render anything. This is up to the {@link JXG.SVGRenderer}, {@link JXG.VMLRenderer}, 43 * and {@link JXG.CanvasRenderer} classes. We strongly discourage you from using the methods in these classes 44 * directly. Only the methods which are defined in this class and are not marked as private are guaranteed 45 * to exist in any renderer instance you can access via {@link JXG.Board#renderer}. But not all methods may 46 * work as expected.</p> 47 * <p>The methods of this renderer can be divided into different categories: 48 * <dl> 49 * <dt>Draw basic elements</dt> 50 * <dd>In this category we find methods to draw basic elements like {@link JXG.Point}, {@link JXG.Line}, 51 * and {@link JXG.Curve} as well as assisting methods tightly bound to these basic painters. You do not 52 * need to implement these methods in a descendant renderer but instead implement the primitive drawing 53 * methods described below. This approach is encouraged when you're using a XML based rendering engine 54 * like VML and SVG. If you want to use a bitmap based rendering technique you are supposed to override 55 * these methods instead of the primitive drawing methods.</dd> 56 * <dt>Draw primitives</dt> 57 * <dd>This category summarizes methods to handle primitive nodes. As creation and management of these nodes 58 * is different among different the rendering techniques most of these methods are purely virtual and need 59 * proper implementation if you choose to not overwrite the basic element drawing methods.</dd> 60 * <dt>Attribute manipulation</dt> 61 * <dd>In XML based renders you have to manipulate XML nodes and their attributes to change the graphics. 62 * For that purpose attribute manipulation methods are defined to set the color, opacity, and other things. 63 * Please note that some of these methods are required in bitmap based renderers, too, because some elements 64 * like {@link JXG.Text} can be HTML nodes floating over the construction.</dd> 65 * <dt>Renderer control</dt> 66 * <dd>Methods to clear the drawing board or to stop and to resume the rendering engine.</dd> 67 * </dl></p> 68 * @class JXG.AbstractRenderer 69 * @see JXG.SVGRenderer 70 * @see JXG.VMLRenderer 71 * @see JXG.CanvasRenderer 72 */ 73 JXG.AbstractRenderer = function () { 74 75 // WHY THIS IS A CLASS INSTEAD OF A SINGLETON OBJECT: 76 // 77 // The renderers need to keep track of some stuff which is not always the same on different boards, 78 // like enhancedRendering, reference to the container object, and resolution in VML. Sure, those 79 // things could be stored in board. But they are rendering related and JXG.Board is already very 80 // very big. 81 // 82 // And we can't save the rendering related data in {SVG,VML,Canvas}Renderer and make only the 83 // JXG.AbstractRenderer a singleton because of that: 84 // 85 // Given an object o with property a set to true 86 // var o = {a: true}; 87 // and a class c doing nothing 88 // c = function() {}; 89 // Set c's prototype to o 90 // c.prototype = o; 91 // and create an instance of c we get i.a to be true 92 // i = new c(); 93 // i.a; 94 // > true 95 // But we can overwrite this property via 96 // c.prototype.a = false; 97 // i.a; 98 // > false 99 100 /** 101 * The vertical offset for {@link Text} elements. Every {@link Text} element will 102 * be placed this amount of pixels below the user given coordinates. 103 * @type number 104 * @default 8 105 */ 106 this.vOffsetText = 3; 107 108 /** 109 * If this property is set to <tt>true</tt> the visual properties of the elements are updated 110 * on every update. Visual properties means: All the stuff stored in the 111 * {@link JXG.GeometryElement#visProp} property won't be set if enhancedRendering is <tt>false</tt> 112 * @type Boolean 113 * @default true 114 */ 115 this.enhancedRendering = true; 116 117 /** 118 * The HTML element that stores the JSXGraph board in it. 119 * @type Node 120 */ 121 this.container = null; 122 123 /** 124 * This is used to easily determine which renderer we are using 125 * @example if (board.renderer.type === 'vml') { 126 * // do something 127 * } 128 * @type String 129 */ 130 this.type = ''; 131 }; 132 133 JXG.extend(JXG.AbstractRenderer.prototype, /** @lends JXG.AbstractRenderer.prototype */ { 134 135 /* ******************************** * 136 * private methods * 137 * should not be called from * 138 * outside AbstractRenderer * 139 * ******************************** */ 140 141 /** 142 * Update visual properties, but only if {@link JXG.AbstractRenderer#enhancedRendering} or <tt>enhanced</tt> is set to true. 143 * @param {JXG.GeometryElement} element The element to update 144 * @param {Object} [not={}] Select properties you don't want to be updated: <tt>{fill: true, dash: true}</tt> updates 145 * everything except for fill and dash. Possible values are <tt>stroke, fill, dash, shadow, gradient</tt>. 146 * @param {Boolean} [enhanced=false] If true, {@link JXG.AbstractRenderer#enhancedRendering} is assumed to be true. 147 * @private 148 */ 149 _updateVisual: function (element, not, enhanced) { 150 var rgbo; 151 152 if (enhanced || this.enhancedRendering) { 153 not = not || {}; 154 155 if (!element.visProp.draft) { 156 /* 157 if (JXG.isFunction(element.visProp.visible)) { 158 if (element.visProp.visible()) { 159 this.hide(element); 160 } else { 161 this.show(element); 162 } 163 } 164 */ 165 166 if (!not.stroke) { 167 this.setObjectStrokeWidth(element, element.visProp.strokewidth); 168 this.setObjectStrokeColor(element, element.visProp.strokecolor, element.visProp.strokeopacity); 169 } 170 171 if (!not.fill) { 172 this.setObjectFillColor(element, element.visProp.fillcolor, element.visProp.fillopacity); 173 } 174 175 if (!not.dash) { 176 this.setDashStyle(element, element.visProp); 177 } 178 179 if (!not.shadow) { 180 this.setShadow(element); 181 } 182 183 if (!not.gradient) { 184 this.setShadow(element); 185 } 186 } else { 187 this.setDraft(element); 188 } 189 } 190 }, 191 192 193 /* ******************************** * 194 * Point drawing and updating * 195 * ******************************** */ 196 197 /** 198 * Draws a point on the {@link JXG.Board}. 199 * @param {JXG.Point} element Reference to a {@link JXG.Point} object that has to be drawn. 200 * @see Point 201 * @see JXG.Point 202 * @see JXG.AbstractRenderer#updatePoint 203 * @see JXG.AbstractRenderer#changePointStyle 204 */ 205 drawPoint: function (element) { 206 var prim, 207 face = JXG.Point.prototype.normalizeFace.call(this, element.visProp.face); 208 209 // determine how the point looks like 210 if (face === 'o') { 211 prim = 'ellipse'; 212 } else if (face === '[]') { 213 prim = 'rect'; 214 } else { 215 // cross/x, diamond/<>, triangleup/a/^, triangledown/v, triangleleft/<, 216 // triangleright/>, plus/+, 217 prim = 'path'; 218 } 219 220 this.appendChildPrim(this.createPrim(prim, element.id), element.visProp.layer); 221 this.appendNodesToElement(element, prim); 222 223 // adjust visual propertys 224 this._updateVisual(element, {dash: true, shadow: true}, true); 225 226 // By now we only created the xml nodes and set some styles, in updatePoint 227 // the attributes are filled with data. 228 this.updatePoint(element); 229 }, 230 231 /** 232 * Updates visual appearance of the renderer element assigned to the given {@link JXG.Point}. 233 * @param {JXG.Point} element Reference to a {@link JXG.Point} object, that has to be updated. 234 * @see Point 235 * @see JXG.Point 236 * @see JXG.AbstractRenderer#drawPoint 237 * @see JXG.AbstractRenderer#changePointStyle 238 */ 239 updatePoint: function (element) { 240 var size = element.visProp.size, 241 face = JXG.Point.prototype.normalizeFace.call(this, element.visProp.face); 242 243 if (!isNaN(element.coords.scrCoords[2] + element.coords.scrCoords[1])) { 244 this._updateVisual(element, {dash: false, shadow: false}); 245 246 // Zoom does not work for traces. 247 size *= ((!element.board || !element.board.options.point.zoom) ? 1.0 : Math.sqrt(element.board.zoomX * element.board.zoomY)); 248 249 if (face === 'o') { // circle 250 this.updateEllipsePrim(element.rendNode, element.coords.scrCoords[1], element.coords.scrCoords[2], size + 1, size + 1); 251 } else if (face === '[]') { // rectangle 252 this.updateRectPrim(element.rendNode, element.coords.scrCoords[1] - size, element.coords.scrCoords[2] - size, size * 2, size * 2); 253 } else { // x, +, <>, ^, v, <, > 254 this.updatePathPrim(element.rendNode, this.updatePathStringPoint(element, size, face), element.board); 255 } 256 this.setShadow(element); 257 } 258 }, 259 260 /** 261 * Changes the style of a {@link JXG.Point}. This is required because the point styles differ in what 262 * elements have to be drawn, e.g. if the point is marked by a "x" or a "+" two lines are drawn, if 263 * it's marked by spot a circle is drawn. This method removes the old renderer element(s) and creates 264 * the new one(s). 265 * @param {JXG.Point} element Reference to a {@link JXG.Point} object, that's style is changed. 266 * @see Point 267 * @see JXG.Point 268 * @see JXG.AbstractRenderer#updatePoint 269 * @see JXG.AbstractRenderer#drawPoint 270 */ 271 changePointStyle: function (element) { 272 var node = this.getElementById(element.id); 273 274 // remove the existing point rendering node 275 if (JXG.exists(node)) { 276 this.remove(node); 277 } 278 279 // and make a new one 280 this.drawPoint(element); 281 JXG.clearVisPropOld(element); 282 283 if (!element.visProp.visible) { 284 this.hide(element); 285 } 286 287 if (element.visProp.draft) { 288 this.setDraft(element); 289 } 290 }, 291 292 /* ******************************** * 293 * Lines * 294 * ******************************** */ 295 296 /** 297 * Draws a line on the {@link JXG.Board}. 298 * @param {JXG.Line} element Reference to a line object, that has to be drawn. 299 * @see Line 300 * @see JXG.Line 301 * @see JXG.AbstractRenderer#updateLine 302 */ 303 drawLine: function (element) { 304 this.appendChildPrim(this.createPrim('line', element.id), element.visProp.layer); 305 this.appendNodesToElement(element, 'lines'); 306 this.updateLine(element); 307 }, 308 309 /** 310 * Updates visual appearance of the renderer element assigned to the given {@link JXG.Line}. 311 * @param {JXG.Line} element Reference to the {@link JXG.Line} object that has to be updated. 312 * @see Line 313 * @see JXG.Line 314 * @see JXG.AbstractRenderer#drawLine 315 */ 316 updateLine: function (element) { 317 var screenCoords1 = new JXG.Coords(JXG.COORDS_BY_USER, element.point1.coords.usrCoords, element.board), 318 screenCoords2 = new JXG.Coords(JXG.COORDS_BY_USER, element.point2.coords.usrCoords, element.board); 319 320 JXG.Math.Geometry.calcStraight(element, screenCoords1, screenCoords2); 321 this.updateLinePrim(element.rendNode, screenCoords1.scrCoords[1], screenCoords1.scrCoords[2], 322 screenCoords2.scrCoords[1], screenCoords2.scrCoords[2], element.board); 323 324 this.makeArrows(element); 325 this._updateVisual(element, {fill: true}); 326 }, 327 328 /** 329 * Creates a rendering node for ticks added to a line. 330 * @param {JXG.Line} element A arbitrary line. 331 * @see Line 332 * @see Ticks 333 * @see JXG.Line 334 * @see JXG.Ticks 335 * @see JXG.AbstractRenderer#updateTicks 336 */ 337 drawTicks: function (element) { 338 var node = this.createPrim('path', element.id); 339 340 this.appendChildPrim(node, element.visProp.layer); 341 this.appendNodesToElement(element, 'path'); 342 }, 343 344 /** 345 * Update {@link Ticks} on a {@link JXG.Line}. This method is only a stub and has to be implemented 346 * in any descendant renderer class. 347 * @param {JXG.Line} element Reference of an line object, thats ticks have to be updated. 348 * @param {Number} dxMaj Number of pixels a major tick counts in x direction. 349 * @param {Number} dyMaj Number of pixels a major tick counts in y direction. 350 * @param {Number} dxMin Number of pixels a minor tick counts in x direction. 351 * @param {Number} dyMin Number of pixels a minor tick counts in y direction. 352 * @see Line 353 * @see Ticks 354 * @see JXG.Line 355 * @see JXG.Ticks 356 * @see JXG.AbstractRenderer#drawTicks 357 */ 358 updateTicks: function (element, dxMaj, dyMaj, dxMin, dyMin) { /* stub */ }, 359 360 /* ************************** 361 * Curves 362 * **************************/ 363 364 /** 365 * Draws a {@link JXG.Curve} on the {@link JXG.Board}. 366 * @param {JXG.Curve} element Reference to a graph object, that has to be plotted. 367 * @see Curve 368 * @see JXG.Curve 369 * @see JXG.AbstractRenderer#updateCurve 370 */ 371 drawCurve: function (element) { 372 this.appendChildPrim(this.createPrim('path', element.id), element.visProp.layer); 373 this.appendNodesToElement(element, 'path'); 374 this._updateVisual(element, {shadow: true}, true); 375 this.updateCurve(element); 376 }, 377 378 /** 379 * Updates visual appearance of the renderer element assigned to the given {@link JXG.Curve}. 380 * @param {JXG.Curve} element Reference to a {@link JXG.Curve} object, that has to be updated. 381 * @see Curve 382 * @see JXG.Curve 383 * @see JXG.AbstractRenderer#drawCurve 384 */ 385 updateCurve: function (element) { 386 this._updateVisual(element); 387 if (element.visProp.handdrawing) { 388 this.updatePathPrim(element.rendNode, this.updatePathStringBezierPrim(element), element.board); 389 } else { 390 this.updatePathPrim(element.rendNode, this.updatePathStringPrim(element), element.board); 391 } 392 this.makeArrows(element); 393 }, 394 395 /* ************************** 396 * Circle related stuff 397 * **************************/ 398 399 /** 400 * Draws a {@link JXG.Circle} 401 * @param {JXG.Circle} element Reference to a {@link JXG.Circle} object that has to be drawn. 402 * @see Circle 403 * @see JXG.Circle 404 * @see JXG.AbstractRenderer#updateEllipse 405 */ 406 drawEllipse: function (element) { 407 this.appendChildPrim(this.createPrim('ellipse', element.id), element.visProp.layer); 408 this.appendNodesToElement(element, 'ellipse'); 409 this.updateEllipse(element); 410 }, 411 412 /** 413 * Updates visual appearance of a given {@link JXG.Circle} on the {@link JXG.Board}. 414 * @param {JXG.Circle} element Reference to a {@link JXG.Circle} object, that has to be updated. 415 * @see Circle 416 * @see JXG.Circle 417 * @see JXG.AbstractRenderer#drawEllipse 418 */ 419 updateEllipse: function (element) { 420 this._updateVisual(element); 421 422 // Radius umrechnen: 423 var radius = element.Radius(); 424 if (radius > 0.0 && !isNaN(radius + element.center.coords.scrCoords[1] + element.center.coords.scrCoords[2]) && radius * element.board.unitX < 20000) { 425 this.updateEllipsePrim(element.rendNode, element.center.coords.scrCoords[1], element.center.coords.scrCoords[2], 426 (radius * element.board.unitX), (radius * element.board.unitY)); 427 } 428 }, 429 430 431 /* ************************** 432 * Polygon related stuff 433 * **************************/ 434 435 /** 436 * Draws a {@link JXG.Polygon} on the {@link JXG.Board}. 437 * @param {JXG.Polygon} element Reference to a Polygon object, that is to be drawn. 438 * @see Polygon 439 * @see JXG.Polygon 440 * @see JXG.AbstractRenderer#updatePolygon 441 */ 442 drawPolygon: function (element) { 443 this.appendChildPrim(this.createPrim('polygon', element.id), element.visProp.layer); 444 this.appendNodesToElement(element, 'polygon'); 445 this.updatePolygon(element); 446 }, 447 448 /** 449 * Updates properties of a {@link JXG.Polygon}'s rendering node. 450 * @param {JXG.Polygon} element Reference to a {@link JXG.Polygon} object, that has to be updated. 451 * @see Polygon 452 * @see JXG.Polygon 453 * @see JXG.AbstractRenderer#drawPolygon 454 */ 455 updatePolygon: function (element) { 456 // here originally strokecolor wasn't updated but strokewidth was 457 // but if there's no strokecolor i don't see why we should update strokewidth. 458 this._updateVisual(element, {stroke: true, dash: true}); 459 this.updatePolygonPrim(element.rendNode, element); 460 }, 461 462 /* ************************** 463 * Text related stuff 464 * **************************/ 465 466 /** 467 * Shows a small copyright notice in the top left corner of the board. 468 * @param {String} str The copyright notice itself 469 * @param {Number} fontsize Size of the font the copyright notice is written in 470 */ 471 displayCopyright: function (str, fontsize) { /* stub */ }, 472 473 /** 474 * An internal text is a {@link JXG.Text} element which is drawn using only 475 * the given renderer but no HTML. This method is only a stub, the drawing 476 * is done in the special renderers. 477 * @param {JXG.Text} element Reference to a {@link JXG.Text} object 478 * @see Text 479 * @see JXG.Text 480 * @see JXG.AbstractRenderer#updateInternalText 481 * @see JXG.AbstractRenderer#drawText 482 * @see JXG.AbstractRenderer#updateText 483 * @see JXG.AbstractRenderer#updateTextStyle 484 */ 485 drawInternalText: function (element) { /* stub */ }, 486 487 /** 488 * Updates visual properties of an already existing {@link JXG.Text} element. 489 * @param {JXG.Text} element Reference to an {@link JXG.Text} object, that has to be updated. 490 * @see Text 491 * @see JXG.Text 492 * @see JXG.AbstractRenderer#drawInternalText 493 * @see JXG.AbstractRenderer#drawText 494 * @see JXG.AbstractRenderer#updateText 495 * @see JXG.AbstractRenderer#updateTextStyle 496 */ 497 updateInternalText: function (element) { /* stub */ }, 498 499 /** 500 * Displays a {@link JXG.Text} on the {@link JXG.Board} by putting a HTML div over it. 501 * @param {JXG.Text} element Reference to an {@link JXG.Text} object, that has to be displayed 502 * @see Text 503 * @see JXG.Text 504 * @see JXG.AbstractRenderer#drawInternalText 505 * @see JXG.AbstractRenderer#updateText 506 * @see JXG.AbstractRenderer#updateInternalText 507 * @see JXG.AbstractRenderer#updateTextStyle 508 */ 509 drawText: function (element) { 510 var node, z; 511 512 if (element.visProp.display === 'html') { 513 node = this.container.ownerDocument.createElement('div'); 514 node.style.position = 'absolute'; 515 516 node.className = element.visProp.cssclass; 517 if (this.container.style.zIndex=='') { 518 z = 0; 519 } else { 520 z = parseInt(this.container.style.zIndex); 521 } 522 523 node.style.zIndex = z+element.board.options.layer.text; //'10'; 524 this.container.appendChild(node); 525 node.setAttribute('id', this.container.id + '_' + element.id); 526 } else { 527 node = this.drawInternalText(element); 528 } 529 530 element.rendNode = node; 531 element.htmlStr = ''; 532 this.updateText(element); 533 }, 534 535 /** 536 * Updates visual properties of an already existing {@link JXG.Text} element. 537 * @param {JXG.Text} element Reference to an {@link JXG.Text} object, that has to be updated. 538 * @see Text 539 * @see JXG.Text 540 * @see JXG.AbstractRenderer#drawText 541 * @see JXG.AbstractRenderer#drawInternalText 542 * @see JXG.AbstractRenderer#updateInternalText 543 * @see JXG.AbstractRenderer#updateTextStyle 544 */ 545 updateText: function (element) { 546 var content = element.plaintext; 547 548 if (element.visProp.visible) { 549 this.updateTextStyle(element); 550 551 if (element.visProp.display === 'html') { 552 if (!isNaN(element.coords.scrCoords[1] + element.coords.scrCoords[2])) { 553 element.rendNode.style.left = parseInt(element.coords.scrCoords[1]) + 'px'; 554 element.rendNode.style.top = parseInt(element.coords.scrCoords[2] - parseInt(element.visProp.fontsize) + this.vOffsetText) + 'px'; 555 } 556 557 if (element.htmlStr !== content) { 558 element.rendNode.innerHTML = content; 559 element.htmlStr = content; 560 561 if (element.visProp.usemathjax) { 562 MathJax.Hub.Typeset(element.rendNode); 563 } else if (element.visProp.useasciimathml) { 564 AMprocessNode(element.rendNode, false); 565 } 566 } 567 this.transformImage(element, element.transformations); 568 } else { 569 this.updateInternalText(element); 570 } 571 } 572 }, 573 574 /** 575 * Updates CSS style properties of a {@link JXG.Text} node. 576 * @param {JXG.Text} element Reference to the {@link JXG.Text} object, that has to be updated. 577 * @see Text 578 * @see JXG.Text 579 * @see JXG.AbstractRenderer#drawText 580 * @see JXG.AbstractRenderer#drawInternalText 581 * @see JXG.AbstractRenderer#updateText 582 * @see JXG.AbstractRenderer#updateInternalText 583 */ 584 updateTextStyle: function (element) { 585 var fs = JXG.evaluate(element.visProp.fontsize); 586 587 if (element.visProp.display === 'html' || this.type != 'canvas') { 588 //if (element.visProp.display === 'html') { 589 //element.rendNode.setAttribute("class", element.visProp.cssclass); 590 element.rendNode.className = element.visProp.cssclass; 591 try { 592 element.rendNode.style.fontSize = fs + 'px'; 593 } catch (e) { 594 // IE needs special treatment. 595 element.rendNode.style.fontSize = fs; 596 } 597 } 598 this.setObjectStrokeColor(element, element.visProp.strokecolor, element.visProp.strokeopacity); 599 }, 600 601 /* ************************** 602 * Image related stuff 603 * **************************/ 604 605 /** 606 * Draws an {@link JXG.Image} on a board; This is just a template that has to be implemented by special renderers. 607 * @param {JXG.Image} element Reference to the image object that is to be drawn 608 * @see Image 609 * @see JXG.Image 610 * @see JXG.AbstractRenderer#updateImage 611 */ 612 drawImage: function (element) { /* stub */ }, 613 614 /** 615 * Updates the properties of an {@link JXG.Image} element. 616 * @param {JXG.Image} element Reference to an {@link JXG.Image} object, that has to be updated. 617 * @see Image 618 * @see JXG.Image 619 * @see JXG.AbstractRenderer#drawImage 620 */ 621 updateImage: function (element) { 622 this.updateRectPrim(element.rendNode, element.coords.scrCoords[1], element.coords.scrCoords[2] - element.size[1], 623 element.size[0], element.size[1]); 624 625 this.updateImageURL(element); 626 this.transformImage(element, element.transformations); 627 this._updateVisual(element, {stroke: true, dash: true}, true); 628 }, 629 630 /** 631 * Multiplication of transformations without updating. That means, at that point it is expected that the matrices 632 * contain numbers only. First, the origin in user coords is translated to <tt>(0,0)</tt> in screen coords. 633 * Then, the stretch factors are divided out. After the transformations in user coords, the stretch factors 634 * are multiplied in again, and the origin in user coords is translated back to its position. 635 * This method does not have to be implemented in a new renderer. 636 * @param {JXG.GeometryElement} element A JSXGraph element. We only need its board property. 637 * @param {Array} transformations An array of JXG.Transformations. 638 * @returns {Array} A matrix represented by a two dimensional array of numbers. 639 * @see JXG.AbstractRenderer#transformImage 640 */ 641 joinTransforms: function (element, transformations) { 642 var m = [[1, 0, 0], [0, 1, 0], [0, 0, 1]], 643 ox = element.board.origin.scrCoords[1], 644 oy = element.board.origin.scrCoords[2], 645 ux = element.board.unitX, 646 uy = element.board.unitY, 647 mpre1 = [[1, 0, 0], // Translate to 0,0 in screen coords 648 [-ox, 1, 0], 649 [-oy, 0, 1]], 650 mpre2 = [[1, 0, 0], // Scale 651 [0, 1/ux, 0], 652 [0, 0, -1/uy]], 653 mpost2 = [[1, 0, 0], // Scale back 654 [0, ux, 0], 655 [0, 0, -uy]], 656 mpost1 = [[1, 0, 0], // Translate back 657 [ox, 1, 0], 658 [oy, 0, 1]], 659 i, len = transformations.length; 660 661 for (i = 0; i < len; i++) { 662 m = JXG.Math.matMatMult(mpre1, m); 663 m = JXG.Math.matMatMult(mpre2, m); 664 m = JXG.Math.matMatMult(transformations[i].matrix, m); 665 m = JXG.Math.matMatMult(mpost2, m); 666 m = JXG.Math.matMatMult(mpost1, m); 667 } 668 return m; 669 }, 670 671 /** 672 * Applies transformations on images and text elements. This method is just a stub and has to be implemented in all 673 * descendant classes where text and image transformations are to be supported. 674 * @param {JXG.Image|JXG.Text} element A {@link JXG.Image} or {@link JXG.Text} object. 675 * @param {Array} transformations An array of {@link JXG.Transformation} objects. This is usually the transformations property 676 * of the given element <tt>el</tt>. 677 */ 678 transformImage: function (element, transformations) { /* stub */ }, 679 680 /** 681 * If the URL of the image is provided by a function the URL has to be updated during updateImage() 682 * @param {JXG.Image} element Reference to an image object. 683 * @see JXG.AbstractRenderer#updateImage 684 */ 685 updateImageURL: function (element) { /* stub */ }, 686 687 /* ************************** 688 * Render primitive objects 689 * **************************/ 690 691 /** 692 * Appends a node to a specific layer level. This is just an abstract method and has to be implemented 693 * in all renderers that want to use the <tt>createPrim</tt> model to draw. 694 * @param {Node} node A DOM tree node. 695 * @param {Number} level The layer the node is attached to. This is the index of the layer in 696 * {@link JXG.SVGRenderer#layer} or the <tt>z-index</tt> style property of the node in VMLRenderer. 697 */ 698 appendChildPrim: function (node, level) { /* stub */ }, 699 700 /** 701 * Stores the rendering nodes. This is an abstract method which has to be implemented in all renderers that use 702 * the <tt>createPrim</tt> method. 703 * @param {JXG.GeometryElement} element A JSXGraph element. 704 * @param {String} type The XML node name. Only used in VMLRenderer. 705 */ 706 appendNodesToElement: function (element, type) { /* stub */ }, 707 708 /** 709 * Creates a node of a given type with a given id. 710 * @param {String} type The type of the node to create. 711 * @param {String} id Set the id attribute to this. 712 * @returns {Node} Reference to the created node. 713 */ 714 createPrim: function (type, id) { 715 /* stub */ 716 return null; 717 }, 718 719 /** 720 * Removes an element node. Just a stub. 721 * @param {Node} node The node to remove. 722 */ 723 remove: function (node) { /* stub */ }, 724 725 /** 726 * Can be used to create the nodes to display arrows. This is an abstract method which has to be implemented 727 * in any descendant renderer. 728 * @param {JXG.GeometryElement} element The element the arrows are to be attached to. 729 */ 730 makeArrows: function(element) { /* stub */ }, 731 732 /** 733 * Updates an ellipse node primitive. This is an abstract method which has to be implemented in all renderers 734 * that use the <tt>createPrim</tt> method. 735 * @param {Node} node Reference to the node. 736 * @param {Number} x Centre X coordinate 737 * @param {Number} y Centre Y coordinate 738 * @param {Number} rx The x-axis radius. 739 * @param {Number} ry The y-axis radius. 740 */ 741 updateEllipsePrim: function(node, x, y, rx, ry) { /* stub */ }, 742 743 /** 744 * Refreshes a line node. This is an abstract method which has to be implemented in all renderers that use 745 * the <tt>createPrim</tt> method. 746 * @param {Node} node The node to be refreshed. 747 * @param {Number} p1x The first point's x coordinate. 748 * @param {Number} p1y The first point's y coordinate. 749 * @param {Number} p2x The second point's x coordinate. 750 * @param {Number} p2y The second point's y coordinate. 751 * @param {JXG.Board} board 752 */ 753 updateLinePrim: function(node, p1x, p1y, p2x, p2y, board) { /* stub */ }, 754 755 /** 756 * Updates a path element. This is an abstract method which has to be implemented in all renderers that use 757 * the <tt>createPrim</tt> method. 758 * @param {Node} node The path node. 759 * @param {String} pathString A string formatted like e.g. <em>'M 1,2 L 3,1 L5,5'</em>. The format of the string 760 * depends on the rendering engine. 761 * @param {JXG.Board} board Reference to the element's board. 762 */ 763 updatePathPrim: function (node, pathString, board) { /* stub */ }, 764 765 /** 766 * Builds a path data string to draw a point with a face other than <em>rect</em> and <em>circle</em>. Since 767 * the format of such a string usually depends on the renderer this method 768 * is only an abstract method. Therefore, it has to be implemented in the descendant renderer itself unless 769 * the renderer does not use the createPrim interface but the draw* interfaces to paint. 770 * @param {JXG.Point} element The point element 771 * @param {Number} size A positive number describing the size. Usually the half of the width and height of 772 * the drawn point. 773 * @param {String} type A string describing the point's face. This method only accepts the shortcut version of 774 * each possible face: <tt>x, +, <>, ^, v, >, < 775 */ 776 updatePathStringPoint: function (element, size, type) { /* stub */ }, 777 778 /** 779 * Builds a path data string from a {@link JXG.Curve} element. Since the path data strings heavily depend on the 780 * underlying rendering technique this method is just a stub. Although such a path string is of no use for the 781 * CanvasRenderer, this method is used there to draw a path directly. 782 * @param element 783 */ 784 updatePathStringPrim: function (element) { /* stub */ }, 785 786 /** 787 * Builds a path data string from a {@link JXG.Curve} element such that the curve looks like 788 * hand drawn. 789 * Since the path data strings heavily depend on the 790 * underlying rendering technique this method is just a stub. Although such a path string is of no use for the 791 * CanvasRenderer, this method is used there to draw a path directly. 792 * @param element 793 */ 794 updatePathStringBezierPrim: function (element) { /* stub */ }, 795 796 797 /** 798 * Update a polygon primitive. 799 * @param {Node} node 800 * @param {JXG.Polygon} element A JSXGraph element of type {@link JXG.Polygon} 801 */ 802 updatePolygonPrim: function (node, element) { /* stub */ }, 803 804 /** 805 * Update a rectangle primitive. This is used only for points with face of type 'rect'. 806 * @param {Node} node The node yearning to be updated. 807 * @param {Number} x x coordinate of the top left vertex. 808 * @param {Number} y y coordinate of the top left vertex. 809 * @param {Number} w Width of the rectangle. 810 * @param {Number} h The rectangle's height. 811 */ 812 updateRectPrim: function(node, x, y, w, h) { /* stub */ }, 813 814 /* ************************** 815 * Set Attributes 816 * **************************/ 817 818 /** 819 * Sets a node's attribute. 820 * @param {Node} node The node that is to be updated. 821 * @param {String} key Name of the attribute. 822 * @param {String} val New value for the attribute. 823 */ 824 setPropertyPrim: function (node, key, val) { /* stub */ }, 825 826 /** 827 * Shows a hidden element on the canvas; Only a stub, requires implementation in the derived renderer. 828 * @param {JXG.GeometryElement} element Reference to the object that has to appear. 829 * @see JXG.AbstractRenderer#hide 830 */ 831 show: function (element) { /* stub */ }, 832 833 /** 834 * Hides an element on the canvas; Only a stub, requires implementation in the derived renderer. 835 * @param {JXG.GeometryElement} element Reference to the geometry element that has to disappear. 836 * @see JXG.AbstractRenderer#show 837 */ 838 hide: function (element) { /* stub */ }, 839 840 /** 841 * Sets the buffering as recommended by SVGWG. Until now only Opera supports this and will be ignored by 842 * other browsers. Although this feature is only supported by SVG we have this method in {@link JXG.AbstractRenderer} 843 * because it is called from outside the renderer. 844 * @param {Node} node The SVG DOM Node which buffering type to update. 845 * @param {String} type Either 'auto', 'dynamic', or 'static'. For an explanation see 846 * {@link http://www.w3.org/TR/SVGTiny12/painting.html#BufferedRenderingProperty}. 847 */ 848 setBuffering: function (node, type) { /* stub */ }, 849 850 /** 851 * Sets an element's dash style. 852 * @param {JXG.GeometryElement} element An JSXGraph element. 853 */ 854 setDashStyle: function (element) { /* stub */ }, 855 856 /** 857 * Puts an object into draft mode, i.e. it's visual appearance will be changed. For GEONE<sub>x</sub>T backwards compatibility. 858 * @param {JXG.GeometryElement} element Reference of the object that is in draft mode. 859 */ 860 setDraft: function (element) { 861 if (!element.visProp.draft) { 862 return; 863 } 864 var draftColor = element.board.options.elements.draft.color, 865 draftOpacity = element.board.options.elements.draft.opacity; 866 867 if (element.type === JXG.OBJECT_TYPE_POLYGON) { 868 this.setObjectFillColor(element, draftColor, draftOpacity); 869 } 870 else { 871 if (element.elementClass === JXG.OBJECT_CLASS_POINT) { 872 this.setObjectFillColor(element, draftColor, draftOpacity); 873 } 874 else { 875 this.setObjectFillColor(element, 'none', 0); 876 } 877 this.setObjectStrokeColor(element, draftColor, draftOpacity); 878 this.setObjectStrokeWidth(element, element.board.options.elements.draft.strokeWidth); 879 } 880 }, 881 882 /** 883 * Puts an object from draft mode back into normal mode. 884 * @param {JXG.GeometryElement} element Reference of the object that no longer is in draft mode. 885 */ 886 removeDraft: function (element) { 887 if (element.type === JXG.OBJECT_TYPE_POLYGON) { 888 this.setObjectFillColor(element, element.visProp.fillcolor, element.visProp.fillopacity); 889 } 890 else { 891 if (element.type === JXG.OBJECT_CLASS_POINT) { 892 this.setObjectFillColor(element, element.visProp.fillcolor, element.visProp.fillopacity); 893 } 894 this.setObjectStrokeColor(element, element.visProp.strokecolor, element.visProp.strokeopacity); 895 this.setObjectStrokeWidth(element, element.visProp.strokewidth); 896 } 897 }, 898 899 /** 900 * Sets up nodes for rendering a gradient fill. 901 * @param element 902 */ 903 setGradient: function (element) { /* stub */ }, 904 905 /** 906 * Updates the gradient fill. 907 * @param {JXG.GeometryElement} element An JSXGraph element with an area that can be filled. 908 */ 909 updateGradient: function (element) { /* stub */ }, 910 911 /** 912 * Sets an objects fill color. 913 * @param {JXG.GeometryElement} element Reference of the object that wants a new fill color. 914 * @param {String} color Color in a HTML/CSS compatible format. If you don't want any fill color at all, choose 'none'. 915 * @param {Number} opacity Opacity of the fill color. Must be between 0 and 1. 916 */ 917 setObjectFillColor: function (element, color, opacity) { /* stub */ }, 918 919 /** 920 * Changes an objects stroke color to the given color. 921 * @param {JXG.GeometryElement} element Reference of the {@link JXG.GeometryElement} that gets a new stroke color. 922 * @param {String} color Color value in a HTML compatible format, e.g. <strong>#00ff00</strong> or <strong>green</strong> for green. 923 * @param {Number} opacity Opacity of the fill color. Must be between 0 and 1. 924 */ 925 setObjectStrokeColor: function (element, color, opacity) { /* stub */ }, 926 927 /** 928 * Sets an element's stroke width. 929 * @param {JXG.GeometryElement} element Reference to the geometry element. 930 * @param {Number} width The new stroke width to be assigned to the element. 931 */ 932 setObjectStrokeWidth: function (element, width) { /* stub */ }, 933 934 /** 935 * Sets the shadow properties to a geometry element. This method is only a stub, it is implemented in the actual renderers. 936 * @param {JXG.GeometryElement} element Reference to a geometry object, that should get a shadow 937 */ 938 setShadow: function (element) { /* stub */ }, 939 940 /** 941 * Highlights an object, i.e. changes the current colors of the object to its highlighting colors 942 * @param {JXG.GeometryElement} element Reference of the object that will be highlighted. 943 * @returns {JXG.AbstractRenderer} Reference to the renderer 944 */ 945 highlight: function (element) { 946 var i, ev = element.visProp; 947 948 if (!ev.draft) { 949 /* 950 // Why did we have this? A.W. 951 if (element.elementClass === JXG.OBJECT_CLASS_POINT) { 952 this.setObjectStrokeColor(element, ev.highlightstrokecolor, ev.highlightstrokeopacity); 953 this.setObjectFillColor(element, ev.highlightstrokecolor, ev.highlightstrokeopacity); 954 } else 955 */ 956 if (element.type === JXG.OBJECT_TYPE_POLYGON) { 957 this.setObjectFillColor(element, ev.highlightfillcolor, ev.highlightfillopacity); 958 for (i = 0; i < element.borders.length; i++) { 959 this.setObjectStrokeColor(element.borders[i], element.borders[i].visProp.highlightstrokecolor, element.borders[i].visProp.highlightstrokeopacity); 960 } 961 } else { 962 this.setObjectStrokeColor(element, ev.highlightstrokecolor, ev.highlightstrokeopacity); 963 this.setObjectFillColor(element, ev.highlightfillcolor, ev.highlightfillopacity); 964 } 965 if (element.type === JXG.OBJECT_TYPE_TEXT) { 966 if (element.visProp.display === 'html') { 967 element.rendNode.className = element.visProp.highlightcssclass; 968 } 969 } 970 if (ev.highlightstrokewidth) { 971 this.setObjectStrokeWidth(element, Math.max(ev.highlightstrokewidth, ev.strokewidth)); 972 } 973 } 974 975 return this; 976 }, 977 978 /** 979 * Uses the normal colors of an object, i.e. the opposite of {@link JXG.AbstractRenderer#highlight}. 980 * @param {JXG.GeometryElement} element Reference of the object that will get its normal colors. 981 * @returns {JXG.AbstractRenderer} Reference to the renderer 982 */ 983 noHighlight: function (element) { 984 var i, ev = element.visProp; 985 986 if (!element.visProp.draft) { 987 /* 988 // Why did we have this? A.W. 989 if (element.elementClass === JXG.OBJECT_CLASS_POINT) { 990 this.setObjectStrokeColor(element, ev.strokecolor, ev.strokeopacity); 991 this.setObjectFillColor(element, ev.strokecolor, ev.strokeopacity); 992 } else 993 */ 994 if (element.type === JXG.OBJECT_TYPE_POLYGON) { 995 this.setObjectFillColor(element, ev.fillcolor, ev.fillopacity); 996 for (i = 0; i < element.borders.length; i++) { 997 this.setObjectStrokeColor(element.borders[i], element.borders[i].visProp.strokecolor, element.borders[i].visProp.strokeopacity); 998 } 999 } else { 1000 this.setObjectStrokeColor(element, ev.strokecolor, ev.strokeopacity); 1001 this.setObjectFillColor(element, ev.fillcolor, ev.fillopacity); 1002 } 1003 if (element.type === JXG.OBJECT_TYPE_TEXT) { 1004 if (element.visProp.display === 'html') { 1005 element.rendNode.className = element.visProp.cssclass; 1006 } 1007 } 1008 this.setObjectStrokeWidth(element, ev.strokewidth); 1009 } 1010 1011 return this; 1012 }, 1013 1014 1015 /* ************************** 1016 * renderer control 1017 * **************************/ 1018 1019 /** 1020 * Stop redraw. This method is called before every update, so a non-vector-graphics based renderer 1021 * can use this method to delete the contents of the drawing panel. This is an abstract method every 1022 * descendant renderer should implement, if appropriate. 1023 * @see JXG.AbstractRenderer#unsuspendRedraw 1024 */ 1025 suspendRedraw: function () { /* stub */ }, 1026 1027 /** 1028 * Restart redraw. This method is called after updating all the rendering node attributes. 1029 * @see JXG.AbstractRenderer#suspendRedraw 1030 */ 1031 unsuspendRedraw: function () { /* stub */ }, 1032 1033 /** 1034 * The tiny zoom bar shown on the bottom of a board (if showNavigation on board creation is true). 1035 * @param {JXG.Board} board Reference to a JSXGraph board. 1036 */ 1037 drawZoomBar: function (board) { 1038 var doc, 1039 node, 1040 createButton = function (label, handler) { 1041 var button; 1042 1043 button = doc.createElement('span'); 1044 node.appendChild(button); 1045 button.appendChild(document.createTextNode(label)); 1046 /* button.innerHTML = label; */ // Does not work in XHTML 1047 JXG.addEvent(button, 'click', handler, board); 1048 }; 1049 1050 doc = board.containerObj.ownerDocument; 1051 node = doc.createElement('div'); 1052 1053 node.setAttribute('id', board.containerObj.id + '_navigationbar'); 1054 1055 node.style.color = board.options.navbar.strokeColor; 1056 node.style.backgroundColor = board.options.navbar.fillColor; 1057 node.style.padding = board.options.navbar.padding; 1058 node.style.position = board.options.navbar.position; 1059 node.style.fontSize = board.options.navbar.fontSize; 1060 node.style.cursor = board.options.navbar.cursor; 1061 node.style.zIndex = board.options.navbar.zIndex; 1062 board.containerObj.appendChild(node); 1063 node.style.right = board.options.navbar.right; 1064 node.style.bottom = board.options.navbar.bottom; 1065 1066 // For XHTML we need unicode instead of HTML entities 1067 createButton('\u00A0\u2013\u00A0', board.zoomOut); 1068 createButton('\u00A0o\u00A0', board.zoom100); 1069 createButton('\u00A0+\u00A0', board.zoomIn); 1070 createButton('\u00A0\u2190\u00A0', board.clickLeftArrow); 1071 createButton('\u00A0\u2193\u00A0', board.clickUpArrow); 1072 createButton('\u00A0\u2191\u00A0', board.clickDownArrow); 1073 createButton('\u00A0\u2192\u00A0', board.clickRightArrow); 1074 }, 1075 1076 /** 1077 * Wrapper for getElementById for maybe other renderers which elements are not directly accessible by DOM methods like document.getElementById(). 1078 * @param {String} id Unique identifier for element. 1079 * @returns {Object} Reference to a JavaScript object. In case of SVG/VMLRenderer it's a reference to a SVG/VML node. 1080 */ 1081 getElementById: function (id) { 1082 return document.getElementById(this.container.id + '_' + id); 1083 }, 1084 1085 /** 1086 * Resizes the rendering element 1087 * @param {Number} w New width 1088 * @param {Number} h New height 1089 */ 1090 resize: function (w, h) { /* stub */} 1091 1092 }); 1093