1 /* 2 Copyright 2008,2009 3 Matthias Ehmann, 4 Michael Gerhaeuser, 5 Carsten Miller, 6 Bianca Valentin, 7 Alfred Wassermann, 8 Peter Wilfahrt 9 10 This file is part of JSXGraph. 11 12 JSXGraph is free software: you can redistribute it and/or modify 13 it under the terms of the GNU Lesser General Public License as published by 14 the Free Software Foundation, either version 3 of the License, or 15 (at your option) any later version. 16 17 JSXGraph is distributed in the hope that it will be useful, 18 but WITHOUT ANY WARRANTY; without even the implied warranty of 19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 GNU Lesser General Public License for more details. 21 22 You should have received a copy of the GNU Lesser General Public License 23 along with JSXGraph. If not, see <http://www.gnu.org/licenses/>. 24 */ 25 26 JXG.SVGRenderer = function(container) { 27 var i; 28 this.constructor(); 29 30 /* 31 Enable easy test which renderer is used. 32 */ 33 this.type = 'svg'; 34 35 this.svgRoot = null; 36 this.suspendHandle = null; 37 38 this.svgNamespace = 'http://www.w3.org/2000/svg'; 39 this.xlinkNamespace ='http://www.w3.org/1999/xlink'; 40 41 this.container = container; 42 this.container.style.MozUserSelect = 'none'; 43 44 this.container.style.overflow = 'hidden'; 45 if (this.container.style.position=='') { 46 this.container.style.position = 'relative'; 47 } 48 49 this.svgRoot = this.container.ownerDocument.createElementNS(this.svgNamespace, "svg"); 50 this.svgRoot.style.overflow = 'hidden'; 51 this.svgRoot.style.width = this.container.style.width; 52 this.svgRoot.style.height = this.container.style.height; 53 this.container.appendChild(this.svgRoot); 54 55 this.defs = this.container.ownerDocument.createElementNS(this.svgNamespace,'defs'); 56 this.svgRoot.appendChild(this.defs); 57 this.filter = this.container.ownerDocument.createElementNS(this.svgNamespace,'filter'); 58 this.filter.setAttributeNS(null, 'id', this.container.id+'_'+'f1'); 59 this.filter.setAttributeNS(null, 'width', '300%'); 60 this.filter.setAttributeNS(null, 'height', '300%'); 61 this.feOffset = this.container.ownerDocument.createElementNS(this.svgNamespace,'feOffset'); 62 this.feOffset.setAttributeNS(null, 'result', 'offOut'); 63 this.feOffset.setAttributeNS(null, 'in', 'SourceAlpha'); 64 this.feOffset.setAttributeNS(null, 'dx', '5'); 65 this.feOffset.setAttributeNS(null, 'dy', '5'); 66 this.filter.appendChild(this.feOffset); 67 this.feGaussianBlur = this.container.ownerDocument.createElementNS(this.svgNamespace,'feGaussianBlur'); 68 this.feGaussianBlur.setAttributeNS(null, 'result', 'blurOut'); 69 this.feGaussianBlur.setAttributeNS(null, 'in', 'offOut'); 70 this.feGaussianBlur.setAttributeNS(null, 'stdDeviation', '3'); 71 this.filter.appendChild(this.feGaussianBlur); 72 this.feBlend = this.container.ownerDocument.createElementNS(this.svgNamespace,'feBlend'); 73 this.feBlend.setAttributeNS(null, 'in', 'SourceGraphic'); 74 this.feBlend.setAttributeNS(null, 'in2', 'blurOut'); 75 this.feBlend.setAttributeNS(null, 'mode', 'normal'); 76 this.filter.appendChild(this.feBlend); 77 this.defs.appendChild(this.filter); 78 79 /* 80 * ~ 10 Layers. highest number = highest visibility 81 */ 82 this.layer = []; 83 for (i=0;i<JXG.Options.layer.numlayers;i++) { 84 this.layer[i] = this.container.ownerDocument.createElementNS(this.svgNamespace,'g'); 85 this.svgRoot.appendChild(this.layer[i]); 86 } 87 88 // um Dashes zu realisieren 89 this.dashArray = ['2, 2', '5, 5', '10, 10', '20, 20', '20, 10, 10, 10', '20, 5, 10, 5']; 90 }; 91 92 JXG.SVGRenderer.prototype = JXG.AbstractRenderer(); 93 94 JXG.SVGRenderer.prototype.setShadow = function(el) { 95 if (el.visPropOld['shadow']==el.visProp['shadow']) { 96 return; 97 } 98 if(el.rendNode != null) { 99 if(el.visProp['shadow']) { 100 el.rendNode.setAttributeNS(null,'filter','url(#'+this.container.id+'_'+'f1)'); 101 } 102 else { 103 el.rendNode.removeAttributeNS(null,'filter'); 104 } 105 } 106 el.visPropOld['shadow']=el.visProp['shadow']; 107 }; 108 109 JXG.SVGRenderer.prototype.setGradient = function(el) { 110 var fillNode = el.rendNode, col, op, 111 node, node2, node3, x1, x2, y1, y2; 112 113 if (typeof el.visProp['fillOpacity']=='function') { 114 op = el.visProp['fillOpacity'](); 115 } else { 116 op = el.visProp['fillOpacity']; 117 } 118 op = (op>0)?op:0; 119 if (typeof el.visProp['fillColor']=='function') { 120 col = el.visProp['fillColor'](); 121 } else { 122 col = el.visProp['fillColor']; 123 } 124 125 if(el.visProp['gradient'] == 'linear') { 126 node = this.createPrim('linearGradient', el.id+'_gradient'); 127 x1 = '0%'; // TODO: get x1,x2,y1,y2 from el.visProp['angle'] 128 x2 = '100%'; 129 y1 = '0%'; 130 y2 = '0%'; //means 270 degrees 131 132 node.setAttributeNS(null,'x1',x1); 133 node.setAttributeNS(null,'x2',x2); 134 node.setAttributeNS(null,'y1',y1); 135 node.setAttributeNS(null,'y2',y2); 136 node2 = this.createPrim('stop',el.id+'_gradient1'); 137 node2.setAttributeNS(null,'offset','0%'); 138 node2.setAttributeNS(null,'style','stop-color:'+col+';stop-opacity:'+op); 139 node3 = this.createPrim('stop',el.id+'_gradient2'); 140 node3.setAttributeNS(null,'offset','100%'); 141 node3.setAttributeNS(null,'style','stop-color:'+el.visProp['gradientSecondColor']+';stop-opacity:'+el.visProp['gradientSecondOpacity']); 142 node.appendChild(node2); 143 node.appendChild(node3); 144 this.defs.appendChild(node); 145 fillNode.setAttributeNS(null, 'style', 'fill:url(#'+this.container.id+'_'+el.id+'_gradient)'); 146 el.gradNode1 = node2; 147 el.gradNode2 = node3; 148 } 149 else if (el.visProp['gradient'] == 'radial') { 150 node = this.createPrim('radialGradient',el.id+'_gradient'); 151 152 node.setAttributeNS(null, 'cx', '50%'); 153 node.setAttributeNS(null, 'cy', '50%'); 154 node.setAttributeNS(null, 'r', '50%'); 155 node.setAttributeNS(null, 'fx', el.visProp['gradientPositionX']*100+'%'); 156 node.setAttributeNS(null, 'fy', el.visProp['gradientPositionY']*100+'%'); 157 158 node2 = this.createPrim('stop',el.id+'_gradient1'); 159 node2.setAttributeNS(null,'offset','0%'); 160 node2.setAttributeNS(null,'style','stop-color:'+el.visProp['gradientSecondColor']+';stop-opacity:'+el.visProp['gradientSecondOpacity']); 161 node3 = this.createPrim('stop',el.id+'_gradient2'); 162 node3.setAttributeNS(null,'offset','100%'); 163 node3.setAttributeNS(null,'style','stop-color:'+col+';stop-opacity:'+op); 164 165 node.appendChild(node2); 166 node.appendChild(node3); 167 this.defs.appendChild(node); 168 fillNode.setAttributeNS(null, 'style', 'fill:url(#'+this.container.id+'_'+el.id+'_gradient)'); 169 el.gradNode1 = node2; 170 el.gradNode2 = node3; 171 } 172 else { 173 fillNode.removeAttributeNS(null,'style'); 174 } 175 }; 176 177 JXG.SVGRenderer.prototype.updateGradient = function(el) { 178 var node2 = el.gradNode1, 179 node3 = el.gradNode2, 180 col, op; 181 182 if (node2==null || node3==0) { 183 return; 184 } 185 if (typeof el.visProp['fillOpacity']=='function') { 186 op = el.visProp['fillOpacity'](); 187 } else { 188 op = el.visProp['fillOpacity']; 189 } 190 op = (op>0)?op:0; 191 if (typeof el.visProp['fillColor']=='function') { 192 col = el.visProp['fillColor'](); 193 } else { 194 col = el.visProp['fillColor']; 195 } 196 197 if(el.visProp['gradient'] == 'linear') { 198 node2.setAttributeNS(null,'style','stop-color:'+col+';stop-opacity:'+op); 199 node3.setAttributeNS(null,'style','stop-color:'+el.visProp['gradientSecondColor']+';stop-opacity:'+el.visProp['gradientSecondOpacity']); 200 } else if (el.visProp['gradient'] == 'radial') { 201 node2.setAttributeNS(null,'style','stop-color:'+el.visProp['gradientSecondColor']+';stop-opacity:'+el.visProp['gradientSecondOpacity']); 202 node3.setAttributeNS(null,'style','stop-color:'+col+';stop-opacity:'+op); 203 } 204 }; 205 206 JXG.SVGRenderer.prototype.displayCopyright = function(str,fontsize) { 207 var node = this.createPrim('text','licenseText'), 208 t; 209 node.setAttributeNS(null,'x','20'); 210 node.setAttributeNS(null,'y',2+fontsize); 211 node.setAttributeNS(null, "style", "font-family:Arial,Helvetica,sans-serif; font-size:"+fontsize+"px; fill:#356AA0; opacity:0.3;"); 212 t = document.createTextNode(str); 213 node.appendChild(t); 214 this.appendChildPrim(node,0); 215 }; 216 217 JXG.SVGRenderer.prototype.drawInternalText = function(el) { 218 var node = this.createPrim('text',el.id); 219 node.setAttributeNS(null, "class", "JXGtext"); 220 //node.setAttributeNS(null, "style", "fill:"+ el.visProp['strokeColor']); not available at that time 221 node.setAttributeNS(null, "style", "'alignment-baseline:middle;"); 222 el.rendNodeText = document.createTextNode(''); 223 node.appendChild(el.rendNodeText); 224 this.appendChildPrim(node,9); 225 return node; 226 }; 227 228 JXG.SVGRenderer.prototype.updateInternalText = function(/** JXG.Text */ el) { 229 el.rendNode.setAttributeNS(null, 'x', (el.coords.scrCoords[1])+'px'); 230 el.rendNode.setAttributeNS(null, 'y', (el.coords.scrCoords[2])+'px'); 231 el.updateText(); 232 if (el.htmlStr!= el.plaintextStr) { 233 el.rendNodeText.data = el.plaintextStr; 234 el.htmlStr = el.plaintextStr; 235 } 236 this.transformImage(el, el.transformations); 237 }; 238 239 JXG.SVGRenderer.prototype.drawTicks = function(axis) { 240 var node = this.createPrim('path', axis.id); 241 //node.setAttributeNS(null, 'shape-rendering', 'crispEdges'); 242 this.appendChildPrim(node,axis.layer); 243 this.appendNodesToElement(axis,'path'); 244 }; 245 246 JXG.SVGRenderer.prototype.updateTicks = function(axis,dxMaj,dyMaj,dxMin,dyMin) { 247 var tickStr = "", 248 i, c, node, 249 len = axis.ticks.length; 250 251 for (i=0; i<len; i++) { 252 c = axis.ticks[i].scrCoords; 253 if (axis.ticks[i].major) { 254 if ((axis.board.needsFullUpdate||axis.needsRegularUpdate) && axis.labels[i].visProp['visible']) { 255 this.drawText(axis.labels[i]); 256 } 257 tickStr += "M " + (c[1]+dxMaj) + " " + (c[2]-dyMaj) + " L " + (c[1]-dxMaj) + " " + (c[2]+dyMaj) + " "; 258 } 259 else 260 tickStr += "M " + (c[1]+dxMin) + " " + (c[2]-dyMin) + " L " + (c[1]-dxMin) + " " + (c[2]+dyMin) + " "; 261 262 } 263 264 node = this.getElementById(axis.id); 265 if(node == null) { 266 node = this.createPrim('path', axis.id); 267 //node.setAttributeNS(null, 'shape-rendering', 'crispEdges'); 268 this.appendChildPrim(node,axis.layer); 269 this.appendNodesToElement(axis,'path'); 270 } 271 node.setAttributeNS(null, 'stroke', axis.visProp['strokeColor']); 272 node.setAttributeNS(null, 'stroke-opacity', axis.visProp['strokeOpacity']); 273 node.setAttributeNS(null, 'stroke-width', axis.visProp['strokeWidth']); 274 this.updatePathPrim(node, tickStr, axis.board); 275 }; 276 277 JXG.SVGRenderer.prototype.drawImage = function(el) { 278 var node = this.createPrim('image',el.id); 279 280 node.setAttributeNS(null, 'preserveAspectRatio', 'none'); 281 this.appendChildPrim(node,el.layer); 282 el.rendNode = node; 283 284 this.updateImage(el); 285 }; 286 287 JXG.SVGRenderer.prototype.updateImageURL = function(el) { 288 var url; 289 if (JXG.isFunction(el.url)) { 290 url = el.url(); 291 } else { 292 url = el.url; 293 } 294 el.rendNode.setAttributeNS(this.xlinkNamespace, 'xlink:href', url); 295 }; 296 297 JXG.SVGRenderer.prototype.transformImage = function(el,t) { 298 var node = el.rendNode, m, 299 str = "", //node.getAttributeNS(null, 'transform'), 300 s, len = t.length; 301 302 if (len>0) { 303 m = this.joinTransforms(el,t); 304 s = m[1][1]+','+m[2][1]+','+m[1][2]+','+m[2][2]+','+m[1][0]+','+m[2][0]; 305 str += ' matrix('+s+') '; 306 node.setAttributeNS(null, 'transform', str); 307 //node.style.MozTransform = str; 308 } 309 }; 310 311 /* 312 JXG.SVGRenderer.prototype.removeGrid = function(board) { 313 var c = this.layer[board.options.layer['grid']]; 314 board.hasGrid = false; 315 while (c.childNodes.length>0) { 316 c.removeChild(c.firstChild); 317 } 318 }; 319 */ 320 321 JXG.SVGRenderer.prototype.setArrowAtts = function(node, c, o) { 322 if (!node) return; 323 node.setAttributeNS(null, 'stroke', c); 324 node.setAttributeNS(null, 'stroke-opacity', o); 325 node.setAttributeNS(null, 'fill', c); 326 node.setAttributeNS(null, 'fill-opacity', o); 327 }; 328 329 JXG.SVGRenderer.prototype.setObjectStrokeColor = function(el, color, opacity) { 330 var c = this.evaluate(color), 331 o = this.evaluate(opacity), 332 node; 333 334 o = (o>0)?o:0; 335 336 if (el.visPropOld['strokeColor']==c && el.visPropOld['strokeOpacity']==o) { 337 return; 338 } 339 node = el.rendNode; 340 if(el.type == JXG.OBJECT_TYPE_TEXT) { 341 if (el.display=='html') { 342 node.style.color = c; // Schriftfarbe 343 } else { 344 node.setAttributeNS(null, "style", "fill:"+ c); 345 } 346 } 347 else { 348 node.setAttributeNS(null, 'stroke', c); 349 node.setAttributeNS(null, 'stroke-opacity', o); 350 } 351 if(el.type == JXG.OBJECT_TYPE_ARROW) { 352 this.setArrowAtts(el.rendNodeTriangle,c,o); 353 } else if (el.elementClass == JXG.OBJECT_CLASS_CURVE || el.elementClass == JXG.OBJECT_CLASS_LINE) { 354 if(el.visProp['firstArrow']) { 355 this.setArrowAtts(el.rendNodeTriangleStart,c,o); 356 } 357 if(el.visProp['lastArrow']) { 358 this.setArrowAtts(el.rendNodeTriangleEnd,c,o); 359 } 360 } 361 el.visPropOld['strokeColor'] = c; 362 el.visPropOld['strokeOpacity'] = o; 363 }; 364 365 JXG.SVGRenderer.prototype.setObjectFillColor = function(el, color, opacity) { 366 var node, c = this.evaluate(color), 367 o = this.evaluate(opacity); 368 369 o = (o>0)?o:0; 370 371 if (el.visPropOld['fillColor']==c && el.visPropOld['fillOpacity']==o) { 372 return; 373 } 374 node = el.rendNode; 375 node.setAttributeNS(null, 'fill', c); 376 if (el.type==JXG.OBJECT_TYPE_IMAGE) { 377 node.setAttributeNS(null, 'opacity', o); 378 } else { 379 node.setAttributeNS(null, 'fill-opacity', o); 380 } 381 382 if (el.visProp['gradient']!=null) { 383 this.updateGradient(el); 384 } 385 el.visPropOld['fillColor'] = c; 386 el.visPropOld['fillOpacity'] = o; 387 } ; 388 389 /** 390 * Sets an elements stroke width. 391 * @param {Object} el Reference to the geometry element. 392 * @param {int} width The new stroke width to be assigned to the element. 393 */ 394 JXG.SVGRenderer.prototype.setObjectStrokeWidth = function(el, width) { 395 var w = this.evaluate(width), 396 node; 397 //w = (w>0)?w:0; 398 try { 399 if (el.visPropOld['strokeWidth']==w) { 400 return; 401 } 402 } catch (e){ 403 //alert(el.id); 404 } 405 406 node = el.rendNode; 407 this.setPropertyPrim(node,'stroked', 'true'); 408 if (w!=null) { 409 this.setPropertyPrim(node,'stroke-width',w); 410 } 411 el.visPropOld['strokeWidth'] = w; 412 }; 413 414 JXG.SVGRenderer.prototype.hide = function(el) { 415 var node; 416 417 if (!JXG.exists(el)) 418 return; 419 node = el.rendNode; 420 if(JXG.exists(node)) { 421 node.setAttributeNS(null, 'display', 'none'); 422 node.style.visibility = "hidden"; 423 } 424 }; 425 426 JXG.SVGRenderer.prototype.show = function(el) { 427 var node; 428 429 if (!JXG.exists(el)) 430 return; 431 node = el.rendNode; 432 if(JXG.exists(node)) { 433 node.setAttributeNS(null, 'display', 'inline'); 434 node.style.visibility = "inherit"; 435 } 436 }; 437 438 JXG.SVGRenderer.prototype.remove = function(shape) { 439 if(shape!=null && shape.parentNode != null) 440 shape.parentNode.removeChild(shape); 441 }; 442 443 JXG.SVGRenderer.prototype.suspendRedraw = function() { 444 // It seems to be important for the Linux version of firefox 445 this.suspendHandle = this.svgRoot.suspendRedraw(10000); 446 }; 447 448 JXG.SVGRenderer.prototype.unsuspendRedraw = function() { 449 this.svgRoot.unsuspendRedraw(this.suspendHandle); 450 this.svgRoot.forceRedraw(); 451 }; 452 453 JXG.SVGRenderer.prototype.setDashStyle = function(el,visProp) { 454 var dashStyle = el.visProp['dash'], node = el.rendNode; 455 if(el.visProp['dash'] > 0) { 456 node.setAttributeNS(null, 'stroke-dasharray', this.dashArray[dashStyle-1]); 457 } 458 else { 459 if(node.hasAttributeNS(null, 'stroke-dasharray')) { 460 node.removeAttributeNS(null, 'stroke-dasharray'); 461 } 462 } 463 }; 464 465 JXG.SVGRenderer.prototype.setGridDash = function(id) { 466 var node = this.getElementById(id); 467 this.setPropertyPrim(node,'stroke-dasharray', '5, 5'); 468 }; 469 470 JXG.SVGRenderer.prototype.createPrim = function(type,id) { 471 var node = this.container.ownerDocument.createElementNS(this.svgNamespace, type); 472 node.setAttributeNS(null, 'id', this.container.id+'_'+id); 473 node.style.position = 'absolute'; 474 if (type=='path') { 475 node.setAttributeNS(null, 'stroke-linecap', 'butt'); 476 node.setAttributeNS(null, 'stroke-linejoin', 'round'); 477 //node.setAttributeNS(null, 'shape-rendering', 'geometricPrecision'); // 'crispEdges' 478 } 479 return node; 480 }; 481 482 JXG.SVGRenderer.prototype.createArrowHead = function(el,idAppendix) { 483 var id = el.id+'Triangle', 484 node2, node3; 485 486 if (idAppendix!=null) { id += idAppendix; } 487 node2 = this.createPrim('marker',id); 488 /* 489 node2.setAttributeNS(null, 'viewBox', '0 0 10 6'); 490 node2.setAttributeNS(null, 'refY', '3'); 491 node2.setAttributeNS(null, 'markerHeight', '6'); 492 node2.setAttributeNS(null, 'markerWidth', '6'); 493 if (idAppendix=='End') { 494 node2.setAttributeNS(null, 'refX', '0'); 495 node3.setAttributeNS(null, 'd', 'M 0 3 L 10 6 L 10 0 z'); 496 } else { 497 node2.setAttributeNS(null, 'refX', '10'); 498 node3.setAttributeNS(null, 'd', 'M 0 0 L 10 3 L 0 6 z'); 499 } 500 */ 501 node2.setAttributeNS(null, 'viewBox', '0 0 10 6'); 502 node2.setAttributeNS(null, 'refY', '3'); 503 node2.setAttributeNS(null, 'markerUnits', 'strokeWidth'); 504 node2.setAttributeNS(null, 'markerHeight', '12'); 505 node2.setAttributeNS(null, 'markerWidth', '10'); 506 node2.setAttributeNS(null, 'orient', 'auto'); 507 node2.setAttributeNS(null, 'stroke', el.visProp['strokeColor']); 508 node2.setAttributeNS(null, 'stroke-opacity', el.visProp['strokeOpacity']); 509 node2.setAttributeNS(null, 'fill', el.visProp['strokeColor']); 510 node2.setAttributeNS(null, 'fill-opacity', el.visProp['strokeOpacity']); 511 node3 = this.container.ownerDocument.createElementNS(this.svgNamespace,'path'); 512 if (idAppendix=='End') { 513 node2.setAttributeNS(null, 'refX', '0'); 514 node3.setAttributeNS(null, 'd', 'M 0 3 L 10 6 L 10 0 z'); 515 } else { 516 node2.setAttributeNS(null, 'refX', '10'); 517 node3.setAttributeNS(null, 'd', 'M 0 0 L 10 3 L 0 6 z'); 518 } 519 node2.appendChild(node3); 520 return node2; 521 }; 522 523 /* 524 // seems to be unused 525 JXG.SVGRenderer.prototype.makeArrow = function(node,el,idAppendix) { 526 var node2 = this.createArrowHead(el,idAppendix); 527 this.defs.appendChild(node2); 528 node.setAttributeNS(null, 'marker-end', 'url(#'+this.container.id+'_'+el.id+'Triangle)'); 529 el.rendNodeTriangle = node2; 530 }; 531 */ 532 533 JXG.SVGRenderer.prototype.makeArrows = function(el) { 534 var node2; 535 if (el.visPropOld['firstArrow']==el.visProp['firstArrow'] && el.visPropOld['lastArrow']==el.visProp['lastArrow']) { 536 return; 537 } 538 if(el.visProp['firstArrow']) { 539 node2 = el.rendNodeTriangleStart; 540 if(node2 == null) { 541 node2 = this.createArrowHead(el,'End'); 542 this.defs.appendChild(node2); 543 el.rendNodeTriangleStart = node2; 544 el.rendNode.setAttributeNS(null, 'marker-start', 'url(#'+this.container.id+'_'+el.id+'TriangleEnd)'); 545 } 546 } 547 else { 548 node2 = el.rendNodeTriangleStart; 549 if(node2 != null) { 550 this.remove(node2); 551 } 552 } 553 if(el.visProp['lastArrow']) { 554 node2 = el.rendNodeTriangleEnd; 555 if(node2 == null) { 556 node2 = this.createArrowHead(el,'Start'); 557 this.defs.appendChild(node2); 558 el.rendNodeTriangleEnd = node2; 559 el.rendNode.setAttributeNS(null, 'marker-end', 'url(#'+this.container.id+'_'+el.id+'TriangleStart)'); 560 } 561 } 562 else { 563 node2 = el.rendNodeTriangleEnd; 564 if(node2 != null) { 565 this.remove(node2); 566 } 567 } 568 el.visPropOld['firstArrow'] = el.visProp['firstArrow']; 569 el.visPropOld['lastArrow'] = el.visProp['lastArrow']; 570 }; 571 572 JXG.SVGRenderer.prototype.updateLinePrim = function(node,p1x,p1y,p2x,p2y) { 573 node.setAttributeNS(null, 'x1', p1x); 574 node.setAttributeNS(null, 'y1', p1y); 575 node.setAttributeNS(null, 'x2', p2x); 576 node.setAttributeNS(null, 'y2', p2y); 577 }; 578 579 JXG.SVGRenderer.prototype.updateCirclePrim = function(node,x,y,r) { 580 node.setAttributeNS(null, 'cx', (x)); 581 node.setAttributeNS(null, 'cy', (y)); 582 node.setAttributeNS(null, 'r', (r)); 583 }; 584 585 JXG.SVGRenderer.prototype.updateEllipsePrim = function(node,x,y,rx,ry) { 586 node.setAttributeNS(null, 'cx', (x)); 587 node.setAttributeNS(null, 'cy', (y)); 588 node.setAttributeNS(null, 'rx', (rx)); 589 node.setAttributeNS(null, 'ry', (ry)); 590 }; 591 592 JXG.SVGRenderer.prototype.updateRectPrim = function(node,x,y,w,h) { 593 node.setAttributeNS(null, 'x', (x)); 594 node.setAttributeNS(null, 'y', (y)); 595 node.setAttributeNS(null, 'width', (w)); 596 node.setAttributeNS(null, 'height', (h)); 597 }; 598 599 JXG.SVGRenderer.prototype.updatePathPrim = function(node, pointString, board) { // board not necessary in SVG 600 /* 601 node.setAttributeNS(null, 'stroke-linecap', 'butt'); 602 node.setAttributeNS(null, 'stroke-linejoin', 'round'); 603 //node.setAttributeNS(null, 'shape-rendering', 'geometricPrecision'); 604 //node.setAttributeNS(null, 'shape-rendering', 'crispEdges'); 605 */ 606 node.setAttributeNS(null, 'd', pointString); 607 }; 608 609 JXG.SVGRenderer.prototype.updatePathStringPrim = function(el) { 610 var symbm = ' M ', 611 symbl = ' L ', 612 nextSymb = symbm, 613 maxSize = 5000.0, 614 pStr = '', 615 //h = 3*el.board.canvasHeight, 616 //w = 100*el.board.canvasWidth, 617 i, scr, 618 isNoPlot = (el.curveType!='plot'), 619 //isFunctionGraph = (el.curveType=='functiongraph'), 620 len; 621 622 if (el.numberPoints<=0) { return ''; } 623 624 if (isNoPlot && el.board.options.curve.RDPsmoothing) { 625 el.points = this.RamenDouglasPeuker(el.points,0.5); 626 } 627 len = Math.min(el.points.length,el.numberPoints); 628 for (i=0; i<len; i++) { 629 scr = el.points[i].scrCoords; 630 //if (isNaN(scr[1]) || isNaN(scr[2]) /*|| Math.abs(scr[1])>w || (isFunctionGraph && (scr[2]>h || scr[2]<-0.5*h))*/ ) { // PenUp 631 if (isNaN(scr[1]) || isNaN(scr[2])) { // PenUp 632 nextSymb = symbm; 633 } else { 634 // Chrome has problems with values being too far away. 635 if (scr[1]>maxSize) { scr[1] = maxSize; } 636 else if (scr[1]<-maxSize) { scr[1] = -maxSize; } 637 if (scr[2]>maxSize) { scr[2] = maxSize; } 638 else if (scr[2]<-maxSize) { scr[2] = -maxSize; } 639 640 pStr += [nextSymb,scr[1],' ',scr[2]].join(''); // Attention: first coordinate may be inaccurate if far way 641 nextSymb = symbl; 642 } 643 } 644 return pStr; 645 }; 646 647 JXG.SVGRenderer.prototype.updatePathStringPoint = function(el, size, type) { 648 var s = '', 649 scr = el.coords.scrCoords, 650 sqrt32 = size*Math.sqrt(3)*0.5, 651 s05 = size*0.5; 652 653 if(type == 'x') { 654 s = 'M ' + (scr[1]-size) + ' ' + (scr[2]-size) + ' L ' + 655 (scr[1]+size) + ' ' + (scr[2]+size) + ' M ' + 656 (scr[1]+size) + ' ' + (scr[2]-size) + ' L ' + 657 (scr[1]-size) + ' ' + (scr[2]+size); 658 } 659 else if(type == '+') { 660 s = 'M ' + (scr[1]-size) + ' ' + (scr[2]) + ' L ' + 661 (scr[1]+size) + ' ' + (scr[2]) + ' M ' + 662 (scr[1]) + ' ' + (scr[2]-size) + ' L ' + 663 (scr[1]) + ' ' + (scr[2]+size); 664 } 665 else if(type == '<>') { 666 s = 'M ' + (scr[1]-size) + ' ' + (scr[2]) + ' L ' + 667 (scr[1]) + ' ' + (scr[2]+size) + ' L ' + 668 (scr[1]+size) + ' ' + (scr[2]) + ' L ' + 669 (scr[1]) + ' ' + (scr[2]-size) + ' Z '; 670 } 671 else if(type == '^') { 672 s = 'M ' + (scr[1]) + ' ' + (scr[2]-size) + ' L ' + 673 (scr[1]-sqrt32) + ' ' + (scr[2]+s05) + ' L ' + 674 (scr[1]+sqrt32) + ' ' + (scr[2]+s05) + ' Z '; 675 } 676 else if(type == 'v') { 677 s = 'M ' + (scr[1]) + ' ' + (scr[2]+size) + ' L ' + 678 (scr[1]-sqrt32) + ' ' + (scr[2]-s05) + ' L ' + 679 (scr[1]+sqrt32) + ' ' + (scr[2]-s05) + ' Z '; 680 } 681 else if(type == '>') { 682 s = 'M ' + (scr[1]+size) + ' ' + (scr[2]) + ' L ' + 683 (scr[1]-s05) + ' ' + (scr[2]-sqrt32) + ' L ' + 684 (scr[1]-s05) + ' ' + (scr[2]+sqrt32) + ' Z '; 685 } 686 else if(type == '<') { 687 s = 'M ' + (scr[1]-size) + ' ' + (scr[2]) + ' L ' + 688 (scr[1]+s05) + ' ' + (scr[2]-sqrt32) + ' L ' + 689 (scr[1]+s05) + ' ' + (scr[2]+sqrt32) + ' Z '; 690 } 691 return s; 692 }; 693 694 JXG.SVGRenderer.prototype.updatePolygonPrim = function(node, el) { 695 var pStr = '', 696 scrCoords, i, 697 len = el.vertices.length; 698 699 node.setAttributeNS(null, 'stroke', 'none'); 700 for(i=0; i<len-1; i++) { 701 scrCoords = el.vertices[i].coords.scrCoords; 702 pStr = pStr + scrCoords[1] + "," + scrCoords[2]; 703 if(i<len-2) { pStr += " "; } 704 } 705 node.setAttributeNS(null, 'points', pStr); 706 }; 707 708 JXG.SVGRenderer.prototype.appendChildPrim = function(node,level) { 709 if (typeof level=='undefined') { // trace nodes have level not set 710 level = 0; 711 } else if (level>=JXG.Options.layer.numlayers) { 712 level = JXG.Options.layer.numlayers-1; 713 } 714 this.layer[level].appendChild(node); 715 }; 716 717 JXG.SVGRenderer.prototype.setPropertyPrim = function(node,key,val) { 718 if (key=='stroked') { 719 return; 720 } 721 node.setAttributeNS(null, key, val); 722 }; 723 724 JXG.SVGRenderer.prototype.drawVerticalGrid = function(topLeft, bottomRight, gx, board) { 725 var node = this.createPrim('path', 'gridx'), 726 gridArr = ''; 727 728 while(topLeft.scrCoords[1] < bottomRight.scrCoords[1] + gx - 1) { 729 gridArr += ' M ' + topLeft.scrCoords[1] + ' ' + 0 + ' L ' + topLeft.scrCoords[1] + ' ' + board.canvasHeight+' '; 730 topLeft.setCoordinates(JXG.COORDS_BY_SCREEN, [topLeft.scrCoords[1] + gx, topLeft.scrCoords[2]]); 731 } 732 this.updatePathPrim(node, gridArr, board); 733 return node; 734 }; 735 736 JXG.SVGRenderer.prototype.drawHorizontalGrid = function(topLeft, bottomRight, gy, board) { 737 var node = this.createPrim('path', 'gridy'), 738 gridArr = ''; 739 740 while(topLeft.scrCoords[2] <= bottomRight.scrCoords[2] + gy - 1) { 741 gridArr += ' M ' + 0 + ' ' + topLeft.scrCoords[2] + ' L ' + board.canvasWidth + ' ' + topLeft.scrCoords[2]+' '; 742 topLeft.setCoordinates(JXG.COORDS_BY_SCREEN, [topLeft.scrCoords[1], topLeft.scrCoords[2] + gy]); 743 } 744 this.updatePathPrim(node, gridArr, board); 745 return node; 746 }; 747 748 JXG.SVGRenderer.prototype.appendNodesToElement = function(element, type) { 749 element.rendNode = this.getElementById(element.id); 750 }; 751 752 /** 753 * Sets the buffering as recommended by SVGWG. Until now only Opera supports this and will be ignored by other browsers. 754 * @param {String} type Either 'auto', 'dynamic', or 'static'. For an explanation see {@link http://www.w3.org/TR/SVGTiny12/painting.html#BufferedRenderingProperty}. 755 */ 756 JXG.SVGRenderer.prototype.setBuffering = function(el, type) { 757 el.rendNode.setAttribute('buffered-rendering', type); 758 }; 759