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