1/* 2 3 P R O C E S S I N G . J S - 0.9.7 4 a port of the Processing visualization language 5 6 License : MIT 7 Developer : John Resig: http://ejohn.org 8 Web Site : http://processingjs.org 9 Java Version : http://processing.org 10 Github Repo. : http://github.com/jeresig/processing-js 11 Bug Tracking : http://processing-js.lighthouseapp.com 12 Mozilla POW! : http://wiki.Mozilla.org/Education/Projects/ProcessingForTheWeb 13 Maintained by : Seneca: http://zenit.senecac.on.ca/wiki/index.php/Processing.js 14 Hyper-Metrix: http://hyper-metrix.com/#Processing 15 BuildingSky: http://weare.buildingsky.net/pages/processing-js 16 17 */ 18 /* 19 * This code searches for all the <script type="application/processing" target="canvasid"> 20 * in your page and loads each script in the target canvas with the proper id. 21 * It is useful to smooth the process of adding Processing code in your page and starting 22 * the Processing.js engine. 23 */ 24 25if (window.addEventListener) { 26 window.addEventListener("load", function() { 27 var scripts = document.getElementsByTagName("script"); 28 var canvasArray = Array.prototype.slice.call(document.getElementsByTagName("canvas")); 29 var canvas; 30 for (var i = 0, j = 0; i < scripts.length; i++) { 31 if (scripts[i].type == "application/processing") { 32 var src = scripts[i].getAttribute("target"); 33 if (src && src.indexOf("#") > -1) { 34 canvas = document.getElementById(src.substr(src.indexOf("#") + 1)); 35 if (canvas) { 36 new Processing(canvas, scripts[i].text); 37 for (var k = 0; k< canvasArray.length; k++) 38 { 39 if (canvasArray[k] === canvas) { 40 // remove the canvas from the array so we dont override it in the else 41 canvasArray.splice(k,1); 42 } 43 } 44 } 45 } else { 46 if (canvasArray.length >= j) { 47 new Processing(canvasArray[j], scripts[i].text); 48 } 49 j++; 50 } 51 } 52 } 53 }, false); 54} 55 56 57(function() { 58 59 var undef; // intentionally left undefined 60 61 var ajax = function ajax(url) { 62 var xhr = new XMLHttpRequest(); 63 xhr.open("GET", url, false); 64 xhr.setRequestHeader("If-Modified-Since", "Fri, 1 Jan 1960 00:00:00 GMT"); 65 xhr.send(null); 66 // failed request? 67 if (xhr.status !== 200 && xhr.status !== 0) { throw ("XMLHttpRequest failed, status code " + xhr.status); } 68 return xhr.responseText; 69 }; 70 71 /* Browsers fixes start */ 72 function fixReplaceByRegExp() { 73 var re = /t/g; 74 if ("t".replace(re,"") !== null && re.exec("t")) { 75 return; // it is not necessary 76 } 77 var _ie_replace = String.prototype.replace; 78 String.prototype.replace = function(searchValue, repaceValue) { 79 var result = _ie_replace.apply(this, arguments); 80 if (searchValue instanceof RegExp && searchValue.global) { 81 searchValue.lastIndex = 0; 82 } 83 return result; 84 }; 85 } 86 87 function fixMatchByRegExp() { 88 var re = /t/g; 89 if ("t".match(re) !== null && re.exec("t")) { 90 return; // it is not necessary 91 } 92 var _ie_match = String.prototype.match; 93 String.prototype.match = function(searchValue) { 94 var result = _ie_match.apply(this, arguments); 95 if(searchValue instanceof RegExp && searchValue.global) { 96 searchValue.lastIndex = 0; 97 } 98 return result; 99 }; 100 } 101 fixReplaceByRegExp(); 102 fixMatchByRegExp(); 103 104 (function fixOperaCreateImageData() { 105 try { 106 if (!("createImageData" in CanvasRenderingContext2D.prototype)) { 107 CanvasRenderingContext2D.prototype.createImageData = function (sw, sh) { 108 return new ImageData(sw, sh); 109 }; 110 } 111 } catch(e) {} 112 }()); 113 /* Browsers fixes end */ 114 115 var PConstants = { 116 X: 0, 117 Y: 1, 118 Z: 2, 119 120 R: 3, 121 G: 4, 122 B: 5, 123 A: 6, 124 125 U: 7, 126 V: 8, 127 128 NX: 9, 129 NY: 10, 130 NZ: 11, 131 132 EDGE: 12, 133 134 // Stroke 135 SR: 13, 136 SG: 14, 137 SB: 15, 138 SA: 16, 139 140 SW: 17, 141 142 // Transformations (2D and 3D) 143 TX: 18, 144 TY: 19, 145 TZ: 20, 146 147 VX: 21, 148 VY: 22, 149 VZ: 23, 150 VW: 24, 151 152 // Material properties 153 AR: 25, 154 AG: 26, 155 AB: 27, 156 157 DR: 3, 158 DG: 4, 159 DB: 5, 160 DA: 6, 161 162 SPR: 28, 163 SPG: 29, 164 SPB: 30, 165 166 SHINE: 31, 167 168 ER: 32, 169 EG: 33, 170 EB: 34, 171 172 BEEN_LIT: 35, 173 174 VERTEX_FIELD_COUNT: 36, 175 176 // Renderers 177 P2D: 1, 178 JAVA2D: 1, 179 WEBGL: 2, 180 P3D: 2, 181 OPENGL: 2, 182 PDF: 0, 183 DXF: 0, 184 185 // Platform IDs 186 OTHER: 0, 187 WINDOWS: 1, 188 MAXOSX: 2, 189 LINUX: 3, 190 191 EPSILON: 0.0001, 192 193 MAX_FLOAT: 3.4028235e+38, 194 MIN_FLOAT: -3.4028235e+38, 195 MAX_INT: 2147483647, 196 MIN_INT: -2147483648, 197 198 PI: Math.PI, 199 TWO_PI: 2 * Math.PI, 200 HALF_PI: Math.PI / 2, 201 THIRD_PI: Math.PI / 3, 202 QUARTER_PI: Math.PI / 4, 203 204 DEG_TO_RAD: Math.PI / 180, 205 RAD_TO_DEG: 180 / Math.PI, 206 207 WHITESPACE: " \t\n\r\f\u00A0", 208 209 // Color modes 210 RGB: 1, 211 ARGB: 2, 212 HSB: 3, 213 ALPHA: 4, 214 CMYK: 5, 215 216 // Image file types 217 TIFF: 0, 218 TARGA: 1, 219 JPEG: 2, 220 GIF: 3, 221 222 // Filter/convert types 223 BLUR: 11, 224 GRAY: 12, 225 INVERT: 13, 226 OPAQUE: 14, 227 POSTERIZE: 15, 228 THRESHOLD: 16, 229 ERODE: 17, 230 DILATE: 18, 231 232 // Blend modes 233 REPLACE: 0, 234 BLEND: 1 << 0, 235 ADD: 1 << 1, 236 SUBTRACT: 1 << 2, 237 LIGHTEST: 1 << 3, 238 DARKEST: 1 << 4, 239 DIFFERENCE: 1 << 5, 240 EXCLUSION: 1 << 6, 241 MULTIPLY: 1 << 7, 242 SCREEN: 1 << 8, 243 OVERLAY: 1 << 9, 244 HARD_LIGHT: 1 << 10, 245 SOFT_LIGHT: 1 << 11, 246 DODGE: 1 << 12, 247 BURN: 1 << 13, 248 249 // Color component bit masks 250 ALPHA_MASK: 0xff000000, 251 RED_MASK: 0x00ff0000, 252 GREEN_MASK: 0x0000ff00, 253 BLUE_MASK: 0x000000ff, 254 255 // Projection matrices 256 CUSTOM: 0, 257 ORTHOGRAPHIC: 2, 258 PERSPECTIVE: 3, 259 260 // Shapes 261 POINT: 2, 262 POINTS: 2, 263 LINE: 4, 264 LINES: 4, 265 TRIANGLE: 8, 266 TRIANGLES: 9, 267 TRIANGLE_STRIP: 10, 268 TRIANGLE_FAN: 11, 269 QUAD: 16, 270 QUADS: 16, 271 QUAD_STRIP: 17, 272 POLYGON: 20, 273 PATH: 21, 274 RECT: 30, 275 ELLIPSE: 31, 276 ARC: 32, 277 SPHERE: 40, 278 BOX: 41, 279 280 GROUP: 0, 281 PRIMITIVE: 1, 282 //PATH: 21, // shared with Shape PATH 283 GEOMETRY: 3, 284 285 // Shape Vertex 286 VERTEX: 0, 287 BEZIER_VERTEX: 1, 288 CURVE_VERTEX: 2, 289 BREAK: 3, 290 CLOSESHAPE: 4, 291 292 // Shape closing modes 293 OPEN: 1, 294 CLOSE: 2, 295 296 // Shape drawing modes 297 CORNER: 0, // Draw mode convention to use (x, y) to (width, height) 298 CORNERS: 1, // Draw mode convention to use (x1, y1) to (x2, y2) coordinates 299 RADIUS: 2, // Draw mode from the center, and using the radius 300 CENTER_RADIUS: 2, // Deprecated! Use RADIUS instead 301 CENTER: 3, // Draw from the center, using second pair of values as the diameter 302 DIAMETER: 3, // Synonym for the CENTER constant. Draw from the center 303 CENTER_DIAMETER: 3, // Deprecated! Use DIAMETER instead 304 305 // Text vertical alignment modes 306 BASELINE: 0, // Default vertical alignment for text placement 307 TOP: 101, // Align text to the top 308 BOTTOM: 102, // Align text from the bottom, using the baseline 309 310 // UV Texture coordinate modes 311 NORMAL: 1, 312 NORMALIZED: 1, 313 IMAGE: 2, 314 315 // Text placement modes 316 MODEL: 4, 317 SHAPE: 5, 318 319 // Stroke modes 320 SQUARE: 'butt', 321 ROUND: 'round', 322 PROJECT: 'square', 323 MITER: 'miter', 324 BEVEL: 'bevel', 325 326 // Lighting modes 327 AMBIENT: 0, 328 DIRECTIONAL: 1, 329 //POINT: 2, Shared with Shape constant 330 SPOT: 3, 331 332 // Key constants 333 334 // Both key and keyCode will be equal to these values 335 BACKSPACE: 8, 336 TAB: 9, 337 ENTER: 10, 338 RETURN: 13, 339 ESC: 27, 340 DELETE: 127, 341 CODED: 0xffff, 342 343 // p.key will be CODED and p.keyCode will be this value 344 SHIFT: 16, 345 CONTROL: 17, 346 ALT: 18, 347 UP: 38, 348 RIGHT: 39, 349 DOWN: 40, 350 LEFT: 37, 351 352 // Cursor types 353 ARROW: 'default', 354 CROSS: 'crosshair', 355 HAND: 'pointer', 356 MOVE: 'move', 357 TEXT: 'text', 358 WAIT: 'wait', 359 NOCURSOR: "url('data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=='), auto", 360 361 // Hints 362 DISABLE_OPENGL_2X_SMOOTH: 1, 363 ENABLE_OPENGL_2X_SMOOTH: -1, 364 ENABLE_OPENGL_4X_SMOOTH: 2, 365 ENABLE_NATIVE_FONTS: 3, 366 DISABLE_DEPTH_TEST: 4, 367 ENABLE_DEPTH_TEST: -4, 368 ENABLE_DEPTH_SORT: 5, 369 DISABLE_DEPTH_SORT: -5, 370 DISABLE_OPENGL_ERROR_REPORT: 6, 371 ENABLE_OPENGL_ERROR_REPORT: -6, 372 ENABLE_ACCURATE_TEXTURES: 7, 373 DISABLE_ACCURATE_TEXTURES: -7, 374 HINT_COUNT: 10, 375 376 // PJS defined constants 377 SINCOS_LENGTH: parseInt(360 / 0.5, 10), 378 PRECISIONB: 15, // fixed point precision is limited to 15 bits!! 379 PRECISIONF: 1 << 15, 380 PREC_MAXVAL: (1 << 15) - 1, 381 PREC_ALPHA_SHIFT: 24 - 15, 382 PREC_RED_SHIFT: 16 - 15, 383 NORMAL_MODE_AUTO: 0, 384 NORMAL_MODE_SHAPE: 1, 385 NORMAL_MODE_VERTEX: 2, 386 MAX_LIGHTS: 8 387 }; 388 389 // Typed Arrays: fallback to WebGL arrays or Native JS arrays if unavailable 390 function setupTypedArray(name, fallback) { 391 // check if TypedArray exists 392 if (typeof this[name] !== "function") { 393 // nope.. check if WebGLArray exists 394 if (typeof this[fallback] === "function") { 395 this[name] = this[fallback]; 396 } else { 397 // nope.. set as Native JS array 398 this[name] = function(obj) { 399 if (obj instanceof Array) { 400 return obj; 401 } else if (typeof obj === "number") { 402 return new Array(obj); 403 } 404 }; 405 } 406 } 407 } 408 409 setupTypedArray("Float32Array", "WebGLFloatArray"); 410 setupTypedArray("Uint16Array", "WebGLUnsignedShortArray"); 411 setupTypedArray("Uint8Array", "WebGLUnsignedByteArray"); 412 413 var ArrayList = function() { 414 function createArrayList(args) { 415 var arr = []; 416 for (var i = 0; i < args[0]; i++) { 417 arr[i] = (args.length > 1 ? createArrayList(args.slice(1)) : 0 ); 418 } 419 420 arr.get = function(i) { 421 return this[i]; 422 }; 423 424 arr.contains = function(item) { 425 return this.indexOf(item) !== -1; 426 }; 427 428 arr.add = function() { 429 if (arguments.length === 1) { 430 this.push(arguments[0]); // for add(Object) 431 } else if (arguments.length === 2) { 432 if (typeof arguments[0] === 'number') { 433 if (arguments[0] >= 0 && arguments[0] <= this.length) { 434 this.splice(arguments[0], 0, arguments[1]); // for add(i, Object) 435 } else { 436 throw(arguments[0] + " is not a valid index"); 437 } 438 } else { 439 throw(typeof arguments[0] + " is not a number"); 440 } 441 } else { 442 throw("Please use the proper number of parameters."); 443 } 444 }; 445 446 arr.set = function() { 447 if (arguments.length === 2) { 448 if (typeof arguments[0] === 'number') { 449 if (arguments[0] >= 0 && arguments[0] < this.length) { 450 this.splice(arguments[0], 1, arguments[1]); 451 } else { 452 throw(arguments[0] + " is not a valid index."); 453 } 454 } else { 455 throw(typeof arguments[0] + " is not a number"); 456 } 457 } else { 458 throw("Please use the proper number of parameters."); 459 } 460 }; 461 462 arr.size = function() { 463 return this.length; 464 }; 465 466 arr.clear = function() { 467 this.length = 0; 468 }; 469 470 arr.remove = function(i) { 471 return this.splice(i, 1)[0]; 472 }; 473 474 arr.isEmpty = function() { 475 return !this.length; 476 }; 477 478 arr.clone = function() { 479 return this.slice(0); 480 }; 481 482 arr.toArray = function() { 483 return this.slice(0); 484 }; 485 486 return arr; 487 } 488 489 return createArrayList(Array.prototype.slice.call(arguments)); 490 }; 491 492 var HashMap = (function() { 493 function virtHashCode(obj) { 494 if (obj.constructor === String) { 495 var hash = 0; 496 for (var i = 0; i < obj.length; ++i) { 497 hash = (hash * 31 + obj.charCodeAt(i)) & 0xFFFFFFFF; 498 } 499 return hash; 500 } else if (typeof(obj) !== "object") { 501 return obj & 0xFFFFFFFF; 502 } else if ("hashCode" in obj) { 503 return obj.hashCode.call(obj); 504 } else { 505 if (obj.$id === undef) { 506 obj.$id = ((Math.floor(Math.random() * 0x10000) - 0x8000) << 16) | Math.floor(Math.random() * 0x10000); 507 } 508 return obj.$id; 509 } 510 } 511 512 function virtEquals(obj, other) { 513 if (obj === null || other === null) { 514 return (obj === null) && (other === null); 515 } else if (obj.constructor === String) { 516 return obj === other; 517 } else if (typeof(obj) !== "object") { 518 return obj === other; 519 } else if ("equals" in obj) { 520 return obj.equals.call(obj, other); 521 } else { 522 return obj === other; 523 } 524 } 525 526 function HashMap() { 527 if (arguments.length === 1 && arguments[0].constructor === HashMap) { 528 return arguments[0].clone(); 529 } 530 531 var initialCapacity = arguments.length > 0 ? arguments[0] : 16; 532 var loadFactor = arguments.length > 1 ? arguments[1] : 0.75; 533 var buckets = new Array(initialCapacity); 534 var count = 0; 535 var hashMap = this; 536 537 function ensureLoad() { 538 if (count <= loadFactor * buckets.length) { 539 return; 540 } 541 var allEntries = []; 542 for (var i = 0; i < buckets.length; ++i) { 543 if (buckets[i] !== undef) { 544 allEntries = allEntries.concat(buckets[i]); 545 } 546 } 547 buckets = new Array(buckets.length * 2); 548 for (var j = 0; j < allEntries.length; ++j) { 549 var index = virtHashCode(allEntries[j].key) % buckets.length; 550 var bucket = buckets[index]; 551 if (bucket === undef) { 552 buckets[index] = bucket = []; 553 } 554 bucket.push(allEntries[j]); 555 } 556 } 557 558 function Iterator(conversion, removeItem) { 559 var bucketIndex = 0; 560 var itemIndex = -1; 561 var endOfBuckets = false; 562 563 function findNext() { 564 while (!endOfBuckets) { 565 ++itemIndex; 566 if (bucketIndex >= buckets.length) { 567 endOfBuckets = true; 568 } else if (buckets[bucketIndex] === undef || itemIndex >= buckets[bucketIndex].length) { 569 itemIndex = -1; 570 ++bucketIndex; 571 } else { 572 return; 573 } 574 } 575 } 576 577 this.hasNext = function() { 578 return !endOfBuckets; 579 }; 580 581 this.next = function() { 582 var result = conversion(buckets[bucketIndex][itemIndex]); 583 findNext(); 584 return result; 585 }; 586 587 this.remove = function() { 588 removeItem(this.next()); 589 --itemIndex; 590 }; 591 592 findNext(); 593 } 594 595 function Set(conversion, isIn, removeItem) { 596 this.clear = function() { 597 hashMap.clear(); 598 }; 599 600 this.contains = function(o) { 601 return isIn(o); 602 }; 603 604 this.containsAll = function(o) { 605 var it = o.iterator(); 606 while (it.hasNext()) { 607 if (!this.contains(it.next())) { 608 return false; 609 } 610 } 611 return true; 612 }; 613 614 this.isEmpty = function() { 615 return hashMap.isEmpty(); 616 }; 617 618 this.iterator = function() { 619 return new Iterator(conversion, removeItem); 620 }; 621 622 this.remove = function(o) { 623 if (this.contains(o)) { 624 removeItem(o); 625 return true; 626 } 627 return false; 628 }; 629 630 this.removeAll = function(c) { 631 var it = c.iterator(); 632 var changed = false; 633 while (it.hasNext()) { 634 var item = it.next(); 635 if (this.contains(item)) { 636 removeItem(item); 637 changed = true; 638 } 639 } 640 return true; 641 }; 642 643 this.retainAll = function(c) { 644 var it = this.iterator(); 645 var toRemove = []; 646 while (it.hasNext()) { 647 var entry = it.next(); 648 if (!c.contains(entry)) { 649 toRemove.push(entry); 650 } 651 } 652 for (var i = 0; i < toRemove.length; ++i) { 653 removeItem(toRemove[i]); 654 } 655 return toRemove.length > 0; 656 }; 657 658 this.size = function() { 659 return hashMap.size(); 660 }; 661 662 this.toArray = function() { 663 var result = new ArrayList(0); 664 var it = this.iterator(); 665 while (it.hasNext()) { 666 result.push(it.next()); 667 } 668 return result; 669 }; 670 } 671 672 function Entry(pair) { 673 this._isIn = function(map) { 674 return map === hashMap && (pair.removed === undef); 675 }; 676 677 this.equals = function(o) { 678 return virtEquals(pair.key, o.getKey()); 679 }; 680 681 this.getKey = function() { 682 return pair.key; 683 }; 684 685 this.getValue = function() { 686 return pair.value; 687 }; 688 689 this.hashCode = function(o) { 690 return virtHashCode(pair.key); 691 }; 692 693 this.setValue = function(value) { 694 var old = pair.value; 695 pair.value = value; 696 return old; 697 }; 698 } 699 700 this.clear = function() { 701 count = 0; 702 buckets = new Array(initialCapacity); 703 }; 704 705 this.clone = function() { 706 var map = new HashMap(); 707 map.putAll(this); 708 return map; 709 }; 710 711 this.containsKey = function(key) { 712 var index = virtHashCode(key) % buckets.length; 713 var bucket = buckets[index]; 714 if (bucket === undef) { 715 return false; 716 } 717 for (var i = 0; i < bucket.length; ++i) { 718 if (virtEquals(bucket[i].key, key)) { 719 return true; 720 } 721 } 722 return false; 723 }; 724 725 this.containsValue = function(value) { 726 for (var i = 0; i < buckets.length; ++i) { 727 var bucket = buckets[i]; 728 if (bucket === undef) { 729 continue; 730 } 731 for (var j = 0; j < bucket.length; ++j) { 732 if (virtEquals(bucket[j].value, value)) { 733 return true; 734 } 735 } 736 } 737 return false; 738 }; 739 740 this.entrySet = function() { 741 return new Set( 742 743 function(pair) { 744 return new Entry(pair); 745 }, 746 747 function(pair) { 748 return pair.constructor === Entry && pair._isIn(hashMap); 749 }, 750 751 function(pair) { 752 return hashMap.remove(pair.getKey()); 753 }); 754 }; 755 756 this.get = function(key) { 757 var index = virtHashCode(key) % buckets.length; 758 var bucket = buckets[index]; 759 if (bucket === undef) { 760 return null; 761 } 762 for (var i = 0; i < bucket.length; ++i) { 763 if (virtEquals(bucket[i].key, key)) { 764 return bucket[i].value; 765 } 766 } 767 return null; 768 }; 769 770 this.isEmpty = function() { 771 return count === 0; 772 }; 773 774 this.keySet = function() { 775 return new Set( 776 777 function(pair) { 778 return pair.key; 779 }, 780 781 function(key) { 782 return hashMap.containsKey(key); 783 }, 784 785 function(key) { 786 return hashMap.remove(key); 787 }); 788 }; 789 790 this.put = function(key, value) { 791 var index = virtHashCode(key) % buckets.length; 792 var bucket = buckets[index]; 793 if (bucket === undef) { 794 ++count; 795 buckets[index] = [{ 796 key: key, 797 value: value 798 }]; 799 ensureLoad(); 800 return null; 801 } 802 for (var i = 0; i < bucket.length; ++i) { 803 if (virtEquals(bucket[i].key, key)) { 804 var previous = bucket[i].value; 805 bucket[i].value = value; 806 return previous; 807 } 808 } 809 ++count; 810 bucket.push({ 811 key: key, 812 value: value 813 }); 814 ensureLoad(); 815 return null; 816 }; 817 818 this.putAll = function(m) { 819 var it = m.entrySet().iterator(); 820 while (it.hasNext()) { 821 var entry = it.next(); 822 this.put(entry.getKey(), entry.getValue()); 823 } 824 }; 825 826 this.remove = function(key) { 827 var index = virtHashCode(key) % buckets.length; 828 var bucket = buckets[index]; 829 if (bucket === undef) { 830 return null; 831 } 832 for (var i = 0; i < bucket.length; ++i) { 833 if (virtEquals(bucket[i].key, key)) { 834 --count; 835 var previous = bucket[i].value; 836 bucket[i].removed = true; 837 if (bucket.length > 1) { 838 bucket.splice(i, 1); 839 } else { 840 buckets[index] = undef; 841 } 842 return previous; 843 } 844 } 845 return null; 846 }; 847 848 this.size = function() { 849 return count; 850 }; 851 852 this.values = function() { 853 var result = new ArrayList(0); 854 var it = this.entrySet().iterator(); 855 while (it.hasNext()) { 856 var entry = it.next(); 857 result.push(entry.getValue()); 858 } 859 return result; 860 }; 861 } 862 863 return HashMap; 864 }()); 865 866 var PVector = (function() { 867 function PVector(x, y, z) { 868 this.x = x || 0; 869 this.y = y || 0; 870 this.z = z || 0; 871 } 872 873 function createPVectorMethod(method) { 874 return function(v1, v2) { 875 var v = v1.get(); 876 v[method](v2); 877 return v; 878 }; 879 } 880 881 function createSimplePVectorMethod(method) { 882 return function(v1, v2) { 883 return v1[method](v2); 884 }; 885 } 886 887 var simplePVMethods = "dist dot cross".split(" "); 888 var method = simplePVMethods.length; 889 890 PVector.angleBetween = function(v1, v2) { 891 return Math.acos(v1.dot(v2) / (v1.mag() * v2.mag())); 892 }; 893 894 // Common vector operations for PVector 895 PVector.prototype = { 896 set: function(v, y, z) { 897 if (arguments.length === 1) { 898 this.set(v.x || v[0], v.y || v[1], v.z || v[2]); 899 } else { 900 this.x = v; 901 this.y = y; 902 this.z = z; 903 } 904 }, 905 get: function() { 906 return new PVector(this.x, this.y, this.z); 907 }, 908 mag: function() { 909 return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z); 910 }, 911 add: function(v, y, z) { 912 if (arguments.length === 3) { 913 this.x += v; 914 this.y += y; 915 this.z += z; 916 } else if (arguments.length === 1) { 917 this.x += v.x; 918 this.y += v.y; 919 this.z += v.z; 920 } 921 }, 922 sub: function(v, y, z) { 923 if (arguments.length === 3) { 924 this.x -= v; 925 this.y -= y; 926 this.z -= z; 927 } else if (arguments.length === 1) { 928 this.x -= v.x; 929 this.y -= v.y; 930 this.z -= v.z; 931 } 932 }, 933 mult: function(v) { 934 if (typeof v === 'number') { 935 this.x *= v; 936 this.y *= v; 937 this.z *= v; 938 } else if (typeof v === 'object') { 939 this.x *= v.x; 940 this.y *= v.y; 941 this.z *= v.z; 942 } 943 }, 944 div: function(v) { 945 if (typeof v === 'number') { 946 this.x /= v; 947 this.y /= v; 948 this.z /= v; 949 } else if (typeof v === 'object') { 950 this.x /= v.x; 951 this.y /= v.y; 952 this.z /= v.z; 953 } 954 }, 955 dist: function(v) { 956 var dx = this.x - v.x, 957 dy = this.y - v.y, 958 dz = this.z - v.z; 959 return Math.sqrt(dx * dx + dy * dy + dz * dz); 960 }, 961 dot: function(v, y, z) { 962 if (arguments.length === 3) { 963 return (this.x * v + this.y * y + this.z * z); 964 } else if (arguments.length === 1) { 965 return (this.x * v.x + this.y * v.y + this.z * v.z); 966 } 967 }, 968 cross: function(v) { 969 return new PVector(this.y * v.z - v.y * this.z, 970 this.z * v.x - v.z * this.x, 971 this.x * v.y - v.x * this.y); 972 }, 973 normalize: function() { 974 var m = this.mag(); 975 if (m > 0) { 976 this.div(m); 977 } 978 }, 979 limit: function(high) { 980 if (this.mag() > high) { 981 this.normalize(); 982 this.mult(high); 983 } 984 }, 985 heading2D: function() { 986 return (-Math.atan2(-this.y, this.x)); 987 }, 988 toString: function() { 989 return "[" + this.x + ", " + this.y + ", " + this.z + "]"; 990 }, 991 array: function() { 992 return [this.x, this.y, this.z]; 993 } 994 }; 995 996 while (method--) { 997 PVector[simplePVMethods[method]] = createSimplePVectorMethod(simplePVMethods[method]); 998 } 999 1000 for (method in PVector.prototype) { 1001 if (PVector.prototype.hasOwnProperty(method) && !PVector.hasOwnProperty(method)) { 1002 PVector[method] = createPVectorMethod(method); 1003 } 1004 } 1005 1006 return PVector; 1007 }()); 1008 1009 var Processing = this.Processing = function Processing(curElement, aCode) { 1010 var p = this; 1011 1012 // Include Package Classes -- do this differently in the future. 1013 p.ArrayList = ArrayList; 1014 p.HashMap = HashMap; 1015 p.PVector = PVector; 1016 //p.PImage = PImage; // TODO 1017 //p.PShape = PShape; // TODO 1018 //p.PShapeSVG = PShapeSVG; // TODO 1019 1020 // PJS specific (non-p5) methods and properties to externalize 1021 p.externals = { 1022 canvas: curElement, 1023 context: undef, 1024 sketch: undef, 1025 onblur: function() {}, 1026 onfocus: function() {} 1027 }; 1028 1029 p.name = 'Processing.js Instance'; // Set Processing defaults / environment variables 1030 p.use3DContext = false; // default '2d' canvas context 1031 1032 p.focused = true; 1033 p.breakShape = false; 1034 1035 // Glyph path storage for textFonts 1036 p.glyphTable = {}; 1037 1038 // Global vars for tracking mouse position 1039 p.pmouseX = 0; 1040 p.pmouseY = 0; 1041 p.mouseX = 0; 1042 p.mouseY = 0; 1043 p.mouseButton = 0; 1044 p.mouseScroll = 0; 1045 1046 // Undefined event handlers to be replaced by user when needed 1047 p.mouseClicked = undef; 1048 p.mouseDragged = undef; 1049 p.mouseMoved = undef; 1050 p.mousePressed = undef; 1051 p.mouseReleased = undef; 1052 p.mouseScrolled = undef; 1053 p.key = undef; 1054 p.keyCode = undef; 1055 p.keyPressed = undef; 1056 p.keyReleased = undef; 1057 p.keyTyped = undef; 1058 p.draw = undef; 1059 p.setup = undef; 1060 1061 // Remapped vars 1062 p.__mousePressed = false; 1063 p.__keyPressed = false; 1064 p.__frameRate = 0; 1065 1066 // The current animation frame 1067 p.frameCount = 0; 1068 1069 // The height/width of the canvas 1070 p.width = curElement.width - 0; 1071 p.height = curElement.height - 0; 1072 1073 p.defineProperty = function(obj, name, desc) { 1074 if("defineProperty" in Object) { 1075 Object.defineProperty(obj, name, desc); 1076 } else { 1077 if (desc.hasOwnProperty("get")) { 1078 obj.__defineGetter__(name, desc.get); 1079 } 1080 if (desc.hasOwnProperty("set")) { 1081 obj.__defineSetter__(name, desc.set); 1082 } 1083 } 1084 }; 1085 1086 // "Private" variables used to maintain state 1087 var curContext, 1088 curSketch, 1089 online = true, 1090 doFill = true, 1091 fillStyle = [1.0, 1.0, 1.0, 1.0], 1092 currentFillColor = 0xFFFFFFFF, 1093 isFillDirty = true, 1094 doStroke = true, 1095 strokeStyle = [0.8, 0.8, 0.8, 1.0], 1096 currentStrokeColor = 0xFFFDFDFD, 1097 isStrokeDirty = true, 1098 lineWidth = 1, 1099 loopStarted = false, 1100 doLoop = true, 1101 looping = 0, 1102 curRectMode = PConstants.CORNER, 1103 curEllipseMode = PConstants.CENTER, 1104 normalX = 0, 1105 normalY = 0, 1106 normalZ = 0, 1107 normalMode = PConstants.NORMAL_MODE_AUTO, 1108 inDraw = false, 1109 curFrameRate = 60, 1110 curCursor = PConstants.ARROW, 1111 oldCursor = curElement.style.cursor, 1112 curMsPerFrame = 1, 1113 curShape = PConstants.POLYGON, 1114 curShapeCount = 0, 1115 curvePoints = [], 1116 curTightness = 0, 1117 curveDet = 20, 1118 curveInited = false, 1119 bezDetail = 20, 1120 colorModeA = 255, 1121 colorModeX = 255, 1122 colorModeY = 255, 1123 colorModeZ = 255, 1124 pathOpen = false, 1125 mouseDragging = false, 1126 curColorMode = PConstants.RGB, 1127 curTint = function() {}, 1128 curTextSize = 12, 1129 curTextFont = "Arial", 1130 getLoaded = false, 1131 start = new Date().getTime(), 1132 timeSinceLastFPS = start, 1133 framesSinceLastFPS = 0, 1134 textcanvas, 1135 curveBasisMatrix, 1136 curveToBezierMatrix, 1137 curveDrawMatrix, 1138 bezierDrawMatrix, 1139 bezierBasisInverse, 1140 bezierBasisMatrix, 1141 // Shaders 1142 programObject3D, 1143 programObject2D, 1144 programObjectUnlitShape, 1145 boxBuffer, 1146 boxNormBuffer, 1147 boxOutlineBuffer, 1148 rectBuffer, 1149 rectNormBuffer, 1150 sphereBuffer, 1151 lineBuffer, 1152 fillBuffer, 1153 fillColorBuffer, 1154 strokeColorBuffer, 1155 pointBuffer, 1156 shapeTexVBO, 1157 canTex, // texture for createGraphics 1158 curTexture = {width:0,height:0}, 1159 curTextureMode = PConstants.IMAGE, 1160 usingTexture = false, 1161 textBuffer, 1162 textureBuffer, 1163 indexBuffer, 1164 // Text alignment 1165 horizontalTextAlignment = PConstants.LEFT, 1166 verticalTextAlignment = PConstants.BASELINE, 1167 baselineOffset = 0.2, // percent 1168 tMode = PConstants.MODEL, 1169 // Pixels cache 1170 originalContext, 1171 proxyContext = null, 1172 isContextReplaced = false, 1173 setPixelsCached, 1174 maxPixelsCached = 1000, 1175 codedKeys = [PConstants.SHIFT, PConstants.CONTROL, PConstants.ALT, PConstants.UP, PConstants.RIGHT, PConstants.DOWN, PConstants.LEFT]; 1176 1177 // Get padding and border style widths for mouse offsets 1178 var stylePaddingLeft, stylePaddingTop, styleBorderLeft, styleBorderTop; 1179 1180 if (document.defaultView && document.defaultView.getComputedStyle) { 1181 stylePaddingLeft = parseInt(document.defaultView.getComputedStyle(curElement, null)['paddingLeft'], 10) || 0; 1182 stylePaddingTop = parseInt(document.defaultView.getComputedStyle(curElement, null)['paddingTop'], 10) || 0; 1183 styleBorderLeft = parseInt(document.defaultView.getComputedStyle(curElement, null)['borderLeftWidth'], 10) || 0; 1184 styleBorderTop = parseInt(document.defaultView.getComputedStyle(curElement, null)['borderTopWidth'], 10) || 0; 1185 } 1186 1187 // User can only have MAX_LIGHTS lights 1188 var lightCount = 0; 1189 1190 //sphere stuff 1191 var sphereDetailV = 0, 1192 sphereDetailU = 0, 1193 sphereX = [], 1194 sphereY = [], 1195 sphereZ = [], 1196 sinLUT = new Array(PConstants.SINCOS_LENGTH), 1197 cosLUT = new Array(PConstants.SINCOS_LENGTH), 1198 sphereVerts, 1199 sphereNorms; 1200 1201 // Camera defaults and settings 1202 var cam, 1203 cameraInv, 1204 forwardTransform, 1205 reverseTransform, 1206 modelView, 1207 modelViewInv, 1208 userMatrixStack, 1209 inverseCopy, 1210 projection, 1211 manipulatingCamera = false, 1212 frustumMode = false, 1213 cameraFOV = 60 * (Math.PI / 180), 1214 cameraX = curElement.width / 2, 1215 cameraY = curElement.height / 2, 1216 cameraZ = cameraY / Math.tan(cameraFOV / 2), 1217 cameraNear = cameraZ / 10, 1218 cameraFar = cameraZ * 10, 1219 cameraAspect = curElement.width / curElement.height; 1220 1221 var vertArray = [], 1222 curveVertArray = [], 1223 curveVertCount = 0, 1224 isCurve = false, 1225 isBezier = false, 1226 firstVert = true; 1227 1228 //PShape stuff 1229 var curShapeMode = PConstants.CORNER; 1230 1231 var colors = { 1232 aliceblue: "#f0f8ff", 1233 antiquewhite: "#faebd7", 1234 aqua: "#00ffff", 1235 aquamarine: "#7fffd4", 1236 azure: "#f0ffff", 1237 beige: "#f5f5dc", 1238 bisque: "#ffe4c4", 1239 black: "#000000", 1240 blanchedalmond: "#ffebcd", 1241 blue: "#0000ff", 1242 blueviolet: "#8a2be2", 1243 brown: "#a52a2a", 1244 burlywood: "#deb887", 1245 cadetblue: "#5f9ea0", 1246 chartreuse: "#7fff00", 1247 chocolate: "#d2691e", 1248 coral: "#ff7f50", 1249 cornflowerblue: "#6495ed", 1250 cornsilk: "#fff8dc", 1251 crimson: "#dc143c", 1252 cyan: "#00ffff", 1253 darkblue: "#00008b", 1254 darkcyan: "#008b8b", 1255 darkgoldenrod: "#b8860b", 1256 darkgray: "#a9a9a9", 1257 darkgreen: "#006400", 1258 darkkhaki: "#bdb76b", 1259 darkmagenta: "#8b008b", 1260 darkolivegreen: "#556b2f", 1261 darkorange: "#ff8c00", 1262 darkorchid: "#9932cc", 1263 darkred: "#8b0000", 1264 darksalmon: "#e9967a", 1265 darkseagreen: "#8fbc8f", 1266 darkslateblue: "#483d8b", 1267 darkslategray: "#2f4f4f", 1268 darkturquoise: "#00ced1", 1269 darkviolet: "#9400d3", 1270 deeppink: "#ff1493", 1271 deepskyblue: "#00bfff", 1272 dimgray: "#696969", 1273 dodgerblue: "#1e90ff", 1274 firebrick: "#b22222", 1275 floralwhite: "#fffaf0", 1276 forestgreen: "#228b22", 1277 fuchsia: "#ff00ff", 1278 gainsboro: "#dcdcdc", 1279 ghostwhite: "#f8f8ff", 1280 gold: "#ffd700", 1281 goldenrod: "#daa520", 1282 gray: "#808080", 1283 green: "#008000", 1284 greenyellow: "#adff2f", 1285 honeydew: "#f0fff0", 1286 hotpink: "#ff69b4", 1287 indianred: "#cd5c5c", 1288 indigo: "#4b0082", 1289 ivory: "#fffff0", 1290 khaki: "#f0e68c", 1291 lavender: "#e6e6fa", 1292 lavenderblush: "#fff0f5", 1293 lawngreen: "#7cfc00", 1294 lemonchiffon: "#fffacd", 1295 lightblue: "#add8e6", 1296 lightcoral: "#f08080", 1297 lightcyan: "#e0ffff", 1298 lightgoldenrodyellow: "#fafad2", 1299 lightgrey: "#d3d3d3", 1300 lightgreen: "#90ee90", 1301 lightpink: "#ffb6c1", 1302 lightsalmon: "#ffa07a", 1303 lightseagreen: "#20b2aa", 1304 lightskyblue: "#87cefa", 1305 lightslategray: "#778899", 1306 lightsteelblue: "#b0c4de", 1307 lightyellow: "#ffffe0", 1308 lime: "#00ff00", 1309 limegreen: "#32cd32", 1310 linen: "#faf0e6", 1311 magenta: "#ff00ff", 1312 maroon: "#800000", 1313 mediumaquamarine: "#66cdaa", 1314 mediumblue: "#0000cd", 1315 mediumorchid: "#ba55d3", 1316 mediumpurple: "#9370d8", 1317 mediumseagreen: "#3cb371", 1318 mediumslateblue: "#7b68ee", 1319 mediumspringgreen: "#00fa9a", 1320 mediumturquoise: "#48d1cc", 1321 mediumvioletred: "#c71585", 1322 midnightblue: "#191970", 1323 mintcream: "#f5fffa", 1324 mistyrose: "#ffe4e1", 1325 moccasin: "#ffe4b5", 1326 navajowhite: "#ffdead", 1327 navy: "#000080", 1328 oldlace: "#fdf5e6", 1329 olive: "#808000", 1330 olivedrab: "#6b8e23", 1331 orange: "#ffa500", 1332 orangered: "#ff4500", 1333 orchid: "#da70d6", 1334 palegoldenrod: "#eee8aa", 1335 palegreen: "#98fb98", 1336 paleturquoise: "#afeeee", 1337 palevioletred: "#d87093", 1338 papayawhip: "#ffefd5", 1339 peachpuff: "#ffdab9", 1340 peru: "#cd853f", 1341 pink: "#ffc0cb", 1342 plum: "#dda0dd", 1343 powderblue: "#b0e0e6", 1344 purple: "#800080", 1345 red: "#ff0000", 1346 rosybrown: "#bc8f8f", 1347 royalblue: "#4169e1", 1348 saddlebrown: "#8b4513", 1349 salmon: "#fa8072", 1350 sandybrown: "#f4a460", 1351 seagreen: "#2e8b57", 1352 seashell: "#fff5ee", 1353 sienna: "#a0522d", 1354 silver: "#c0c0c0", 1355 skyblue: "#87ceeb", 1356 slateblue: "#6a5acd", 1357 slategray: "#708090", 1358 snow: "#fffafa", 1359 springgreen: "#00ff7f", 1360 steelblue: "#4682b4", 1361 tan: "#d2b48c", 1362 teal: "#008080", 1363 thistle: "#d8bfd8", 1364 tomato: "#ff6347", 1365 turquoise: "#40e0d0", 1366 violet: "#ee82ee", 1367 wheat: "#f5deb3", 1368 white: "#ffffff", 1369 whitesmoke: "#f5f5f5", 1370 yellow: "#ffff00", 1371 yellowgreen: "#9acd32" 1372 }; 1373 1374 // Stores states for pushStyle() and popStyle(). 1375 var styleArray = new Array(0); 1376 1377 // Vertices are specified in a counter-clockwise order 1378 // triangles are in this order: back, front, right, bottom, left, top 1379 var boxVerts = new Float32Array([ 1380 0.5, 0.5, -0.5, 0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5, 1381 -0.5, 0.5, -0.5, 0.5, 0.5, -0.5, 0.5, 0.5, 0.5, -0.5, 0.5, 0.5, 1382 -0.5, -0.5, 0.5, -0.5, -0.5, 0.5, 0.5, -0.5, 0.5, 0.5, 0.5, 0.5, 1383 0.5, 0.5, -0.5, 0.5, 0.5, 0.5, 0.5, -0.5, 0.5, 0.5, -0.5, 0.5, 1384 0.5, -0.5, -0.5, 0.5, 0.5, -0.5, 0.5, -0.5, -0.5, 0.5, -0.5, 0.5, 1385 -0.5, -0.5, 0.5, -0.5, -0.5, 0.5, -0.5, -0.5, -0.5, 0.5, -0.5, -0.5, 1386 -0.5, -0.5, -0.5, -0.5, -0.5, 0.5, -0.5, 0.5, 0.5, -0.5, 0.5, 0.5, 1387 -0.5, 0.5, -0.5, -0.5, -0.5, -0.5, 0.5, 0.5, 0.5, 0.5, 0.5, -0.5, 1388 -0.5, 0.5, -0.5, -0.5, 0.5, -0.5, -0.5, 0.5, 0.5, 0.5, 0.5, 0.5]); 1389 1390 var boxOutlineVerts = new Float32Array([ 1391 0.5, 0.5, 0.5, 0.5, -0.5, 0.5, 0.5, 0.5, -0.5, 0.5, -0.5, -0.5, 1392 -0.5, 0.5, -0.5, -0.5, -0.5, -0.5, -0.5, 0.5, 0.5, -0.5, -0.5, 0.5, 1393 0.5, 0.5, 0.5, 0.5, 0.5, -0.5, 0.5, 0.5, -0.5, -0.5, 0.5, -0.5, 1394 -0.5, 0.5, -0.5, -0.5, 0.5, 0.5, -0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 1395 0.5, -0.5, 0.5, 0.5, -0.5, -0.5, 0.5, -0.5, -0.5, -0.5, -0.5, -0.5, 1396 -0.5, -0.5, -0.5, -0.5, -0.5, 0.5, -0.5, -0.5, 0.5, 0.5, -0.5, 0.5]); 1397 1398 var boxNorms = new Float32Array([ 1399 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 1400 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1401 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1402 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 1403 -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, 1404 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0]); 1405 1406 // These verts are used for the fill and stroke using TRIANGLE_FAN and LINE_LOOP 1407 var rectVerts = new Float32Array([0,0,0, 0,1,0, 1,1,0, 1,0,0]); 1408 1409 var rectNorms = new Float32Array([0,0,-1, 0,0,-1, 0,0,-1, 0,0,-1]); 1410 1411 // Vertex shader for points and lines 1412 var vShaderSrcUnlitShape = 1413 "varying vec4 frontColor;" + 1414 1415 "attribute vec3 aVertex;" + 1416 "attribute vec4 aColor;" + 1417 1418 "uniform mat4 uView;" + 1419 "uniform mat4 uProjection;" + 1420 1421 "void main(void) {" + 1422 " frontColor = aColor;" + 1423 " gl_Position = uProjection * uView * vec4(aVertex, 1.0);" + 1424 "}"; 1425 1426 var fShaderSrcUnlitShape = 1427 "#ifdef GL_ES\n" + 1428 "precision highp float;\n" + 1429 "#endif\n" + 1430 1431 "varying vec4 frontColor;" + 1432 1433 "void main(void){" + 1434 " gl_FragColor = frontColor;" + 1435 "}"; 1436 1437 // Vertex shader for points and lines 1438 var vertexShaderSource2D = 1439 "varying vec4 frontColor;" + 1440 1441 "attribute vec3 Vertex;" + 1442 "attribute vec2 aTextureCoord;" + 1443 "uniform vec4 color;" + 1444 1445 "uniform mat4 model;" + 1446 "uniform mat4 view;" + 1447 "uniform mat4 projection;" + 1448 "uniform float pointSize;" + 1449 "varying vec2 vTextureCoord;"+ 1450 1451 "void main(void) {" + 1452 " gl_PointSize = pointSize;" + 1453 " frontColor = color;" + 1454 " gl_Position = projection * view * model * vec4(Vertex, 1.0);" + 1455 " vTextureCoord = aTextureCoord;" + 1456 "}"; 1457 1458 var fragmentShaderSource2D = 1459 "#ifdef GL_ES\n" + 1460 "precision highp float;\n" + 1461 "#endif\n" + 1462 1463 "varying vec4 frontColor;" + 1464 "varying vec2 vTextureCoord;"+ 1465 1466 "uniform sampler2D uSampler;"+ 1467 "uniform int picktype;"+ 1468 1469 "void main(void){" + 1470 " if(picktype == 0){"+ 1471 " gl_FragColor = frontColor;" + 1472 " }" + 1473 " else if(picktype == 1){"+ 1474 " float alpha = texture2D(uSampler, vTextureCoord).a;"+ 1475 " gl_FragColor = vec4(frontColor.rgb*alpha, alpha);\n"+ 1476 " }"+ 1477 "}"; 1478 1479 // Vertex shader for boxes and spheres 1480 var vertexShaderSource3D = 1481 "varying vec4 frontColor;" + 1482 1483 "attribute vec3 Vertex;" + 1484 "attribute vec3 Normal;" + 1485 "attribute vec4 aColor;" + 1486 "attribute vec2 aTexture;" + 1487 "varying vec2 vTexture;" + 1488 1489 "uniform vec4 color;" + 1490 1491 "uniform bool usingMat;" + 1492 "uniform vec3 specular;" + 1493 "uniform vec3 mat_emissive;" + 1494 "uniform vec3 mat_ambient;" + 1495 "uniform vec3 mat_specular;" + 1496 "uniform float shininess;" + 1497 1498 "uniform mat4 model;" + 1499 "uniform mat4 view;" + 1500 "uniform mat4 projection;" + 1501 "uniform mat4 normalTransform;" + 1502 1503 "uniform int lightCount;" + 1504 "uniform vec3 falloff;" + 1505 1506 "struct Light {" + 1507 " bool dummy;" + 1508 " int type;" + 1509 " vec3 color;" + 1510 " vec3 position;" + 1511 " vec3 direction;" + 1512 " float angle;" + 1513 " vec3 halfVector;" + 1514 " float concentration;" + 1515 "};" + 1516 "uniform Light lights[8];" + 1517 1518 "void AmbientLight( inout vec3 totalAmbient, in vec3 ecPos, in Light light ) {" + 1519 // Get the vector from the light to the vertex 1520 // Get the distance from the current vector to the light position 1521 " float d = length( light.position - ecPos );" + 1522 " float attenuation = 1.0 / ( falloff[0] + ( falloff[1] * d ) + ( falloff[2] * d * d ));" + " totalAmbient += light.color * attenuation;" + 1523 "}" + 1524 1525 "void DirectionalLight( inout vec3 col, in vec3 ecPos, inout vec3 spec, in vec3 vertNormal, in Light light ) {" + 1526 " float powerfactor = 0.0;" + 1527 " float nDotVP = max(0.0, dot( vertNormal, light.position ));" + 1528 " float nDotVH = max(0.0, dot( vertNormal, normalize( light.position-ecPos )));" + 1529 1530 " if( nDotVP != 0.0 ){" + 1531 " powerfactor = pow( nDotVH, shininess );" + 1532 " }" + 1533 1534 " col += light.color * nDotVP;" + 1535 " spec += specular * powerfactor;" + 1536 "}" + 1537 1538 "void PointLight( inout vec3 col, inout vec3 spec, in vec3 vertNormal, in vec3 ecPos, in vec3 eye, in Light light ) {" + 1539 " float powerfactor;" + 1540 1541 // Get the vector from the light to the vertex 1542 " vec3 VP = light.position - ecPos;" + 1543 1544 // Get the distance from the current vector to the light position 1545 " float d = length( VP ); " + 1546 1547 // Normalize the light ray so it can be used in the dot product operation. 1548 " VP = normalize( VP );" + 1549 1550 " float attenuation = 1.0 / ( falloff[0] + ( falloff[1] * d ) + ( falloff[2] * d * d ));" + 1551 1552 " float nDotVP = max( 0.0, dot( vertNormal, VP ));" + 1553 " vec3 halfVector = normalize( VP + eye );" + 1554 " float nDotHV = max( 0.0, dot( vertNormal, halfVector ));" + 1555 1556 " if( nDotVP == 0.0) {" + 1557 " powerfactor = 0.0;" + 1558 " }" + 1559 " else{" + 1560 " powerfactor = pow( nDotHV, shininess );" + 1561 " }" + 1562 1563 " spec += specular * powerfactor * attenuation;" + 1564 " col += light.color * nDotVP * attenuation;" + 1565 "}" + 1566 1567 /* 1568 */ 1569 "void SpotLight( inout vec3 col, inout vec3 spec, in vec3 vertNormal, in vec3 ecPos, in vec3 eye, in Light light ) {" + 1570 " float spotAttenuation;" + 1571 " float powerfactor;" + 1572 1573 // calculate the vector from the current vertex to the light. 1574 " vec3 VP = light.position - ecPos; " + 1575 " vec3 ldir = normalize( light.direction );" + 1576 1577 // get the distance from the spotlight and the vertex 1578 " float d = length( VP );" + 1579 " VP = normalize( VP );" + 1580 1581 " float attenuation = 1.0 / ( falloff[0] + ( falloff[1] * d ) + ( falloff[2] * d * d ) );" + 1582 1583 // dot product of the vector from vertex to light and light direction. 1584 " float spotDot = dot( VP, ldir );" + 1585 1586 // if the vertex falls inside the cone 1587 " if( spotDot < cos( light.angle ) ) {" + 1588 " spotAttenuation = pow( spotDot, light.concentration );" + 1589 " }" + 1590 " else{" + 1591 " spotAttenuation = 1.0;" + 1592 " }" + 1593 " attenuation *= spotAttenuation;" + 1594 1595 " float nDotVP = max( 0.0, dot( vertNormal, VP ));" + 1596 " vec3 halfVector = normalize( VP + eye );" + 1597 " float nDotHV = max( 0.0, dot( vertNormal, halfVector ));" + 1598 1599 " if( nDotVP == 0.0 ) {" + 1600 " powerfactor = 0.0;" + 1601 " }" + 1602 " else {" + 1603 " powerfactor = pow( nDotHV, shininess );" + 1604 " }" + 1605 1606 " spec += specular * powerfactor * attenuation;" + 1607 " col += light.color * nDotVP * attenuation;" + 1608 "}" + 1609 1610 "void main(void) {" + 1611 " vec3 finalAmbient = vec3( 0.0, 0.0, 0.0 );" + 1612 " vec3 finalDiffuse = vec3( 0.0, 0.0, 0.0 );" + 1613 " vec3 finalSpecular = vec3( 0.0, 0.0, 0.0 );" + 1614 1615 " vec4 col = color;" + 1616 " if(color[0] == -1.0){" + 1617 " col = aColor;" + 1618 " }" + 1619 1620 " vec3 norm = vec3( normalTransform * vec4( Normal, 0.0 ) );" + 1621 1622 " vec4 ecPos4 = view * model * vec4(Vertex,1.0);" + 1623 " vec3 ecPos = (vec3(ecPos4))/ecPos4.w;" + 1624 " vec3 eye = vec3( 0.0, 0.0, 1.0 );" + 1625 1626 // If there were no lights this draw call, just use the 1627 // assigned fill color of the shape and the specular value 1628 " if( lightCount == 0 ) {" + 1629 " frontColor = col + vec4(mat_specular,1.0);" + 1630 " }" + 1631 " else {" + 1632 " for( int i = 0; i < lightCount; i++ ) {" + 1633 " if( lights[i].type == 0 ) {" + 1634 " AmbientLight( finalAmbient, ecPos, lights[i] );" + 1635 " }" + 1636 " else if( lights[i].type == 1 ) {" + 1637 " DirectionalLight( finalDiffuse,ecPos, finalSpecular, norm, lights[i] );" + 1638 " }" + 1639 " else if( lights[i].type == 2 ) {" + 1640 " PointLight( finalDiffuse, finalSpecular, norm, ecPos, eye, lights[i] );" + 1641 " }" + 1642 " else if( lights[i].type == 3 ) {" + 1643 " SpotLight( finalDiffuse, finalSpecular, norm, ecPos, eye, lights[i] );" + 1644 " }" + 1645 " }" + 1646 1647 " if( usingMat == false ) {" + 1648 " frontColor = vec4( " + 1649 " vec3(col) * finalAmbient +" + 1650 " vec3(col) * finalDiffuse +" + 1651 " vec3(col) * finalSpecular," + 1652 " col[3] );" + 1653 " }" + 1654 " else{" + 1655 " frontColor = vec4( " + 1656 " mat_emissive + " + 1657 " (vec3(col) * mat_ambient * finalAmbient) + " + 1658 " (vec3(col) * finalDiffuse) + " + 1659 " (mat_specular * finalSpecular), " + 1660 " col[3] );" + 1661 " }" + 1662 " }" + 1663 " vTexture.xy = aTexture.xy;" + 1664 " gl_Position = projection * view * model * vec4( Vertex, 1.0 );" + 1665 "}"; 1666 1667 var fragmentShaderSource3D = 1668 "#ifdef GL_ES\n" + 1669 "precision highp float;\n" + 1670 "#endif\n" + 1671 1672 "varying vec4 frontColor;" + 1673 1674 "uniform sampler2D sampler;" + 1675 "uniform bool usingTexture;" + 1676 "varying vec2 vTexture;" + 1677 1678 // In Processing, when a texture is used, the fill color is ignored 1679 "void main(void){" + 1680 " if(usingTexture){" + 1681 " gl_FragColor = vec4(texture2D(sampler, vTexture.xy));" + 1682 " }"+ 1683 " else{" + 1684 " gl_FragColor = frontColor;" + 1685 " }" + 1686 "}"; 1687 1688 //////////////////////////////////////////////////////////////////////////// 1689 // 3D Functions 1690 //////////////////////////////////////////////////////////////////////////// 1691 1692 /* 1693 Sets the uniform variable 'varName' to the value specified by 'value'. 1694 Before calling this function, make sure the correct program object 1695 has been installed as part of the current rendering state. 1696 1697 On some systems, if the variable exists in the shader but isn't used, 1698 the compiler will optimize it out and this function will fail. 1699 */ 1700 function uniformf(programObj, varName, varValue) { 1701 var varLocation = curContext.getUniformLocation(programObj, varName); 1702 // the variable won't be found if it was optimized out. 1703 if (varLocation !== -1) { 1704 if (varValue.length === 4) { 1705 curContext.uniform4fv(varLocation, varValue); 1706 } else if (varValue.length === 3) { 1707 curContext.uniform3fv(varLocation, varValue); 1708 } else if (varValue.length === 2) { 1709 curContext.uniform2fv(varLocation, varValue); 1710 } else { 1711 curContext.uniform1f(varLocation, varValue); 1712 } 1713 } 1714 } 1715 1716 function uniformi(programObj, varName, varValue) { 1717 var varLocation = curContext.getUniformLocation(programObj, varName); 1718 // the variable won't be found if it was optimized out. 1719 if (varLocation !== -1) { 1720 if (varValue.length === 4) { 1721 curContext.uniform4iv(varLocation, varValue); 1722 } else if (varValue.length === 3) { 1723 curContext.uniform3iv(varLocation, varValue); 1724 } else if (varValue.length === 2) { 1725 curContext.uniform2iv(varLocation, varValue); 1726 } else { 1727 curContext.uniform1i(varLocation, varValue); 1728 } 1729 } 1730 } 1731 1732 function vertexAttribPointer(programObj, varName, size, VBO) { 1733 var varLocation = curContext.getAttribLocation(programObj, varName); 1734 if (varLocation !== -1) { 1735 curContext.bindBuffer(curContext.ARRAY_BUFFER, VBO); 1736 curContext.vertexAttribPointer(varLocation, size, curContext.FLOAT, false, 0, 0); 1737 curContext.enableVertexAttribArray(varLocation); 1738 } 1739 } 1740 1741 function disableVertexAttribPointer(programObj, varName){ 1742 var varLocation = curContext.getAttribLocation(programObj, varName); 1743 if (varLocation !== -1) { 1744 curContext.disableVertexAttribArray(varLocation); 1745 } 1746 } 1747 1748 function uniformMatrix(programObj, varName, transpose, matrix) { 1749 var varLocation = curContext.getUniformLocation(programObj, varName); 1750 // the variable won't be found if it was optimized out. 1751 if (varLocation !== -1) { 1752 if (matrix.length === 16) { 1753 curContext.uniformMatrix4fv(varLocation, transpose, matrix); 1754 } else if (matrix.length === 9) { 1755 curContext.uniformMatrix3fv(varLocation, transpose, matrix); 1756 } else { 1757 curContext.uniformMatrix2fv(varLocation, transpose, matrix); 1758 } 1759 } 1760 } 1761 1762 var imageModeCorner = function imageModeCorner(x, y, w, h, whAreSizes) { 1763 return { 1764 x: x, 1765 y: y, 1766 w: w, 1767 h: h 1768 }; 1769 }; 1770 var imageModeConvert = imageModeCorner; 1771 1772 var imageModeCorners = function imageModeCorners(x, y, w, h, whAreSizes) { 1773 return { 1774 x: x, 1775 y: y, 1776 w: whAreSizes ? w : w - x, 1777 h: whAreSizes ? h : h - y 1778 }; 1779 }; 1780 1781 var imageModeCenter = function imageModeCenter(x, y, w, h, whAreSizes) { 1782 return { 1783 x: x - w / 2, 1784 y: y - h / 2, 1785 w: w, 1786 h: h 1787 }; 1788 }; 1789 1790 var createProgramObject = function(curContext, vetexShaderSource, fragmentShaderSource) { 1791 var vertexShaderObject = curContext.createShader(curContext.VERTEX_SHADER); 1792 curContext.shaderSource(vertexShaderObject, vetexShaderSource); 1793 curContext.compileShader(vertexShaderObject); 1794 if (!curContext.getShaderParameter(vertexShaderObject, curContext.COMPILE_STATUS)) { 1795 throw curContext.getShaderInfoLog(vertexShaderObject); 1796 } 1797 1798 var fragmentShaderObject = curContext.createShader(curContext.FRAGMENT_SHADER); 1799 curContext.shaderSource(fragmentShaderObject, fragmentShaderSource); 1800 curContext.compileShader(fragmentShaderObject); 1801 if (!curContext.getShaderParameter(fragmentShaderObject, curContext.COMPILE_STATUS)) { 1802 throw curContext.getShaderInfoLog(fragmentShaderObject); 1803 } 1804 1805 var programObject = curContext.createProgram(); 1806 curContext.attachShader(programObject, vertexShaderObject); 1807 curContext.attachShader(programObject, fragmentShaderObject); 1808 curContext.linkProgram(programObject); 1809 if (!curContext.getProgramParameter(programObject, curContext.LINK_STATUS)) { 1810 throw "Error linking shaders."; 1811 } 1812 1813 return programObject; 1814 }; 1815 1816 //////////////////////////////////////////////////////////////////////////// 1817 // Char handling 1818 //////////////////////////////////////////////////////////////////////////// 1819 var charMap = {}; 1820 1821 var Char = p.Character = function Char(chr) { 1822 if (typeof chr === 'string' && chr.length === 1) { 1823 this.code = chr.charCodeAt(0); 1824 } else { 1825 this.code = NaN; 1826 } 1827 1828 return (charMap[this.code] === undef) ? charMap[this.code] = this : charMap[this.code]; 1829 }; 1830 1831 Char.prototype.toString = function() { 1832 return String.fromCharCode(this.code); 1833 }; 1834 1835 Char.prototype.valueOf = function() { 1836 return this.code; 1837 }; 1838 1839 //////////////////////////////////////////////////////////////////////////// 1840 // PShape 1841 //////////////////////////////////////////////////////////////////////////// 1842 var PShape = p.PShape = function(family) { 1843 this.family = family || PConstants.GROUP; 1844 this.visible = true; 1845 this.style = true; 1846 this.children = []; 1847 this.nameTable = []; 1848 this.params = []; 1849 this.name = ""; 1850 this.image = null; //type PImage 1851 this.matrix = null; 1852 this.kind = null; 1853 this.close = null; 1854 this.width = null; 1855 this.height = null; 1856 this.parent = null; 1857 /* methods */ 1858 this.isVisible = function(){ 1859 return this.visible; 1860 }; 1861 this.setVisible = function (visible){ 1862 this.visible = visible; 1863 }; 1864 this.disableStyle = function(){ 1865 this.style = false; 1866 for(var i = 0; i < this.children.length; i++) 1867 { 1868 this.children[i].disableStyle(); 1869 } 1870 }; 1871 this.enableStyle = function(){ 1872 this.style = true; 1873 for(var i = 0; i < this.children.length; i++) 1874 { 1875 this.children[i].enableStyle(); 1876 } 1877 }; 1878 this.getFamily = function(){ 1879 return this.family; 1880 }; 1881 this.getWidth = function(){ 1882 return this.width; 1883 }; 1884 this.getHeight = function(){ 1885 return this.height; 1886 }; 1887 this.setName = function(name){ 1888 this.name = name; 1889 }; 1890 this.getName = function(){ 1891 return this.name; 1892 }; 1893 this.draw = function(){ 1894 if (this.visible) { 1895 this.pre(); 1896 this.drawImpl(); 1897 this.post(); 1898 } 1899 }; 1900 this.drawImpl = function(){ 1901 if (this.family === PConstants.GROUP) { 1902 this.drawGroup(); 1903 } else if (this.family === PConstants.PRIMITIVE) { 1904 this.drawPrimitive(); 1905 } else if (this.family === PConstants.GEOMETRY) { 1906 this.drawGeometry(); 1907 } else if (this.family === PConstants.PATH) { 1908 this.drawPath(); 1909 } 1910 }; 1911 this.drawPath = function(){ 1912 if (this.vertices.length === 0) { return; } 1913 1914 p.beginShape(); 1915 var i; 1916 if (this.vertexCodes.length === 0) { // each point is a simple vertex 1917 if (this.vertices[0].length === 2) { // drawing 2D vertices 1918 for (i = 0; i < this.vertices.length; i++) { 1919 p.vertex(this.vertices[i][0], this.vertices[i][1]); 1920 } 1921 } else { // drawing 3D vertices 1922 for (i = 0; i < this.vertices.length; i++) { 1923 p.vertex(this.vertices[i][0], this.vertices[i][1], this.vertices[i][2]); 1924 } 1925 } 1926 } else { // coded set of vertices 1927 var index = 0; 1928 var j; 1929 if (this.vertices[0].length === 2) { // drawing a 2D path 1930 for (j = 0; j < this.vertexCodes.length; j++) { 1931 switch (this.vertexCodes[j]) { 1932 case PConstants.VERTEX: 1933 p.vertex(this.vertices[index][0], this.vertices[index][1]); 1934 if ( this.vertices[index]["moveTo"] === true) { 1935 vertArray[vertArray.length-1]["moveTo"] = true; 1936 } else if ( this.vertices[index]["moveTo"] === false) { 1937 vertArray[vertArray.length-1]["moveTo"] = false; 1938 } 1939 p.breakShape = false; 1940 index++; 1941 break; 1942 case PConstants.BEZIER_VERTEX: 1943 p.bezierVertex(this.vertices[index+0][0], this.vertices[index+0][1], 1944 this.vertices[index+1][0], this.vertices[index+1][1], 1945 this.vertices[index+2][0], this.vertices[index+2][1]); 1946 index += 3; 1947 break; 1948 case PConstants.CURVE_VERTEX: 1949 p.curveVertex(this.vertices[index][0], this.vertices[index][1]); 1950 index++; 1951 break; 1952 case PConstants.BREAK: 1953 p.breakShape = true; 1954 break; 1955 } 1956 } 1957 } else { // drawing a 3D path 1958 for (j = 0; j < this.vertexCodes.length; j++) { 1959 switch (this.vertexCodes[j]) { 1960 case PConstants.VERTEX: 1961 p.vertex(this.vertices[index][0], this.vertices[index][1], this.vertices[index][2]); 1962 if (this.vertices[index]["moveTo"] === true) { 1963 vertArray[vertArray.length-1]["moveTo"] = true; 1964 } else if (this.vertices[index]["moveTo"] === false) { 1965 vertArray[vertArray.length-1]["moveTo"] = false; 1966 } 1967 p.breakShape = false; 1968 break; 1969 case PConstants.BEZIER_VERTEX: 1970 p.bezierVertex(this.vertices[index+0][0], this.vertices[index+0][1], this.vertices[index+0][2], 1971 this.vertices[index+1][0], this.vertices[index+1][1], this.vertices[index+1][2], 1972 this.vertices[index+2][0], this.vertices[index+2][1], this.vertices[index+2][2]); 1973 index += 3; 1974 break; 1975 case PConstants.CURVE_VERTEX: 1976 p.curveVertex(this.vertices[index][0], this.vertices[index][1], this.vertices[index][2]); 1977 index++; 1978 break; 1979 case PConstants.BREAK: 1980 p.breakShape = true; 1981 break; 1982 } 1983 } 1984 } 1985 } 1986 p.endShape(this.close ? PConstants.CLOSE : PConstants.OPEN); 1987 }; 1988 this.drawGeometry = function() { 1989 p.beginShape(this.kind); 1990 var i; 1991 if (this.style) { 1992 for (i = 0; i < this.vertices.length; i++) { 1993 p.vertex(this.vertices[i]); 1994 } 1995 } else { 1996 for (i = 0; i < this.vertices.length; i++) { 1997 var vert = this.vertices[i]; 1998 if (vert[2] === 0) { 1999 p.vertex(vert[0], vert[1]); 2000 } else { 2001 p.vertex(vert[0], vert[1], vert[2]); 2002 } 2003 } 2004 } 2005 p.endShape(); 2006 }; 2007 this.drawGroup = function() { 2008 for (var i = 0; i < this.children.length; i++) { 2009 this.children[i].draw(); 2010 } 2011 }; 2012 this.drawPrimitive = function() { 2013 switch (this.kind) { 2014 case PConstants.POINT: 2015 p.point(this.params[0], this.params[1]); 2016 break; 2017 case PConstants.LINE: 2018 if (this.params.length === 4) { // 2D 2019 p.line(this.params[0], this.params[1], 2020 this.params[2], this.params[3]); 2021 } else { // 3D 2022 p.line(this.params[0], this.params[1], this.params[2], 2023 this.params[3], this.params[4], this.params[5]); 2024 } 2025 break; 2026 case PConstants.TRIANGLE: 2027 p.triangle(this.params[0], this.params[1], 2028 this.params[2], this.params[3], 2029 this.params[4], this.params[5]); 2030 break; 2031 case PConstants.QUAD: 2032 p.quad(this.params[0], this.params[1], 2033 this.params[2], this.params[3], 2034 this.params[4], this.params[5], 2035 this.params[6], this.params[7]); 2036 break; 2037 case PConstants.RECT: 2038 if (this.image !== null) { 2039 p.imageMode(PConstants.CORNER); 2040 p.image(this.image, this.params[0], this.params[1], this.params[2], this.params[3]); 2041 } else { 2042 p.rectMode(PConstants.CORNER); 2043 p.rect(this.params[0], this.params[1], this.params[2], this.params[3]); 2044 } 2045 break; 2046 case PConstants.ELLIPSE: 2047 p.ellipseMode(PConstants.CORNER); 2048 p.ellipse(this.params[0], this.params[1], this.params[2], this.params[3]); 2049 break; 2050 case PConstants.ARC: 2051 p.ellipseMode(PConstants.CORNER); 2052 p.arc(this.params[0], this.params[1], this.params[2], this.params[3], this.params[4], this.params[5]); 2053 break; 2054 case PConstants.BOX: 2055 if (this.params.length === 1) { 2056 p.box(this.params[0]); 2057 } else { 2058 p.box(this.params[0], this.params[1], this.params[2]); 2059 } 2060 break; 2061 case PConstants.SPHERE: 2062 p.sphere(this.params[0]); 2063 break; 2064 } 2065 }; 2066 this.pre = function() { 2067 if (this.matrix) { 2068 p.pushMatrix(); 2069 curContext.transform(this.matrix.elements[0], this.matrix.elements[3], this.matrix.elements[1], this.matrix.elements[4], this.matrix.elements[2], this.matrix.elements[5]); 2070 //p.applyMatrix(this.matrix.elements[0],this.matrix.elements[0]); 2071 } 2072 if (this.style) { 2073 p.pushStyle(); 2074 this.styles(); 2075 } 2076 }; 2077 this.post = function() { 2078 if (this.matrix) { 2079 p.popMatrix(); 2080 } 2081 if (this.style) { 2082 p.popStyle(); 2083 } 2084 }; 2085 this.styles = function() { 2086 if (this.stroke) { 2087 p.stroke(this.strokeColor); 2088 p.strokeWeight(this.strokeWeight); 2089 p.strokeCap(this.strokeCap); 2090 p.strokeJoin(this.strokeJoin); 2091 } else { 2092 p.noStroke(); 2093 } 2094 2095 if (this.fill) { 2096 p.fill(this.fillColor); 2097 2098 } else { 2099 p.noFill(); 2100 } 2101 }; 2102 2103 // return the PShape at the specific index from the children array or 2104 // return the Phape from a parent shape specified by its name 2105 this.getChild = function(child) { 2106 if (typeof child === 'number') { 2107 return this.children[child]; 2108 } else { 2109 var found, 2110 i; 2111 if(child === "" || this.name === child){ 2112 return this; 2113 } else { 2114 if(this.nameTable.length > 0) 2115 { 2116 for(i = 0; i < this.nameTable.length || found; i++) 2117 { 2118 if(this.nameTable[i].getName === child) { 2119 found = this.nameTable[i]; 2120 } 2121 } 2122 if (found) { return found; } 2123 } 2124 for(i = 0; i < this.children.lenth; i++) 2125 { 2126 found = this.children[i].getChild(child); 2127 if(found) { return found; } 2128 } 2129 } 2130 return null; 2131 } 2132 }; 2133 this.getChildCount = function () { 2134 return this.children.length; 2135 }; 2136 this.addChild = function( child ) { 2137 this.children.push(child); 2138 child.parent = this; 2139 if (child.getName() !== null) { 2140 this.addName(child.getName(), child); 2141 } 2142 }; 2143 this.addName = function(name, shape) { 2144 if (this.parent !== null) { 2145 this.parent.addName( name, shape ); 2146 } else { 2147 this.nameTable.push( [name, shape] ); 2148 } 2149 }; 2150 this.translate = function() { 2151 if(arguments.length === 2) 2152 { 2153 this.checkMatrix(2); 2154 this.matrix.translate(arguments[0], arguments[1]); 2155 } else { 2156 this.checkMatrix(3); 2157 this.matrix.translate(arguments[0], arguments[1], 0); 2158 } 2159 }; 2160 this.checkMatrix = function(dimensions) { 2161 if(this.matrix === null) { 2162 if(dimensions === 2) { 2163 this.matrix = new p.PMatrix2D(); 2164 } else { 2165 this.matrix = new p.PMatrix3D(); 2166 } 2167 }else if(dimensions === 3 && this.matrix instanceof p.PMatrix2D) { 2168 this.matrix = new p.PMatrix3D(); 2169 } 2170 }; 2171 this.rotateX = function(angle) { 2172 this.rotate(angle, 1, 0, 0); 2173 }; 2174 this.rotateY = function(angle) { 2175 this.rotate(angle, 0, 1, 0); 2176 }; 2177 this.rotateZ = function(angle) { 2178 this.rotate(angle, 0, 0, 1); 2179 }; 2180 this.rotate = function() { 2181 if(arguments.length === 1){ 2182 this.checkMatrix(2); 2183 this.matrix.rotate(arguments[0]); 2184 } else { 2185 this.checkMatrix(3); 2186 this.matrix.rotate(arguments[0], arguments[1], arguments[2] ,arguments[3]); 2187 } 2188 }; 2189 this.scale = function() { 2190 if(arguments.length === 2) { 2191 this.checkMatrix(2); 2192 this.matrix.scale(arguments[0], arguments[1]); 2193 } else if (arguments.length === 3) { 2194 this.checkMatrix(2); 2195 this.matrix.scale(arguments[0], arguments[1], arguments[2]); 2196 } else { 2197 this.checkMatrix(2); 2198 this.matrix.scale(arguments[0]); 2199 } 2200 }; 2201 this.resetMatrix = function() { 2202 this.checkMatrix(2); 2203 this.matrix.reset(); 2204 }; 2205 this.applyMatrix = function(matrix) { 2206 if (arguments.length === 1) { 2207 this.applyMatrix(matrix.elements[0], matrix.elements[1], 0, matrix.elements[2], 2208 matrix.elements[3], matrix.elements[4], 0, matrix.elements[5], 2209 0, 0, 1, 0, 2210 0, 0, 0, 1); 2211 } else if (arguments.length === 6) { 2212 this.checkMatrix(2); 2213 this.matrix.apply(arguments[0], arguments[1], arguments[2], 0, 2214 arguments[3], arguments[4], arguments[5], 0, 2215 0, 0, 1, 0, 2216 0, 0, 0, 1); 2217 2218 } else if (arguments.length === 16) { 2219 this.checkMatrix(3); 2220 this.matrix.apply(arguments[0], arguments[1], arguments[2], arguments[3], 2221 arguments[4], arguments[5], arguments[6], arguments[7], 2222 arguments[8], arguments[9], arguments[10], arguments[11], 2223 arguments[12], arguments[13], arguments[14], arguments[15]); 2224 } 2225 }; 2226 // findChild not in yet 2227 // apply missing 2228 // contains missing 2229 // find child missing 2230 // getPrimitive missing 2231 // getParams missing 2232 // getVertex , getVertexCount missing 2233 // getVertexCode , getVertexCodes , getVertexCodeCount missing 2234 // getVertexX, getVertexY, getVertexZ missing 2235 2236 }; 2237 2238 var PShapeSVG = function() { 2239 p.PShape.call( this ); // PShape is the base class. 2240 if (arguments.length === 1) { 2241 this.element = new p.XMLElement(null, arguments[0]); 2242 // set values to their defaults according to the SVG spec 2243 this.vertexCodes = []; 2244 this.vertices = []; 2245 this.opacity = 1; 2246 2247 this.stroke = false; 2248 this.strokeColor = PConstants.ALPHA_MASK; 2249 this.strokeWeight = 1; 2250 this.strokeCap = PConstants.SQUARE; // equivalent to BUTT in svg spec 2251 this.strokeJoin = PConstants.MITER; 2252 this.strokeGradient = null; 2253 this.strokeGradientPaint = null; 2254 this.strokeName = null; 2255 this.strokeOpacity = 1; 2256 2257 this.fill = true; 2258 this.fillColor = PConstants.ALPHA_MASK; 2259 this.fillGradient = null; 2260 this.fillGradientPaint = null; 2261 this.fillName = null; 2262 this.fillOpacity = 1; 2263 2264 if (this.element.getName() !== "svg") { 2265 throw("root is not <svg>, it's <" + this.element.getName() + ">"); 2266 } 2267 } 2268 else if (arguments.length === 2) { 2269 if (typeof arguments[1] === 'string') { 2270 if (arguments[1].indexOf(".svg") > -1) { //its a filename 2271 this.element = new p.XMLElement(null, arguments[1]); 2272 // set values to their defaults according to the SVG spec 2273 this.vertexCodes = []; 2274 this.vertices = []; 2275 this.opacity = 1; 2276 2277 this.stroke = false; 2278 this.strokeColor = PConstants.ALPHA_MASK; 2279 this.strokeWeight = 1; 2280 this.strokeCap = PConstants.SQUARE; // equivalent to BUTT in svg spec 2281 this.strokeJoin = PConstants.MITER; 2282 this.strokeGradient = ""; 2283 this.strokeGradientPaint = ""; 2284 this.strokeName = ""; 2285 this.strokeOpacity = 1; 2286 2287 this.fill = true; 2288 this.fillColor = PConstants.ALPHA_MASK; 2289 this.fillGradient = null; 2290 this.fillGradientPaint = null; 2291 this.fillOpacity = 1; 2292 2293 } 2294 } else { // XMLElement 2295 if (arguments[0]) { // PShapeSVG 2296 this.element = arguments[1]; 2297 this.vertexCodes = arguments[0].vertexCodes.slice(); 2298 this.vertices = arguments[0].vertices.slice(); 2299 2300 this.stroke = arguments[0].stroke; 2301 this.strokeColor = arguments[0].strokeColor; 2302 this.strokeWeight = arguments[0].strokeWeight; 2303 this.strokeCap = arguments[0].strokeCap; 2304 this.strokeJoin = arguments[0].strokeJoin; 2305 this.strokeGradient = arguments[0].strokeGradient; 2306 this.strokeGradientPaint = arguments[0].strokeGradientPaint; 2307 this.strokeName = arguments[0].strokeName; 2308 2309 this.fill = arguments[0].fill; 2310 this.fillColor = arguments[0].fillColor; 2311 this.fillGradient = arguments[0].fillGradient; 2312 this.fillGradientPaint = arguments[0].fillGradientPaint; 2313 this.fillName = arguments[0].fillName; 2314 this.strokeOpacity = arguments[0].strokeOpacity; 2315 this.fillOpacity = arguments[0].fillOpacity; 2316 this.opacity = arguments[0].opacity; 2317 } 2318 } 2319 } 2320 2321 this.name = this.element.getStringAttribute("id"); 2322 var displayStr = this.element.getStringAttribute("display", "inline"); 2323 this.visible = displayStr !== "none"; 2324 var str = this.element.getAttribute("transform"); 2325 if (str) { 2326 this.matrix = this.parseMatrix(str); 2327 } 2328 // not proper parsing of the viewBox, but will cover us for cases where 2329 // the width and height of the object is not specified 2330 var viewBoxStr = this.element.getStringAttribute("viewBox"); 2331 if ( viewBoxStr !== null ) { 2332 var viewBox = viewBoxStr.split(" "); 2333 this.width = viewBox[2]; 2334 this.height = viewBox[3]; 2335 } 2336 2337 // TODO if viewbox is not same as width/height, then use it to scale 2338 // the original objects. for now, viewbox only used when width/height 2339 // are empty values (which by the spec means w/h of "100%" 2340 var unitWidth = this.element.getStringAttribute("width"); 2341 var unitHeight = this.element.getStringAttribute("height"); 2342 if (unitWidth !== null) { 2343 this.width = this.parseUnitSize(unitWidth); 2344 this.height = this.parseUnitSize(unitHeight); 2345 } else { 2346 if ((this.width === 0) || (this.height === 0)) { 2347 // For the spec, the default is 100% and 100%. For purposes 2348 // here, insert a dummy value because this is prolly just a 2349 // font or something for which the w/h doesn't matter. 2350 this.width = 1; 2351 this.height = 1; 2352 2353 //show warning 2354 throw("The width and/or height is not " + 2355 "readable in the <svg> tag of this file."); 2356 } 2357 } 2358 this.parseColors(this.element); 2359 this.parseChildren(this.element); 2360 2361 }; 2362 2363 PShapeSVG.prototype = { 2364 // getChild missing 2365 // print missing 2366 // parse style attributes 2367 // styles missing but deals with strokeGradient and fillGradient 2368 parseMatrix: function(str) { 2369 this.checkMatrix(2); 2370 var pieces = []; 2371 str.replace(/\s*(\w+)\((.*?)\)/g, function(all) { 2372 // get a list of transform definitions 2373 pieces.push(p.trim(all)); 2374 }); 2375 if (pieces.length === 0) { 2376 p.println("Transformation:" + str + " is empty"); 2377 return null; 2378 } 2379 for (var i =0; i< pieces.length; i++) { 2380 var m = []; 2381 pieces[i].replace(/\((.*?)\)/, (function() { 2382 return function(all, params) { 2383 // get the coordinates that can be separated by spaces or a comma 2384 m = params.replace(/,+/g, " ").split(/\s+/); 2385 }; 2386 }())); 2387 2388 if (pieces[i].indexOf("matrix") !== -1) { 2389 this.matrix.set(m[0], m[2], m[4], m[1], m[3], m[5]); 2390 } else if (pieces[i].indexOf("translate") !== -1) { 2391 var tx = m[0]; 2392 var ty = (m.length === 2) ? m[1] : 0; 2393 this.matrix.translate(tx,ty); 2394 } else if (pieces[i].indexOf("scale") !== -1) { 2395 var sx = m[0]; 2396 var sy = (m.length === 2) ? m[1] : m[0]; 2397 this.matrix.scale(sx,sy); 2398 } else if (pieces[i].indexOf("rotate") !== -1) { 2399 var angle = m[0]; 2400 if (m.length === 1) { 2401 this.matrix.rotate(p.radians(angle)); 2402 } else if (m.length === 3) { 2403 this.matrix.translate(m[1], m[2]); 2404 this.matrix.rotate(p.radians(m[0])); 2405 this.matrix.translate(-m[1], -m[2]); 2406 } 2407 } else if (pieces[i].indexOf("skewX") !== -1) { 2408 this.matrix.skewX(parseFloat(m[0])); 2409 } else if (pieces[i].indexOf("skewY") !== -1) { 2410 this.matrix.skewY(m[0]); 2411 } 2412 } 2413 return this.matrix; 2414 }, 2415 parseChildren:function(element) { 2416 var newelement = element.getChildren(); 2417 var children = new p.PShape(); 2418 for (var i = 0; i < newelement.length; i++) { 2419 var kid = this.parseChild(newelement[i]); 2420 if (kid) { 2421 children.addChild(kid); 2422 } 2423 } 2424 this.children.push(children); 2425 }, 2426 getName: function() { 2427 return this.name; 2428 }, 2429 parseChild: function( elem ) { 2430 var name = elem.getName(); 2431 var shape; 2432 switch (name) { 2433 case "g": 2434 shape = new PShapeSVG(this, elem); 2435 break; 2436 case "defs": 2437 // generally this will contain gradient info, so may 2438 // as well just throw it into a group element for parsing 2439 shape = new PShapeSVG(this, elem); 2440 break; 2441 case "line": 2442 shape = new PShapeSVG(this, elem); 2443 shape.parseLine(); 2444 break; 2445 case "circle": 2446 shape = new PShapeSVG(this, elem); 2447 shape.parseEllipse(true); 2448 break; 2449 case "ellipse": 2450 shape = new PShapeSVG(this, elem); 2451 shape.parseEllipse(false); 2452 break; 2453 case "rect": 2454 shape = new PShapeSVG(this, elem); 2455 shape.parseRect(); 2456 break; 2457 case "polygon": 2458 shape = new PShapeSVG(this, elem); 2459 shape.parsePoly(true); 2460 break; 2461 case "polyline": 2462 shape = new PShapeSVG(this, elem); 2463 shape.parsePoly(false); 2464 break; 2465 case "path": 2466 shape = new PShapeSVG(this, elem); 2467 shape.parsePath(); 2468 break; 2469 case "radialGradient": 2470 //return new RadialGradient(this, elem); 2471 break; 2472 case "linearGradient": 2473 //return new LinearGradient(this, elem); 2474 break; 2475 case "text": 2476 p.println("Text in SVG files is not currently supported, convert text to outlines instead." ); 2477 break; 2478 case "filter": 2479 p.println("Filters are not supported."); 2480 break; 2481 case "mask": 2482 p.println("Masks are not supported."); 2483 break; 2484 default: 2485 p.println("Ignoring <" + name + "> tag."); 2486 break; 2487 } 2488 return shape; 2489 }, 2490 parsePath: function() { 2491 this.family = PConstants.PATH; 2492 this.kind = 0; 2493 var pathDataChars = []; 2494 var c; 2495 var pathData = p.trim(this.element.getStringAttribute("d").replace(/[\s,]+/g,' ')); //change multiple spaces and commas to single space 2496 if (pathData === null) { return; } 2497 pathData = pathData.toCharArray(); 2498 var cx = 0, 2499 cy = 0, 2500 ctrlX = 0, 2501 ctrlY = 0, 2502 ctrlX1 = 0, 2503 ctrlX2 = 0, 2504 ctrlY1 = 0, 2505 ctrlY2 = 0, 2506 endX = 0, 2507 endY = 0, 2508 ppx = 0, 2509 ppy = 0, 2510 px = 0, 2511 py = 0, 2512 i = 0, 2513 j = 0, 2514 valOf = 0; 2515 var str = ""; 2516 var tmpArray =[]; 2517 var flag = false; 2518 var lastInstruction; 2519 var command; 2520 while (i< pathData.length) { 2521 valOf = pathData[i].valueOf(); 2522 if ((valOf >= 65 && valOf <= 90) || (valOf >= 97 && valOf <= 122)) { // if its a letter 2523 // populate the tmpArray with coordinates 2524 j = i; 2525 i++; 2526 if (i < pathData.length) { // dont go over boundary of array 2527 tmpArray = []; 2528 valOf = pathData[i].valueOf(); 2529 while (!((valOf >= 65 && valOf <= 90) || (valOf >= 97 && valOf <= 100) || (valOf >= 102 && valOf <= 122)) && flag === false) { // if its NOT a letter 2530 if (valOf === 32) { //if its a space and the str isn't empty 2531 // somethimes you get a space after the letter 2532 if (str !== "") { 2533 tmpArray.push(parseFloat(str)); 2534 str = ""; 2535 } 2536 i++; 2537 } else if (valOf === 45) { //if its a - 2538 // allow for 'e' notation in numbers, e.g. 2.10e-9 2539 if (pathData[i-1].valueOf() === 101) { 2540 str += pathData[i].toString(); 2541 i++; 2542 } else { 2543 // sometimes no space separator after (ex: 104.535-16.322) 2544 if (str !== "") { 2545 tmpArray.push(parseFloat(str)); 2546 } 2547 str = pathData[i].toString(); 2548 i++; 2549 } 2550 } else { 2551 str += pathData[i].toString(); 2552 i++; 2553 } 2554 if (i === pathData.length) { // dont go over boundary of array 2555 flag = true; 2556 } else { 2557 valOf = pathData[i].valueOf(); 2558 } 2559 } 2560 } 2561 if (str !== "") { 2562 tmpArray.push(parseFloat(str)); 2563 str = ""; 2564 } 2565 command = pathData[j]; 2566 switch (command.valueOf()) { 2567 case 77: // M - move to (absolute) 2568 if (tmpArray.length >= 2 && tmpArray.length % 2 ===0) { // need one+ pairs of co-ordinates 2569 cx = tmpArray[0]; 2570 cy = tmpArray[1]; 2571 this.parsePathMoveto(cx, cy); 2572 if (tmpArray.length > 2) { 2573 for (j = 2; j < tmpArray.length; j+=2) { 2574 // absolute line to 2575 cx = tmpArray[j]; 2576 cy = tmpArray[j+1]; 2577 this.parsePathLineto(cx,cy); 2578 } 2579 } 2580 } 2581 break; 2582 case 109: // m - move to (relative) 2583 if (tmpArray.length >= 2 && tmpArray.length % 2 === 0) { // need one+ pairs of co-ordinates 2584 this.parsePathMoveto(cx,cy); 2585 if (tmpArray.length > 2) { 2586 for (j = 2; j < tmpArray.length; j+=2) { 2587 // relative line to 2588 cx += tmpArray[j]; 2589 cy += tmpArray[j + 1]; 2590 this.parsePathLineto(cx,cy); 2591 } 2592 } 2593 } 2594 break; 2595 case 76: // L - lineto (absolute) 2596 if (tmpArray.length >= 2 && tmpArray.length % 2 === 0) { // need one+ pairs of co-ordinates 2597 for (j = 0; j < tmpArray.length; j+=2) { 2598 cx = tmpArray[j]; 2599 cy = tmpArray[j + 1]; 2600 this.parsePathLineto(cx,cy); 2601 } 2602 } 2603 break; 2604 2605 case 108: // l - lineto (relative) 2606 if (tmpArray.length >= 2 && tmpArray.length % 2 === 0) { // need one+ pairs of co-ordinates 2607 for (j = 0; j < tmpArray.length; j+=2) { 2608 cx += tmpArray[j]; 2609 cy += tmpArray[j+1]; 2610 this.parsePathLineto(cx,cy); 2611 } 2612 } 2613 break; 2614 2615 case 72: // H - horizontal lineto (absolute) 2616 for (j = 0; j < tmpArray.length; j++) { // multiple x co-ordinates can be provided 2617 cx = tmpArray[j]; 2618 this.parsePathLineto(cx, cy); 2619 } 2620 break; 2621 2622 case 104: // h - horizontal lineto (relative) 2623 for (j = 0; j < tmpArray.length; j++) { // multiple x co-ordinates can be provided 2624 cx += tmpArray[j]; 2625 this.parsePathLineto(cx, cy); 2626 } 2627 break; 2628 2629 case 86: // V - vertical lineto (absolute) 2630 for (j = 0; j < tmpArray.length; j++) { // multiple y co-ordinates can be provided 2631 cy = tmpArray[j]; 2632 this.parsePathLineto(cx, cy); 2633 } 2634 break; 2635 2636 case 118: // v - vertical lineto (relative) 2637 for (j = 0; j < tmpArray.length; j++) { // multiple y co-ordinates can be provided 2638 cy += tmpArray[j]; 2639 this.parsePathLineto(cx, cy); 2640 } 2641 break; 2642 2643 case 67: // C - curve to (absolute) 2644 if (tmpArray.length >= 6 && tmpArray.length % 6 === 0) { // need one+ multiples of 6 co-ordinates 2645 for (j = 0; j < tmpArray.length; j+=6) { 2646 ctrlX1 = tmpArray[j]; 2647 ctrlY1 = tmpArray[j + 1]; 2648 ctrlX2 = tmpArray[j + 2]; 2649 ctrlY2 = tmpArray[j + 3]; 2650 endX = tmpArray[j + 4]; 2651 endY = tmpArray[j + 5]; 2652 this.parsePathCurveto(ctrlX1, ctrlY1, ctrlX2, ctrlY2, endX, endY); 2653 cx = endX; 2654 cy = endY; 2655 } 2656 } 2657 break; 2658 2659 case 99: // c - curve to (relative) 2660 if (tmpArray.length >= 6 && tmpArray.length % 6 === 0) { // need one+ multiples of 6 co-ordinates 2661 for (j = 0; j < tmpArray.length; j+=6) { 2662 ctrlX1 = cx + tmpArray[j]; 2663 ctrlY1 = cy + tmpArray[j + 1]; 2664 ctrlX2 = cx + tmpArray[j + 2]; 2665 ctrlY2 = cy + tmpArray[j + 3]; 2666 endX = cx + tmpArray[j + 4]; 2667 endY = cy + tmpArray[j + 5]; 2668 this.parsePathCurveto(ctrlX1, ctrlY1, ctrlX2, ctrlY2, endX, endY); 2669 cx = endX; 2670 cy = endY; 2671 } 2672 } 2673 break; 2674 2675 case 83: // S - curve to shorthand (absolute) 2676 if (tmpArray.length >= 4 && tmpArray.length % 4 === 0) { // need one+ multiples of 4 co-ordinates 2677 for (j = 0; j < tmpArray.length; j+=4) { 2678 if (lastInstruction.toLowerCase() === "c" || lastInstruction.toLowerCase() === "s") { 2679 ppx = this.vertices[ this.vertices.length-2 ][0]; 2680 ppy = this.vertices[ this.vertices.length-2 ][1]; 2681 px = this.vertices[ this.vertices.length-1 ][0]; 2682 py = this.vertices[ this.vertices.length-1 ][1]; 2683 ctrlX1 = px + (px - ppx); 2684 ctrlY1 = py + (py - ppy); 2685 } else { 2686 //If there is no previous curve, the current point will be used as the first control point. 2687 ctrlX1 = this.vertices[this.vertices.length-1][0]; 2688 ctrlY1 = this.vertices[this.vertices.length-1][1]; 2689 } 2690 ctrlX2 = tmpArray[j]; 2691 ctrlY2 = tmpArray[j + 1]; 2692 endX = tmpArray[j + 2]; 2693 endY = tmpArray[j + 3]; 2694 this.parsePathCurveto(ctrlX1, ctrlY1, ctrlX2, ctrlY2, endX, endY); 2695 cx = endX; 2696 cy = endY; 2697 } 2698 } 2699 break; 2700 2701 case 115: // s - curve to shorthand (relative) 2702 if (tmpArray.length >= 4 && tmpArray.length % 4 === 0) { // need one+ multiples of 4 co-ordinates 2703 for (j = 0; j < tmpArray.length; j+=4) { 2704 if (lastInstruction.toLowerCase() === "c" || lastInstruction.toLowerCase() === "s") { 2705 ppx = this.vertices[this.vertices.length-2][0]; 2706 ppy = this.vertices[this.vertices.length-2][1]; 2707 px = this.vertices[this.vertices.length-1][0]; 2708 py = this.vertices[this.vertices.length-1][1]; 2709 ctrlX1 = px + (px - ppx); 2710 ctrlY1 = py + (py - ppy); 2711 } else { 2712 //If there is no previous curve, the current point will be used as the first control point. 2713 ctrlX1 = this.vertices[this.vertices.length-1][0]; 2714 ctrlY1 = this.vertices[this.vertices.length-1][1]; 2715 } 2716 ctrlX2 = cx + tmpArray[j]; 2717 ctrlY2 = cy + tmpArray[j + 1]; 2718 endX = cx + tmpArray[j + 2]; 2719 endY = cy + tmpArray[j + 3]; 2720 this.parsePathCurveto(ctrlX1, ctrlY1, ctrlX2, ctrlY2, endX, endY); 2721 cx = endX; 2722 cy = endY; 2723 } 2724 } 2725 break; 2726 2727 case 81: // Q - quadratic curve to (absolute) 2728 if (tmpArray.length >= 4 && tmpArray.length % 4 === 0) { // need one+ multiples of 4 co-ordinates 2729 for (j = 0; j < tmpArray.length; j+=4) { 2730 ctrlX = tmpArray[j]; 2731 ctrlY = tmpArray[j + 1]; 2732 endX = tmpArray[j + 2]; 2733 endY = tmpArray[j + 3]; 2734 this.parsePathQuadto(cx, cy, ctrlX, ctrlY, endX, endY); 2735 cx = endX; 2736 cy = endY; 2737 } 2738 } 2739 break; 2740 2741 case 113: // q - quadratic curve to (relative) 2742 if (tmpArray.length >= 4 && tmpArray.length % 4 === 0) { // need one+ multiples of 4 co-ordinates 2743 for (j = 0; j < tmpArray.length; j+=4) { 2744 ctrlX = cx + tmpArray[j]; 2745 ctrlY = cy + tmpArray[j + 1]; 2746 endX = cx + tmpArray[j + 2]; 2747 endY = cy + tmpArray[j + 3]; 2748 this.parsePathQuadto(cx, cy, ctrlX, ctrlY, endX, endY); 2749 cx = endX; 2750 cy = endY; 2751 } 2752 } 2753 break; 2754 2755 case 84: // T - quadratic curve to shorthand (absolute) 2756 if (tmpArray.length >= 2 && tmpArray.length % 2 === 0) { // need one+ pairs of co-ordinates 2757 for (j = 0; j < tmpArray.length; j+=2) { 2758 if (lastInstruction.toLowerCase() === "q" || lastInstruction.toLowerCase() === "t") { 2759 ppx = this.vertices[this.vertices.length-2][0]; 2760 ppy = this.vertices[this.vertices.length-2][1]; 2761 px = this.vertices[this.vertices.length-1][0]; 2762 py = this.vertices[this.vertices.length-1][1]; 2763 ctrlX = px + (px - ppx); 2764 ctrlY = py + (py - ppy); 2765 } else { 2766 // If there is no previous command or if the previous command was not a Q, q, T or t, 2767 // assume the control point is coincident with the current point. 2768 ctrlX = cx; 2769 ctrlY = cy; 2770 } 2771 endX = tmpArray[j]; 2772 endY = tmpArray[j + 1]; 2773 this.parsePathQuadto(cx, cy, ctrlX, ctrlY, endX, endY); 2774 cx = endX; 2775 cy = endY; 2776 } 2777 } 2778 break; 2779 2780 case 116: // t - quadratic curve to shorthand (relative) 2781 if (tmpArray.length >= 2 && tmpArray.length % 2 === 0) { // need one+ pairs of co-ordinates 2782 for (j = 0; j < tmpArray.length; j+=2) { 2783 if (lastInstruction.toLowerCase() === "q" || lastInstruction.toLowerCase() === "t") { 2784 ppx = this.vertices[this.vertices.length-2][0]; 2785 ppy = this.vertices[this.vertices.length-2][1]; 2786 px = this.vertices[this.vertices.length-1][0]; 2787 py = this.vertices[this.vertices.length-1][1]; 2788 ctrlX = px + (px - ppx); 2789 ctrlY = py + (py - ppy); 2790 } else { 2791 // If there is no previous command or if the previous command was not a Q, q, T or t, 2792 // assume the control point is coincident with the current point. 2793 ctrlX = cx; 2794 ctrlY = cy; 2795 } 2796 endX = cx + tmpArray[j]; 2797 endY = cy + tmpArray[j + 1]; 2798 this.parsePathQuadto(cx, cy, ctrlX, ctrlY, endX, endY); 2799 cx = endX; 2800 cy = endY; 2801 } 2802 } 2803 break; 2804 2805 case 90: //Z 2806 case 122: //z 2807 this.close = true; 2808 break; 2809 } 2810 lastInstruction = command.toString(); 2811 } else { i++;} 2812 } 2813 }, 2814 parsePathQuadto: function(x1, y1, cx, cy, x2, y2) { 2815 if (this.vertices.length > 0) { 2816 this.parsePathCode(PConstants.BEZIER_VERTEX); 2817 // x1/y1 already covered by last moveto, lineto, or curveto 2818 this.parsePathVertex(x1 + ((cx-x1)*2/3), y1 + ((cy-y1)*2/3)); 2819 this.parsePathVertex(x2 + ((cx-x2)*2/3), y2 + ((cy-y2)*2/3)); 2820 this.parsePathVertex(x2, y2); 2821 } else { 2822 throw ("Path must start with M/m"); 2823 } 2824 }, 2825 parsePathCurveto : function(x1, y1, x2, y2, x3, y3) { 2826 if (this.vertices.length > 0) { 2827 this.parsePathCode(PConstants.BEZIER_VERTEX ); 2828 this.parsePathVertex(x1, y1); 2829 this.parsePathVertex(x2, y2); 2830 this.parsePathVertex(x3, y3); 2831 } else { 2832 throw ("Path must start with M/m"); 2833 } 2834 }, 2835 parsePathLineto: function(px, py) { 2836 if (this.vertices.length > 0) { 2837 this.parsePathCode(PConstants.VERTEX); 2838 this.parsePathVertex(px, py); 2839 // add property to distinguish between curContext.moveTo or curContext.lineTo 2840 this.vertices[this.vertices.length-1]["moveTo"] = false; 2841 } else { 2842 throw ("Path must start with M/m"); 2843 } 2844 }, 2845 parsePathMoveto: function(px, py) { 2846 if (this.vertices.length > 0) { 2847 this.parsePathCode(PConstants.BREAK); 2848 } 2849 this.parsePathCode(PConstants.VERTEX); 2850 this.parsePathVertex(px, py); 2851 // add property to distinguish between curContext.moveTo or curContext.lineTo 2852 this.vertices[this.vertices.length-1]["moveTo"] = true; 2853 }, 2854 parsePathVertex: function(x, y) { 2855 var verts = []; 2856 verts[0] = x; 2857 verts[1] = y; 2858 this.vertices.push(verts); 2859 }, 2860 parsePathCode: function(what) { 2861 this.vertexCodes.push(what); 2862 }, 2863 parsePoly: function(val) { 2864 this.family = PConstants.PATH; 2865 this.close = val; 2866 var pointsAttr = p.trim(this.element.getStringAttribute("points").replace(/[,\s]+/g,' ')); 2867 if (pointsAttr !== null) { 2868 //split into array 2869 var pointsBuffer = pointsAttr.split(" "); 2870 if (pointsBuffer.length % 2 === 0) { 2871 for (var i = 0; i < pointsBuffer.length; i++) { 2872 var verts = []; 2873 verts[0] = pointsBuffer[i]; 2874 verts[1] = pointsBuffer[++i]; 2875 this.vertices.push(verts); 2876 } 2877 } else { 2878 p.println("Error parsing polygon points: odd number of coordinates provided"); 2879 } 2880 } 2881 }, 2882 parseRect: function() { 2883 this.kind = PConstants.RECT; 2884 this.family = PConstants.PRIMITIVE; 2885 this.params = []; 2886 this.params[0] = this.element.getFloatAttribute("x"); 2887 this.params[1] = this.element.getFloatAttribute("y"); 2888 this.params[2] = this.element.getFloatAttribute("width"); 2889 this.params[3] = this.element.getFloatAttribute("height"); 2890 2891 }, 2892 parseEllipse: function(val) { 2893 this.kind = PConstants.ELLIPSE; 2894 this.family = PConstants.PRIMITIVE; 2895 this.params = []; 2896 2897 this.params[0] = this.element.getFloatAttribute("cx"); 2898 this.params[1] = this.element.getFloatAttribute("cy"); 2899 2900 var rx, ry; 2901 if (val) { 2902 rx = ry = this.element.getFloatAttribute("r"); 2903 } else { 2904 rx = this.element.getFloatAttribute("rx"); 2905 ry = this.element.getFloatAttribute("ry"); 2906 } 2907 this.params[0] -= rx; 2908 this.params[1] -= ry; 2909 2910 this.params[2] = rx*2; 2911 this.params[3] = ry*2; 2912 }, 2913 parseLine: function() { 2914 this.kind = PConstants.LINE; 2915 this.family = PConstants.PRIMITIVE; 2916 this.params = []; 2917 this.params[0] = this.element.getFloatAttribute("x1"); 2918 this.params[1] = this.element.getFloatAttribute("y1"); 2919 this.params[2] = this.element.getFloatAttribute("x2"); 2920 this.params[3] = this.element.getFloatAttribute("y2"); 2921 }, 2922 parseColors: function(element) { 2923 if (element.hasAttribute("opacity")) { 2924 this.setOpacity(element.getAttribute("opacity")); 2925 } 2926 if (element.hasAttribute("stroke")) { 2927 this.setStroke(element.getAttribute("stroke")); 2928 } 2929 if (element.hasAttribute("stroke-width")) { 2930 // if NaN (i.e. if it's 'inherit') then default back to the inherit setting 2931 this.setStrokeWeight(element.getAttribute("stroke-width")); 2932 } 2933 if (element.hasAttribute("stroke-linejoin") ) { 2934 this.setStrokeJoin(element.getAttribute("stroke-linejoin")); 2935 } 2936 if (element.hasAttribute("stroke-linecap")) { 2937 this.setStrokeCap(element.getStringAttribute("stroke-linecap")); 2938 } 2939 // fill defaults to black (though stroke defaults to "none") 2940 // http://www.w3.org/TR/SVG/painting.html#FillProperties 2941 if (element.hasAttribute("fill")) { 2942 this.setFill(element.getStringAttribute("fill")); 2943 } 2944 if (element.hasAttribute("style")) { 2945 var styleText = element.getStringAttribute("style"); 2946 var styleTokens = styleText.toString().split( ";" ); 2947 2948 for (var i = 0; i < styleTokens.length; i++) { 2949 var tokens = p.trim(styleTokens[i].split( ":" )); 2950 switch(tokens[0]){ 2951 case "fill": 2952 this.setFill(tokens[1]); 2953 break; 2954 case "fill-opacity": 2955 2956 this.setFillOpacity(tokens[1]); 2957 2958 break; 2959 case "stroke": 2960 this.setStroke(tokens[1]); 2961 break; 2962 case "stroke-width": 2963 this.setStrokeWeight(tokens[1]); 2964 break; 2965 case "stroke-linecap": 2966 this.setStrokeCap(tokens[1]); 2967 break; 2968 case "stroke-linejoin": 2969 this.setStrokeJoin(tokens[1]); 2970 break; 2971 case "stroke-opacity": 2972 this.setStrokeOpacity(tokens[1]); 2973 break; 2974 case "opacity": 2975 this.setOpacity(tokens[1]); 2976 break; 2977 // Other attributes are not yet implemented 2978 } 2979 } 2980 } 2981 }, 2982 setFillOpacity: function(opacityText) { 2983 this.fillOpacity = parseFloat(opacityText); 2984 this.fillColor = this.fillOpacity * 255 << 24 | this.fillColor & 0xFFFFFF; 2985 }, 2986 setFill: function (fillText) { 2987 var opacityMask = this.fillColor & 0xFF000000; 2988 if (fillText === "none") { 2989 this.fill = false; 2990 } else if (fillText.indexOf("#") === 0) { 2991 this.fill = true; 2992 this.fillColor = opacityMask | (parseInt(fillText.substring(1), 16 )) & 0xFFFFFF; 2993 } else if (fillText.indexOf("rgb") === 0) { 2994 this.fill = true; 2995 this.fillColor = opacityMask | this.parseRGB(fillText); 2996 } else if (fillText.indexOf("url(#") === 0) { 2997 this.fillName = fillText.substring(5, fillText.length - 1 ); 2998 /*Object fillObject = findChild(fillName); 2999 if (fillObject instanceof Gradient) { 3000 fill = true; 3001 fillGradient = (Gradient) fillObject; 3002 fillGradientPaint = calcGradientPaint(fillGradient); //, opacity); 3003 } else { 3004 System.err.println("url " + fillName + " refers to unexpected data"); 3005 }*/ 3006 } else { 3007 if (colors[fillText]) { 3008 this.fill = true; 3009 this.fillColor = opacityMask | (parseInt(colors[fillText].substring(1), 16)) & 0xFFFFFF; 3010 } 3011 } 3012 }, 3013 setOpacity: function(opacity) { 3014 this.strokeColor = parseFloat(opacity) * 255 << 24 | this.strokeColor & 0xFFFFFF; 3015 this.fillColor = parseFloat(opacity) * 255 << 24 | this.fillColor & 0xFFFFFF; 3016 }, 3017 setStroke: function(strokeText) { 3018 var opacityMask = this.strokeColor & 0xFF000000; 3019 if (strokeText === "none") { 3020 this.stroke = false; 3021 } else if (strokeText.charAt( 0 ) === "#") { 3022 this.stroke = true; 3023 this.strokeColor = opacityMask | (parseInt( strokeText.substring( 1 ), 16 )) & 0xFFFFFF; 3024 } else if (strokeText.indexOf( "rgb" ) === 0 ) { 3025 this.stroke = true; 3026 this.strokeColor = opacityMask | this.parseRGB(strokeText); 3027 } else if (strokeText.indexOf( "url(#" ) === 0) { 3028 this.strokeName = strokeText.substring(5, strokeText.length - 1); 3029 //this.strokeObject = findChild(strokeName); 3030 /*if (strokeObject instanceof Gradient) { 3031 strokeGradient = (Gradient) strokeObject; 3032 strokeGradientPaint = calcGradientPaint(strokeGradient); //, opacity); 3033 } else { 3034 System.err.println("url " + strokeName + " refers to unexpected data"); 3035 }*/ 3036 } else { 3037 if (colors[strokeText]){ 3038 this.stroke = true; 3039 this.strokeColor = opacityMask | (parseInt(colors[strokeText].substring(1), 16)) & 0xFFFFFF; 3040 } 3041 } 3042 }, 3043 setStrokeWeight: function(weight) { 3044 this.strokeWeight = this.parseUnitSize(weight); 3045 }, 3046 setStrokeJoin: function(linejoin) { 3047 if (linejoin === "miter") { 3048 this.strokeJoin = PConstants.MITER; 3049 3050 } else if (linejoin === "round") { 3051 this.strokeJoin = PConstants.ROUND; 3052 3053 } else if (linejoin === "bevel") { 3054 this.strokeJoin = PConstants.BEVEL; 3055 } 3056 }, 3057 setStrokeCap: function (linecap) { 3058 if (linecap === "butt") { 3059 this.strokeCap = PConstants.SQUARE; 3060 3061 } else if (linecap === "round") { 3062 this.strokeCap = PConstants.ROUND; 3063 3064 } else if (linecap === "square") { 3065 this.strokeCap = PConstants.PROJECT; 3066 } 3067 }, 3068 setStrokeOpacity: function (opacityText) { 3069 this.strokeOpacity = parseFloat(opacityText); 3070 this.strokeColor = this.strokeOpacity * 255 << 24 | this.strokeColor & 0xFFFFFF; 3071 }, 3072 parseRGB: function(color) { 3073 var sub = color.substring(color.indexOf('(') + 1, color.indexOf(')')); 3074 var values = sub.split(", "); 3075 return (values[0] << 16) | (values[1] << 8) | (values[2]); 3076 }, 3077 parseUnitSize: function (text) { 3078 var len = text.length - 2; 3079 if (len < 0) { return text; } 3080 if (text.indexOf("pt") === len) { 3081 return parseFloat(text.substring(0, len)) * 1.25; 3082 } else if (text.indexOf("pc") === len) { 3083 return parseFloat( text.substring( 0, len)) * 15; 3084 } else if (text.indexOf("mm") === len) { 3085 return parseFloat( text.substring(0, len)) * 3.543307; 3086 } else if (text.indexOf("cm") === len) { 3087 return parseFloat(text.substring(0, len)) * 35.43307; 3088 } else if (text.indexOf("in") === len) { 3089 return parseFloat(text.substring(0, len)) * 90; 3090 } else if (text.indexOf("px") === len) { 3091 return parseFloat(text.substring(0, len)); 3092 } else { 3093 return parseFloat(text); 3094 } 3095 } 3096 }; 3097 3098 p.shape = function(shape, x, y, width, height) { 3099 if (arguments.length >= 1 && arguments[0] !== null) { 3100 if (shape.isVisible()) { 3101 p.pushMatrix(); 3102 if (curShapeMode === PConstants.CENTER) { 3103 if (arguments.length === 5) { 3104 p.translate(x - width/2, y - height/2); 3105 p.scale(width / shape.getWidth(), height / shape.getHeight()); 3106 } else if (arguments.length === 3) { 3107 p.translate(x - shape.getWidth()/2, - shape.getHeight()/2); 3108 } else { 3109 p.translate(-shape.getWidth()/2, -shape.getHeight()/2); 3110 } 3111 } else if (curShapeMode === PConstants.CORNER) { 3112 if (arguments.length === 5) { 3113 p.translate(x, y); 3114 p.scale(width / shape.getWidth(), height / shape.getHeight()); 3115 } else if (arguments.length === 3) { 3116 p.translate(x, y); 3117 } 3118 } else if (curShapeMode === PConstants.CORNERS) { 3119 if (arguments.length === 5) { 3120 width -= x; 3121 height -= y; 3122 p.translate(x, y); 3123 p.scale(width / shape.getWidth(), height / shape.getHeight()); 3124 } else if (arguments.length === 3) { 3125 p.translate(x, y); 3126 } 3127 } 3128 shape.draw(); 3129 if ((arguments.length === 1 && curShapeMode === PConstants.CENTER ) || arguments.length > 1) { 3130 p.popMatrix(); 3131 } 3132 } 3133 } 3134 }; 3135 3136 p.shapeMode = function (mode) { 3137 curShapeMode = mode; 3138 }; 3139 3140 p.loadShape = function (filename) { 3141 if (arguments.length === 1) { 3142 if (filename.indexOf(".svg") > -1) { 3143 return new PShapeSVG(null, filename); 3144 } 3145 } 3146 return null; 3147 }; 3148 3149 3150 //////////////////////////////////////////////////////////////////////////// 3151 // XMLAttribute 3152 //////////////////////////////////////////////////////////////////////////// 3153 var XMLAttribute = function(fname, n, nameSpace, v, t){ 3154 this.fullName = fname || ""; 3155 this.name = n || ""; 3156 this.namespace = nameSpace || ""; 3157 this.value = v; 3158 this.type = t; 3159 }; 3160 XMLAttribute.prototype = { 3161 getName: function() { 3162 return this.name; 3163 }, 3164 getFullName: function() { 3165 return this.fullName; 3166 }, 3167 getNamespace: function() { 3168 return this.namespace; 3169 }, 3170 getValue: function() { 3171 return this.value; 3172 }, 3173 getType: function() { 3174 return this.type; 3175 }, 3176 setValue: function(newval) { 3177 this.value = newval; 3178 } 3179 }; 3180 3181 //////////////////////////////////////////////////////////////////////////// 3182 // XMLElement 3183 //////////////////////////////////////////////////////////////////////////// 3184 var XMLElement = p.XMLElement = function() { 3185 if (arguments.length === 4) { 3186 this.attributes = []; 3187 this.children = []; 3188 this.fullName = arguments[0] || ""; 3189 if (arguments[1]) { 3190 this.name = arguments[1]; 3191 } else { 3192 var index = this.fullName.indexOf(':'); 3193 if (index >= 0) { 3194 this.name = this.fullName.substring(index + 1); 3195 } else { 3196 this.name = this.fullName; 3197 } 3198 } 3199 this.namespace = arguments[1]; 3200 this.content = ""; 3201 this.lineNr = arguments[3]; 3202 this.systemID = arguments[2]; 3203 this.parent = null; 3204 } 3205 else if ((arguments.length === 2 && arguments[1].indexOf(".") > -1) ) { // filename or svg xml element 3206 this.attributes = []; 3207 this.children = []; 3208 this.fullName = ""; 3209 this.name = ""; 3210 this.namespace = ""; 3211 this.content = ""; 3212 this.systemID = ""; 3213 this.lineNr = ""; 3214 this.parent = null; 3215 this.parse(arguments[arguments.length -1]); 3216 } else if (arguments.length === 1 && typeof arguments[0] === "string"){ 3217 //xml string 3218 this.attributes = []; 3219 this.children = []; 3220 this.fullName = ""; 3221 this.name = ""; 3222 this.namespace = ""; 3223 this.content = ""; 3224 this.systemID = ""; 3225 this.lineNr = ""; 3226 this.parent = null; 3227 this.parse(arguments[0]); 3228 } 3229 else { //empty ctor 3230 this.attributes = []; 3231 this.children = []; 3232 this.fullName = ""; 3233 this.name = ""; 3234 this.namespace = ""; 3235 this.content = ""; 3236 this.systemID = ""; 3237 this.lineNr = ""; 3238 this.parent = null; 3239 3240 } 3241 return this; 3242 }; 3243 /*XMLElement methods 3244 missing: enumerateAttributeNames(), enumerateChildren(), 3245 NOTE: parse does not work when a url is passed in 3246 */ 3247 XMLElement.prototype = { 3248 parse: function(filename) { 3249 var xmlDoc; 3250 try { 3251 if (filename.indexOf(".xml") > -1 || filename.indexOf(".svg") > -1) { 3252 filename = ajax(filename); 3253 } 3254 xmlDoc = new DOMParser().parseFromString(filename, "text/xml"); 3255 var elements = xmlDoc.documentElement; 3256 if (elements) { 3257 this.parseChildrenRecursive(null, elements); 3258 } else { 3259 throw ("Error loading document"); 3260 } 3261 return this; 3262 } catch(e) { 3263 throw(e); 3264 } 3265 }, 3266 createElement: function () { 3267 if (arguments.length === 2) { 3268 return new XMLElement(arguments[0], arguments[1], null, null); 3269 } else { 3270 return new XMLElement(arguments[0], arguments[1], arguments[2], arguments[3]); 3271 } 3272 }, 3273 hasAttribute: function (name) { 3274 return this.getAttribute(name) !== null; 3275 //2 parameter call missing 3276 }, 3277 createPCDataElement: function () { 3278 return new XMLElement(); 3279 }, 3280 equals: function(object){ 3281 if (typeof object === "Object") { 3282 return this.equalsXMLElement(object); 3283 } 3284 }, 3285 equalsXMLElement: function (object) { 3286 if (object instanceof XMLElement) { 3287 if (this.name !== object.getLocalName) { return false; } 3288 if (this.attributes.length !== object.getAttributeCount()) { return false; } 3289 for (var i = 0; i < this.attributes.length; i++){ 3290 if (! object.hasAttribute(this.attributes[i].getName(), this.attributes[i].getNamespace())) { return false; } 3291 if (this.attributes[i].getValue() !== object.attributes[i].getValue()) { return false; } 3292 if (this.attributes[i].getType() !== object.attributes[i].getType()) { return false; } 3293 } 3294 if (this.children.length !== object.getChildCount()) { return false; } 3295 var child1, child2; 3296 for (i = 0; i < this.children.length; i++) { 3297 child1 = this.getChildAtIndex(i); 3298 child2 = object.getChildAtIndex(i); 3299 if (! child1.equalsXMLElement(child2)) { return false; } 3300 } 3301 return true; 3302 } 3303 }, 3304 getContent: function(){ 3305 return this.content; 3306 }, 3307 getAttribute: function (){ 3308 var attribute; 3309 if( arguments.length === 2 ){ 3310 attribute = this.findAttribute(arguments[0]); 3311 if (attribute) { 3312 return attribute.getValue(); 3313 } else { 3314 return arguments[1]; 3315 } 3316 } else if (arguments.length === 1) { 3317 attribute = this.findAttribute(arguments[0]); 3318 if (attribute) { 3319 return attribute.getValue(); 3320 } else { 3321 return null; 3322 } 3323 } 3324 }, 3325 getStringAttribute: function() { 3326 if (arguments.length === 1) { 3327 return this.getAttribute(arguments[0]); 3328 } else if (arguments.length === 2){ 3329 return this.getAttribute(arguments[0], arguments[1]); 3330 } else { 3331 return this.getAttribute(arguments[0], arguments[1],arguments[2]); 3332 } 3333 }, 3334 getFloatAttribute: function() { 3335 if (arguments.length === 1 ) { 3336 return parseFloat(this.getAttribute(arguments[0], 0)); 3337 } else if (arguments.length === 2 ){ 3338 return this.getAttribute(arguments[0], arguments[1]); 3339 } else { 3340 return this.getAttribute(arguments[0], arguments[1],arguments[2]); 3341 } 3342 }, 3343 getIntAttribute: function () { 3344 if (arguments.length === 1) { 3345 return this.getAttribute( arguments[0], 0 ); 3346 } else if (arguments.length === 2) { 3347 return this.getAttribute(arguments[0], arguments[1]); 3348 } else { 3349 return this.getAttribute(arguments[0], arguments[1],arguments[2]); 3350 } 3351 }, 3352 hasChildren: function () { 3353 return this.children.length > 0 ; 3354 }, 3355 addChild: function (child) { 3356 if (child !== null) { 3357 child.parent = this; 3358 this.children.push(child); 3359 } 3360 }, 3361 insertChild: function (child, index) { 3362 if (child) { 3363 if ((child.getLocalName() === null) && (! this.hasChildren())) { 3364 var lastChild = this.children[this.children.length -1]; 3365 if (lastChild.getLocalName() === null) { 3366 lastChild.setContent(lastChild.getContent() + child.getContent()); 3367 return; 3368 } 3369 } 3370 child.parent = this; 3371 this.children.splice(index,0,child); 3372 } 3373 }, 3374 getChild: function (index){ 3375 if (typeof index === "number") { 3376 return this.children[index]; 3377 } 3378 else if (index.indexOf('/') !== -1) { // path was given 3379 this.getChildRecursive(index.split("/"), 0); 3380 } else { 3381 var kid, kidName; 3382 for (var i = 0; i < this.getChildCount(); i++) { 3383 kid = this.getChild(i); 3384 kidName = kid.getName(); 3385 if (kidName !== null && kidName === index) { 3386 return kid; 3387 } 3388 } 3389 return null; 3390 } 3391 }, 3392 getChildren: function(){ 3393 if (arguments.length === 1) { 3394 if (typeof arguments[0] === "number") { 3395 return this.getChild( arguments[0]); 3396 } else if (arguments[0].indexOf('/') !== -1) { // path was given 3397 return this.getChildrenRecursive( arguments[0].split("/"), 0); 3398 } else { 3399 var matches = []; 3400 var kid, kidName; 3401 for (var i = 0; i < this.getChildCount(); i++) { 3402 kid = this.getChild(i); 3403 kidName = kid.getName(); 3404 if (kidName !== null && kidName === arguments[0]) { 3405 matches.push(kid); 3406 } 3407 } 3408 return matches; 3409 } 3410 }else { 3411 return this.children; 3412 } 3413 }, 3414 getChildCount: function(){ 3415 return this.children.length; 3416 }, 3417 getChildRecursive: function (items, offset) { 3418 var kid, kidName; 3419 for(var i = 0; i < this.getChildCount(); i++) { 3420 kid = this.getChild(i); 3421 kidName = kid.getName(); 3422 if (kidName !== null && kidName === items[offset]) { 3423 if (offset === items.length-1) { 3424 return kid; 3425 } else { 3426 offset += 1; 3427 return kid.getChildRecursive(items, offset); 3428 } 3429 } 3430 } 3431 return null; 3432 }, 3433 getChildrenRecursive: function (items, offset) { 3434 if (offset === items.length-1) { 3435 return this.getChildren(items[offset]); 3436 } 3437 var matches = this.getChildren(items[offset]); 3438 var kidMatches; 3439 for (var i = 0; i < matches.length; i++) { 3440 kidMatches = matches[i].getChildrenRecursive(items, offset+1); 3441 } 3442 return kidMatches; 3443 }, 3444 parseChildrenRecursive: function (parent , elementpath){ 3445 var xmlelement, 3446 xmlattribute, 3447 tmpattrib; 3448 if (!parent) { 3449 this.fullName = elementpath.localName; 3450 this.name = elementpath.nodeName; 3451 this.content = elementpath.textContent || ""; 3452 xmlelement = this; 3453 } else { // a parent 3454 xmlelement = new XMLElement(elementpath.localName, elementpath.nodeName, "", ""); 3455 xmlelement.content = elementpath.textContent || ""; 3456 xmlelement.parent = parent; 3457 } 3458 3459 for (var l = 0; l < elementpath.attributes.length; l++) { 3460 tmpattrib = elementpath.attributes[l]; 3461 xmlattribute = new XMLAttribute(tmpattrib.getname , tmpattrib.nodeName, tmpattrib.namespaceURI , tmpattrib.nodeValue , tmpattrib.nodeType); 3462 xmlelement.attributes.push(xmlattribute); 3463 } 3464 3465 for (var node in elementpath.childNodes){ 3466 if(elementpath.childNodes[node].nodeType === 1) { //ELEMENT_NODE type 3467 xmlelement.children.push( xmlelement.parseChildrenRecursive(xmlelement, elementpath.childNodes[node])); 3468 } 3469 } 3470 return xmlelement; 3471 }, 3472 isLeaf: function(){ 3473 return this.hasChildren(); 3474 }, 3475 listChildren: function() { 3476 var arr = []; 3477 for (var i = 0; i < this.children.length; i++) { 3478 arr.push( this.getChild(i).getName()); 3479 } 3480 return arr; 3481 }, 3482 removeAttribute: function (name , namespace) { 3483 this.namespace = namespace || ""; 3484 for (var i = 0; i < this.attributes.length; i++){ 3485 if (this.attributes[i].getName() === name && this.attributes[i].getNamespace() === this.namespace) { 3486 this.attributes.splice(i, 0); 3487 } 3488 } 3489 }, 3490 removeChild: function(child) { 3491 if (child) { 3492 for (var i = 0; i < this.children.length; i++) { 3493 if (this.children[i].equalsXMLElement(child)) { 3494 this.children.splice(i, 0); 3495 } 3496 } 3497 } 3498 }, 3499 removeChildAtIndex: function(index) { 3500 if (this.children.length > index) { //make sure its not outofbounds 3501 this.children.splice(index, 0); 3502 } 3503 }, 3504 findAttribute: function (name, namespace) { 3505 this.namespace = namespace || ""; 3506 for (var i = 0; i < this.attributes.length; i++ ) { 3507 if (this.attributes[i].getName() === name && this.attributes[i].getNamespace() === this.namespace) { 3508 return this.attributes[i]; 3509 } 3510 } 3511 }, 3512 setAttribute: function() { 3513 var attr; 3514 if (arguments.length === 3) { 3515 var index = arguments[0].indexOf(':'); 3516 var name = arguments[0].substring(index + 1); 3517 attr = this.findAttribute( name, arguments[1] ); 3518 if (attr) { 3519 attr.setValue(arguments[2]); 3520 } else { 3521 attr = new XMLAttribute(arguments[0], name, arguments[1], arguments[2], "CDATA"); 3522 this.attributes.addElement(attr); 3523 } 3524 } else { 3525 attr = this.findAttribute(arguments[0]); 3526 if (attr) { 3527 attr.setValue(arguments[1]); 3528 } else { 3529 attr = new XMLAttribute(arguments[0], arguments[0], null, arguments[1], "CDATA"); 3530 this.attributes.addElement(attr); 3531 } 3532 } 3533 }, 3534 setContent: function(content) { 3535 this.content = content; 3536 }, 3537 setName: function() { 3538 if (arguments.length === 1) { 3539 this.name = arguments[0]; 3540 this.fullName = arguments[0]; 3541 this.namespace = arguments[0]; 3542 } else { 3543 var index = arguments[0].indexOf(':'); 3544 if ((arguments[1] === null) || (index < 0)) { 3545 this.name = arguments[0]; 3546 } else { 3547 this.name = arguments[0].substring(index + 1); 3548 } 3549 this.fullName = arguments[0]; 3550 this.namespace = arguments[1]; 3551 } 3552 }, 3553 getName: function() { 3554 return this.fullName; 3555 } 3556 }; 3557 3558 3559 //////////////////////////////////////////////////////////////////////////// 3560 // 2D Matrix 3561 //////////////////////////////////////////////////////////////////////////// 3562 3563 /* 3564 Helper function for printMatrix(). Finds the largest scalar 3565 in the matrix, then number of digits left of the decimal. 3566 Call from PMatrix2D and PMatrix3D's print() function. 3567 */ 3568 var printMatrixHelper = function printMatrixHelper(elements) { 3569 var big = 0; 3570 for (var i = 0; i < elements.length; i++) { 3571 if (i !== 0) { 3572 big = Math.max(big, Math.abs(elements[i])); 3573 } else { 3574 big = Math.abs(elements[i]); 3575 } 3576 } 3577 3578 var digits = (big + "").indexOf("."); 3579 if (digits === 0) { 3580 digits = 1; 3581 } else if (digits === -1) { 3582 digits = (big + "").length; 3583 } 3584 3585 return digits; 3586 }; 3587 3588 var PMatrix2D = p.PMatrix2D = function() { 3589 if (arguments.length === 0) { 3590 this.reset(); 3591 } else if (arguments.length === 1 && arguments[0] instanceof PMatrix2D) { 3592 this.set(arguments[0].array()); 3593 } else if (arguments.length === 6) { 3594 this.set(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5]); 3595 } 3596 }; 3597 3598 PMatrix2D.prototype = { 3599 set: function() { 3600 if (arguments.length === 6) { 3601 var a = arguments; 3602 this.set([a[0], a[1], a[2], 3603 a[3], a[4], a[5]]); 3604 } else if (arguments.length === 1 && arguments[0] instanceof PMatrix2D) { 3605 this.elements = arguments[0].array(); 3606 } else if (arguments.length === 1 && arguments[0] instanceof Array) { 3607 this.elements = arguments[0].slice(); 3608 } 3609 }, 3610 get: function() { 3611 var outgoing = new PMatrix2D(); 3612 outgoing.set(this.elements); 3613 return outgoing; 3614 }, 3615 reset: function() { 3616 this.set([1, 0, 0, 0, 1, 0]); 3617 }, 3618 // Returns a copy of the element values. 3619 array: function array() { 3620 return this.elements.slice(); 3621 }, 3622 translate: function(tx, ty) { 3623 this.elements[2] = tx * this.elements[0] + ty * this.elements[1] + this.elements[2]; 3624 this.elements[5] = tx * this.elements[3] + ty * this.elements[4] + this.elements[5]; 3625 }, 3626 transpose: function() { 3627 // Does nothing in Processing. 3628 }, 3629 mult: function(source, target) { 3630 var x, y; 3631 if (source instanceof PVector) { 3632 x = source.x; 3633 y = source.y; 3634 if (!target) { 3635 target = new PVector(); 3636 } 3637 } else if (source instanceof Array) { 3638 x = source[0]; 3639 y = source[1]; 3640 if (!target) { 3641 target = []; 3642 } 3643 } 3644 if (target instanceof Array) { 3645 target[0] = this.elements[0] * x + this.elements[1] * y + this.elements[2]; 3646 target[1] = this.elements[3] * x + this.elements[4] * y + this.elements[5]; 3647 } else if (target instanceof PVector) { 3648 target.x = this.elements[0] * x + this.elements[1] * y + this.elements[2]; 3649 target.y = this.elements[3] * x + this.elements[4] * y + this.elements[5]; 3650 target.z = 0; 3651 } 3652 return target; 3653 }, 3654 multX: function(x, y) { 3655 return (x * this.elements[0] + y * this.elements[1] + this.elements[2]); 3656 }, 3657 multY: function(x, y) { 3658 return (x * this.elements[3] + y * this.elements[4] + this.elements[5]); 3659 }, 3660 skewX: function(angle) { 3661 this.apply(1, 0, 1, angle, 0, 0); 3662 }, 3663 skewY: function(angle) { 3664 this.apply(1, 0, 1, 0, angle, 0); 3665 }, 3666 determinant: function() { 3667 return (this.elements[0] * this.elements[4] - this.elements[1] * this.elements[3]); 3668 }, 3669 invert: function() { 3670 var d = this.determinant(); 3671 if ( Math.abs( d ) > PConstants.FLOAT_MIN ) { 3672 var old00 = this.elements[0]; 3673 var old01 = this.elements[1]; 3674 var old02 = this.elements[2]; 3675 var old10 = this.elements[3]; 3676 var old11 = this.elements[4]; 3677 var old12 = this.elements[5]; 3678 this.elements[0] = old11 / d; 3679 this.elements[3] = -old10 / d; 3680 this.elements[1] = -old01 / d; 3681 this.elements[1] = old00 / d; 3682 this.elements[2] = (old01 * old12 - old11 * old02) / d; 3683 this.elements[5] = (old10 * old02 - old00 * old12) / d; 3684 return true; 3685 } 3686 return false; 3687 }, 3688 scale: function(sx, sy) { 3689 if (sx && !sy) { 3690 sy = sx; 3691 } 3692 if (sx && sy) { 3693 this.elements[0] *= sx; 3694 this.elements[1] *= sy; 3695 this.elements[3] *= sx; 3696 this.elements[4] *= sy; 3697 } 3698 }, 3699 apply: function() { 3700 var source; 3701 if (arguments.length === 1 && arguments[0] instanceof PMatrix2D) { 3702 source = arguments[0].array(); 3703 } else if (arguments.length === 6) { 3704 source = Array.prototype.slice.call(arguments); 3705 } else if (arguments.length === 1 && arguments[0] instanceof Array) { 3706 source = arguments[0]; 3707 } 3708 3709 var result = [0, 0, this.elements[2], 3710 0, 0, this.elements[5]]; 3711 var e = 0; 3712 for (var row = 0; row < 2; row++) { 3713 for (var col = 0; col < 3; col++, e++) { 3714 result[e] += this.elements[row * 3 + 0] * source[col + 0] + 3715 this.elements[row * 3 + 1] * source[col + 3]; 3716 } 3717 } 3718 this.elements = result.slice(); 3719 }, 3720 preApply: function() { 3721 var source; 3722 if (arguments.length === 1 && arguments[0] instanceof PMatrix2D) { 3723 source = arguments[0].array(); 3724 } else if (arguments.length === 6) { 3725 source = Array.prototype.slice.call(arguments); 3726 } else if (arguments.length === 1 && arguments[0] instanceof Array) { 3727 source = arguments[0]; 3728 } 3729 var result = [0, 0, source[2], 3730 0, 0, source[5]]; 3731 result[2] = source[2] + this.elements[2] * source[0] + this.elements[5] * source[1]; 3732 result[5] = source[5] + this.elements[2] * source[3] + this.elements[5] * source[4]; 3733 result[0] = this.elements[0] * source[0] + this.elements[3] * source[1]; 3734 result[3] = this.elements[0] * source[3] + this.elements[3] * source[4]; 3735 result[1] = this.elements[1] * source[0] + this.elements[4] * source[1]; 3736 result[4] = this.elements[1] * source[3] + this.elements[4] * source[4]; 3737 this.elements = result.slice(); 3738 }, 3739 rotate: function(angle) { 3740 var c = Math.cos(angle); 3741 var s = Math.sin(angle); 3742 var temp1 = this.elements[0]; 3743 var temp2 = this.elements[1]; 3744 this.elements[0] = c * temp1 + s * temp2; 3745 this.elements[1] = -s * temp1 + c * temp2; 3746 temp1 = this.elements[3]; 3747 temp2 = this.elements[4]; 3748 this.elements[3] = c * temp1 + s * temp2; 3749 this.elements[4] = -s * temp1 + c * temp2; 3750 }, 3751 rotateZ: function(angle) { 3752 this.rotate(angle); 3753 }, 3754 print: function() { 3755 var digits = printMatrixHelper(this.elements); 3756 var output = "" + p.nfs(this.elements[0], digits, 4) + " " + 3757 p.nfs(this.elements[1], digits, 4) + " " + 3758 p.nfs(this.elements[2], digits, 4) + "\n" + 3759 p.nfs(this.elements[3], digits, 4) + " " + 3760 p.nfs(this.elements[4], digits, 4) + " " + 3761 p.nfs(this.elements[5], digits, 4) + "\n\n"; 3762 p.println(output); 3763 } 3764 }; 3765 3766 //////////////////////////////////////////////////////////////////////////// 3767 // PMatrix3D 3768 //////////////////////////////////////////////////////////////////////////// 3769 3770 var PMatrix3D = p.PMatrix3D = function PMatrix3D() { 3771 // When a matrix is created, it is set to an identity matrix 3772 this.reset(); 3773 }; 3774 3775 PMatrix3D.prototype = { 3776 set: function() { 3777 if (arguments.length === 16) { 3778 this.elements = Array.prototype.slice.call(arguments); 3779 } else if (arguments.length === 1 && arguments[0] instanceof PMatrix3D) { 3780 this.elements = arguments[0].array(); 3781 } else if (arguments.length === 1 && arguments[0] instanceof Array) { 3782 this.elements = arguments[0].slice(); 3783 } 3784 }, 3785 get: function() { 3786 var outgoing = new PMatrix3D(); 3787 outgoing.set(this.elements); 3788 return outgoing; 3789 }, 3790 reset: function() { 3791 this.set([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]); 3792 }, 3793 // Returns a copy of the element values. 3794 array: function array() { 3795 return this.elements.slice(); 3796 }, 3797 translate: function(tx, ty, tz) { 3798 if (tz === undef) { 3799 tz = 0; 3800 } 3801 3802 this.elements[3] += tx * this.elements[0] +