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 /*jshint bitwise: false, curly: true, debug: false, eqeqeq: true, devel: false, evil: false, 27 forin: false, immed: true, laxbreak: false, newcap: false, noarg: true, nonew: true, onevar: true, 28 undef: true, white: false, sub: false*/ 29 /*global JXG: true, AMprocessNode: true, MathJax: true, document: true */ 30 31 /** 32 * Uses VML to implement the rendering methods defined in {@link JXG.AbstractRenderer}. 33 * @class JXG.AbstractRenderer 34 * @augments JXG.AbstractRenderer 35 * @param {Node} container Reference to a DOM node containing the board. 36 * @see JXG.AbstractRenderer 37 */ 38 JXG.VMLRenderer = function (container) { 39 this.type = 'vml'; 40 41 this.container = container; 42 this.container.style.overflow = 'hidden'; 43 this.container.onselectstart = function () { 44 return false; 45 }; 46 47 this.resolution = 10; // Paths are drawn with a a resolution of this.resolution/pixel. 48 49 // Add VML includes and namespace 50 // Original: IE <=7 51 //container.ownerDocument.createStyleSheet().addRule("v\\:*", "behavior: url(#default#VML);"); 52 if (!JXG.exists(JXG.vmlStylesheet)) { 53 container.ownerDocument.namespaces.add("jxgvml", "urn:schemas-microsoft-com:vml"); 54 JXG.vmlStylesheet = this.container.ownerDocument.createStyleSheet(); 55 JXG.vmlStylesheet.addRule(".jxgvml", "behavior:url(#default#VML)"); 56 } 57 58 try { 59 !container.ownerDocument.namespaces.jxgvml && container.ownerDocument.namespaces.add("jxgvml", "urn:schemas-microsoft-com:vml"); 60 this.createNode = function (tagName) { 61 return container.ownerDocument.createElement('<jxgvml:' + tagName + ' class="jxgvml">'); 62 }; 63 } catch (e) { 64 this.createNode = function (tagName) { 65 return container.ownerDocument.createElement('<' + tagName + ' xmlns="urn:schemas-microsoft.com:vml" class="jxgvml">'); 66 }; 67 } 68 69 // dash styles 70 this.dashArray = ['Solid', '1 1', 'ShortDash', 'Dash', 'LongDash', 'ShortDashDot', 'LongDashDot']; 71 }; 72 73 JXG.VMLRenderer.prototype = new JXG.AbstractRenderer(); 74 75 JXG.extend(JXG.VMLRenderer.prototype, /** @lends JXG.VMLRenderer */ { 76 77 /** 78 * Sets attribute <tt>key</tt> of node <tt>node</tt> to <tt>value</tt>. 79 * @param {Node} node A DOM node. 80 * @param {String} key Name of the attribute. 81 * @param {String} val New value of the attribute. 82 * @param {Boolean} [iFlag=false] If false, the attribute's name is case insensitive. 83 */ 84 _setAttr: function (node, key, val, iFlag) { 85 try { 86 if (document.documentMode === 8) { 87 node[key] = val; 88 } else { 89 node.setAttribute(key, val, iFlag); 90 } 91 } catch (e) { 92 JXG.debug('_setAttr:'/*node.id*/ + ' ' + key + ' ' + val + '<br>\n'); 93 } 94 }, 95 96 /* ******************************** * 97 * This renderer does not need to 98 * override draw/update* methods 99 * since it provides draw/update*Prim 100 * methods. 101 * ******************************** */ 102 103 /* ************************** 104 * Lines 105 * **************************/ 106 107 // documented in AbstractRenderer 108 updateTicks: function (axis, dxMaj, dyMaj, dxMin, dyMin) { 109 var tickArr = [], i, len, c, ticks, r = this.resolution; 110 111 len = axis.ticks.length; 112 for (i = 0; i < len; i++) { 113 c = axis.ticks[i]; 114 x = c[0]; 115 y = c[1]; 116 if (typeof x[0] != 'undefined' && typeof x[1] != 'undefined') { 117 tickArr.push( 118 ' m ' + Math.round(r * x[0]) + ', ' + Math.round(r * y[0]) + 119 ' l ' + Math.round(r * x[1]) + ', ' + Math.round(r * y[1]) + ' ' 120 ); 121 } 122 } 123 // Labels 124 for (i = 0; i < len; i++) { 125 c = axis.ticks[i].scrCoords; 126 if (axis.ticks[i].major 127 && (axis.board.needsFullUpdate || axis.needsRegularUpdate) 128 && axis.labels[i] 129 && axis.labels[i].visProp.visible) { 130 this.updateText(axis.labels[i]); 131 } 132 } 133 134 //ticks = this.getElementById(axis.id); 135 if (!JXG.exists(axis)) { 136 ticks = this.createPrim('path', axis.id); 137 this.appendChildPrim(ticks, axis.visProp.layer); 138 this.appendNodesToElement(axis, 'path'); 139 } 140 this._setAttr(axis.rendNode, 'stroked', 'true'); 141 this._setAttr(axis.rendNode, 'strokecolor', axis.visProp.strokecolor, 1); 142 this._setAttr(axis.rendNode, 'strokeweight', axis.visProp.strokewidth); 143 this._setAttr(axis.rendNodeStroke, 'opacity', (axis.visProp.strokeopacity * 100) + '%'); 144 this.updatePathPrim(axis.rendNode, tickArr, axis.board); 145 }, 146 147 /* ************************** 148 * Text related stuff 149 * **************************/ 150 151 // already documented in JXG.AbstractRenderer 152 displayCopyright: function (str, fontsize) { 153 var node, t; 154 155 node = this.createNode('textbox'); 156 node.style.position = 'absolute'; 157 this._setAttr(node, 'id', this.container.id + '_' + 'licenseText'); 158 159 node.style.left = 20; 160 node.style.top = 2; 161 node.style.fontSize = fontsize; 162 node.style.color = '#356AA0'; 163 node.style.fontFamily = 'Arial,Helvetica,sans-serif'; 164 this._setAttr(node, 'opacity', '30%'); 165 node.style.filter = 'alpha(opacity = 30)'; 166 167 t = document.createTextNode(str); 168 node.appendChild(t); 169 this.appendChildPrim(node, 0); 170 }, 171 172 // documented in AbstractRenderer 173 drawInternalText: function (el) { 174 var node; 175 node = this.createNode('textbox'); 176 node.style.position = 'absolute'; 177 /* 178 if (document.documentMode === 8) { // IE 8 179 node.setAttribute('class', el.visProp.cssclass); 180 } else { 181 node.setAttribute(document.all ? 'className' : 'class', el.visProp.cssclass); 182 } 183 */ 184 el.rendNodeText = document.createTextNode(''); 185 node.appendChild(el.rendNodeText); 186 this.appendChildPrim(node, 9); 187 return node; 188 }, 189 190 // documented in AbstractRenderer 191 updateInternalText: function (el) { 192 var content = el.plaintext; 193 /* 194 if (document.documentMode === 8) { // IE 8 195 el.rendNode.setAttribute('class', el.visProp.cssclass); 196 } else { 197 el.rendNode.setAttribute(document.all ? 'className' : 'class', el.visProp.cssclass); 198 } 199 */ 200 if (!isNaN(el.coords.scrCoords[1]+el.coords.scrCoords[2])) { 201 el.rendNode.style.left = parseInt(el.coords.scrCoords[1]) + 'px'; 202 el.rendNode.style.top = parseInt(el.coords.scrCoords[2] - parseInt(el.visProp.fontsize) + this.vOffsetText) + 'px'; 203 } 204 205 if (el.htmlStr !== content) { 206 el.rendNodeText.data = content; 207 el.htmlStr = content; 208 } 209 210 this.transformImage(el, el.transformations); 211 }, 212 213 /* ************************** 214 * Image related stuff 215 * **************************/ 216 217 // already documented in JXG.AbstractRenderer 218 drawImage: function (el) { 219 // IE 8: Bilder ueber data URIs werden bis 32kB unterstuetzt. 220 var node; 221 222 node = this.container.ownerDocument.createElement('img'); 223 node.style.position = 'absolute'; 224 this._setAttr(node, 'id', this.container.id + '_' + el.id); 225 226 this.container.appendChild(node); 227 this.appendChildPrim(node, el.visProp.layer); 228 229 // Adding the rotation filter. This is always filter item 0: 230 // node.filters.item(0), see transformImage 231 //node.style.filter = node.style['-ms-filter'] = "progid:DXImageTransform.Microsoft.Matrix(M11='1.0', sizingMethod='auto expand')"; 232 node.style.filter = "progid:DXImageTransform.Microsoft.Matrix(M11='1.0', sizingMethod='auto expand')"; 233 el.rendNode = node; 234 this.updateImage(el); 235 }, 236 237 // already documented in JXG.AbstractRenderer 238 transformImage: function (el, t) { 239 var node = el.rendNode, 240 m, p = [], s, len = t.length, 241 maxX, maxY, minX, minY, i, nt; 242 243 if (el.type === JXG.OBJECT_TYPE_TEXT) { 244 el.updateSize(); 245 } 246 if (len > 0) { 247 nt = el.rendNode.style.filter.toString(); 248 if (!nt.match(/DXImageTransform/)) { 249 node.style.filter = "progid:DXImageTransform.Microsoft.Matrix(M11='1.0', sizingMethod='auto expand') " + nt; 250 } 251 252 m = this.joinTransforms(el, t); 253 p[0] = JXG.Math.matVecMult(m, el.coords.scrCoords); 254 p[0][1] /= p[0][0]; 255 p[0][2] /= p[0][0]; 256 p[1] = JXG.Math.matVecMult(m, [1, el.coords.scrCoords[1] + el.size[0], el.coords.scrCoords[2]]); 257 p[1][1] /= p[1][0]; 258 p[1][2] /= p[1][0]; 259 p[2] = JXG.Math.matVecMult(m, [1, el.coords.scrCoords[1] + el.size[0], el.coords.scrCoords[2] - el.size[1]]); 260 p[2][1] /= p[2][0]; 261 p[2][2] /= p[2][0]; 262 p[3] = JXG.Math.matVecMult(m, [1, el.coords.scrCoords[1], el.coords.scrCoords[2] - el.size[1]]); 263 p[3][1] /= p[3][0]; 264 p[3][2] /= p[3][0]; 265 maxX = p[0][1]; 266 minX = p[0][1]; 267 maxY = p[0][2]; 268 minY = p[0][2]; 269 for (i = 1; i < 4; i++) { 270 maxX = Math.max(maxX, p[i][1]); 271 minX = Math.min(minX, p[i][1]); 272 maxY = Math.max(maxY, p[i][2]); 273 minY = Math.min(minY, p[i][2]); 274 } 275 node.style.left = parseInt(minX) + 'px'; 276 node.style.top = parseInt(minY) + 'px'; 277 278 node.filters.item(0).M11 = m[1][1]; 279 node.filters.item(0).M12 = m[1][2]; 280 node.filters.item(0).M21 = m[2][1]; 281 node.filters.item(0).M22 = m[2][2]; 282 } 283 }, 284 285 // already documented in JXG.AbstractRenderer 286 updateImageURL: function (el) { 287 var url = JXG.evaluate(el.url); 288 this._setAttr(el.rendNode, 'src', url); 289 }, 290 291 /* ************************** 292 * Render primitive objects 293 * **************************/ 294 295 // already documented in JXG.AbstractRenderer 296 appendChildPrim: function (node, level) { 297 if (!JXG.exists(level)) { // For trace nodes 298 level = 0; 299 } 300 node.style.zIndex = level; 301 this.container.appendChild(node); 302 }, 303 304 // already documented in JXG.AbstractRenderer 305 appendNodesToElement: function (element, type) { 306 if (type === 'shape' || type === 'path' || type === 'polygon') { 307 element.rendNodePath = this.getElementById(element.id + '_path'); 308 } 309 element.rendNodeFill = this.getElementById(element.id + '_fill'); 310 element.rendNodeStroke = this.getElementById(element.id + '_stroke'); 311 element.rendNodeShadow = this.getElementById(element.id + '_shadow'); 312 element.rendNode = this.getElementById(element.id); 313 }, 314 315 // already documented in JXG.AbstractRenderer 316 createPrim: function (type, id) { 317 var node, 318 fillNode = this.createNode('fill'), 319 strokeNode = this.createNode('stroke'), 320 shadowNode = this.createNode('shadow'), 321 pathNode; 322 323 this._setAttr(fillNode, 'id', this.container.id + '_' + id + '_fill'); 324 this._setAttr(strokeNode, 'id', this.container.id + '_' + id + '_stroke'); 325 this._setAttr(shadowNode, 'id', this.container.id + '_' + id + '_shadow'); 326 327 if (type === 'circle' || type === 'ellipse') { 328 node = this.createNode('oval'); 329 node.appendChild(fillNode); 330 node.appendChild(strokeNode); 331 node.appendChild(shadowNode); 332 } else if (type === 'polygon' || type === 'path' || type === 'shape' || type === 'line') { 333 node = this.createNode('shape'); 334 node.appendChild(fillNode); 335 node.appendChild(strokeNode); 336 node.appendChild(shadowNode); 337 pathNode = this.createNode('path'); 338 this._setAttr(pathNode, 'id', this.container.id + '_' + id + '_path'); 339 node.appendChild(pathNode); 340 } else { 341 node = this.createNode(type); 342 node.appendChild(fillNode); 343 node.appendChild(strokeNode); 344 node.appendChild(shadowNode); 345 } 346 node.style.position = 'absolute'; 347 node.style.left = '0px'; 348 node.style.top = '0px'; 349 this._setAttr(node, 'id', this.container.id + '_' + id); 350 351 return node; 352 }, 353 354 // already documented in JXG.AbstractRenderer 355 remove: function (node) { 356 if (JXG.exists(node)) { 357 node.removeNode(true); 358 } 359 }, 360 361 // already documented in JXG.AbstractRenderer 362 makeArrows: function (el) { 363 var nodeStroke; 364 365 if (el.visPropOld.firstarrow === el.visProp.firstarrow && el.visPropOld.lastarrow === el.visProp.lastarrow) { 366 return; 367 } 368 369 if (el.visProp.firstarrow) { 370 nodeStroke = el.rendNodeStroke; 371 this._setAttr(nodeStroke, 'startarrow', 'block'); 372 this._setAttr(nodeStroke, 'startarrowlength', 'long'); 373 } else { 374 nodeStroke = el.rendNodeStroke; 375 if (JXG.exists(nodeStroke)) { 376 this._setAttr(nodeStroke, 'startarrow', 'none'); 377 } 378 } 379 380 if (el.visProp.lastarrow) { 381 nodeStroke = el.rendNodeStroke; 382 this._setAttr(nodeStroke, 'id', this.container.id + '_' + el.id + "stroke"); 383 this._setAttr(nodeStroke, 'endarrow', 'block'); 384 this._setAttr(nodeStroke, 'endarrowlength', 'long'); 385 } else { 386 nodeStroke = el.rendNodeStroke; 387 if (JXG.exists(nodeStroke)) { 388 this._setAttr(nodeStroke, 'endarrow', 'none'); 389 } 390 } 391 el.visPropOld.firstarrow = el.visProp.firstarrow; 392 el.visPropOld.lastarrow = el.visProp.lastarrow; 393 }, 394 395 // already documented in JXG.AbstractRenderer 396 updateEllipsePrim: function (node, x, y, rx, ry) { 397 node.style.left = parseInt(x - rx) + 'px'; 398 node.style.top = parseInt(y - ry) + 'px'; 399 node.style.width = parseInt(Math.abs(rx) * 2) + 'px'; 400 node.style.height = parseInt(Math.abs(ry) * 2) + 'px'; 401 }, 402 403 // already documented in JXG.AbstractRenderer 404 updateLinePrim: function (node, p1x, p1y, p2x, p2y, board) { 405 var s, r = this.resolution; 406 407 if (!isNaN(p1x+p1y+p2x+p2y)) { 408 s = ['m ', parseInt(r * p1x), ', ', parseInt(r * p1y), ' l ', parseInt(r * p2x), ', ', parseInt(r * p2y)]; 409 this.updatePathPrim(node, s, board); 410 } 411 }, 412 413 // already documented in JXG.AbstractRenderer 414 updatePathPrim: function (node, pointString, board) { 415 var x = board.canvasWidth, 416 y = board.canvasHeight; 417 if (pointString.length <= 0) { 418 pointString = ['m 0,0']; 419 } 420 node.style.width = x; 421 node.style.height = y; 422 this._setAttr(node, 'coordsize', [parseInt(this.resolution * x), parseInt(this.resolution * y)].join(',')); 423 this._setAttr(node, 'path', pointString.join("")); 424 }, 425 426 // already documented in JXG.AbstractRenderer 427 updatePathStringPoint: function (el, size, type) { 428 var s = [], 429 mround = Math.round, 430 scr = el.coords.scrCoords, 431 sqrt32 = size * Math.sqrt(3) * 0.5, 432 s05 = size * 0.5, 433 r = this.resolution; 434 435 if (type === 'x') { 436 s.push([ 437 ' m ', mround(r * (scr[1] - size)), ', ', mround(r * (scr[2] - size)), 438 ' l ', mround(r * (scr[1] + size)), ', ', mround(r * (scr[2] + size)), 439 ' m ', mround(r * (scr[1] + size)), ', ', mround(r * (scr[2] - size)), 440 ' l ', mround(r * (scr[1] - size)), ', ', mround(r * (scr[2] + size)) 441 ].join('')); 442 } 443 else if (type === '+') { 444 s.push([ 445 ' m ', mround(r * (scr[1] - size)), ', ', mround(r * (scr[2])), 446 ' l ', mround(r * (scr[1] + size)), ', ', mround(r * (scr[2])), 447 ' m ', mround(r * (scr[1])), ', ', mround(r * (scr[2] - size)), 448 ' l ', mround(r * (scr[1])), ', ', mround(r * (scr[2] + size)) 449 ].join('')); 450 } 451 else if (type === '<>') { 452 453 s.push([ 454 ' m ', mround(r * (scr[1] - size)), ', ', mround(r * (scr[2])), 455 ' l ', mround(r * (scr[1])), ', ', mround(r * (scr[2] + size)), 456 ' l ', mround(r * (scr[1] + size)), ', ', mround(r * (scr[2])), 457 ' l ', mround(r * (scr[1])), ', ', mround(r * (scr[2] - size)), 458 ' x e ' 459 ].join('')); 460 } 461 else if (type === '^') { 462 s.push([ 463 ' m ', mround(r * (scr[1])), ', ', mround(r * (scr[2] - size)), 464 ' l ', mround(r * (scr[1] - sqrt32)), ', ', mround(r * (scr[2] + s05)), 465 ' l ', mround(r * (scr[1] + sqrt32)), ', ', mround(r * (scr[2] + s05)), 466 ' x e ' 467 ].join('')); 468 } 469 else if (type === 'v') { 470 s.push([ 471 ' m ', mround(r * (scr[1])), ', ', mround(r * (scr[2] + size)), 472 ' l ', mround(r * (scr[1] - sqrt32)), ', ', mround(r * (scr[2] - s05)), 473 ' l ', mround(r * (scr[1] + sqrt32)), ', ', mround(r * (scr[2] - s05)), 474 ' x e ' 475 ].join('')); 476 } 477 else if (type === '>') { 478 s.push([ 479 ' m ', mround(r * (scr[1] + size)), ', ', mround(r * (scr[2])), 480 ' l ', mround(r * (scr[1] - s05)), ', ', mround(r * (scr[2] - sqrt32)), 481 ' l ', mround(r * (scr[1] - s05)), ', ', mround(r * (scr[2] + sqrt32)), 482 ' l ', mround(r * (scr[1] + size)), ', ', mround(r * (scr[2])) 483 ].join('')); 484 } 485 else if (type === '<') { 486 s.push([ 487 ' m ', mround(r * (scr[1] - size)), ', ', mround(r * (scr[2])), 488 ' l ', mround(r * (scr[1] + s05)), ', ', mround(r * (scr[2] - sqrt32)), 489 ' l ', mround(r * (scr[1] + s05)), ', ', mround(r * (scr[2] + sqrt32)), 490 ' x e ' 491 ].join('')); 492 } 493 return s; 494 }, 495 496 // already documented in JXG.AbstractRenderer 497 updatePathStringPrim: function (el) { 498 var pStr = [], 499 i, scr, 500 r = this.resolution, 501 mround = Math.round, 502 symbm = ' m ', 503 symbl = ' l ', 504 nextSymb = symbm, 505 isNotPlot = (el.visProp.curvetype !== 'plot'), 506 len = Math.min(el.numberPoints, 8192); // otherwise IE 7 crashes in hilbert.html 507 508 if (el.numberPoints <= 0) { 509 return ''; 510 } 511 if (isNotPlot && el.board.options.curve.RDPsmoothing) { 512 el.points = JXG.Math.Numerics.RamerDouglasPeuker(el.points, 1.0); 513 } 514 len = Math.min(len, el.points.length); 515 516 for (i = 0; i < len; i++) { 517 scr = el.points[i].scrCoords; 518 if (isNaN(scr[1]) || isNaN(scr[2])) { // PenUp 519 nextSymb = symbm; 520 } else { 521 // IE has problems with values being too far away. 522 if (scr[1] > 20000.0) { 523 scr[1] = 20000.0; 524 } else if (scr[1] < -20000.0) { 525 scr[1] = -20000.0; 526 } 527 528 if (scr[2] > 20000.0) { 529 scr[2] = 20000.0; 530 } else if (scr[2] < -20000.0) { 531 scr[2] = -20000.0; 532 } 533 534 pStr.push([nextSymb, mround(r * scr[1]), ', ', mround(r * scr[2])].join('')); 535 nextSymb = symbl; 536 } 537 } 538 pStr.push(' e'); 539 return pStr; 540 }, 541 542 // already documented in JXG.AbstractRenderer 543 updatePathStringBezierPrim: function (el) { 544 var pStr = [], 545 i, j, scr, 546 lx, ly, f = el.visProp.strokewidth, 547 r = this.resolution, 548 mround = Math.round, 549 symbm = ' m ', 550 symbl = ' c ', 551 nextSymb = symbm, 552 isNoPlot = (el.visProp.curvetype !== 'plot'), 553 len = Math.min(el.numberPoints, 8192); // otherwise IE 7 crashes in hilbert.html 554 555 if (el.numberPoints <= 0) { 556 return ''; 557 } 558 if (isNoPlot && el.board.options.curve.RDPsmoothing) { 559 el.points = JXG.Math.Numerics.RamerDouglasPeuker(el.points, 1.0); 560 } 561 len = Math.min(len, el.points.length); 562 563 for (j=1; j<3; j++) { 564 nextSymb = symbm; 565 for (i = 0; i < len; i++) { 566 scr = el.points[i].scrCoords; 567 if (isNaN(scr[1]) || isNaN(scr[2])) { // PenUp 568 nextSymb = symbm; 569 } else { 570 // IE has problems with values being too far away. 571 if (scr[1] > 20000.0) { 572 scr[1] = 20000.0; 573 } else if (scr[1] < -20000.0) { 574 scr[1] = -20000.0; 575 } 576 577 if (scr[2] > 20000.0) { 578 scr[2] = 20000.0; 579 } else if (scr[2] < -20000.0) { 580 scr[2] = -20000.0; 581 } 582 583 if (nextSymb == symbm) { 584 pStr.push([nextSymb, 585 mround(r * (scr[1]+0*f*(2*j*Math.random()-j))), ' ', 586 mround(r * (scr[2]+0*f*(2*j*Math.random()-j)))].join('')); 587 } else { 588 pStr.push([nextSymb, 589 mround(r * (lx + (scr[1]-lx)*0.333 + f*(2*j*Math.random()-j))), ' ', 590 mround(r * (ly + (scr[2]-ly)*0.333 + f*(2*j*Math.random()-j))), ' ', 591 mround(r * (lx + 2*(scr[1]-lx)*0.333 + f*(2*j*Math.random()-j))), ' ', 592 mround(r * (ly + 2*(scr[2]-ly)*0.333 + f*(2*j*Math.random()-j))), ' ', 593 mround(r * scr[1]), ' ', 594 mround(r * scr[2]) 595 ].join('')); 596 } 597 nextSymb = symbl; 598 lx = scr[1]; 599 ly = scr[2]; 600 } 601 } 602 } 603 pStr.push(' e'); 604 return pStr; 605 }, 606 607 // already documented in JXG.AbstractRenderer 608 updatePolygonPrim: function (node, el) { 609 var i, 610 len = el.vertices.length, 611 r = this.resolution, 612 scr, 613 pStr = []; 614 615 this._setAttr(node, 'stroked', 'false'); 616 617 scr = el.vertices[0].coords.scrCoords; 618 if (isNaN(scr[1]+scr[2])) return; 619 pStr.push(["m ", parseInt(r * scr[1]), ",", parseInt(r * scr[2]), " l "].join('')); 620 621 for (i = 1; i < len - 1; i++) { 622 if (el.vertices[i].isReal) { 623 scr = el.vertices[i].coords.scrCoords; 624 if (isNaN(scr[1]+scr[2])) return; 625 pStr.push(parseInt(r * scr[1]) + "," + parseInt(r * scr[2])); 626 } else { 627 this.updatePathPrim(node, '', el.board); 628 return; 629 } 630 if (i < len - 2) { 631 pStr.push(", "); 632 } 633 } 634 pStr.push(" x e"); 635 this.updatePathPrim(node, pStr, el.board); 636 }, 637 638 // already documented in JXG.AbstractRenderer 639 updateRectPrim: function (node, x, y, w, h) { 640 node.style.left = parseInt(x) + 'px'; 641 node.style.top = parseInt(y) + 'px'; 642 643 if (w >= 0) { 644 node.style.width = w + 'px'; 645 } 646 647 if (h >= 0) { 648 node.style.height = h + 'px'; 649 } 650 }, 651 652 /* ************************** 653 * Set Attributes 654 * **************************/ 655 656 // already documented in JXG.AbstractRenderer 657 setPropertyPrim: function (node, key, val) { 658 var keyVml = '', 659 v; 660 661 switch (key) { 662 case 'stroke': 663 keyVml = 'strokecolor'; 664 break; 665 case 'stroke-width': 666 keyVml = 'strokeweight'; 667 break; 668 case 'stroke-dasharray': 669 keyVml = 'dashstyle'; 670 break; 671 } 672 673 if (keyVml !== '') { 674 v = JXG.evaluate(val); 675 this._setAttr(node, keyVml, v); 676 } 677 }, 678 679 // already documented in JXG.AbstractRenderer 680 show: function (el) { 681 if (el && el.rendNode) { 682 el.rendNode.style.visibility = "inherit"; 683 } 684 }, 685 686 // already documented in JXG.AbstractRenderer 687 hide: function (el) { 688 if (el && el.rendNode) { 689 el.rendNode.style.visibility = "hidden"; 690 } 691 }, 692 693 // already documented in JXG.AbstractRenderer 694 setDashStyle: function (el, visProp) { 695 var node; 696 if (visProp.dash >= 0) { 697 node = el.rendNodeStroke; 698 this._setAttr(node, 'dashstyle', this.dashArray[visProp.dash]); 699 } 700 }, 701 702 // already documented in JXG.AbstractRenderer 703 setGradient: function (el) { 704 var nodeFill = el.rendNodeFill; 705 706 if (el.visProp.gradient === 'linear') { 707 this._setAttr(nodeFill, 'type', 'gradient'); 708 this._setAttr(nodeFill, 'color2', el.visProp.gradientsecondcolor); 709 this._setAttr(nodeFill, 'opacity2', el.visProp.gradientsecondopacity); 710 this._setAttr(nodeFill, 'angle', el.visProp.gradientangle); 711 } else if (el.visProp.gradient === 'radial') { 712 this._setAttr(nodeFill, 'type', 'gradientradial'); 713 this._setAttr(nodeFill, 'color2', el.visProp.gradientsecondcolor); 714 this._setAttr(nodeFill, 'opacity2', el.visProp.gradientsecondopacity); 715 this._setAttr(nodeFill, 'focusposition', el.visProp.gradientpositionx * 100 + '%,' + el.visProp.gradientpositiony * 100 + '%'); 716 this._setAttr(nodeFill, 'focussize', '0,0'); 717 } else { 718 this._setAttr(nodeFill, 'type', 'solid'); 719 } 720 }, 721 722 // already documented in JXG.AbstractRenderer 723 setObjectFillColor: function (el, color, opacity) { 724 var rgba = JXG.evaluate(color), c, rgbo, 725 o = JXG.evaluate(opacity), oo, 726 node = el.rendNode, 727 t; 728 729 o = (o > 0) ? o : 0; 730 731 if (el.visPropOld.fillcolor === rgba && el.visPropOld.fillopacity === o) { 732 return; 733 } 734 735 if (JXG.exists(rgba) && rgba !== false) { 736 if (rgba.length!=9) { // RGB, not RGBA 737 c = rgba; 738 oo = o; 739 } else { // True RGBA, not RGB 740 rgbo = JXG.rgba2rgbo(rgba); 741 c = rgbo[0]; 742 oo = o*rgbo[1]; 743 } 744 if (c === 'none' || c === false) { 745 this._setAttr(el.rendNode, 'filled', 'false'); 746 } else { 747 this._setAttr(el.rendNode, 'filled', 'true'); 748 this._setAttr(el.rendNode, 'fillcolor', c); 749 750 if (JXG.exists(oo) && el.rendNodeFill) { 751 this._setAttr(el.rendNodeFill, 'opacity', (oo * 100) + '%'); 752 } 753 } 754 if (el.type === JXG.OBJECT_TYPE_IMAGE) { 755 t = el.rendNode.style.filter.toString(); 756 if (t.match(/alpha/)) { 757 el.rendNode.style.filter = t.replace(/alpha\(opacity *= *[0-9\.]+\)/, 'alpha(opacity = ' + (oo * 100) + ')'); 758 } else { 759 el.rendNode.style.filter += ' alpha(opacity = ' + (oo * 100) +')'; 760 } 761 } 762 } 763 el.visPropOld.fillcolor = rgba; 764 el.visPropOld.fillopacity = o; 765 }, 766 767 // already documented in JXG.AbstractRenderer 768 setObjectStrokeColor: function (el, color, opacity) { 769 var rgba = JXG.evaluate(color), c, rgbo, 770 o = JXG.evaluate(opacity), oo, 771 node = el.rendNode, nodeStroke; 772 773 o = (o > 0) ? o : 0; 774 775 if (el.visPropOld.strokecolor === rgba && el.visPropOld.strokeopacity === o) { 776 return; 777 } 778 779 if (JXG.exists(rgba) && rgba !== false) { 780 if (rgba.length!=9) { // RGB, not RGBA 781 c = rgba; 782 oo = o; 783 } else { // True RGBA, not RGB 784 rgbo = JXG.rgba2rgbo(rgba); 785 c = rgbo[0]; 786 oo = o*rgbo[1]; 787 } 788 if (el.type === JXG.OBJECT_TYPE_TEXT) { 789 oo = Math.round(oo*100); 790 node.style.filter = ' alpha(opacity = ' + oo +')'; 791 //node.style.filter = node.style['-ms-filter'] = "progid:DXImageTransform.Microsoft.Alpha(Opacity="+oo+")"; 792 node.style.color = c; 793 } else { 794 if (c !== false) { 795 this._setAttr(node, 'stroked', 'true'); 796 this._setAttr(node, 'strokecolor', c); 797 } 798 799 nodeStroke = el.rendNodeStroke; 800 if (JXG.exists(oo) && el.type !== JXG.OBJECT_TYPE_IMAGE) { 801 this._setAttr(nodeStroke, 'opacity', (oo * 100) + '%'); 802 } 803 } 804 } 805 el.visPropOld.strokecolor = rgba; 806 el.visPropOld.strokeopacity = o; 807 }, 808 809 // already documented in JXG.AbstractRenderer 810 setObjectStrokeWidth: function (el, width) { 811 var w = JXG.evaluate(width), 812 node; 813 814 if (el.visPropOld.strokewidth === w) { 815 return; 816 } 817 818 node = el.rendNode; 819 this.setPropertyPrim(node, 'stroked', 'true'); 820 if (JXG.exists(w)) { 821 this.setPropertyPrim(node, 'stroke-width', w); 822 } 823 el.visPropOld.strokewidth = w; 824 }, 825 826 // already documented in JXG.AbstractRenderer 827 setShadow: function (el) { 828 var nodeShadow = el.rendNodeShadow; 829 830 if (!nodeShadow || el.visPropOld.shadow === el.visProp.shadow) { 831 return; 832 } 833 834 if (el.visProp.shadow) { 835 this._setAttr(nodeShadow, 'On', 'True'); 836 this._setAttr(nodeShadow, 'Offset', '3pt,3pt'); 837 this._setAttr(nodeShadow, 'Opacity', '60%'); 838 this._setAttr(nodeShadow, 'Color', '#aaaaaa'); 839 } else { 840 this._setAttr(nodeShadow, 'On', 'False'); 841 } 842 843 el.visPropOld.shadow = el.visProp.shadow; 844 }, 845 846 /* ************************** 847 * renderer control 848 * **************************/ 849 850 // already documented in JXG.AbstractRenderer 851 suspendRedraw: function () { 852 this.container.style.display = 'none'; 853 }, 854 855 // already documented in JXG.AbstractRenderer 856 unsuspendRedraw: function () { 857 this.container.style.display = ''; 858 } 859 860 }); 861