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