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