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