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] + ty * this.elements[1] + tz * this.elements[2]; 3803 this.elements[7] += tx * this.elements[4] + ty * this.elements[5] + tz * this.elements[6]; 3804 this.elements[11] += tx * this.elements[8] + ty * this.elements[9] + tz * this.elements[10]; 3805 this.elements[15] += tx * this.elements[12] + ty * this.elements[13] + tz * this.elements[14]; 3806 }, 3807 transpose: function() { 3808 var temp = this.elements.slice(); 3809 this.elements[0] = temp[0]; 3810 this.elements[1] = temp[4]; 3811 this.elements[2] = temp[8]; 3812 this.elements[3] = temp[12]; 3813 this.elements[4] = temp[1]; 3814 this.elements[5] = temp[5]; 3815 this.elements[6] = temp[9]; 3816 this.elements[7] = temp[13]; 3817 this.elements[8] = temp[2]; 3818 this.elements[9] = temp[6]; 3819 this.elements[10] = temp[10]; 3820 this.elements[11] = temp[14]; 3821 this.elements[12] = temp[3]; 3822 this.elements[13] = temp[7]; 3823 this.elements[14] = temp[11]; 3824 this.elements[15] = temp[15]; 3825 }, 3826 /* 3827 You must either pass in two PVectors or two arrays, 3828 don't mix between types. You may also omit a second 3829 argument and simply read the result from the return. 3830 */ 3831 mult: function(source, target) { 3832 var x, y, z, w; 3833 if (source instanceof PVector) { 3834 x = source.x; 3835 y = source.y; 3836 z = source.z; 3837 w = 1; 3838 if (!target) { 3839 target = new PVector(); 3840 } 3841 } else if (source instanceof Array) { 3842 x = source[0]; 3843 y = source[1]; 3844 z = source[2]; 3845 w = source[3] || 1; 3846 3847 if (!target || target.length !== 3 && target.length !== 4) { 3848 target = [0, 0, 0]; 3849 } 3850 } 3851 3852 if (target instanceof Array) { 3853 if (target.length === 3) { 3854 target[0] = this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3]; 3855 target[1] = this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7]; 3856 target[2] = this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11]; 3857 } else if (target.length === 4) { 3858 target[0] = this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3] * w; 3859 target[1] = this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7] * w; 3860 target[2] = this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11] * w; 3861 target[3] = this.elements[12] * x + this.elements[13] * y + this.elements[14] * z + this.elements[15] * w; 3862 } 3863 } 3864 if (target instanceof PVector) { 3865 target.x = this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3]; 3866 target.y = this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7]; 3867 target.z = this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11]; 3868 } 3869 return target; 3870 }, 3871 preApply: function() { 3872 var source; 3873 if (arguments.length === 1 && arguments[0] instanceof PMatrix3D) { 3874 source = arguments[0].array(); 3875 } else if (arguments.length === 16) { 3876 source = Array.prototype.slice.call(arguments); 3877 } else if (arguments.length === 1 && arguments[0] instanceof Array) { 3878 source = arguments[0]; 3879 } 3880 3881 var result = [0, 0, 0, 0, 3882 0, 0, 0, 0, 3883 0, 0, 0, 0, 3884 0, 0, 0, 0]; 3885 var e = 0; 3886 for (var row = 0; row < 4; row++) { 3887 for (var col = 0; col < 4; col++, e++) { 3888 result[e] += this.elements[col + 0] * source[row * 4 + 0] + this.elements[col + 4] * 3889 source[row * 4 + 1] + this.elements[col + 8] * source[row * 4 + 2] + 3890 this.elements[col + 12] * source[row * 4 + 3]; 3891 } 3892 } 3893 this.elements = result.slice(); 3894 }, 3895 apply: function() { 3896 var source; 3897 if (arguments.length === 1 && arguments[0] instanceof PMatrix3D) { 3898 source = arguments[0].array(); 3899 } else if (arguments.length === 16) { 3900 source = Array.prototype.slice.call(arguments); 3901 } else if (arguments.length === 1 && arguments[0] instanceof Array) { 3902 source = arguments[0]; 3903 } 3904 3905 var result = [0, 0, 0, 0, 3906 0, 0, 0, 0, 3907 0, 0, 0, 0, 3908 0, 0, 0, 0]; 3909 var e = 0; 3910 for (var row = 0; row < 4; row++) { 3911 for (var col = 0; col < 4; col++, e++) { 3912 result[e] += this.elements[row * 4 + 0] * source[col + 0] + this.elements[row * 4 + 1] * 3913 source[col + 4] + this.elements[row * 4 + 2] * source[col + 8] + 3914 this.elements[row * 4 + 3] * source[col + 12]; 3915 } 3916 } 3917 this.elements = result.slice(); 3918 }, 3919 rotate: function(angle, v0, v1, v2) { 3920 if (!v1) { 3921 this.rotateZ(angle); 3922 } else { 3923 // TODO should make sure this vector is normalized 3924 var c = p.cos(angle); 3925 var s = p.sin(angle); 3926 var t = 1.0 - c; 3927 3928 this.apply((t * v0 * v0) + c, 3929 (t * v0 * v1) - (s * v2), 3930 (t * v0 * v2) + (s * v1), 3931 0, 3932 (t * v0 * v1) + (s * v2), 3933 (t * v1 * v1) + c, 3934 (t * v1 * v2) - (s * v0), 3935 0, 3936 (t * v0 * v2) - (s * v1), 3937 (t * v1 * v2) + (s * v0), 3938 (t * v2 * v2) + c, 3939 0, 0, 0, 0, 1); 3940 } 3941 }, 3942 invApply: function() { 3943 if (inverseCopy === undef) { 3944 inverseCopy = new PMatrix3D(); 3945 } 3946 var a = arguments; 3947 inverseCopy.set(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], 3948 a[9], a[10], a[11], a[12], a[13], a[14], a[15]); 3949 3950 if (!inverseCopy.invert()) { 3951 return false; 3952 } 3953 this.preApply(inverseCopy); 3954 return true; 3955 }, 3956 rotateX: function(angle) { 3957 var c = p.cos(angle); 3958 var s = p.sin(angle); 3959 this.apply([1, 0, 0, 0, 0, c, -s, 0, 0, s, c, 0, 0, 0, 0, 1]); 3960 }, 3961 3962 rotateY: function(angle) { 3963 var c = p.cos(angle); 3964 var s = p.sin(angle); 3965 this.apply([c, 0, s, 0, 0, 1, 0, 0, -s, 0, c, 0, 0, 0, 0, 1]); 3966 }, 3967 rotateZ: function(angle) { 3968 var c = Math.cos(angle); 3969 var s = Math.sin(angle); 3970 this.apply([c, -s, 0, 0, s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]); 3971 }, 3972 // Uniform scaling if only one value passed in 3973 scale: function(sx, sy, sz) { 3974 if (sx && !sy && !sz) { 3975 sy = sz = sx; 3976 } else if (sx && sy && !sz) { 3977 sz = 1; 3978 } 3979 3980 if (sx && sy && sz) { 3981 this.elements[0] *= sx; 3982 this.elements[1] *= sy; 3983 this.elements[2] *= sz; 3984 this.elements[4] *= sx; 3985 this.elements[5] *= sy; 3986 this.elements[6] *= sz; 3987 this.elements[8] *= sx; 3988 this.elements[9] *= sy; 3989 this.elements[10] *= sz; 3990 this.elements[12] *= sx; 3991 this.elements[13] *= sy; 3992 this.elements[14] *= sz; 3993 } 3994 }, 3995 skewX: function(angle) { 3996 var t = Math.tan(angle); 3997 this.apply(1, t, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); 3998 }, 3999 skewY: function(angle) { 4000 var t = Math.tan(angle); 4001 this.apply(1, 0, 0, 0, t, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); 4002 }, 4003 multX: function(x, y, z, w) { 4004 if (!z) { 4005 return this.elements[0] * x + this.elements[1] * y + this.elements[3]; 4006 } else if (!w) { 4007 return this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3]; 4008 } else { 4009 return this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3] * w; 4010 } 4011 }, 4012 multY: function(x, y, z, w) { 4013 if (!z) { 4014 return this.elements[4] * x + this.elements[5] * y + this.elements[7]; 4015 } else if (!w) { 4016 return this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7]; 4017 } else { 4018 return this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7] * w; 4019 } 4020 }, 4021 multZ: function(x, y, z, w) { 4022 if (!w) { 4023 return this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11]; 4024 } else { 4025 return this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11] * w; 4026 } 4027 }, 4028 multW: function(x, y, z, w) { 4029 if (!w) { 4030 return this.elements[12] * x + this.elements[13] * y + this.elements[14] * z + this.elements[15]; 4031 } else { 4032 return this.elements[12] * x + this.elements[13] * y + this.elements[14] * z + this.elements[15] * w; 4033 } 4034 }, 4035 invert: function() { 4036 var fA0 = this.elements[0] * this.elements[5] - this.elements[1] * this.elements[4]; 4037 var fA1 = this.elements[0] * this.elements[6] - this.elements[2] * this.elements[4]; 4038 var fA2 = this.elements[0] * this.elements[7] - this.elements[3] * this.elements[4]; 4039 var fA3 = this.elements[1] * this.elements[6] - this.elements[2] * this.elements[5]; 4040 var fA4 = this.elements[1] * this.elements[7] - this.elements[3] * this.elements[5]; 4041 var fA5 = this.elements[2] * this.elements[7] - this.elements[3] * this.elements[6]; 4042 var fB0 = this.elements[8] * this.elements[13] - this.elements[9] * this.elements[12]; 4043 var fB1 = this.elements[8] * this.elements[14] - this.elements[10] * this.elements[12]; 4044 var fB2 = this.elements[8] * this.elements[15] - this.elements[11] * this.elements[12]; 4045 var fB3 = this.elements[9] * this.elements[14] - this.elements[10] * this.elements[13]; 4046 var fB4 = this.elements[9] * this.elements[15] - this.elements[11] * this.elements[13]; 4047 var fB5 = this.elements[10] * this.elements[15] - this.elements[11] * this.elements[14]; 4048 4049 // Determinant 4050 var fDet = fA0 * fB5 - fA1 * fB4 + fA2 * fB3 + fA3 * fB2 - fA4 * fB1 + fA5 * fB0; 4051 4052 // Account for a very small value 4053 // return false if not successful. 4054 if (Math.abs(fDet) <= 1e-9) { 4055 return false; 4056 } 4057 4058 var kInv = []; 4059 kInv[0] = +this.elements[5] * fB5 - this.elements[6] * fB4 + this.elements[7] * fB3; 4060 kInv[4] = -this.elements[4] * fB5 + this.elements[6] * fB2 - this.elements[7] * fB1; 4061 kInv[8] = +this.elements[4] * fB4 - this.elements[5] * fB2 + this.elements[7] * fB0; 4062 kInv[12] = -this.elements[4] * fB3 + this.elements[5] * fB1 - this.elements[6] * fB0; 4063 kInv[1] = -this.elements[1] * fB5 + this.elements[2] * fB4 - this.elements[3] * fB3; 4064 kInv[5] = +this.elements[0] * fB5 - this.elements[2] * fB2 + this.elements[3] * fB1; 4065 kInv[9] = -this.elements[0] * fB4 + this.elements[1] * fB2 - this.elements[3] * fB0; 4066 kInv[13] = +this.elements[0] * fB3 - this.elements[1] * fB1 + this.elements[2] * fB0; 4067 kInv[2] = +this.elements[13] * fA5 - this.elements[14] * fA4 + this.elements[15] * fA3; 4068 kInv[6] = -this.elements[12] * fA5 + this.elements[14] * fA2 - this.elements[15] * fA1; 4069 kInv[10] = +this.elements[12] * fA4 - this.elements[13] * fA2 + this.elements[15] * fA0; 4070 kInv[14] = -this.elements[12] * fA3 + this.elements[13] * fA1 - this.elements[14] * fA0; 4071 kInv[3] = -this.elements[9] * fA5 + this.elements[10] * fA4 - this.elements[11] * fA3; 4072 kInv[7] = +this.elements[8] * fA5 - this.elements[10] * fA2 + this.elements[11] * fA1; 4073 kInv[11] = -this.elements[8] * fA4 + this.elements[9] * fA2 - this.elements[11] * fA0; 4074 kInv[15] = +this.elements[8] * fA3 - this.elements[9] * fA1 + this.elements[10] * fA0; 4075 4076 // Inverse using Determinant 4077 var fInvDet = 1.0 / fDet; 4078 kInv[0] *= fInvDet; 4079 kInv[1] *= fInvDet; 4080 kInv[2] *= fInvDet; 4081 kInv[3] *= fInvDet; 4082 kInv[4] *= fInvDet; 4083 kInv[5] *= fInvDet; 4084 kInv[6] *= fInvDet; 4085 kInv[7] *= fInvDet; 4086 kInv[8] *= fInvDet; 4087 kInv[9] *= fInvDet; 4088 kInv[10] *= fInvDet; 4089 kInv[11] *= fInvDet; 4090 kInv[12] *= fInvDet; 4091 kInv[13] *= fInvDet; 4092 kInv[14] *= fInvDet; 4093 kInv[15] *= fInvDet; 4094 4095 this.elements = kInv.slice(); 4096 return true; 4097 }, 4098 toString: function() { 4099 var str = ""; 4100 for (var i = 0; i < 15; i++) { 4101 str += this.elements[i] + ", "; 4102 } 4103 str += this.elements[15]; 4104 return str; 4105 }, 4106 print: function() { 4107 var digits = printMatrixHelper(this.elements); 4108 4109 var output = "" + p.nfs(this.elements[0], digits, 4) + " " + p.nfs(this.elements[1], digits, 4) + 4110 " " + p.nfs(this.elements[2], digits, 4) + " " + p.nfs(this.elements[3], digits, 4) + 4111 "\n" + p.nfs(this.elements[4], digits, 4) + " " + p.nfs(this.elements[5], digits, 4) + 4112 " " + p.nfs(this.elements[6], digits, 4) + " " + p.nfs(this.elements[7], digits, 4) + 4113 "\n" + p.nfs(this.elements[8], digits, 4) + " " + p.nfs(this.elements[9], digits, 4) + 4114 " " + p.nfs(this.elements[10], digits, 4) + " " + p.nfs(this.elements[11], digits, 4) + 4115 "\n" + p.nfs(this.elements[12], digits, 4) + " " + p.nfs(this.elements[13], digits, 4) + 4116 " " + p.nfs(this.elements[14], digits, 4) + " " + p.nfs(this.elements[15], digits, 4) + "\n\n"; 4117 p.println(output); 4118 }, 4119 invTranslate: function(tx, ty, tz) { 4120 this.preApply(1, 0, 0, -tx, 0, 1, 0, -ty, 0, 0, 1, -tz, 0, 0, 0, 1); 4121 }, 4122 invRotateX: function(angle) { 4123 var c = Math.cos(-angle); 4124 var s = Math.sin(-angle); 4125 this.preApply([1, 0, 0, 0, 0, c, -s, 0, 0, s, c, 0, 0, 0, 0, 1]); 4126 }, 4127 invRotateY: function(angle) { 4128 var c = Math.cos(-angle); 4129 var s = Math.sin(-angle); 4130 this.preApply([c, 0, s, 0, 0, 1, 0, 0, -s, 0, c, 0, 0, 0, 0, 1]); 4131 }, 4132 invRotateZ: function(angle) { 4133 var c = Math.cos(-angle); 4134 var s = Math.sin(-angle); 4135 this.preApply([c, -s, 0, 0, s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]); 4136 }, 4137 invScale: function(x, y, z) { 4138 this.preApply([1 / x, 0, 0, 0, 0, 1 / y, 0, 0, 0, 0, 1 / z, 0, 0, 0, 0, 1]); 4139 } 4140 }; 4141 4142 //////////////////////////////////////////////////////////////////////////// 4143 // Matrix Stack 4144 //////////////////////////////////////////////////////////////////////////// 4145 4146 var PMatrixStack = p.PMatrixStack = function PMatrixStack() { 4147 this.matrixStack = []; 4148 }; 4149 4150 PMatrixStack.prototype.load = function load() { 4151 var tmpMatrix; 4152 if (p.use3DContext) { 4153 tmpMatrix = new PMatrix3D(); 4154 } else { 4155 tmpMatrix = new PMatrix2D(); 4156 } 4157 4158 if (arguments.length === 1) { 4159 tmpMatrix.set(arguments[0]); 4160 } else { 4161 tmpMatrix.set(arguments); 4162 } 4163 this.matrixStack.push(tmpMatrix); 4164 }; 4165 4166 PMatrixStack.prototype.push = function push() { 4167 this.matrixStack.push(this.peek()); 4168 }; 4169 4170 PMatrixStack.prototype.pop = function pop() { 4171 return this.matrixStack.pop(); 4172 }; 4173 4174 PMatrixStack.prototype.peek = function peek() { 4175 var tmpMatrix; 4176 if (p.use3DContext) { 4177 tmpMatrix = new PMatrix3D(); 4178 } else { 4179 tmpMatrix = new PMatrix2D(); 4180 } 4181 4182 tmpMatrix.set(this.matrixStack[this.matrixStack.length - 1]); 4183 return tmpMatrix; 4184 }; 4185 4186 PMatrixStack.prototype.mult = function mult(matrix) { 4187 this.matrixStack[this.matrixStack.length - 1].apply(matrix); 4188 }; 4189 4190 //////////////////////////////////////////////////////////////////////////// 4191 // Array handling 4192 //////////////////////////////////////////////////////////////////////////// 4193 4194 p.split = function(str, delim) { 4195 return str.split(delim); 4196 }; 4197 4198 p.splitTokens = function(str, tokens) { 4199 if (arguments.length === 1) { 4200 tokens = "\n\t\r\f "; 4201 } 4202 4203 tokens = "[" + tokens + "]"; 4204 4205 var ary = []; 4206 var index = 0; 4207 var pos = str.search(tokens); 4208 4209 while (pos >= 0) { 4210 if (pos === 0) { 4211 str = str.substring(1); 4212 } else { 4213 ary[index] = str.substring(0, pos); 4214 index++; 4215 str = str.substring(pos); 4216 } 4217 pos = str.search(tokens); 4218 } 4219 4220 if (str.length > 0) { 4221 ary[index] = str; 4222 } 4223 4224 if (ary.length === 0) { 4225 ary = undef; 4226 } 4227 4228 return ary; 4229 }; 4230 4231 p.append = function(array, element) { 4232 array[array.length] = element; 4233 return array; 4234 }; 4235 4236 p.concat = function(array1, array2) { 4237 return array1.concat(array2); 4238 }; 4239 4240 p.sort = function(array, numElem) { 4241 var ret = []; 4242 4243 // depending on the type used (int, float) or string 4244 // we'll need to use a different compare function 4245 if (array.length > 0) { 4246 // copy since we need to return another array 4247 var elemsToCopy = numElem > 0 ? numElem : array.length; 4248 for (var i = 0; i < elemsToCopy; i++) { 4249 ret.push(array[i]); 4250 } 4251 if (typeof array[0] === "string") { 4252 ret.sort(); 4253 } 4254 // int or float 4255 else { 4256 ret.sort(function(a, b) { 4257 return a - b; 4258 }); 4259 } 4260 4261 // copy on the rest of the elements that were not sorted in case the user 4262 // only wanted a subset of an array to be sorted. 4263 if (numElem > 0) { 4264 for (var j = ret.length; j < array.length; j++) { 4265 ret.push(array[j]); 4266 } 4267 } 4268 } 4269 return ret; 4270 }; 4271 4272 /** 4273 splice inserts "value" which can be either a scalar or an array 4274 into "array" at position "index". 4275 */ 4276 p.splice = function(array, value, index) { 4277 4278 // Trying to splice an empty array into "array" in P5 won't do 4279 // anything, just return the original. 4280 if(value.length === 0) 4281 { 4282 return array; 4283 } 4284 4285 // If the second argument was an array, we'll need to iterate over all 4286 // the "value" elements and add one by one because 4287 // array.splice(index, 0, value); 4288 // would create a multi-dimensional array which isn't what we want. 4289 if(value instanceof Array) { 4290 for(var i = 0, j = index; i < value.length; j++,i++) { 4291 array.splice(j, 0, value[i]); 4292 } 4293 } else { 4294 array.splice(index, 0, value); 4295 } 4296 4297 return array; 4298 }; 4299 4300 p.subset = function(array, offset, length) { 4301 if (arguments.length === 2) { 4302 return array.slice(offset, array.length - offset); 4303 } else if (arguments.length === 3) { 4304 return array.slice(offset, offset + length); 4305 } 4306 }; 4307 4308 p.join = function(array, seperator) { 4309 return array.join(seperator); 4310 }; 4311 4312 p.shorten = function(ary) { 4313 var newary = []; 4314 4315 // copy array into new array 4316 var len = ary.length; 4317 for (var i = 0; i < len; i++) { 4318 newary[i] = ary[i]; 4319 } 4320 newary.pop(); 4321 4322 return newary; 4323 }; 4324 4325 p.expand = function(ary, newSize) { 4326 var temp = ary.slice(0); 4327 if (arguments.length === 1) { 4328 // double size of array 4329 temp.length = ary.length * 2; 4330 return temp; 4331 } else if (arguments.length === 2) { 4332 // size is newSize 4333 temp.length = newSize; 4334 return temp; 4335 } 4336 }; 4337 4338 p.arrayCopy = function() { // src, srcPos, dest, destPos, length) { 4339 var src, srcPos = 0, dest, destPos = 0, length; 4340 4341 if (arguments.length === 2) { 4342 // recall itself and copy src to dest from start index 0 to 0 of src.length 4343 src = arguments[0]; 4344 dest = arguments[1]; 4345 length = src.length; 4346 } else if (arguments.length === 3) { 4347 // recall itself and copy src to dest from start index 0 to 0 of length 4348 src = arguments[0]; 4349 dest = arguments[1]; 4350 length = arguments[2]; 4351 } else if (arguments.length === 5) { 4352 src = arguments[0]; 4353 srcPos = arguments[1]; 4354 dest = arguments[2]; 4355 destPos = arguments[3]; 4356 length = arguments[4]; 4357 } 4358 4359 // copy src to dest from index srcPos to index destPos of length recursivly on objects 4360 for (var i = srcPos, j = destPos; i < length + srcPos; i++, j++) { 4361 if (dest[j] !== undef) { 4362 dest[j] = src[i]; 4363 } else { 4364 throw "array index out of bounds exception"; 4365 } 4366 } 4367 }; 4368 4369 p.reverse = function(array) { 4370 return array.reverse(); 4371 }; 4372 4373 4374 //////////////////////////////////////////////////////////////////////////// 4375 // Color functions 4376 //////////////////////////////////////////////////////////////////////////// 4377 4378 // helper functions for internal blending modes 4379 p.mix = function(a, b, f) { 4380 return a + (((b - a) * f) >> 8); 4381 }; 4382 4383 p.peg = function(n) { 4384 return (n < 0) ? 0 : ((n > 255) ? 255 : n); 4385 }; 4386 4387 // blending modes 4388 p.modes = { 4389 replace: function(c1, c2) { 4390 return c2; 4391 }, 4392 blend: function(c1, c2) { 4393 var f = (c2 & PConstants.ALPHA_MASK) >>> 24; 4394 return (Math.min(((c1 & PConstants.ALPHA_MASK) >>> 24) + f, 0xff) << 24 | 4395 p.mix(c1 & PConstants.RED_MASK, c2 & PConstants.RED_MASK, f) & PConstants.RED_MASK | 4396 p.mix(c1 & PConstants.GREEN_MASK, c2 & PConstants.GREEN_MASK, f) & PConstants.GREEN_MASK | 4397 p.mix(c1 & PConstants.BLUE_MASK, c2 & PConstants.BLUE_MASK, f)); 4398 }, 4399 add: function(c1, c2) { 4400 var f = (c2 & PConstants.ALPHA_MASK) >>> 24; 4401 return (Math.min(((c1 & PConstants.ALPHA_MASK) >>> 24) + f, 0xff) << 24 | 4402 Math.min(((c1 & PConstants.RED_MASK) + ((c2 & PConstants.RED_MASK) >> 8) * f), PConstants.RED_MASK) & PConstants.RED_MASK | 4403 Math.min(((c1 & PConstants.GREEN_MASK) + ((c2 & PConstants.GREEN_MASK) >> 8) * f), PConstants.GREEN_MASK) & PConstants.GREEN_MASK | 4404 Math.min((c1 & PConstants.BLUE_MASK) + (((c2 & PConstants.BLUE_MASK) * f) >> 8), PConstants.BLUE_MASK)); 4405 }, 4406 subtract: function(c1, c2) { 4407 var f = (c2 & PConstants.ALPHA_MASK) >>> 24; 4408 return (Math.min(((c1 & PConstants.ALPHA_MASK) >>> 24) + f, 0xff) << 24 | 4409 Math.max(((c1 & PConstants.RED_MASK) - ((c2 & PConstants.RED_MASK) >> 8) * f), PConstants.GREEN_MASK) & PConstants.RED_MASK | 4410 Math.max(((c1 & PConstants.GREEN_MASK) - ((c2 & PConstants.GREEN_MASK) >> 8) * f), PConstants.BLUE_MASK) & PConstants.GREEN_MASK | 4411 Math.max((c1 & PConstants.BLUE_MASK) - (((c2 & PConstants.BLUE_MASK) * f) >> 8), 0)); 4412 }, 4413 lightest: function(c1, c2) { 4414 var f = (c2 & PConstants.ALPHA_MASK) >>> 24; 4415 return (Math.min(((c1 & PConstants.ALPHA_MASK) >>> 24) + f, 0xff) << 24 | 4416 Math.max(c1 & PConstants.RED_MASK, ((c2 & PConstants.RED_MASK) >> 8) * f) & PConstants.RED_MASK | 4417 Math.max(c1 & PConstants.GREEN_MASK, ((c2 & PConstants.GREEN_MASK) >> 8) * f) & PConstants.GREEN_MASK | 4418 Math.max(c1 & PConstants.BLUE_MASK, ((c2 & PConstants.BLUE_MASK) * f) >> 8)); 4419 }, 4420 darkest: function(c1, c2) { 4421 var f = (c2 & PConstants.ALPHA_MASK) >>> 24; 4422 return (Math.min(((c1 & PConstants.ALPHA_MASK) >>> 24) + f, 0xff) << 24 | 4423 p.mix(c1 & PConstants.RED_MASK, Math.min(c1 & PConstants.RED_MASK, ((c2 & PConstants.RED_MASK) >> 8) * f), f) & PConstants.RED_MASK | 4424 p.mix(c1 & PConstants.GREEN_MASK, Math.min(c1 & PConstants.GREEN_MASK, ((c2 & PConstants.GREEN_MASK) >> 8) * f), f) & PConstants.GREEN_MASK | 4425 p.mix(c1 & PConstants.BLUE_MASK, Math.min(c1 & PConstants.BLUE_MASK, ((c2 & PConstants.BLUE_MASK) * f) >> 8), f)); 4426 }, 4427 difference: function(c1, c2) { 4428 var f = (c2 & PConstants.ALPHA_MASK) >>> 24; 4429 var ar = (c1 & PConstants.RED_MASK) >> 16; 4430 var ag = (c1 & PConstants.GREEN_MASK) >> 8; 4431 var ab = (c1 & PConstants.BLUE_MASK); 4432 var br = (c2 & PConstants.RED_MASK) >> 16; 4433 var bg = (c2 & PConstants.GREEN_MASK) >> 8; 4434 var bb = (c2 & PConstants.BLUE_MASK); 4435 // formula: 4436 var cr = (ar > br) ? (ar - br) : (br - ar); 4437 var cg = (ag > bg) ? (ag - bg) : (bg - ag); 4438 var cb = (ab > bb) ? (ab - bb) : (bb - ab); 4439 // alpha blend (this portion will always be the same) 4440 return (Math.min(((c1 & PConstants.ALPHA_MASK) >>> 24) + f, 0xff) << 24 | 4441 (p.peg(ar + (((cr - ar) * f) >> 8)) << 16) | 4442 (p.peg(ag + (((cg - ag) * f) >> 8)) << 8) | 4443 (p.peg(ab + (((cb - ab) * f) >> 8)))); 4444 }, 4445 exclusion: function(c1, c2) { 4446 var f = (c2 & PConstants.ALPHA_MASK) >>> 24; 4447 var ar = (c1 & PConstants.RED_MASK) >> 16; 4448 var ag = (c1 & PConstants.GREEN_MASK) >> 8; 4449 var ab = (c1 & PConstants.BLUE_MASK); 4450 var br = (c2 & PConstants.RED_MASK) >> 16; 4451 var bg = (c2 & PConstants.GREEN_MASK) >> 8; 4452 var bb = (c2 & PConstants.BLUE_MASK); 4453 // formula: 4454 var cr = ar + br - ((ar * br) >> 7); 4455 var cg = ag + bg - ((ag * bg) >> 7); 4456 var cb = ab + bb - ((ab * bb) >> 7); 4457 // alpha blend (this portion will always be the same) 4458 return (Math.min(((c1 & PConstants.ALPHA_MASK) >>> 24) + f, 0xff) << 24 | 4459 (p.peg(ar + (((cr - ar) * f) >> 8)) << 16) | 4460 (p.peg(ag + (((cg - ag) * f) >> 8)) << 8) | 4461 (p.peg(ab + (((cb - ab) * f) >> 8)))); 4462 }, 4463 multiply: function(c1, c2) { 4464 var f = (c2 & PConstants.ALPHA_MASK) >>> 24; 4465 var ar = (c1 & PConstants.RED_MASK) >> 16; 4466 var ag = (c1 & PConstants.GREEN_MASK) >> 8; 4467 var ab = (c1 & PConstants.BLUE_MASK); 4468 var br = (c2 & PConstants.RED_MASK) >> 16; 4469 var bg = (c2 & PConstants.GREEN_MASK) >> 8; 4470 var bb = (c2 & PConstants.BLUE_MASK); 4471 // formula: 4472 var cr = (ar * br) >> 8; 4473 var cg = (ag * bg) >> 8; 4474 var cb = (ab * bb) >> 8; 4475 // alpha blend (this portion will always be the same) 4476 return (Math.min(((c1 & PConstants.ALPHA_MASK) >>> 24) + f, 0xff) << 24 | 4477 (p.peg(ar + (((cr - ar) * f) >> 8)) << 16) | 4478 (p.peg(ag + (((cg - ag) * f) >> 8)) << 8) | 4479 (p.peg(ab + (((cb - ab) * f) >> 8)))); 4480 }, 4481 screen: function(c1, c2) { 4482 var f = (c2 & PConstants.ALPHA_MASK) >>> 24; 4483 var ar = (c1 & PConstants.RED_MASK) >> 16; 4484 var ag = (c1 & PConstants.GREEN_MASK) >> 8; 4485 var ab = (c1 & PConstants.BLUE_MASK); 4486 var br = (c2 & PConstants.RED_MASK) >> 16; 4487 var bg = (c2 & PConstants.GREEN_MASK) >> 8; 4488 var bb = (c2 & PConstants.BLUE_MASK); 4489 // formula: 4490 var cr = 255 - (((255 - ar) * (255 - br)) >> 8); 4491 var cg = 255 - (((255 - ag) * (255 - bg)) >> 8); 4492 var cb = 255 - (((255 - ab) * (255 - bb)) >> 8); 4493 // alpha blend (this portion will always be the same) 4494 return (Math.min(((c1 & PConstants.ALPHA_MASK) >>> 24) + f, 0xff) << 24 | 4495 (p.peg(ar + (((cr - ar) * f) >> 8)) << 16) | 4496 (p.peg(ag + (((cg - ag) * f) >> 8)) << 8) | 4497 (p.peg(ab + (((cb - ab) * f) >> 8)))); 4498 }, 4499 hard_light: function(c1, c2) { 4500 var f = (c2 & PConstants.ALPHA_MASK) >>> 24; 4501 var ar = (c1 & PConstants.RED_MASK) >> 16; 4502 var ag = (c1 & PConstants.GREEN_MASK) >> 8; 4503 var ab = (c1 & PConstants.BLUE_MASK); 4504 var br = (c2 & PConstants.RED_MASK) >> 16; 4505 var bg = (c2 & PConstants.GREEN_MASK) >> 8; 4506 var bb = (c2 & PConstants.BLUE_MASK); 4507 // formula: 4508 var cr = (br < 128) ? ((ar * br) >> 7) : (255 - (((255 - ar) * (255 - br)) >> 7)); 4509 var cg = (bg < 128) ? ((ag * bg) >> 7) : (255 - (((255 - ag) * (255 - bg)) >> 7)); 4510 var cb = (bb < 128) ? ((ab * bb) >> 7) : (255 - (((255 - ab) * (255 - bb)) >> 7)); 4511 // alpha blend (this portion will always be the same) 4512 return (Math.min(((c1 & PConstants.ALPHA_MASK) >>> 24) + f, 0xff) << 24 | 4513 (p.peg(ar + (((cr - ar) * f) >> 8)) << 16) | 4514 (p.peg(ag + (((cg - ag) * f) >> 8)) << 8) | 4515 (p.peg(ab + (((cb - ab) * f) >> 8)))); 4516 }, 4517 soft_light: function(c1, c2) { 4518 var f = (c2 & PConstants.ALPHA_MASK) >>> 24; 4519 var ar = (c1 & PConstants.RED_MASK) >> 16; 4520 var ag = (c1 & PConstants.GREEN_MASK) >> 8; 4521 var ab = (c1 & PConstants.BLUE_MASK); 4522 var br = (c2 & PConstants.RED_MASK) >> 16; 4523 var bg = (c2 & PConstants.GREEN_MASK) >> 8; 4524 var bb = (c2 & PConstants.BLUE_MASK); 4525 // formula: 4526 var cr = ((ar * br) >> 7) + ((ar * ar) >> 8) - ((ar * ar * br) >> 15); 4527 var cg = ((ag * bg) >> 7) + ((ag * ag) >> 8) - ((ag * ag * bg) >> 15); 4528 var cb = ((ab * bb) >> 7) + ((ab * ab) >> 8) - ((ab * ab * bb) >> 15); 4529 // alpha blend (this portion will always be the same) 4530 return (Math.min(((c1 & PConstants.ALPHA_MASK) >>> 24) + f, 0xff) << 24 | 4531 (p.peg(ar + (((cr - ar) * f) >> 8)) << 16) | 4532 (p.peg(ag + (((cg - ag) * f) >> 8)) << 8) | 4533 (p.peg(ab + (((cb - ab) * f) >> 8)))); 4534 }, 4535 overlay: function(c1, c2) { 4536 var f = (c2 & PConstants.ALPHA_MASK) >>> 24; 4537 var ar = (c1 & PConstants.RED_MASK) >> 16; 4538 var ag = (c1 & PConstants.GREEN_MASK) >> 8; 4539 var ab = (c1 & PConstants.BLUE_MASK); 4540 var br = (c2 & PConstants.RED_MASK) >> 16; 4541 var bg = (c2 & PConstants.GREEN_MASK) >> 8; 4542 var bb = (c2 & PConstants.BLUE_MASK); 4543 // formula: 4544 var cr = (ar < 128) ? ((ar * br) >> 7) : (255 - (((255 - ar) * (255 - br)) >> 7)); 4545 var cg = (ag < 128) ? ((ag * bg) >> 7) : (255 - (((255 - ag) * (255 - bg)) >> 7)); 4546 var cb = (ab < 128) ? ((ab * bb) >> 7) : (255 - (((255 - ab) * (255 - bb)) >> 7)); 4547 // alpha blend (this portion will always be the same) 4548 return (Math.min(((c1 & PConstants.ALPHA_MASK) >>> 24) + f, 0xff) << 24 | 4549 (p.peg(ar + (((cr - ar) * f) >> 8)) << 16) | 4550 (p.peg(ag + (((cg - ag) * f) >> 8)) << 8) | 4551 (p.peg(ab + (((cb - ab) * f) >> 8)))); 4552 }, 4553 dodge: function(c1, c2) { 4554 var f = (c2 & PConstants.ALPHA_MASK) >>> 24; 4555 var ar = (c1 & PConstants.RED_MASK) >> 16; 4556 var ag = (c1 & PConstants.GREEN_MASK) >> 8; 4557 var ab = (c1 & PConstants.BLUE_MASK); 4558 var br = (c2 & PConstants.RED_MASK) >> 16; 4559 var bg = (c2 & PConstants.GREEN_MASK) >> 8; 4560 var bb = (c2 & PConstants.BLUE_MASK); 4561 // formula: 4562 var cr = (br === 255) ? 255 : p.peg((ar << 8) / (255 - br)); // division requires pre-peg()-ing 4563 var cg = (bg === 255) ? 255 : p.peg((ag << 8) / (255 - bg)); // " 4564 var cb = (bb === 255) ? 255 : p.peg((ab << 8) / (255 - bb)); // " 4565 // alpha blend (this portion will always be the same) 4566 return (Math.min(((c1 & PConstants.ALPHA_MASK) >>> 24) + f, 0xff) << 24 | 4567 (p.peg(ar + (((cr - ar) * f) >> 8)) << 16) | 4568 (p.peg(ag + (((cg - ag) * f) >> 8)) << 8) | 4569 (p.peg(ab + (((cb - ab) * f) >> 8)))); 4570 }, 4571 burn: function(c1, c2) { 4572 var f = (c2 & PConstants.ALPHA_MASK) >>> 24; 4573 var ar = (c1 & PConstants.RED_MASK) >> 16; 4574 var ag = (c1 & PConstants.GREEN_MASK) >> 8; 4575 var ab = (c1 & PConstants.BLUE_MASK); 4576 var br = (c2 & PConstants.RED_MASK) >> 16; 4577 var bg = (c2 & PConstants.GREEN_MASK) >> 8; 4578 var bb = (c2 & PConstants.BLUE_MASK); 4579 // formula: 4580 var cr = (br === 0) ? 0 : 255 - p.peg(((255 - ar) << 8) / br); // division requires pre-peg()-ing 4581 var cg = (bg === 0) ? 0 : 255 - p.peg(((255 - ag) << 8) / bg); // " 4582 var cb = (bb === 0) ? 0 : 255 - p.peg(((255 - ab) << 8) / bb); // " 4583 // alpha blend (this portion will always be the same) 4584 return (Math.min(((c1 & PConstants.ALPHA_MASK) >>> 24) + f, 0xff) << 24 | 4585 (p.peg(ar + (((cr - ar) * f) >> 8)) << 16) | 4586 (p.peg(ag + (((cg - ag) * f) >> 8)) << 8) | 4587 (p.peg(ab + (((cb - ab) * f) >> 8)))); 4588 } 4589 }; 4590 4591 function color$4(aValue1, aValue2, aValue3, aValue4) { 4592 var r, g, b, a; 4593 4594 if (curColorMode === PConstants.HSB) { 4595 var rgb = p.color.toRGB(aValue1, aValue2, aValue3); 4596 r = rgb[0]; 4597 g = rgb[1]; 4598 b = rgb[2]; 4599 } else { 4600 r = Math.round(255 * (aValue1 / colorModeX)); 4601 g = Math.round(255 * (aValue2 / colorModeY)); 4602 b = Math.round(255 * (aValue3 / colorModeZ)); 4603 } 4604 4605 a = Math.round(255 * (aValue4 / colorModeA)); 4606 4607 // Limit values greater than 255 4608 r = (r > 255) ? 255 : r; 4609 g = (g > 255) ? 255 : g; 4610 b = (b > 255) ? 255 : b; 4611 a = (a > 255) ? 255 : a; 4612 4613 // Create color int 4614 return (a << 24) & PConstants.ALPHA_MASK | (r << 16) & PConstants.RED_MASK | (g << 8) & PConstants.GREEN_MASK | b & PConstants.BLUE_MASK; 4615 } 4616 4617 function color$2(aValue1, aValue2) { 4618 var a; 4619 4620 // Color int and alpha 4621 if (aValue1 & PConstants.ALPHA_MASK) { 4622 a = Math.round(255 * (aValue2 / colorModeA)); 4623 a = (a > 255) ? 255 : a; 4624 4625 return aValue1 - (aValue1 & PConstants.ALPHA_MASK) + ((a << 24) & PConstants.ALPHA_MASK); 4626 } 4627 // Grayscale and alpha 4628 else { 4629 if (curColorMode === PConstants.RGB) { 4630 return color$4(aValue1, aValue1, aValue1, aValue2); 4631 } else if (curColorMode === PConstants.HSB) { 4632 return color$4(0, 0, (aValue1 / colorModeX) * colorModeZ, aValue2); 4633 } 4634 } 4635 } 4636 4637 function color$1(aValue1) { 4638 // Grayscale 4639 if (aValue1 <= colorModeX && aValue1 >= 0) { 4640 if (curColorMode === PConstants.RGB) { 4641 return color$4(aValue1, aValue1, aValue1, colorModeA); 4642 } else if (curColorMode === PConstants.HSB) { 4643 return color$4(0, 0, (aValue1 / colorModeX) * colorModeZ, colorModeA); 4644 } 4645 } 4646 // Color int 4647 else if (aValue1) { 4648 return aValue1; 4649 } 4650 } 4651 4652 p.color = function color(aValue1, aValue2, aValue3, aValue4) { 4653 // 4 arguments: (R, G, B, A) or (H, S, B, A) 4654 if (aValue1 !== undef && aValue2 !== undef && aValue3 !== undef && aValue4 !== undef) { 4655 return color$4(aValue1, aValue2, aValue3, aValue4); 4656 } 4657 4658 // 3 arguments: (R, G, B) or (H, S, B) 4659 else if (aValue1 !== undef && aValue2 !== undef && aValue3 !== undef) { 4660 return color$4(aValue1, aValue2, aValue3, colorModeA); 4661 } 4662 4663 // 2 arguments: (Color, A) or (Grayscale, A) 4664 else if (aValue1 !== undef && aValue2 !== undef) { 4665 return color$2(aValue1, aValue2); 4666 } 4667 4668 // 1 argument: (Grayscale) or (Color) 4669 else if (typeof aValue1 === "number") { 4670 return color$1(aValue1); 4671 } 4672 4673 // Default 4674 else { 4675 return color$4(colorModeX, colorModeY, colorModeZ, colorModeA); 4676 } 4677 }; 4678 4679 // Ease of use function to extract the colour bits into a string 4680 p.color.toString = function(colorInt) { 4681 return "rgba(" + ((colorInt & PConstants.RED_MASK) >>> 16) + "," + ((colorInt & PConstants.GREEN_MASK) >>> 8) + 4682 "," + ((colorInt & PConstants.BLUE_MASK)) + "," + ((colorInt & PConstants.ALPHA_MASK) >>> 24) / 255 + ")"; 4683 }; 4684 4685 // Easy of use function to pack rgba values into a single bit-shifted color int. 4686 p.color.toInt = function(r, g, b, a) { 4687 return (a << 24) & PConstants.ALPHA_MASK | (r << 16) & PConstants.RED_MASK | (g << 8) & PConstants.GREEN_MASK | b & PConstants.BLUE_MASK; 4688 }; 4689 4690 // Creates a simple array in [R, G, B, A] format, [255, 255, 255, 255] 4691 p.color.toArray = function(colorInt) { 4692 return [(colorInt & PConstants.RED_MASK) >>> 16, (colorInt & PConstants.GREEN_MASK) >>> 8, 4693 colorInt & PConstants.BLUE_MASK, (colorInt & PConstants.ALPHA_MASK) >>> 24]; 4694 }; 4695 4696 // Creates a WebGL color array in [R, G, B, A] format. WebGL wants the color ranges between 0 and 1, [1, 1, 1, 1] 4697 p.color.toGLArray = function(colorInt) { 4698 return [((colorInt & PConstants.RED_MASK) >>> 16) / 255, ((colorInt & PConstants.GREEN_MASK) >>> 8) / 255, 4699 (colorInt & PConstants.BLUE_MASK) / 255, ((colorInt & PConstants.ALPHA_MASK) >>> 24) / 255]; 4700 }; 4701 4702 // HSB conversion function from Mootools, MIT Licensed 4703 p.color.toRGB = function(h, s, b) { 4704 // Limit values greater than range 4705 h = (h > colorModeX) ? colorModeX : h; 4706 s = (s > colorModeY) ? colorModeY : s; 4707 b = (b > colorModeZ) ? colorModeZ : b; 4708 4709 h = (h / colorModeX) * 360; 4710 s = (s / colorModeY) * 100; 4711 b = (b / colorModeZ) * 100; 4712 4713 var br = Math.round(b / 100 * 255); 4714 4715 if (s === 0) { // Grayscale 4716 return [br, br, br]; 4717 } else { 4718 var hue = h % 360; 4719 var f = hue % 60; 4720 var p = Math.round((b * (100 - s)) / 10000 * 255); 4721 var q = Math.round((b * (6000 - s * f)) / 600000 * 255); 4722 var t = Math.round((b * (6000 - s * (60 - f))) / 600000 * 255); 4723 switch (Math.floor(hue / 60)) { 4724 case 0: 4725 return [br, t, p]; 4726 case 1: 4727 return [q, br, p]; 4728 case 2: 4729 return [p, br, t]; 4730 case 3: 4731 return [p, q, br]; 4732 case 4: 4733 return [t, p, br]; 4734 case 5: 4735 return [br, p, q]; 4736 } 4737 } 4738 }; 4739 4740 p.color.toHSB = function( colorInt ) { 4741 var red, green, blue; 4742 4743 red = ((colorInt & PConstants.RED_MASK) >>> 16) / 255; 4744 green = ((colorInt & PConstants.GREEN_MASK) >>> 8) / 255; 4745 blue = (colorInt & PConstants.BLUE_MASK) / 255; 4746 4747 var max = p.max(p.max(red,green), blue), 4748 min = p.min(p.min(red,green), blue), 4749 hue, saturation; 4750 4751 if (min === max) { 4752 return [0, 0, max]; 4753 } else { 4754 saturation = (max - min) / max; 4755 4756 if (red === max) { 4757 hue = (green - blue) / (max - min); 4758 } else if (green === max) { 4759 hue = 2 + ((blue - red) / (max - min)); 4760 } else { 4761 hue = 4 + ((red - green) / (max - min)); 4762 } 4763 4764 hue /= 6; 4765 4766 if (hue < 0) { 4767 hue += 1; 4768 } else if (hue > 1) { 4769 hue -= 1; 4770 } 4771 } 4772 return [hue*colorModeX, saturation*colorModeY, max*colorModeZ]; 4773 }; 4774 4775 p.brightness = function(colInt){ 4776 return p.color.toHSB(colInt)[2]; 4777 }; 4778 4779 p.saturation = function(colInt){ 4780 return p.color.toHSB(colInt)[1]; 4781 }; 4782 4783 p.hue = function(colInt){ 4784 return p.color.toHSB(colInt)[0]; 4785 }; 4786 4787 var verifyChannel = function verifyChannel(aColor) { 4788 if (aColor.constructor === Array) { 4789 return aColor; 4790 } else { 4791 return p.color(aColor); 4792 } 4793 }; 4794 4795 p.red = function(aColor) { 4796 return ((aColor & PConstants.RED_MASK) >>> 16) / 255 * colorModeX; 4797 }; 4798 4799 p.green = function(aColor) { 4800 return ((aColor & PConstants.GREEN_MASK) >>> 8) / 255 * colorModeY; 4801 }; 4802 4803 p.blue = function(aColor) { 4804 return (aColor & PConstants.BLUE_MASK) / 255 * colorModeZ; 4805 }; 4806 4807 p.alpha = function(aColor) { 4808 return ((aColor & PConstants.ALPHA_MASK) >>> 24) / 255 * colorModeA; 4809 }; 4810 4811 p.lerpColor = function lerpColor(c1, c2, amt) { 4812 // Get RGBA values for Color 1 to floats 4813 var colorBits1 = p.color(c1); 4814 var r1 = (colorBits1 & PConstants.RED_MASK) >>> 16; 4815 var g1 = (colorBits1 & PConstants.GREEN_MASK) >>> 8; 4816 var b1 = (colorBits1 & PConstants.BLUE_MASK); 4817 var a1 = ((colorBits1 & PConstants.ALPHA_MASK) >>> 24) / colorModeA; 4818 4819 // Get RGBA values for Color 2 to floats 4820 var colorBits2 = p.color(c2); 4821 var r2 = (colorBits2 & PConstants.RED_MASK) >>> 16; 4822 var g2 = (colorBits2 & PConstants.GREEN_MASK) >>> 8; 4823 var b2 = (colorBits2 & PConstants.BLUE_MASK); 4824 var a2 = ((colorBits2 & PConstants.ALPHA_MASK) >>> 24) / colorModeA; 4825 4826 // Return lerp value for each channel, INT for color, Float for Alpha-range 4827 var r = parseInt(p.lerp(r1, r2, amt), 10); 4828 var g = parseInt(p.lerp(g1, g2, amt), 10); 4829 var b = parseInt(p.lerp(b1, b2, amt), 10); 4830 var a = parseFloat(p.lerp(a1, a2, amt) * colorModeA); 4831 4832 return p.color.toInt(r, g, b, a); 4833 }; 4834 4835 // Forced default color mode for #aaaaaa style 4836 p.defaultColor = function(aValue1, aValue2, aValue3) { 4837 var tmpColorMode = curColorMode; 4838 curColorMode = PConstants.RGB; 4839 var c = p.color(aValue1 / 255 * colorModeX, aValue2 / 255 * colorModeY, aValue3 / 255 * colorModeZ); 4840 curColorMode = tmpColorMode; 4841 return c; 4842 }; 4843 4844 p.colorMode = function colorMode() { // mode, range1, range2, range3, range4 4845 curColorMode = arguments[0]; 4846 if (arguments.length > 1) { 4847 colorModeX = arguments[1]; 4848 colorModeY = arguments[2] || arguments[1]; 4849 colorModeZ = arguments[3] || arguments[1]; 4850 colorModeA = arguments[4] || arguments[1]; 4851 } 4852 }; 4853 4854 p.blendColor = function(c1, c2, mode) { 4855 var color = 0; 4856 switch (mode) { 4857 case PConstants.REPLACE: 4858 color = p.modes.replace(c1, c2); 4859 break; 4860 case PConstants.BLEND: 4861 color = p.modes.blend(c1, c2); 4862 break; 4863 case PConstants.ADD: 4864 color = p.modes.add(c1, c2); 4865 break; 4866 case PConstants.SUBTRACT: 4867 color = p.modes.subtract(c1, c2); 4868 break; 4869 case PConstants.LIGHTEST: 4870 color = p.modes.lightest(c1, c2); 4871 break; 4872 case PConstants.DARKEST: 4873 color = p.modes.darkest(c1, c2); 4874 break; 4875 case PConstants.DIFFERENCE: 4876 color = p.modes.difference(c1, c2); 4877 break; 4878 case PConstants.EXCLUSION: 4879 color = p.modes.exclusion(c1, c2); 4880 break; 4881 case PConstants.MULTIPLY: 4882 color = p.modes.multiply(c1, c2); 4883 break; 4884 case PConstants.SCREEN: 4885 color = p.modes.screen(c1, c2); 4886 break; 4887 case PConstants.HARD_LIGHT: 4888 color = p.modes.hard_light(c1, c2); 4889 break; 4890 case PConstants.SOFT_LIGHT: 4891 color = p.modes.soft_light(c1, c2); 4892 break; 4893 case PConstants.OVERLAY: 4894 color = p.modes.overlay(c1, c2); 4895 break; 4896 case PConstants.DODGE: 4897 color = p.modes.dodge(c1, c2); 4898 break; 4899 case PConstants.BURN: 4900 color = p.modes.burn(c1, c2); 4901 break; 4902 } 4903 return color; 4904 }; 4905 4906 //////////////////////////////////////////////////////////////////////////// 4907 // Canvas-Matrix manipulation 4908 //////////////////////////////////////////////////////////////////////////// 4909 4910 function saveContext() { 4911 curContext.save(); 4912 } 4913 4914 function restoreContext() { 4915 curContext.restore(); 4916 isStrokeDirty = true; 4917 isFillDirty = true; 4918 } 4919 4920 p.printMatrix = function printMatrix() { 4921 modelView.print(); 4922 }; 4923 4924 p.translate = function translate(x, y, z) { 4925 if (p.use3DContext) { 4926 forwardTransform.translate(x, y, z); 4927 reverseTransform.invTranslate(x, y, z); 4928 } else { 4929 curContext.translate(x, y); 4930 } 4931 }; 4932 4933 p.scale = function scale(x, y, z) { 4934 if (p.use3DContext) { 4935 forwardTransform.scale(x, y, z); 4936 reverseTransform.invScale(x, y, z); 4937 } else { 4938 curContext.scale(x, y || x); 4939 } 4940 }; 4941 4942 p.pushMatrix = function pushMatrix() { 4943 if (p.use3DContext) { 4944 userMatrixStack.load(modelView); 4945 } else { 4946 saveContext(); 4947 } 4948 }; 4949 4950 p.popMatrix = function popMatrix() { 4951 if (p.use3DContext) { 4952 modelView.set(userMatrixStack.pop()); 4953 } else { 4954 restoreContext(); 4955 } 4956 }; 4957 4958 p.resetMatrix = function resetMatrix() { 4959 if (p.use3DContext) { 4960 forwardTransform.reset(); 4961 reverseTransform.reset(); 4962 } else { 4963 curContext.setTransform(1,0,0,1,0,0); 4964 } 4965 }; 4966 4967 p.applyMatrix = function applyMatrix() { 4968 var a = arguments; 4969 if (!p.use3DContext) { 4970 for (var cnt = a.length; cnt < 16; cnt++) { 4971 a[cnt] = 0; 4972 } 4973 a[10] = a[15] = 1; 4974 } 4975 4976 forwardTransform.apply(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15]); 4977 reverseTransform.invApply(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15]); 4978 }; 4979 4980 p.rotateX = function(angleInRadians) { 4981 forwardTransform.rotateX(angleInRadians); 4982 reverseTransform.invRotateX(angleInRadians); 4983 }; 4984 4985 p.rotateZ = function(angleInRadians) { 4986 forwardTransform.rotateZ(angleInRadians); 4987 reverseTransform.invRotateZ(angleInRadians); 4988 }; 4989 4990 p.rotateY = function(angleInRadians) { 4991 forwardTransform.rotateY(angleInRadians); 4992 reverseTransform.invRotateY(angleInRadians); 4993 }; 4994 4995 p.rotate = function rotate(angleInRadians) { 4996 if (p.use3DContext) { 4997 forwardTransform.rotateZ(angleInRadians); 4998 reverseTransform.invRotateZ(angleInRadians); 4999 } else { 5000 curContext.rotate(angleInRadians); 5001 } 5002 }; 5003 5004 p.pushStyle = function pushStyle() { 5005 // Save the canvas state. 5006 saveContext(); 5007 5008 p.pushMatrix(); 5009 5010 var newState = { 5011 'doFill': doFill, 5012 'currentFillColor': currentFillColor, 5013 'doStroke': doStroke, 5014 'currentStrokeColor': currentStrokeColor, 5015 'curTint': curTint, 5016 'curRectMode': curRectMode, 5017 'curColorMode': curColorMode, 5018 'colorModeX': colorModeX, 5019 'colorModeZ': colorModeZ, 5020 'colorModeY': colorModeY, 5021 'colorModeA': colorModeA, 5022 'curTextFont': curTextFont, 5023 'curTextSize': curTextSize 5024 }; 5025 5026 styleArray.push(newState); 5027 }; 5028 5029 p.popStyle = function popStyle() { 5030 var oldState = styleArray.pop(); 5031 5032 if (oldState) { 5033 restoreContext(); 5034 5035 p.popMatrix(); 5036 5037 doFill = oldState.doFill; 5038 currentFillColor = oldState.currentFillColor; 5039 doStroke = oldState.doStroke; 5040 currentStrokeColor = oldState.currentStrokeColor; 5041 curTint = oldState.curTint; 5042 curRectMode = oldState.curRectmode; 5043 curColorMode = oldState.curColorMode; 5044 colorModeX = oldState.colorModeX; 5045 colorModeZ = oldState.colorModeZ; 5046 colorModeY = oldState.colorModeY; 5047 colorModeA = oldState.colorModeA; 5048 curTextFont = oldState.curTextFont; 5049 curTextSize = oldState.curTextSize; 5050 } else { 5051 throw "Too many popStyle() without enough pushStyle()"; 5052 } 5053 }; 5054 5055 //////////////////////////////////////////////////////////////////////////// 5056 // Time based functions 5057 //////////////////////////////////////////////////////////////////////////// 5058 5059 p.year = function year() { 5060 return new Date().getFullYear(); 5061 }; 5062 p.month = function month() { 5063 return new Date().getMonth() + 1; 5064 }; 5065 p.day = function day() { 5066 return new Date().getDate(); 5067 }; 5068 p.hour = function hour() { 5069 return new Date().getHours(); 5070 }; 5071 p.minute = function minute() { 5072 return new Date().getMinutes(); 5073 }; 5074 p.second = function second() { 5075 return new Date().getSeconds(); 5076 }; 5077 p.millis = function millis() { 5078 return new Date().getTime() - start; 5079 }; 5080 5081 p.redraw = function redraw() { 5082 var sec = (new Date().getTime() - timeSinceLastFPS) / 1000; 5083 framesSinceLastFPS++; 5084 var fps = framesSinceLastFPS / sec; 5085 5086 // recalculate FPS every half second for better accuracy. 5087 if (sec > 0.5) { 5088 timeSinceLastFPS = new Date().getTime(); 5089 framesSinceLastFPS = 0; 5090 p.__frameRate = fps; 5091 } 5092 5093 p.frameCount++; 5094 5095 inDraw = true; 5096 5097 if (p.use3DContext) { 5098 // even if the color buffer isn't cleared with background(), 5099 // the depth buffer needs to be cleared regardless. 5100 curContext.clear(curContext.DEPTH_BUFFER_BIT); 5101 // Delete all the lighting states and the materials the 5102 // user set in the last draw() call. 5103 p.noLights(); 5104 p.lightFalloff(1, 0, 0); 5105 p.shininess(1); 5106 p.ambient(255, 255, 255); 5107 p.specular(0, 0, 0); 5108 p.camera(); 5109 p.draw(); 5110 } else { 5111 saveContext(); 5112 p.draw(); 5113 restoreContext(); 5114 } 5115 5116 inDraw = false; 5117 }; 5118 5119 p.noLoop = function noLoop() { 5120 doLoop = false; 5121 loopStarted = false; 5122 clearInterval(looping); 5123 }; 5124 5125 p.loop = function loop() { 5126 if (loopStarted) { 5127 return; 5128 } 5129 5130 looping = window.setInterval(function() { 5131 try { 5132 if (document.hasFocus instanceof Function) { 5133 p.focused = document.hasFocus(); 5134 } 5135 p.redraw(); 5136 } catch(e_loop) { 5137 window.clearInterval(looping); 5138 throw e_loop; 5139 } 5140 }, curMsPerFrame); 5141 5142 doLoop = true; 5143 loopStarted = true; 5144 }; 5145 5146 p.frameRate = function frameRate(aRate) { 5147 curFrameRate = aRate; 5148 curMsPerFrame = 1000 / curFrameRate; 5149 5150 // clear and reset interval 5151 if (doLoop) { 5152 p.noLoop(); 5153 p.loop(); 5154 } 5155 }; 5156 5157 var eventHandlers = []; 5158 5159 p.exit = function exit() { 5160 window.clearInterval(looping); 5161 5162 Processing.removeInstance(p.externals.canvas.id); 5163 5164 for (var i=0, ehl=eventHandlers.length; i<ehl; i++) { 5165 var elem = eventHandlers[i][0], 5166 type = eventHandlers[i][1], 5167 fn = eventHandlers[i][2]; 5168 5169 if (elem.removeEventListener) { 5170 elem.removeEventListener(type, fn, false); 5171 } else if (elem.detachEvent) { 5172 elem.detachEvent("on" + type, fn); 5173 } 5174 } 5175 }; 5176 5177 //////////////////////////////////////////////////////////////////////////// 5178 // MISC functions 5179 //////////////////////////////////////////////////////////////////////////// 5180 5181 p.cursor = function cursor() { 5182 if (arguments.length > 1 || (arguments.length === 1 && arguments[0] instanceof p.PImage)) { 5183 var image = arguments[0], 5184 x, y; 5185 if (arguments.length >= 3) { 5186 x = arguments[1]; 5187 y = arguments[2]; 5188 if (x < 0 || y < 0 || y >= image.height || x >= image.width) { 5189 throw "x and y must be non-negative and less than the dimensions of the image"; 5190 } 5191 } else { 5192 x = image.width >>> 1; 5193 y = image.height >>> 1; 5194 } 5195 5196 // see https://developer.mozilla.org/en/Using_URL_values_for_the_cursor_property 5197 var imageDataURL = image.toDataURL(); 5198 var style = "url(\"" + imageDataURL + "\") " + x + " " + y + ", default"; 5199 curCursor = curElement.style.cursor = style; 5200 } else if (arguments.length === 1) { 5201 var mode = arguments[0]; 5202 curCursor = curElement.style.cursor = mode; 5203 } else { 5204 curCursor = curElement.style.cursor = oldCursor; 5205 } 5206 }; 5207 5208 p.noCursor = function noCursor() { 5209 curCursor = curElement.style.cursor = PConstants.NOCURSOR; 5210 }; 5211 5212 p.link = function(href, target) { 5213 if (target !== undef) { 5214 window.open(href, target); 5215 } else { 5216 window.location = href; 5217 } 5218 }; 5219 5220 // PGraphics methods 5221 // TODO: These functions are suppose to be called before any operations are called on the 5222 // PGraphics object. They currently do nothing. 5223 p.beginDraw = function beginDraw() {}; 5224 p.endDraw = function endDraw() {}; 5225 5226 // Imports an external Processing.js library 5227 p.Import = function Import(lib) { 5228 // Replace evil-eval method with a DOM <script> tag insert method that 5229 // binds new lib code to the Processing.lib names-space and the current 5230 // p context. -F1LT3R 5231 }; 5232 5233 var contextMenu = function(e) { 5234 e.preventDefault(); 5235 e.stopPropagation(); 5236 }; 5237 5238 p.disableContextMenu = function disableContextMenu() { 5239 curElement.addEventListener('contextmenu', contextMenu, false); 5240 }; 5241 5242 p.enableContextMenu = function enableContextMenu() { 5243 curElement.removeEventListener('contextmenu', contextMenu, false); 5244 }; 5245 5246 p.status = function(text) { 5247 window.status = text; 5248 }; 5249 5250 //////////////////////////////////////////////////////////////////////////// 5251 // Binary Functions 5252 //////////////////////////////////////////////////////////////////////////// 5253 5254 function decToBin(value, numBitsInValue) { 5255 var mask = 1; 5256 mask = mask << (numBitsInValue - 1); 5257 5258 var str = ""; 5259 for (var i = 0; i < numBitsInValue; i++) { 5260 str += (mask & value) ? "1" : "0"; 5261 mask = mask >>> 1; 5262 } 5263 return str; 5264 } 5265 5266 /* 5267 This function does not always work when trying to convert 5268 colors and bytes to binary values because the types passed in 5269 cannot be determined. 5270 */ 5271 p.binary = function(num, numBits) { 5272 var numBitsInValue = 32; 5273 5274 // color, int, byte 5275 if (typeof num === "number") { 5276 if(numBits){ 5277 numBitsInValue = numBits; 5278 } 5279 return decToBin(num, numBitsInValue); 5280 } 5281 5282 // char 5283 if (num instanceof Char) { 5284 num = num.toString().charCodeAt(0); 5285 if (numBits) { 5286 numBitsInValue = 32; 5287 } else { 5288 numBitsInValue = 16; 5289 } 5290 } 5291 5292 var str = decToBin(num, numBitsInValue); 5293 5294 // trim string if user wanted less chars 5295 if (numBits) { 5296 str = str.substr(-numBits); 5297 } 5298 return str; 5299 }; 5300 5301 p.unbinary = function unbinary(binaryString) { 5302 var binaryPattern = new RegExp("^[0|1]{8}$"); 5303 var addUp = 0; 5304 var i; 5305 5306 if (binaryString instanceof Array) { 5307 var values = []; 5308 for (i = 0; i < binaryString.length; i++) { 5309 values[i] = p.unbinary(binaryString[i]); 5310 } 5311 return values; 5312 } else { 5313 if (isNaN(binaryString)) { 5314 throw "NaN_Err"; 5315 } else { 5316 if (arguments.length === 1 || binaryString.length === 8) { 5317 if (binaryPattern.test(binaryString)) { 5318 for (i = 0; i < 8; i++) { 5319 addUp += (Math.pow(2, i) * parseInt(binaryString.charAt(7 - i), 10)); 5320 } 5321 return addUp + ""; 5322 } else { 5323 throw "notBinary: the value passed into unbinary was not an 8 bit binary number"; 5324 } 5325 } else { 5326 throw "longErr"; 5327 } 5328 } 5329 } 5330 }; 5331 5332 function nfCoreScalar(value, plus, minus, leftDigits, rightDigits, group) { 5333 var sign = (value < 0) ? minus : plus; 5334 var autoDetectDecimals = rightDigits === 0; 5335 var rightDigitsOfDefault = (rightDigits === undef || rightDigits < 0) ? 0 : rightDigits; 5336 5337 5338 // Change the way the number is 'floored' based on whether it is odd or even. 5339 if (rightDigits < 0 && Math.floor(value) % 2 === 1) { 5340 // Make sure 1.49 rounds to 1, but 1.5 rounds to 2. 5341 if ((value - Math.floor(value)) >= 0.5) { 5342 value += 1; 5343 } 5344 } 5345 5346 5347 var absValue = Math.abs(value); 5348 if(autoDetectDecimals) { 5349 rightDigitsOfDefault = 1; 5350 absValue *= 10; 5351 while(Math.abs(Math.round(absValue) - absValue) > 1e-6 && rightDigitsOfDefault < 7) { 5352 ++rightDigitsOfDefault; 5353 absValue *= 10; 5354 } 5355 } else if (rightDigitsOfDefault !== 0) { 5356 absValue *= Math.pow(10, rightDigitsOfDefault); 5357 } 5358 5359 var number = Math.round(absValue); 5360 var buffer = ""; 5361 var totalDigits = leftDigits + rightDigitsOfDefault; 5362 while (totalDigits > 0 || number > 0) { 5363 totalDigits--; 5364 buffer = "" + (number % 10) + buffer; 5365 number = Math.floor(number / 10); 5366 } 5367 if (group !== undef) { 5368 var i = buffer.length - 3 - rightDigitsOfDefault; 5369 while(i > 0) { 5370 buffer = buffer.substring(0,i) + group + buffer.substring(i); 5371 i-=3; 5372 } 5373 } 5374 if (rightDigitsOfDefault > 0) { 5375 return sign + buffer.substring(0, buffer.length - rightDigitsOfDefault) + 5376 "." + buffer.substring(buffer.length - rightDigitsOfDefault, buffer.length); 5377 } else { 5378 return sign + buffer; 5379 } 5380 } 5381 5382 function nfCore(value, plus, minus, leftDigits, rightDigits, group) { 5383 if (value instanceof Array) { 5384 var arr = []; 5385 for (var i = 0, len = value.length; i < len; i++) { 5386 arr.push(nfCoreScalar(value[i], plus, minus, leftDigits, rightDigits, group)); 5387 } 5388 return arr; 5389 } else { 5390 return nfCoreScalar(value, plus, minus, leftDigits, rightDigits, group); 5391 } 5392 } 5393 5394 // TODO: drop this and use nfCore (see below) code when we've fixed the rounding bug in nfCore 5395 p.nf = function() { 5396 var str, num, pad, arr, left, right, isNegative, test, i; 5397 5398 if (arguments.length === 2 && typeof arguments[0] === 'number' && typeof arguments[1] === 'number' && (arguments[0] + "").indexOf('.') === -1) { 5399 num = arguments[0]; 5400 pad = arguments[1]; 5401 5402 isNegative = num < 0; 5403 5404 if (isNegative) { 5405 num = Math.abs(num); 5406 } 5407 5408 str = "" + num; 5409 for (i = pad - str.length; i > 0; i--) { 5410 str = "0" + str; 5411 } 5412 5413 if (isNegative) { 5414 str = "-" + str; 5415 } 5416 } else if (arguments.length === 2 && typeof arguments[0] === 'object' && arguments[0].constructor === Array && typeof arguments[1] === 'number') { 5417 arr = arguments[0]; 5418 pad = arguments[1]; 5419 5420 str = new Array(arr.length); 5421 5422 for (i = 0; i < arr.length && str !== undef; i++) { 5423 test = p.nf(arr[i], pad); 5424 if (test === undef) { 5425 str = undef; 5426 } else { 5427 str[i] = test; 5428 } 5429 } 5430 } else if (arguments.length === 3 && typeof arguments[0] === 'number' && typeof arguments[1] === 'number' && typeof arguments[2] === 'number' && (arguments[0] + "").indexOf('.') >= 0) { 5431 num = arguments[0]; 5432 left = arguments[1]; 5433 right = arguments[2]; 5434 5435 isNegative = num < 0; 5436 5437 if (isNegative) { 5438 num = Math.abs(num); 5439 } 5440 5441 // Change the way the number is 'floored' based on whether it is odd or even. 5442 if (right < 0 && Math.floor(num) % 2 === 1) { 5443 // Make sure 1.49 rounds to 1, but 1.5 rounds to 2. 5444 if ((num) - Math.floor(num) >= 0.5) { 5445 num = num + 1; 5446 } 5447 } 5448 5449 str = "" + num; 5450 5451 for (i = left - str.indexOf('.'); i > 0; i--) { 5452 str = "0" + str; 5453 } 5454 5455 var numDec = str.length - str.indexOf('.') - 1; 5456 if (numDec <= right) { 5457 for (i = right - (str.length - str.indexOf('.') - 1); i > 0; i--) { 5458 str = str + "0"; 5459 } 5460 } else if (right > 0) { 5461 str = str.substring(0, str.length - (numDec - right)); 5462 } else if (right < 0) { 5463 5464 str = str.substring(0, str.indexOf('.')); 5465 } 5466 5467 if (isNegative) { 5468 str = "-" + str; 5469 } 5470 } else if (arguments.length === 3 && typeof arguments[0] === 'object' && arguments[0].constructor === Array && typeof arguments[1] === 'number' && typeof arguments[2] === 'number') { 5471 arr = arguments[0]; 5472 left = arguments[1]; 5473 right = arguments[2]; 5474 5475 str = new Array(arr.length); 5476 5477 for (i = 0; i < arr.length && str !== undef; i++) { 5478 test = p.nf(arr[i], left, right); 5479 if (test === undef) { 5480 str = undef; 5481 } else { 5482 str[i] = test; 5483 } 5484 } 5485 } 5486 5487 return str; 5488 }; 5489 5490// TODO: need to switch nf over to using nfCore... 5491// p.nf = function(value, leftDigits, rightDigits) { return nfCore(value, "", "-", leftDigits, rightDigits); }; 5492 p.nfs = function(value, leftDigits, rightDigits) { return nfCore(value, " ", "-", leftDigits, rightDigits); }; 5493 p.nfp = function(value, leftDigits, rightDigits) { return nfCore(value, "+", "-", leftDigits, rightDigits); }; 5494 p.nfc = function(value, leftDigits, rightDigits) { return nfCore(value, "", "-", leftDigits, rightDigits, ","); }; 5495 5496 var decimalToHex = function decimalToHex(d, padding) { 5497 //if there is no padding value added, default padding to 8 else go into while statement. 5498 padding = (padding === undef || padding === null) ? padding = 8 : padding; 5499 if (d < 0) { 5500 d = 0xFFFFFFFF + d + 1; 5501 } 5502 var hex = Number(d).toString(16).toUpperCase(); 5503 while (hex.length < padding) { 5504 hex = "0" + hex; 5505 } 5506 if (hex.length >= padding) { 5507 hex = hex.substring(hex.length - padding, hex.length); 5508 } 5509 return hex; 5510 }; 5511 5512 // note: since we cannot keep track of byte, int types by default the returned string is 8 chars long 5513 // if no 2nd argument is passed. closest compromise we can use to match java implementation Feb 5 2010 5514 // also the char parser has issues with chars that are not digits or letters IE: !@#$%^&* 5515 p.hex = function hex(value, len) { 5516 if (arguments.length === 1) { 5517 if (value instanceof Char) { 5518 len = 4; 5519 } else { // int or byte, indistinguishable at the moment, default to 8 5520 len = 8; 5521 } 5522 } 5523 return decimalToHex(value, len); 5524 }; 5525 5526 function unhexScalar(hex) { 5527 var value = parseInt("0x" + hex, 16); 5528 5529 // correct for int overflow java expectation 5530 if (value > 2147483647) { 5531 value -= 4294967296; 5532 } 5533 return value; 5534 } 5535 5536 p.unhex = function(hex) { 5537 if (hex instanceof Array) { 5538 var arr = []; 5539 for (var i = 0; i < hex.length; i++) { 5540 arr.push(unhexScalar(hex[i])); 5541 } 5542 return arr; 5543 } else { 5544 return unhexScalar(hex); 5545 } 5546 }; 5547 5548 // Load a file or URL into strings 5549 p.loadStrings = function loadStrings(filename) { 5550 return (localStorage[filename] ? localStorage[filename] : ajax(filename).slice(0, -1)).split("\n"); 5551 }; 5552 5553 // Writes an array of strings to a file, one line per string 5554 p.saveStrings = function saveStrings(filename, strings) { 5555 localStorage[filename] = strings.join('\n'); 5556 }; 5557 5558 p.loadBytes = function loadBytes(url, strings) { 5559 var string = ajax(url); 5560 var ret = []; 5561 5562 for (var i = 0; i < string.length; i++) { 5563 ret.push(string.charCodeAt(i)); 5564 } 5565 5566 return ret; 5567 }; 5568 5569 //////////////////////////////////////////////////////////////////////////// 5570 // String Functions 5571 //////////////////////////////////////////////////////////////////////////// 5572 5573 p.matchAll = function matchAll(aString, aRegExp) { 5574 var results = [], 5575 latest; 5576 var regexp = new RegExp(aRegExp, "g"); 5577 while ((latest = regexp.exec(aString)) !== null) { 5578 results.push(latest); 5579 if (latest[0].length === 0) { 5580 ++regexp.lastIndex; 5581 } 5582 } 5583 return results.length > 0 ? results : null; 5584 }; 5585 5586 String.prototype.replaceAll = function(re, replace) { 5587 return this.replace(new RegExp(re, "g"), replace); 5588 }; 5589 5590 String.prototype.equals = function equals(str) { 5591 return this.valueOf() === str.valueOf(); 5592 }; 5593 5594 String.prototype.toCharArray = function() { 5595 var chars = this.split(""); 5596 for (var i = chars.length - 1; i >= 0; i--) { 5597 chars[i] = new Char(chars[i]); 5598 } 5599 return chars; 5600 }; 5601 5602 p.match = function(str, regexp) { 5603 return str.match(regexp); 5604 }; 5605 5606 // tinylog lite JavaScript library 5607 // http://purl.eligrey.com/tinylog/lite 5608 /*global tinylog,print*/ 5609 var tinylogLite = (function() { 5610 "use strict"; 5611 5612 var tinylogLite = {}, 5613 undef = "undefined", 5614 func = "function", 5615 False = !1, 5616 True = !0, 5617 logLimit = 512, 5618 log = "log"; 5619 5620 if (typeof tinylog !== undef && typeof tinylog[log] === func) { 5621 // pre-existing tinylog present 5622 tinylogLite[log] = tinylog[log]; 5623 } else if (typeof document !== undef && !document.fake) { 5624 (function() { 5625 // DOM document 5626 var doc = document, 5627 5628 $div = "div", 5629 $style = "style", 5630 $title = "title", 5631 5632 containerStyles = { 5633 zIndex: 10000, 5634 position: "fixed", 5635 bottom: "0px", 5636 width: "100%", 5637 height: "15%", 5638 fontFamily: "sans-serif", 5639 color: "#ccc", 5640 backgroundColor: "black" 5641 }, 5642 outputStyles = { 5643 position: "relative", 5644 fontFamily: "monospace", 5645 overflow: "auto", 5646 height: "100%", 5647 paddingTop: "5px" 5648 }, 5649 resizerStyles = { 5650 height: "5px", 5651 marginTop: "-5px", 5652 cursor: "n-resize", 5653 backgroundColor: "darkgrey" 5654 }, 5655 closeButtonStyles = { 5656 position: "absolute", 5657 top: "5px", 5658 right: "20px", 5659 color: "#111", 5660 MozBorderRadius: "4px", 5661 webkitBorderRadius: "4px", 5662 borderRadius: "4px", 5663 cursor: "pointer", 5664 fontWeight: "normal", 5665 textAlign: "center", 5666 padding: "3px 5px", 5667 backgroundColor: "#333", 5668 fontSize: "12px" 5669 }, 5670 entryStyles = { 5671 //borderBottom: "1px solid #d3d3d3", 5672 minHeight: "16px" 5673 }, 5674 entryTextStyles = { 5675 fontSize: "12px", 5676 margin: "0 8px 0 8px", 5677 maxWidth: "100%", 5678 whiteSpace: "pre-wrap", 5679 overflow: "auto" 5680 }, 5681 5682 view = doc.defaultView, 5683 docElem = doc.documentElement, 5684 docElemStyle = docElem[$style], 5685 5686 setStyles = function() { 5687 var i = arguments.length, 5688 elemStyle, styles, style; 5689 5690 while (i--) { 5691 styles = arguments[i--]; 5692 elemStyle = arguments[i][$style]; 5693 5694 for (style in styles) { 5695 if (styles.hasOwnProperty(style)) { 5696 elemStyle[style] = styles[style]; 5697 } 5698 } 5699 } 5700 }, 5701 5702 observer = function(obj, event, handler) { 5703 if (obj.addEventListener) { 5704 obj.addEventListener(event, handler, False); 5705 } else if (obj.attachEvent) { 5706 obj.attachEvent("on" + event, handler); 5707 } 5708 return [obj, event, handler]; 5709 }, 5710 unobserve = function(obj, event, handler) { 5711 if (obj.removeEventListener) { 5712 obj.removeEventListener(event, handler, False); 5713 } else if (obj.detachEvent) { 5714 obj.detachEvent("on" + event, handler); 5715 } 5716 }, 5717 clearChildren = function(node) { 5718 var children = node.childNodes, 5719 child = children.length; 5720 5721 while (child--) { 5722 node.removeChild(children.item(0)); 5723 } 5724 }, 5725 append = function(to, elem) { 5726 return to.appendChild(elem); 5727 }, 5728 createElement = function(localName) { 5729 return doc.createElement(localName); 5730 }, 5731 createTextNode = function(text) { 5732 return doc.createTextNode(text); 5733 }, 5734 5735 createLog = tinylogLite[log] = function(message) { 5736 // don't show output log until called once 5737 var uninit, 5738 originalPadding = docElemStyle.paddingBottom, 5739 container = createElement($div), 5740 containerStyle = container[$style], 5741 resizer = append(container, createElement($div)), 5742 output = append(container, createElement($div)), 5743 closeButton = append(container, createElement($div)), 5744 resizingLog = False, 5745 previousHeight = False, 5746 previousScrollTop = False, 5747 messages = 0, 5748 5749 updateSafetyMargin = function() { 5750 // have a blank space large enough to fit the output box at the page bottom 5751 docElemStyle.paddingBottom = container.clientHeight + "px"; 5752 }, 5753 setContainerHeight = function(height) { 5754 var viewHeight = view.innerHeight, 5755 resizerHeight = resizer.clientHeight; 5756 5757 // constrain the container inside the viewport's dimensions 5758 if (height < 0) { 5759 height = 0; 5760 } else if (height + resizerHeight > viewHeight) { 5761 height = viewHeight - resizerHeight; 5762 } 5763 5764 containerStyle.height = height / viewHeight * 100 + "%"; 5765 5766 updateSafetyMargin(); 5767 }, 5768 observers = [ 5769 observer(doc, "mousemove", function(evt) { 5770 if (resizingLog) { 5771 setContainerHeight(view.innerHeight - evt.clientY); 5772 output.scrollTop = previousScrollTop; 5773 } 5774 }), 5775 5776 observer(doc, "mouseup", function() { 5777 if (resizingLog) { 5778 resizingLog = previousScrollTop = False; 5779 } 5780 }), 5781 5782 observer(resizer, "dblclick", function(evt) { 5783 evt.preventDefault(); 5784 5785 if (previousHeight) { 5786 setContainerHeight(previousHeight); 5787 previousHeight = False; 5788 } else { 5789 previousHeight = container.clientHeight; 5790 containerStyle.height = "0px"; 5791 } 5792 }), 5793 5794 observer(resizer, "mousedown", function(evt) { 5795 evt.preventDefault(); 5796 resizingLog = True; 5797 previousScrollTop = output.scrollTop; 5798 }), 5799 5800 observer(resizer, "contextmenu", function() { 5801 resizingLog = False; 5802 }), 5803 5804 observer(closeButton, "click", function() { 5805 uninit(); 5806 }) 5807 ]; 5808 5809 uninit = function() { 5810 // remove observers 5811 var i = observers.length; 5812 5813 while (i--) { 5814 unobserve.apply(tinylogLite, observers[i]); 5815 } 5816 5817 // remove tinylog lite from the DOM 5818 docElem.removeChild(container); 5819 docElemStyle.paddingBottom = originalPadding; 5820 5821 clearChildren(output); 5822 clearChildren(container); 5823 5824 tinylogLite[log] = createLog; 5825 }; 5826 5827 setStyles( 5828 container, containerStyles, output, outputStyles, resizer, resizerStyles, closeButton, closeButtonStyles); 5829 5830 closeButton[$title] = "Close Log"; 5831 append(closeButton, createTextNode("\u2716")); 5832 5833 resizer[$title] = "Double-click to toggle log minimization"; 5834 5835 docElem.insertBefore(container, docElem.firstChild); 5836 5837 tinylogLite[log] = function(message) { 5838 if (messages === logLimit) { 5839 output.removeChild(output.firstChild); 5840 } else { 5841 messages++; 5842 } 5843 5844 var entry = append(output, createElement($div)), 5845 entryText = append(entry, createElement($div)); 5846 5847 entry[$title] = (new Date()).toLocaleTimeString(); 5848 5849 setStyles( 5850 entry, entryStyles, entryText, entryTextStyles); 5851 5852 append(entryText, createTextNode(message)); 5853 output.scrollTop = output.scrollHeight; 5854 }; 5855 5856 tinylogLite[log](message); 5857 }; 5858 }()); 5859 } else if (typeof print === func) { // JS shell 5860 tinylogLite[log] = print; 5861 } 5862 5863 return tinylogLite; 5864 }()), 5865 5866 logBuffer = []; 5867 5868 p.console = window.console || tinylogLite; 5869 5870 p.println = function println(message) { 5871 var bufferLen = logBuffer.length; 5872 if (bufferLen) { 5873 tinylogLite.log(logBuffer.join("")); 5874 logBuffer.length = 0; // clear log buffer 5875 } 5876 5877 if (arguments.length === 0 && bufferLen === 0) { 5878 tinylogLite.log(""); 5879 } else if (arguments.length !== 0) { 5880 tinylogLite.log(message); 5881 } 5882 }; 5883 5884 p.print = function print(message) { 5885 logBuffer.push(message); 5886 }; 5887 5888 // Alphanumeric chars arguments automatically converted to numbers when 5889 // passed in, and will come out as numbers. 5890 p.str = function str(val) { 5891 if (val instanceof Array) { 5892 var arr = []; 5893 for (var i = 0; i < val.length; i++) { 5894 arr.push(val[i] + ""); 5895 } 5896 return arr; 5897 } else { 5898 return (val + ""); 5899 } 5900 }; 5901 5902 p.trim = function(str) { 5903 if (str instanceof Array) { 5904 var arr = []; 5905 for (var i = 0; i < str.length; i++) { 5906 arr.push(str[i].replace(/^\s*/, '').replace(/\s*$/, '').replace(/\r*$/, '')); 5907 } 5908 return arr; 5909 } else { 5910 return str.replace(/^\s*/, '').replace(/\s*$/, '').replace(/\r*$/, ''); 5911 } 5912 }; 5913 5914 // Conversion 5915 function booleanScalar(val) { 5916 if (typeof val === 'number') { 5917 return val !== 0; 5918 } else if (typeof val === 'boolean') { 5919 return val; 5920 } else if (typeof val === 'string') { 5921 return val.toLowerCase() === 'true'; 5922 } else if (val instanceof Char) { 5923 // 1, T or t 5924 return val.code === 49 || val.code === 84 || val.code === 116; 5925 } 5926 } 5927 5928 p['boolean'] = function(val) { 5929 if (val instanceof Array) { 5930 var ret = []; 5931 for (var i = 0; i < val.length; i++) { 5932 ret.push(booleanScalar(val[i])); 5933 } 5934 return ret; 5935 } else { 5936 return booleanScalar(val); 5937 } 5938 }; 5939 5940 // a byte is a number between -128 and 127 5941 p['byte'] = function(aNumber) { 5942 if (aNumber instanceof Array) { 5943 var bytes = []; 5944 for (var i = 0; i < aNumber.length; i++) { 5945 bytes.push((0 - (aNumber[i] & 0x80)) | (aNumber[i] & 0x7F)); 5946 } 5947 return bytes; 5948 } else { 5949 return (0 - (aNumber & 0x80)) | (aNumber & 0x7F); 5950 } 5951 }; 5952 5953 p['char'] = function(key) { 5954 if (typeof key === "number") { 5955 return new Char(String.fromCharCode(key & 0xFFFF)); 5956 } else if (key instanceof Array) { 5957 var ret = []; 5958 for (var i = 0; i < key.length; i++) { 5959 ret.push(new Char(String.fromCharCode(key[i] & 0xFFFF))); 5960 } 5961 return ret; 5962 } else { 5963 throw "char() may receive only one argument of type int, byte, int[], or byte[]."; 5964 } 5965 }; 5966 5967 // Processing doc claims good argument types are: int, char, byte, boolean, 5968 // String, int[], char[], byte[], boolean[], String[]. 5969 // floats should not work. However, floats with only zeroes right of the 5970 // decimal will work because JS converts those to int. 5971 function floatScalar(val) { 5972 if (typeof val === 'number') { 5973 return val; 5974 } else if (typeof val === 'boolean') { 5975 return val ? 1 : 0; 5976 } else if (typeof val === 'string') { 5977 return parseFloat(val); 5978 } else if (val instanceof Char) { 5979 return val.code; 5980 } 5981 } 5982 5983 p['float'] = function(val) { 5984 if (val instanceof Array) { 5985 var ret = []; 5986 for (var i = 0; i < val.length; i++) { 5987 ret.push(floatScalar(val[i])); 5988 } 5989 return ret; 5990 } else { 5991 return floatScalar(val); 5992 } 5993 }; 5994 5995 function intScalar(val) { 5996 if (typeof val === 'number') { 5997 return val & 0xFFFFFFFF; 5998 } else if (typeof val === 'boolean') { 5999 return val ? 1 : 0; 6000 } else if (typeof val === 'string') { 6001 var number = parseInt(val, 10); // Force decimal radix. Don't convert hex or octal (just like p5) 6002 return number & 0xFFFFFFFF; 6003 } else if (val instanceof Char) { 6004 return val.code; 6005 } 6006 } 6007 6008 p['int'] = function(val) { 6009 if (val instanceof Array) { 6010 var ret = []; 6011 for (var i = 0; i < val.length; i++) { 6012 if (typeof val[i] === 'string' && !/^\s*[+\-]?\d+\s*$/.test(val[i])) { 6013 ret.push(0); 6014 } else { 6015 ret.push(intScalar(val[i])); 6016 } 6017 } 6018 return ret; 6019 } else { 6020 return intScalar(val); 6021 } 6022 }; 6023 6024 //////////////////////////////////////////////////////////////////////////// 6025 // Math functions 6026 //////////////////////////////////////////////////////////////////////////// 6027 6028 // Calculation 6029 p.abs = Math.abs; 6030 6031 p.ceil = Math.ceil; 6032 6033 p.constrain = function(aNumber, aMin, aMax) { 6034 return aNumber > aMax ? aMax : aNumber < aMin ? aMin : aNumber; 6035 }; 6036 6037 p.dist = function() { 6038 var dx, dy, dz; 6039 if (arguments.length === 4) { 6040 dx = arguments[0] - arguments[2]; 6041 dy = arguments[1] - arguments[3]; 6042 return Math.sqrt(dx * dx + dy * dy); 6043 } else if (arguments.length === 6) { 6044 dx = arguments[0] - arguments[3]; 6045 dy = arguments[1] - arguments[4]; 6046 dz = arguments[2] - arguments[5]; 6047 return Math.sqrt(dx * dx + dy * dy + dz * dz); 6048 } 6049 }; 6050 6051 p.exp = Math.exp; 6052 6053 p.floor = Math.floor; 6054 6055 p.lerp = function(value1, value2, amt) { 6056 return ((value2 - value1) * amt) + value1; 6057 }; 6058 6059 p.log = Math.log; 6060 6061 p.mag = function(a, b, c) { 6062 if (arguments.length === 2) { 6063 return Math.sqrt(a * a + b * b); 6064 } else if (arguments.length === 3) { 6065 return Math.sqrt(a * a + b * b + c * c); 6066 } 6067 }; 6068 6069 p.map = function(value, istart, istop, ostart, ostop) { 6070 return ostart + (ostop - ostart) * ((value - istart) / (istop - istart)); 6071 }; 6072 6073 p.max = function() { 6074 if (arguments.length === 2) { 6075 return arguments[0] < arguments[1] ? arguments[1] : arguments[0]; 6076 } else { 6077 var numbers = arguments.length === 1 ? arguments[0] : arguments; // if single argument, array is used 6078 if (! ("length" in numbers && numbers.length > 0)) { 6079 throw "Non-empty array is expected"; 6080 } 6081 var max = numbers[0], 6082 count = numbers.length; 6083 for (var i = 1; i < count; ++i) { 6084 if (max < numbers[i]) { 6085 max = numbers[i]; 6086 } 6087 } 6088 return max; 6089 } 6090 }; 6091 6092 p.min = function() { 6093 if (arguments.length === 2) { 6094 return arguments[0] < arguments[1] ? arguments[0] : arguments[1]; 6095 } else { 6096 var numbers = arguments.length === 1 ? arguments[0] : arguments; // if single argument, array is used 6097 if (! ("length" in numbers && numbers.length > 0)) { 6098 throw "Non-empty array is expected"; 6099 } 6100 var min = numbers[0], 6101 count = numbers.length; 6102 for (var i = 1; i < count; ++i) { 6103 if (min > numbers[i]) { 6104 min = numbers[i]; 6105 } 6106 } 6107 return min; 6108 } 6109 }; 6110 6111 p.norm = function(aNumber, low, high) { 6112 return (aNumber - low) / (high - low); 6113 }; 6114 6115 p.pow = Math.pow; 6116 6117 p.round = Math.round; 6118 6119 p.sq = function(aNumber) { 6120 return aNumber * aNumber; 6121 }; 6122 6123 p.sqrt = Math.sqrt; 6124 6125 // Trigonometry 6126 p.acos = Math.acos; 6127 6128 p.asin = Math.asin; 6129 6130 p.atan = Math.atan; 6131 6132 p.atan2 = Math.atan2; 6133 6134 p.cos = Math.cos; 6135 6136 p.degrees = function(aAngle) { 6137 return (aAngle * 180) / Math.PI; 6138 }; 6139 6140 p.radians = function(aAngle) { 6141 return (aAngle / 180) * Math.PI; 6142 }; 6143 6144 p.sin = Math.sin; 6145 6146 p.tan = Math.tan; 6147 6148 var currentRandom = Math.random; 6149 6150 p.random = function random() { 6151 if(arguments.length === 0) { 6152 return currentRandom(); 6153 } else if(arguments.length === 1) { 6154 return currentRandom() * arguments[0]; 6155 } else { 6156 var aMin = arguments[0], aMax = arguments[1]; 6157 return currentRandom() * (aMax - aMin) + aMin; 6158 } 6159 }; 6160 6161 // Pseudo-random generator 6162 function Marsaglia(i1, i2) { 6163 // from http://www.math.uni-bielefeld.de/~sillke/ALGORITHMS/random/marsaglia-c 6164 var z=i1 || 362436069, w= i2 || 521288629; 6165 var nextInt = function() { 6166 z=(36969*(z&65535)+(z>>>16)) & 0xFFFFFFFF; 6167 w=(18000*(w&65535)+(w>>>16)) & 0xFFFFFFFF; 6168 return (((z&0xFFFF)<<16) | (w&0xFFFF)) & 0xFFFFFFFF; 6169 }; 6170 6171 this.nextDouble = function() { 6172 var i = nextInt() / 4294967296; 6173 return i < 0 ? 1 + i : i; 6174 }; 6175 this.nextInt = nextInt; 6176 } 6177 Marsaglia.createRandomized = function() { 6178 var now = new Date(); 6179 return new Marsaglia((now / 60000) & 0xFFFFFFFF, now & 0xFFFFFFFF); 6180 }; 6181 6182 p.randomSeed = function(seed) { 6183 currentRandom = (new Marsaglia(seed)).nextDouble; 6184 }; 6185 6186 // Random 6187 p.Random = function(seed) { 6188 var haveNextNextGaussian = false, nextNextGaussian, random; 6189 6190 this.nextGaussian = function() { 6191 if (haveNextNextGaussian) { 6192 haveNextNextGaussian = false; 6193 return nextNextGaussian; 6194 } else { 6195 var v1, v2, s; 6196 do { 6197 v1 = 2 * random() - 1; // between -1.0 and 1.0 6198 v2 = 2 * random() - 1; // between -1.0 and 1.0 6199 s = v1 * v1 + v2 * v2; 6200 } 6201 while (s >= 1 || s === 0); 6202 6203 var multiplier = Math.sqrt(-2 * Math.log(s) / s); 6204 nextNextGaussian = v2 * multiplier; 6205 haveNextNextGaussian = true; 6206 6207 return v1 * multiplier; 6208 } 6209 }; 6210 6211 // by default use standard random, otherwise seeded 6212 random = (seed === undef) ? Math.random : (new Marsaglia(seed)).nextDouble; 6213 }; 6214 6215 // Noise functions and helpers 6216 function PerlinNoise(seed) { 6217 var rnd = seed !== undef ? new Marsaglia(seed) : Marsaglia.createRandomized(); 6218 var i, j; 6219 // http://www.noisemachine.com/talk1/17b.html 6220 // http://mrl.nyu.edu/~perlin/noise/ 6221 // generate permutation 6222 var perm = new Array(512); 6223 for(i=0;i<256;++i) { perm[i] = i; } 6224 for(i=0;i<256;++i) { var t = perm[j = rnd.nextInt() & 0xFF]; perm[j] = perm[i]; perm[i] = t; } 6225 // copy to avoid taking mod in perm[0]; 6226 for(i=0;i<256;++i) { perm[i + 256] = perm[i]; } 6227 6228 function grad3d(i,x,y,z) { 6229 var h = i & 15; // convert into 12 gradient directions 6230 var u = h<8 ? x : y, 6231 v = h<4 ? y : h===12||h===14 ? x : z; 6232 return ((h&1) === 0 ? u : -u) + ((h&2) === 0 ? v : -v); 6233 } 6234 6235 function grad2d(i,x,y) { 6236 var v = (i & 1) === 0 ? x : y; 6237 return (i&2) === 0 ? -v : v; 6238 } 6239 6240 function grad1d(i,x) { 6241 return (i&1) === 0 ? -x : x; 6242 } 6243 6244 function lerp(t,a,b) { return a + t * (b - a); } 6245 6246 this.noise3d = function(x, y, z) { 6247 var X = Math.floor(x)&255, Y = Math.floor(y)&255, Z = Math.floor(z)&255; 6248 x -= Math.floor(x); y -= Math.floor(y); z -= Math.floor(z); 6249 var fx = (3-2*x)*x*x, fy = (3-2*y)*y*y, fz = (3-2*z)*z*z; 6250 var p0 = perm[X]+Y, p00 = perm[p0] + Z, p01 = perm[p0 + 1] + Z, 6251 p1 = perm[X + 1] + Y, p10 = perm[p1] + Z, p11 = perm[p1 + 1] + Z; 6252 return lerp(fz, 6253 lerp(fy, lerp(fx, grad3d(perm[p00], x, y, z), grad3d(perm[p10], x-1, y, z)), 6254 lerp(fx, grad3d(perm[p01], x, y-1, z), grad3d(perm[p11], x-1, y-1,z))), 6255 lerp(fy, lerp(fx, grad3d(perm[p00 + 1], x, y, z-1), grad3d(perm[p10 + 1], x-1, y, z-1)), 6256 lerp(fx, grad3d(perm[p01 + 1], x, y-1, z-1), grad3d(perm[p11 + 1], x-1, y-1,z-1)))); 6257 }; 6258 6259 this.noise2d = function(x, y) { 6260 var X = Math.floor(x)&255, Y = Math.floor(y)&255; 6261 x -= Math.floor(x); y -= Math.floor(y); 6262 var fx = (3-2*x)*x*x, fy = (3-2*y)*y*y; 6263 var p0 = perm[X]+Y, p1 = perm[X + 1] + Y; 6264 return lerp(fy, 6265 lerp(fx, grad2d(perm[p0], x, y), grad2d(perm[p1], x-1, y)), 6266 lerp(fx, grad2d(perm[p0 + 1], x, y-1), grad2d(perm[p1 + 1], x-1, y-1))); 6267 }; 6268 6269 this.noise1d = function(x) { 6270 var X = Math.floor(x)&255; 6271 x -= Math.floor(x); 6272 var fx = (3-2*x)*x*x; 6273 return lerp(fx, grad1d(perm[X], x), grad1d(perm[X+1], x-1)); 6274 }; 6275 } 6276 6277 // processing defaults 6278 var noiseProfile = { generator: undef, octaves: 4, fallout: 0.5, seed: undef}; 6279 6280 p.noise = function(x, y, z) { 6281 if(noiseProfile.generator === undef) { 6282 // caching 6283 noiseProfile.generator = new PerlinNoise(noiseProfile.seed); 6284 } 6285 var generator = noiseProfile.generator; 6286 var effect = 1, k = 1, sum = 0; 6287 for(var i=0; i<noiseProfile.octaves; ++i) { 6288 effect *= noiseProfile.fallout; 6289 switch (arguments.length) { 6290 case 1: 6291 sum += effect * (1 + generator.noise1d(k*x))/2; break; 6292 case 2: 6293 sum += effect * (1 + generator.noise2d(k*x, k*y))/2; break; 6294 case 3: 6295 sum += effect * (1 + generator.noise3d(k*x, k*y, k*z))/2; break; 6296 } 6297 k *= 2; 6298 } 6299 return sum; 6300 }; 6301 6302 p.noiseDetail = function(octaves, fallout) { 6303 noiseProfile.octaves = octaves; 6304 if(fallout !== undef) { 6305 noiseProfile.fallout = fallout; 6306 } 6307 }; 6308 6309 p.noiseSeed = function(seed) { 6310 noiseProfile.seed = seed; 6311 noiseProfile.generator = undef; 6312 }; 6313 6314 // Set default background behavior for 2D and 3D contexts 6315 var refreshBackground = function() { 6316 if (!curSketch.options.isTransparent) { 6317 if (p.use3DContext) { 6318 // fill background default opaque gray 6319 curContext.clearColor(204 / 255, 204 / 255, 204 / 255, 1.0); 6320 curContext.clear(curContext.COLOR_BUFFER_BIT | curContext.DEPTH_BUFFER_BIT); 6321 } else { 6322 // fill background default opaque gray 6323 curContext.fillStyle = "rgb(204, 204, 204)"; 6324 curContext.fillRect(0, 0, p.width, p.height); 6325 isFillDirty = true; 6326 } 6327 } 6328 }; 6329 6330 // Changes the size of the Canvas ( this resets context properties like 'lineCap', etc. 6331 p.size = function size(aWidth, aHeight, aMode) { 6332 if (aMode && (aMode === PConstants.WEBGL)) { 6333 // get the 3D rendering context 6334 try { 6335 // If the HTML <canvas> dimensions differ from the 6336 // dimensions specified in the size() call in the sketch, for 6337 // 3D sketches, browsers will either not render or render the 6338 // scene incorrectly. To fix this, we need to adjust the 6339 // width and height attributes of the canvas. 6340 if (curElement.width !== aWidth || curElement.height !== aHeight) { 6341 curElement.setAttribute("width", aWidth); 6342 curElement.setAttribute("height", aHeight); 6343 } 6344 curContext = curElement.getContext("experimental-webgl"); 6345 p.use3DContext = true; 6346 canTex = curContext.createTexture(); // texture 6347 } catch(e_size) { 6348 Processing.debug(e_size); 6349 } 6350 6351 if (!curContext) { 6352 throw "OPENGL 3D context is not supported on this browser."; 6353 } else { 6354 for (var i = 0; i < PConstants.SINCOS_LENGTH; i++) { 6355 sinLUT[i] = p.sin(i * (PConstants.PI / 180) * 0.5); 6356 cosLUT[i] = p.cos(i * (PConstants.PI / 180) * 0.5); 6357 } 6358 // Set defaults 6359 curContext.viewport(0, 0, curElement.width, curElement.height); 6360 curContext.enable(curContext.DEPTH_TEST); 6361 curContext.enable(curContext.BLEND); 6362 curContext.blendFunc(curContext.SRC_ALPHA, curContext.ONE_MINUS_SRC_ALPHA); 6363 refreshBackground(); // sets clearColor default; 6364 6365 // Create the program objects to render 2D (points, lines) and 6366 // 3D (spheres, boxes) shapes. Because 2D shapes are not lit, 6367 // lighting calculations could be ommitted from that program object. 6368 programObject2D = createProgramObject(curContext, vertexShaderSource2D, fragmentShaderSource2D); 6369 6370 // set the defaults 6371 curContext.useProgram(programObject2D); 6372 p.strokeWeight(1.0); 6373 6374 programObject3D = createProgramObject(curContext, vertexShaderSource3D, fragmentShaderSource3D); 6375 programObjectUnlitShape = createProgramObject(curContext, vShaderSrcUnlitShape, fShaderSrcUnlitShape); 6376 6377 // Now that the programs have been compiled, we can set the default 6378 // states for the lights. 6379 curContext.useProgram(programObject3D); 6380 6381 // assume we aren't using textures by default 6382 uniformi(programObject3D, "usingTexture", usingTexture); 6383 p.lightFalloff(1, 0, 0); 6384 p.shininess(1); 6385 p.ambient(255, 255, 255); 6386 p.specular(0, 0, 0); 6387 6388 // Create buffers for 3D primitives 6389 boxBuffer = curContext.createBuffer(); 6390 curContext.bindBuffer(curContext.ARRAY_BUFFER, boxBuffer); 6391 curContext.bufferData(curContext.ARRAY_BUFFER, boxVerts, curContext.STATIC_DRAW); 6392 6393 boxNormBuffer = curContext.createBuffer(); 6394 curContext.bindBuffer(curContext.ARRAY_BUFFER, boxNormBuffer); 6395 curContext.bufferData(curContext.ARRAY_BUFFER, boxNorms, curContext.STATIC_DRAW); 6396 6397 boxOutlineBuffer = curContext.createBuffer(); 6398 curContext.bindBuffer(curContext.ARRAY_BUFFER, boxOutlineBuffer); 6399 curContext.bufferData(curContext.ARRAY_BUFFER, boxOutlineVerts, curContext.STATIC_DRAW); 6400 6401 // used to draw the rectangle and the outline 6402 rectBuffer = curContext.createBuffer(); 6403 curContext.bindBuffer(curContext.ARRAY_BUFFER, rectBuffer); 6404 curContext.bufferData(curContext.ARRAY_BUFFER, rectVerts, curContext.STATIC_DRAW); 6405 6406 rectNormBuffer = curContext.createBuffer(); 6407 curContext.bindBuffer(curContext.ARRAY_BUFFER, rectNormBuffer); 6408 curContext.bufferData(curContext.ARRAY_BUFFER, rectNorms, curContext.STATIC_DRAW); 6409 6410 // The sphere vertices are specified dynamically since the user 6411 // can change the level of detail. Everytime the user does that 6412 // using sphereDetail(), the new vertices are calculated. 6413 sphereBuffer = curContext.createBuffer(); 6414 6415 lineBuffer = curContext.createBuffer(); 6416 6417 // Shape buffers 6418 fillBuffer = curContext.createBuffer(); 6419 fillColorBuffer = curContext.createBuffer(); 6420 strokeColorBuffer = curContext.createBuffer(); 6421 shapeTexVBO = curContext.createBuffer(); 6422 6423 pointBuffer = curContext.createBuffer(); 6424 curContext.bindBuffer(curContext.ARRAY_BUFFER, pointBuffer); 6425 curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array([0, 0, 0]), curContext.STATIC_DRAW); 6426 6427 textBuffer = curContext.createBuffer(); 6428 curContext.bindBuffer(curContext.ARRAY_BUFFER, textBuffer ); 6429 curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array([1,1,0,-1,1,0,-1,-1,0,1,-1,0]), curContext.STATIC_DRAW); 6430 6431 textureBuffer = curContext.createBuffer(); 6432 curContext.bindBuffer(curContext.ARRAY_BUFFER, textureBuffer); 6433 curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array([0,0,1,0,1,1,0,1]), curContext.STATIC_DRAW); 6434 6435 indexBuffer = curContext.createBuffer(); 6436 curContext.bindBuffer(curContext.ELEMENT_ARRAY_BUFFER, indexBuffer); 6437 curContext.bufferData(curContext.ELEMENT_ARRAY_BUFFER, new Uint16Array([0,1,2,2,3,0]), curContext.STATIC_DRAW); 6438 6439 cam = new PMatrix3D(); 6440 cameraInv = new PMatrix3D(); 6441 forwardTransform = new PMatrix3D(); 6442 reverseTransform = new PMatrix3D(); 6443 modelView = new PMatrix3D(); 6444 modelViewInv = new PMatrix3D(); 6445 projection = new PMatrix3D(); 6446 p.camera(); 6447 p.perspective(); 6448 forwardTransform = modelView; 6449 reverseTransform = modelViewInv; 6450 6451 userMatrixStack = new PMatrixStack(); 6452 // used by both curve and bezier, so just init here 6453 curveBasisMatrix = new PMatrix3D(); 6454 curveToBezierMatrix = new PMatrix3D(); 6455 curveDrawMatrix = new PMatrix3D(); 6456 bezierDrawMatrix = new PMatrix3D(); 6457 bezierBasisInverse = new PMatrix3D(); 6458 bezierBasisMatrix = new PMatrix3D(); 6459 bezierBasisMatrix.set(-1, 3, -3, 1, 3, -6, 3, 0, -3, 3, 0, 0, 1, 0, 0, 0); 6460 } 6461 p.stroke(0); 6462 p.fill(255); 6463 } else { 6464 if (curContext === undef) { 6465 // size() was called without p.init() default context, ie. p.createGraphics() 6466 curContext = curElement.getContext("2d"); 6467 p.use3DContext = false; 6468 userMatrixStack = new PMatrixStack(); 6469 modelView = new PMatrix2D(); 6470 } 6471 } 6472 6473 // The default 2d context has already been created in the p.init() stage if 6474 // a 3d context was not specified. This is so that a 2d context will be 6475 // available if size() was not called. 6476 var props = { 6477 fillStyle: curContext.fillStyle, 6478 strokeStyle: curContext.strokeStyle, 6479 lineCap: curContext.lineCap, 6480 lineJoin: curContext.lineJoin 6481 }; 6482 curElement.width = p.width = aWidth || 100; 6483 curElement.height = p.height = aHeight || 100; 6484 6485 for (var j in props) { 6486 if (props) { 6487 curContext[j] = props[j]; 6488 } 6489 } 6490 6491 // redraw the background if background was called before size 6492 refreshBackground(); 6493 6494 // set 5% for pixels to cache (or 1000) 6495 maxPixelsCached = Math.max(1000, aWidth * aHeight * 0.05); 6496 6497 // Externalize the context 6498 p.externals.context = curContext; 6499 6500 p.toImageData = function() { 6501 if(!p.use3DContext){ 6502 return curContext.getImageData(0, 0, this.width, this.height); 6503 } else { 6504 var c = document.createElement("canvas"); 6505 var ctx = c.getContext("2d"); 6506 var obj = ctx.createImageData(this.width, this.height); 6507 var uBuff = new Uint8Array(this.width * this.height * 4); 6508 curContext.readPixels(0,0,this.width,this.height,curContext.RGBA,curContext.UNSIGNED_BYTE, uBuff); 6509 for(var i =0; i < uBuff.length; i++){ 6510 obj.data[i] = uBuff[(this.height - 1 - Math.floor(i / 4 / this.width)) * this.width * 4 + (i % (this.width * 4))]; 6511 } 6512 6513 return obj; 6514 } 6515 }; 6516 }; 6517 6518 //////////////////////////////////////////////////////////////////////////// 6519 // Lights 6520 //////////////////////////////////////////////////////////////////////////// 6521 6522 p.ambientLight = function(r, g, b, x, y, z) { 6523 if (p.use3DContext) { 6524 if (lightCount === PConstants.MAX_LIGHTS) { 6525 throw "can only create " + PConstants.MAX_LIGHTS + " lights"; 6526 } 6527 6528 var pos = new PVector(x, y, z); 6529 var view = new PMatrix3D(); 6530 view.scale(1, -1, 1); 6531 view.apply(modelView.array()); 6532 view.mult(pos, pos); 6533 6534 curContext.useProgram(programObject3D); 6535 uniformf(programObject3D, "lights[" + lightCount + "].color", [r / 255, g / 255, b / 255]); 6536 uniformf(programObject3D, "lights[" + lightCount + "].position", pos.array()); 6537 uniformi(programObject3D, "lights[" + lightCount + "].type", 0); 6538 uniformi(programObject3D, "lightCount", ++lightCount); 6539 } 6540 }; 6541 6542 p.directionalLight = function(r, g, b, nx, ny, nz) { 6543 if (p.use3DContext) { 6544 if (lightCount === PConstants.MAX_LIGHTS) { 6545 throw "can only create " + PConstants.MAX_LIGHTS + " lights"; 6546 } 6547 6548 curContext.useProgram(programObject3D); 6549 6550 // Less code than manually multiplying, but I'll fix 6551 // this when I have more time. 6552 var dir = [nx, ny, nz, 0.0000001]; 6553 6554 var view = new PMatrix3D(); 6555 view.scale(1, -1, 1); 6556 view.apply(modelView.array()); 6557 view.mult(dir, dir); 6558 6559 uniformf(programObject3D, "lights[" + lightCount + "].color", [r / 255, g / 255, b / 255]); 6560 uniformf(programObject3D, "lights[" + lightCount + "].position", [-dir[0], -dir[1], -dir[2]]); 6561 uniformi(programObject3D, "lights[" + lightCount + "].type", 1); 6562 uniformi(programObject3D, "lightCount", ++lightCount); 6563 } 6564 }; 6565 6566 p.lightFalloff = function lightFalloff(constant, linear, quadratic) { 6567 if (p.use3DContext) { 6568 curContext.useProgram(programObject3D); 6569 uniformf(programObject3D, "falloff", [constant, linear, quadratic]); 6570 } 6571 }; 6572 6573 p.lightSpecular = function lightSpecular(r, g, b) { 6574 if (p.use3DContext) { 6575 curContext.useProgram(programObject3D); 6576 uniformf(programObject3D, "specular", [r / 255, g / 255, b / 255]); 6577 } 6578 }; 6579 6580 /* 6581 Sets the default ambient light, directional light, 6582 falloff, and specular values. P5 Documentation says specular() 6583 is set, but the code calls lightSpecular(). 6584 */ 6585 p.lights = function lights() { 6586 p.ambientLight(128, 128, 128); 6587 p.directionalLight(128, 128, 128, 0, 0, -1); 6588 p.lightFalloff(1, 0, 0); 6589 p.lightSpecular(0, 0, 0); 6590 }; 6591 6592 p.pointLight = function(r, g, b, x, y, z) { 6593 if (p.use3DContext) { 6594 if (lightCount === PConstants.MAX_LIGHTS) { 6595 throw "can only create " + PConstants.MAX_LIGHTS + " lights"; 6596 } 6597 6598 // place the point in view space once instead of once per vertex 6599 // in the shader. 6600 var pos = new PVector(x, y, z); 6601 var view = new PMatrix3D(); 6602 view.scale(1, -1, 1); 6603 view.apply(modelView.array()); 6604 view.mult(pos, pos); 6605 6606 curContext.useProgram(programObject3D); 6607 uniformf(programObject3D, "lights[" + lightCount + "].color", [r / 255, g / 255, b / 255]); 6608 uniformf(programObject3D, "lights[" + lightCount + "].position", pos.array()); 6609 uniformi(programObject3D, "lights[" + lightCount + "].type", 2); 6610 uniformi(programObject3D, "lightCount", ++lightCount); 6611 } 6612 }; 6613 6614 /* 6615 Disables lighting so the all shapes drawn after this 6616 will not be lit. 6617 */ 6618 p.noLights = function noLights() { 6619 if (p.use3DContext) { 6620 lightCount = 0; 6621 curContext.useProgram(programObject3D); 6622 uniformi(programObject3D, "lightCount", lightCount); 6623 } 6624 }; 6625 6626 /* 6627 r,g,b - Color of the light 6628 x,y,z - position of the light in modeling space 6629 nx,ny,nz - direction of the spotlight 6630 angle - in radians 6631 concentration - 6632 */ 6633 p.spotLight = function spotLight(r, g, b, x, y, z, nx, ny, nz, angle, concentration) { 6634 if (p.use3DContext) { 6635 if (lightCount === PConstants.MAX_LIGHTS) { 6636 throw "can only create " + PConstants.MAX_LIGHTS + " lights"; 6637 } 6638 6639 curContext.useProgram(programObject3D); 6640 6641 // place the point in view space once instead of once per vertex 6642 // in the shader. 6643 var pos = new PVector(x, y, z); 6644 var view = new PMatrix3D(); 6645 view.scale(1, -1, 1); 6646 view.apply(modelView.array()); 6647 view.mult(pos, pos); 6648 6649 // transform the spotlight's direction 6650 // need to find a solution for this one. Maybe manual mult? 6651 var dir = [nx, ny, nz, 0.0000001]; 6652 view = new PMatrix3D(); 6653 view.scale(1, -1, 1); 6654 view.apply(modelView.array()); 6655 view.mult(dir, dir); 6656 6657 uniformf(programObject3D, "lights[" + lightCount + "].color", [r / 255, g / 255, b / 255]); 6658 uniformf(programObject3D, "lights[" + lightCount + "].position", pos.array()); 6659 uniformf(programObject3D, "lights[" + lightCount + "].direction", [dir[0], dir[1], dir[2]]); 6660 uniformf(programObject3D, "lights[" + lightCount + "].concentration", concentration); 6661 uniformf(programObject3D, "lights[" + lightCount + "].angle", angle); 6662 uniformi(programObject3D, "lights[" + lightCount + "].type", 3); 6663 uniformi(programObject3D, "lightCount", ++lightCount); 6664 } 6665 }; 6666 6667 //////////////////////////////////////////////////////////////////////////// 6668 // Camera functions 6669 //////////////////////////////////////////////////////////////////////////// 6670 6671 p.beginCamera = function beginCamera() { 6672 if (manipulatingCamera) { 6673 throw ("You cannot call beginCamera() again before calling endCamera()"); 6674 } else { 6675 manipulatingCamera = true; 6676 forwardTransform = cameraInv; 6677 reverseTransform = cam; 6678 } 6679 }; 6680 6681 p.endCamera = function endCamera() { 6682 if (!manipulatingCamera) { 6683 throw ("You cannot call endCamera() before calling beginCamera()"); 6684 } else { 6685 modelView.set(cam); 6686 modelViewInv.set(cameraInv); 6687 forwardTransform = modelView; 6688 reverseTransform = modelViewInv; 6689 manipulatingCamera = false; 6690 } 6691 }; 6692 6693 p.camera = function camera(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ) { 6694 if (arguments.length === 0) { 6695 //in case canvas is resized 6696 cameraX = curElement.width / 2; 6697 cameraY = curElement.height / 2; 6698 cameraZ = cameraY / Math.tan(cameraFOV / 2); 6699 eyeX = cameraX; 6700 eyeY = cameraY; 6701 eyeZ = cameraZ; 6702 centerX = cameraX; 6703 centerY = cameraY; 6704 centerZ = 0; 6705 upX = 0; 6706 upY = 1; 6707 upZ = 0; 6708 } 6709 6710 var z = new p.PVector(eyeX - centerX, eyeY - centerY, eyeZ - centerZ); 6711 var y = new p.PVector(upX, upY, upZ); 6712 var transX, transY, transZ; 6713 z.normalize(); 6714 var x = p.PVector.cross(y, z); 6715 y = p.PVector.cross(z, x); 6716 x.normalize(); 6717 y.normalize(); 6718 6719 cam.set(x.x, x.y, x.z, 0, y.x, y.y, y.z, 0, z.x, z.y, z.z, 0, 0, 0, 0, 1); 6720 6721 cam.translate(-eyeX, -eyeY, -eyeZ); 6722 6723 cameraInv.reset(); 6724 cameraInv.invApply(x.x, x.y, x.z, 0, y.x, y.y, y.z, 0, z.x, z.y, z.z, 0, 0, 0, 0, 1); 6725 6726 cameraInv.translate(eyeX, eyeY, eyeZ); 6727 6728 modelView.set(cam); 6729 modelViewInv.set(cameraInv); 6730 }; 6731 6732 p.perspective = function perspective(fov, aspect, near, far) { 6733 if (arguments.length === 0) { 6734 //in case canvas is resized 6735 cameraY = curElement.height / 2; 6736 cameraZ = cameraY / Math.tan(cameraFOV / 2); 6737 cameraNear = cameraZ / 10; 6738 cameraFar = cameraZ * 10; 6739 cameraAspect = curElement.width / curElement.height; 6740 fov = cameraFOV; 6741 aspect = cameraAspect; 6742 near = cameraNear; 6743 far = cameraFar; 6744 } 6745 6746 var yMax, yMin, xMax, xMin; 6747 yMax = near * Math.tan(fov / 2); 6748 yMin = -yMax; 6749 xMax = yMax * aspect; 6750 xMin = yMin * aspect; 6751 p.frustum(xMin, xMax, yMin, yMax, near, far); 6752 }; 6753 6754 p.frustum = function frustum(left, right, bottom, top, near, far) { 6755 frustumMode = true; 6756 projection = new PMatrix3D(); 6757 projection.set((2 * near) / (right - left), 0, (right + left) / (right - left), 6758 0, 0, (2 * near) / (top - bottom), (top + bottom) / (top - bottom), 6759 0, 0, 0, -(far + near) / (far - near), -(2 * far * near) / (far - near), 6760 0, 0, -1, 0); 6761 }; 6762 6763 p.ortho = function ortho(left, right, bottom, top, near, far) { 6764 if (arguments.length === 0) { 6765 left = 0; 6766 right = p.width; 6767 bottom = 0; 6768 top = p.height; 6769 near = -10; 6770 far = 10; 6771 } 6772 6773 var x = 2 / (right - left); 6774 var y = 2 / (top - bottom); 6775 var z = -2 / (far - near); 6776 6777 var tx = -(right + left) / (right - left); 6778 var ty = -(top + bottom) / (top - bottom); 6779 var tz = -(far + near) / (far - near); 6780 6781 projection = new PMatrix3D(); 6782 projection.set(x, 0, 0, tx, 0, y, 0, ty, 0, 0, z, tz, 0, 0, 0, 1); 6783 6784 frustumMode = false; 6785 }; 6786 6787 p.printProjection = function() { 6788 projection.print(); 6789 }; 6790 6791 p.printCamera = function() { 6792 cam.print(); 6793 }; 6794 6795 //////////////////////////////////////////////////////////////////////////// 6796 // Shapes 6797 //////////////////////////////////////////////////////////////////////////// 6798 6799 p.box = function(w, h, d) { 6800 if (p.use3DContext) { 6801 // user can uniformly scale the box by 6802 // passing in only one argument. 6803 if (!h || !d) { 6804 h = d = w; 6805 } 6806 6807 // Modeling transformation 6808 var model = new PMatrix3D(); 6809 model.scale(w, h, d); 6810 6811 // viewing transformation needs to have Y flipped 6812 // becuase that's what Processing does. 6813 var view = new PMatrix3D(); 6814 view.scale(1, -1, 1); 6815 view.apply(modelView.array()); 6816 view.transpose(); 6817 6818 var proj = new PMatrix3D(); 6819 proj.set(projection); 6820 proj.transpose(); 6821 6822 if (doFill === true) { 6823 curContext.useProgram(programObject3D); 6824 6825 disableVertexAttribPointer(programObject3D, "aTexture"); 6826 6827 uniformMatrix(programObject3D, "model", false, model.array()); 6828 uniformMatrix(programObject3D, "view", false, view.array()); 6829 uniformMatrix(programObject3D, "projection", false, proj.array()); 6830 6831 // fix stitching problems. (lines get occluded by triangles 6832 // since they share the same depth values). This is not entirely 6833 // working, but it's a start for drawing the outline. So 6834 // developers can start playing around with styles. 6835 curContext.enable(curContext.POLYGON_OFFSET_FILL); 6836 curContext.polygonOffset(1, 1); 6837 uniformf(programObject3D, "color", fillStyle); 6838 6839 // Create the normal transformation matrix 6840 var v = new PMatrix3D(); 6841 v.set(view); 6842 6843 var m = new PMatrix3D(); 6844 m.set(model); 6845 6846 v.mult(m); 6847 6848 var normalMatrix = new PMatrix3D(); 6849 normalMatrix.set(v); 6850 normalMatrix.invert(); 6851 normalMatrix.transpose(); 6852 6853 uniformMatrix(programObject3D, "normalTransform", false, normalMatrix.array()); 6854 6855 vertexAttribPointer(programObject3D, "Vertex", 3, boxBuffer); 6856 vertexAttribPointer(programObject3D, "Normal", 3, boxNormBuffer); 6857 6858 // Ugly hack. Can't simply disable the vertex attribute 6859 // array. No idea why, so I'm passing in dummy data. 6860 vertexAttribPointer(programObject3D, "aColor", 3, boxNormBuffer); 6861 6862 curContext.drawArrays(curContext.TRIANGLES, 0, boxVerts.length / 3); 6863 curContext.disable(curContext.POLYGON_OFFSET_FILL); 6864 } 6865 6866 if (lineWidth > 0 && doStroke) { 6867 curContext.useProgram(programObject2D); 6868 uniformMatrix(programObject2D, "model", false, model.array()); 6869 uniformMatrix(programObject2D, "view", false, view.array()); 6870 uniformMatrix(programObject2D, "projection", false, proj.array()); 6871 6872 uniformf(programObject2D, "color", strokeStyle); 6873 uniformi(programObject2D, "picktype", 0); 6874 6875 vertexAttribPointer(programObject2D, "Vertex", 3, boxOutlineBuffer); 6876 disableVertexAttribPointer(programObject2D, "aTextureCoord"); 6877 6878 curContext.lineWidth(lineWidth); 6879 curContext.drawArrays(curContext.LINES, 0, boxOutlineVerts.length / 3); 6880 } 6881 } 6882 }; 6883 6884 var initSphere = function() { 6885 var i; 6886 sphereVerts = []; 6887 6888 for (i = 0; i < sphereDetailU; i++) { 6889 sphereVerts.push(0); 6890 sphereVerts.push(-1); 6891 sphereVerts.push(0); 6892 sphereVerts.push(sphereX[i]); 6893 sphereVerts.push(sphereY[i]); 6894 sphereVerts.push(sphereZ[i]); 6895 } 6896 sphereVerts.push(0); 6897 sphereVerts.push(-1); 6898 sphereVerts.push(0); 6899 sphereVerts.push(sphereX[0]); 6900 sphereVerts.push(sphereY[0]); 6901 sphereVerts.push(sphereZ[0]); 6902 6903 var v1, v11, v2; 6904 6905 // middle rings 6906 var voff = 0; 6907 for (i = 2; i < sphereDetailV; i++) { 6908 v1 = v11 = voff; 6909 voff += sphereDetailU; 6910 v2 = voff; 6911 for (var j = 0; j < sphereDetailU; j++) { 6912 sphereVerts.push(parseFloat(sphereX[v1])); 6913 sphereVerts.push(parseFloat(sphereY[v1])); 6914 sphereVerts.push(parseFloat(sphereZ[v1++])); 6915 sphereVerts.push(parseFloat(sphereX[v2])); 6916 sphereVerts.push(parseFloat(sphereY[v2])); 6917 sphereVerts.push(parseFloat(sphereZ[v2++])); 6918 } 6919 6920 // close each ring 6921 v1 = v11; 6922 v2 = voff; 6923 6924 sphereVerts.push(parseFloat(sphereX[v1])); 6925 sphereVerts.push(parseFloat(sphereY[v1])); 6926 sphereVerts.push(parseFloat(sphereZ[v1])); 6927 sphereVerts.push(parseFloat(sphereX[v2])); 6928 sphereVerts.push(parseFloat(sphereY[v2])); 6929 sphereVerts.push(parseFloat(sphereZ[v2])); 6930 } 6931 6932 // add the northern cap 6933 for (i = 0; i < sphereDetailU; i++) { 6934 v2 = voff + i; 6935 6936 sphereVerts.push(parseFloat(sphereX[v2])); 6937 sphereVerts.push(parseFloat(sphereY[v2])); 6938 sphereVerts.push(parseFloat(sphereZ[v2])); 6939 sphereVerts.push(0); 6940 sphereVerts.push(1); 6941 sphereVerts.push(0); 6942 } 6943 6944 sphereVerts.push(parseFloat(sphereX[voff])); 6945 sphereVerts.push(parseFloat(sphereY[voff])); 6946 sphereVerts.push(parseFloat(sphereZ[voff])); 6947 sphereVerts.push(0); 6948 sphereVerts.push(1); 6949 sphereVerts.push(0); 6950 6951 //set the buffer data 6952 curContext.bindBuffer(curContext.ARRAY_BUFFER, sphereBuffer); 6953 curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array(sphereVerts), curContext.STATIC_DRAW); 6954 }; 6955 6956 p.sphereDetail = function sphereDetail(ures, vres) { 6957 var i; 6958 6959 if (arguments.length === 1) { 6960 ures = vres = arguments[0]; 6961 } 6962 6963 if (ures < 3) { 6964 ures = 3; 6965 } // force a minimum res 6966 if (vres < 2) { 6967 vres = 2; 6968 } // force a minimum res 6969 // if it hasn't changed do nothing 6970 if ((ures === sphereDetailU) && (vres === sphereDetailV)) { 6971 return; 6972 } 6973 6974 var delta = PConstants.SINCOS_LENGTH / ures; 6975 var cx = new Array(ures); 6976 var cz = new Array(ures); 6977 // calc unit circle in XZ plane 6978 for (i = 0; i < ures; i++) { 6979 cx[i] = cosLUT[parseInt((i * delta) % PConstants.SINCOS_LENGTH, 10)]; 6980 cz[i] = sinLUT[parseInt((i * delta) % PConstants.SINCOS_LENGTH, 10)]; 6981 } 6982 6983 // computing vertexlist 6984 // vertexlist starts at south pole 6985 var vertCount = ures * (vres - 1) + 2; 6986 var currVert = 0; 6987 6988 // re-init arrays to store vertices 6989 sphereX = new Array(vertCount); 6990 sphereY = new Array(vertCount); 6991 sphereZ = new Array(vertCount); 6992 6993 var angle_step = (PConstants.SINCOS_LENGTH * 0.5) / vres; 6994 var angle = angle_step; 6995 6996 // step along Y axis 6997 for (i = 1; i < vres; i++) { 6998 var curradius = sinLUT[parseInt(angle % PConstants.SINCOS_LENGTH, 10)]; 6999 var currY = -cosLUT[parseInt(angle % PConstants.SINCOS_LENGTH, 10)]; 7000 for (var j = 0; j < ures; j++) { 7001 sphereX[currVert] = cx[j] * curradius; 7002 sphereY[currVert] = currY; 7003 sphereZ[currVert++] = cz[j] * curradius; 7004 } 7005 angle += angle_step; 7006 } 7007 sphereDetailU = ures; 7008 sphereDetailV = vres; 7009 7010 // make the sphere verts and norms 7011 initSphere(); 7012 }; 7013 7014 p.sphere = function() { 7015 if (p.use3DContext) { 7016 var sRad = arguments[0], c; 7017 7018 if ((sphereDetailU < 3) || (sphereDetailV < 2)) { 7019 p.sphereDetail(30); 7020 } 7021 7022 // Modeling transformation 7023 var model = new PMatrix3D(); 7024 model.scale(sRad, sRad, sRad); 7025 7026 // viewing transformation needs to have Y flipped 7027 // becuase that's what Processing does. 7028 var view = new PMatrix3D(); 7029 view.scale(1, -1, 1); 7030 view.apply(modelView.array()); 7031 view.transpose(); 7032 7033 var proj = new PMatrix3D(); 7034 proj.set(projection); 7035 proj.transpose(); 7036 7037 if (doFill === true) { 7038 // Create a normal transformation matrix 7039 var v = new PMatrix3D(); 7040 v.set(view); 7041 7042 var m = new PMatrix3D(); 7043 m.set(model); 7044 7045 v.mult(m); 7046 7047 var normalMatrix = new PMatrix3D(); 7048 normalMatrix.set(v); 7049 normalMatrix.invert(); 7050 normalMatrix.transpose(); 7051 7052 curContext.useProgram(programObject3D); 7053 disableVertexAttribPointer(programObject3D, "aTexture"); 7054 7055 uniformMatrix(programObject3D, "model", false, model.array()); 7056 uniformMatrix(programObject3D, "view", false, view.array()); 7057 uniformMatrix(programObject3D, "projection", false, proj.array()); 7058 uniformMatrix(programObject3D, "normalTransform", false, normalMatrix.array()); 7059 7060 vertexAttribPointer(programObject3D, "Vertex", 3, sphereBuffer); 7061 vertexAttribPointer(programObject3D, "Normal", 3, sphereBuffer); 7062 7063 // Ugly hack. Can't simply disable the vertex attribute 7064 // array. No idea why, so I'm passing in dummy data. 7065 vertexAttribPointer(programObject3D, "aColor", 3, sphereBuffer); 7066 7067 // fix stitching problems. (lines get occluded by triangles 7068 // since they share the same depth values). This is not entirely 7069 // working, but it's a start for drawing the outline. So 7070 // developers can start playing around with styles. 7071 curContext.enable(curContext.POLYGON_OFFSET_FILL); 7072 curContext.polygonOffset(1, 1); 7073 7074 uniformf(programObject3D, "color", fillStyle); 7075 7076 curContext.drawArrays(curContext.TRIANGLE_STRIP, 0, sphereVerts.length / 3); 7077 curContext.disable(curContext.POLYGON_OFFSET_FILL); 7078 } 7079 7080 if (lineWidth > 0 && doStroke) { 7081 curContext.useProgram(programObject2D); 7082 uniformMatrix(programObject2D, "model", false, model.array()); 7083 uniformMatrix(programObject2D, "view", false, view.array()); 7084 uniformMatrix(programObject2D, "projection", false, proj.array()); 7085 7086 vertexAttribPointer(programObject2D, "Vertex", 3, sphereBuffer); 7087 disableVertexAttribPointer(programObject2D, "aTextureCoord"); 7088 7089 uniformf(programObject2D, "color", strokeStyle); 7090 uniformi(programObject2D, "picktype", 0); 7091 7092 curContext.lineWidth(lineWidth); 7093 curContext.drawArrays(curContext.LINE_STRIP, 0, sphereVerts.length / 3); 7094 } 7095 } 7096 }; 7097 7098 //////////////////////////////////////////////////////////////////////////// 7099 // Coordinates 7100 //////////////////////////////////////////////////////////////////////////// 7101 7102 p.modelX = function modelX(x, y, z) { 7103 var mv = modelView.array(); 7104 var ci = cameraInv.array(); 7105 7106 var ax = mv[0] * x + mv[1] * y + mv[2] * z + mv[3]; 7107 var ay = mv[4] * x + mv[5] * y + mv[6] * z + mv[7]; 7108 var az = mv[8] * x + mv[9] * y + mv[10] * z + mv[11]; 7109 var aw = mv[12] * x + mv[13] * y + mv[14] * z + mv[15]; 7110 7111 var ox = ci[0] * ax + ci[1] * ay + ci[2] * az + ci[3] * aw; 7112 var ow = ci[12] * ax + ci[13] * ay + ci[14] * az + ci[15] * aw; 7113 7114 return (ow !== 0) ? ox / ow : ox; 7115 }; 7116 7117 p.modelY = function modelY(x, y, z) { 7118 var mv = modelView.array(); 7119 var ci = cameraInv.array(); 7120 7121 var ax = mv[0] * x + mv[1] * y + mv[2] * z + mv[3]; 7122 var ay = mv[4] * x + mv[5] * y + mv[6] * z + mv[7]; 7123 var az = mv[8] * x + mv[9] * y + mv[10] * z + mv[11]; 7124 var aw = mv[12] * x + mv[13] * y + mv[14] * z + mv[15]; 7125 7126 var oy = ci[4] * ax + ci[5] * ay + ci[6] * az + ci[7] * aw; 7127 var ow = ci[12] * ax + ci[13] * ay + ci[14] * az + ci[15] * aw; 7128 7129 return (ow !== 0) ? oy / ow : oy; 7130 }; 7131 7132 p.modelZ = function modelZ(x, y, z) { 7133 var mv = modelView.array(); 7134 var ci = cameraInv.array(); 7135 7136 var ax = mv[0] * x + mv[1] * y + mv[2] * z + mv[3]; 7137 var ay = mv[4] * x + mv[5] * y + mv[6] * z + mv[7]; 7138 var az = mv[8] * x + mv[9] * y + mv[10] * z + mv[11]; 7139 var aw = mv[12] * x + mv[13] * y + mv[14] * z + mv[15]; 7140 7141 var oz = ci[8] * ax + ci[9] * ay + ci[10] * az + ci[11] * aw; 7142 var ow = ci[12] * ax + ci[13] * ay + ci[14] * az + ci[15] * aw; 7143 7144 return (ow !== 0) ? oz / ow : oz; 7145 }; 7146 7147 //////////////////////////////////////////////////////////////////////////// 7148 // Material Properties 7149 //////////////////////////////////////////////////////////////////////////// 7150 7151 p.ambient = function ambient() { 7152 // create an alias to shorten code 7153 var a = arguments; 7154 7155 // either a shade of gray or a 'color' object. 7156 if (p.use3DContext) { 7157 curContext.useProgram(programObject3D); 7158 uniformi(programObject3D, "usingMat", true); 7159 7160 if (a.length === 1) { 7161 // color object was passed in 7162 if (typeof a[0] === "string") { 7163 var c = a[0].slice(5, -1).split(","); 7164 uniformf(programObject3D, "mat_ambient", [c[0] / 255, c[1] / 255, c[2] / 255]); 7165 } 7166 // else a single number was passed in for gray shade 7167 else { 7168 uniformf(programObject3D, "mat_ambient", [a[0] / 255, a[0] / 255, a[0] / 255]); 7169 } 7170 } 7171 // Otherwise three values were provided (r,g,b) 7172 else { 7173 uniformf(programObject3D, "mat_ambient", [a[0] / 255, a[1] / 255, a[2] / 255]); 7174 } 7175 } 7176 }; 7177 7178 p.emissive = function emissive() { 7179 // create an alias to shorten code 7180 var a = arguments; 7181 7182 if (p.use3DContext) { 7183 curContext.useProgram(programObject3D); 7184 uniformi(programObject3D, "usingMat", true); 7185 7186 // If only one argument was provided, the user either gave us a 7187 // shade of gray or a 'color' object. 7188 if (a.length === 1) { 7189 // color object was passed in 7190 if (typeof a[0] === "string") { 7191 var c = a[0].slice(5, -1).split(","); 7192 uniformf(programObject3D, "mat_emissive", [c[0] / 255, c[1] / 255, c[2] / 255]); 7193 } 7194 // else a regular number was passed in for gray shade 7195 else { 7196 uniformf(programObject3D, "mat_emissive", [a[0] / 255, a[0] / 255, a[0] / 255]); 7197 } 7198 } 7199 // Otherwise three values were provided (r,g,b) 7200 else { 7201 uniformf(programObject3D, "mat_emissive", [a[0] / 255, a[1] / 255, a[2] / 255]); 7202 } 7203 } 7204 }; 7205 7206 p.shininess = function shininess(shine) { 7207 if (p.use3DContext) { 7208 curContext.useProgram(programObject3D); 7209 uniformi(programObject3D, "usingMat", true); 7210 uniformf(programObject3D, "shininess", shine); 7211 } 7212 }; 7213 7214 /* 7215 Documentation says the following calls are valid, but the 7216 Processing throws exceptions: 7217 specular(gray, alpha) 7218 specular(v1, v2, v3, alpha) 7219 So we don't support them either 7220 <corban> I dont think this matters so much, let us let color handle it. alpha values are not sent anyways. 7221 */ 7222 p.specular = function specular() { 7223 var c = p.color.apply(this, arguments); 7224 7225 if (p.use3DContext) { 7226 curContext.useProgram(programObject3D); 7227 uniformi(programObject3D, "usingMat", true); 7228 uniformf(programObject3D, "mat_specular", p.color.toGLArray(c).slice(0, 3)); 7229 } 7230 }; 7231 7232 //////////////////////////////////////////////////////////////////////////// 7233 // Coordinates 7234 //////////////////////////////////////////////////////////////////////////// 7235 p.screenX = function screenX( x, y, z ) { 7236 var mv = modelView.array(); 7237 var pj = projection.array(); 7238 7239 var ax = mv[ 0]*x + mv[ 1]*y + mv[ 2]*z + mv[ 3]; 7240 var ay = mv[ 4]*x + mv[ 5]*y + mv[ 6]*z + mv[ 7]; 7241 var az = mv[ 8]*x + mv[ 9]*y + mv[10]*z + mv[11]; 7242 var aw = mv[12]*x + mv[13]*y + mv[14]*z + mv[15]; 7243 7244 var ox = pj[ 0]*ax + pj[ 1]*ay + pj[ 2]*az + pj[ 3]*aw; 7245 var ow = pj[12]*ax + pj[13]*ay + pj[14]*az + pj[15]*aw; 7246 7247 if ( ow !== 0 ){ 7248 ox /= ow; 7249 } 7250 return p.width * ( 1 + ox ) / 2.0; 7251 }; 7252 7253 p.screenY = function screenY( x, y, z ) { 7254 var mv = modelView.array(); 7255 var pj = projection.array(); 7256 7257 var ax = mv[ 0]*x + mv[ 1]*y + mv[ 2]*z + mv[ 3]; 7258 var ay = mv[ 4]*x + mv[ 5]*y + mv[ 6]*z + mv[ 7]; 7259 var az = mv[ 8]*x + mv[ 9]*y + mv[10]*z + mv[11]; 7260 var aw = mv[12]*x + mv[13]*y + mv[14]*z + mv[15]; 7261 7262 var oy = pj[ 4]*ax + pj[ 5]*ay + pj[ 6]*az + pj[ 7]*aw; 7263 var ow = pj[12]*ax + pj[13]*ay + pj[14]*az + pj[15]*aw; 7264 7265 if ( ow !== 0 ){ 7266 oy /= ow; 7267 } 7268 return p.height * ( 1 + oy ) / 2.0; 7269 }; 7270 7271 p.screenZ = function screenZ( x, y, z ) { 7272 var mv = modelView.array(); 7273 var pj = projection.array(); 7274 7275 var ax = mv[ 0]*x + mv[ 1]*y + mv[ 2]*z + mv[ 3]; 7276 var ay = mv[ 4]*x + mv[ 5]*y + mv[ 6]*z + mv[ 7]; 7277 var az = mv[ 8]*x + mv[ 9]*y + mv[10]*z + mv[11]; 7278 var aw = mv[12]*x + mv[13]*y + mv[14]*z + mv[15]; 7279 7280 var oz = pj[ 8]*ax + pj[ 9]*ay + pj[10]*az + pj[11]*aw; 7281 var ow = pj[12]*ax + pj[13]*ay + pj[14]*az + pj[15]*aw; 7282 7283 if ( ow !== 0 ) { 7284 oz /= ow; 7285 } 7286 return ( oz + 1 ) / 2.0; 7287 }; 7288 7289 //////////////////////////////////////////////////////////////////////////// 7290 // Style functions 7291 //////////////////////////////////////////////////////////////////////////// 7292 7293 p.fill = function fill() { 7294 var color = p.color(arguments[0], arguments[1], arguments[2], arguments[3]); 7295 if(color === currentFillColor && doFill) { 7296 return; 7297 } 7298 doFill = true; 7299 currentFillColor = color; 7300 7301 if (p.use3DContext) { 7302 fillStyle = p.color.toGLArray(color); 7303 } else { 7304 isFillDirty = true; 7305 } 7306 }; 7307 7308 function executeContextFill() { 7309 if(doFill) { 7310 if(isFillDirty) { 7311 curContext.fillStyle = p.color.toString(currentFillColor); 7312 isFillDirty = false; 7313 } 7314 curContext.fill(); 7315 } 7316 } 7317 7318 p.noFill = function noFill() { 7319 doFill = false; 7320 }; 7321 7322 p.stroke = function stroke() { 7323 var color = p.color(arguments[0], arguments[1], arguments[2], arguments[3]); 7324 if(color === currentStrokeColor && doStroke) { 7325 return; 7326 } 7327 doStroke = true; 7328 currentStrokeColor = color; 7329 7330 if (p.use3DContext) { 7331 strokeStyle = p.color.toGLArray(color); 7332 } else { 7333 isStrokeDirty = true; 7334 } 7335 }; 7336 7337 function executeContextStroke() { 7338 if(doStroke) { 7339 if(isStrokeDirty) { 7340 curContext.strokeStyle = p.color.toString(currentStrokeColor); 7341 isStrokeDirty = false; 7342 } 7343 curContext.stroke(); 7344 } 7345 } 7346 7347 p.noStroke = function noStroke() { 7348 doStroke = false; 7349 }; 7350 7351 p.strokeWeight = function strokeWeight(w) { 7352 lineWidth = w; 7353 7354 if (p.use3DContext) { 7355 curContext.useProgram(programObject2D); 7356 uniformf(programObject2D, "pointSize", w); 7357 } else { 7358 curContext.lineWidth = w; 7359 } 7360 }; 7361 7362 p.strokeCap = function strokeCap(value) { 7363 curContext.lineCap = value; 7364 }; 7365 7366 p.strokeJoin = function strokeJoin(value) { 7367 curContext.lineJoin = value; 7368 }; 7369 7370 p.smooth = function() { 7371 curElement.style.setProperty("image-rendering", "optimizeQuality", "important"); 7372 if (!p.use3DContext && "mozImageSmoothingEnabled" in curContext) { 7373 curContext.mozImageSmoothingEnabled = true; 7374 } 7375 }; 7376 7377 p.noSmooth = function() { 7378 curElement.style.setProperty("image-rendering", "optimizeSpeed", "important"); 7379 if (!p.use3DContext && "mozImageSmoothingEnabled" in curContext) { 7380 curContext.mozImageSmoothingEnabled = false; 7381 } 7382 }; 7383 7384 //////////////////////////////////////////////////////////////////////////// 7385 // Vector drawing functions 7386 //////////////////////////////////////////////////////////////////////////// 7387 7388 function colorBlendWithAlpha(c1, c2, k) { 7389 var f = 0|(k * ((c2 & PConstants.ALPHA_MASK) >>> 24)); 7390 return (Math.min(((c1 & PConstants.ALPHA_MASK) >>> 24) + f, 0xff) << 24 | 7391 p.mix(c1 & PConstants.RED_MASK, c2 & PConstants.RED_MASK, f) & PConstants.RED_MASK | 7392 p.mix(c1 & PConstants.GREEN_MASK, c2 & PConstants.GREEN_MASK, f) & PConstants.GREEN_MASK | 7393 p.mix(c1 & PConstants.BLUE_MASK, c2 & PConstants.BLUE_MASK, f)); 7394 } 7395 7396 p.point = function point(x, y, z) { 7397 if (p.use3DContext) { 7398 var model = new PMatrix3D(); 7399 7400 // move point to position 7401 model.translate(x, y, z || 0); 7402 model.transpose(); 7403 7404 var view = new PMatrix3D(); 7405 view.scale(1, -1, 1); 7406 view.apply(modelView.array()); 7407 view.transpose(); 7408 7409 var proj = new PMatrix3D(); 7410 proj.set(projection); 7411 proj.transpose(); 7412 7413 curContext.useProgram(programObject2D); 7414 uniformMatrix(programObject2D, "model", false, model.array()); 7415 uniformMatrix(programObject2D, "view", false, view.array()); 7416 uniformMatrix(programObject2D, "projection", false, proj.array()); 7417 7418 if (lineWidth > 0 && doStroke) { 7419 // this will be replaced with the new bit shifting color code 7420 uniformf(programObject2D, "color", strokeStyle); 7421 uniformi(programObject2D, "picktype", 0); 7422 7423 vertexAttribPointer(programObject2D, "Vertex", 3, pointBuffer); 7424 disableVertexAttribPointer(programObject2D, "aTextureCoord"); 7425 7426 curContext.drawArrays(curContext.POINTS, 0, 1); 7427 } 7428 } else { 7429 if (doStroke) { 7430 // TODO if strokeWeight > 1, do circle 7431 7432 if (curSketch.options.crispLines) { 7433 var alphaOfPointWeight = Math.PI / 4; // TODO dependency of strokeWeight 7434 var c = p.get(x, y); 7435 p.set(x, y, colorBlendWithAlpha(c, currentStrokeColor, alphaOfPointWeight)); 7436 } else { 7437 if (lineWidth > 1){ 7438 curContext.fillStyle = p.color.toString(currentStrokeColor); 7439 isFillDirty = true; 7440 curContext.beginPath(); 7441 curContext.arc(x, y, lineWidth / 2, 0, PConstants.TWO_PI, false); 7442 curContext.fill(); 7443 curContext.closePath(); 7444 } else { 7445 curContext.fillStyle = p.color.toString(currentStrokeColor); 7446 curContext.fillRect(Math.round(x), Math.round(y), 1, 1); 7447 isFillDirty = true; 7448 } 7449 } 7450 } 7451 } 7452 }; 7453 7454 p.beginShape = function beginShape(type) { 7455 curShape = type; 7456 curShapeCount = 0; 7457 curvePoints = []; 7458 //textureImage = null; 7459 vertArray = []; 7460 if(p.use3DContext) 7461 { 7462 //normalMode = NORMAL_MODE_AUTO; 7463 } 7464 }; 7465 7466 p.vertex = function vertex() { 7467 var vert = []; 7468 7469 if (firstVert) { firstVert = false; } 7470 7471 if (arguments.length === 4) { //x, y, u, v 7472 vert[0] = arguments[0]; 7473 vert[1] = arguments[1]; 7474 vert[2] = 0; 7475 vert[3] = arguments[2]; 7476 vert[4] = arguments[3]; 7477 } else { // x, y, z, u, v 7478 vert[0] = arguments[0]; 7479 vert[1] = arguments[1]; 7480 vert[2] = arguments[2] || 0; 7481 vert[3] = arguments[3] || 0; 7482 vert[4] = arguments[4] || 0; 7483 } 7484 7485 vert["isVert"] = true; 7486 7487 if (p.use3DContext) { 7488 // fill rgba 7489 vert[5] = fillStyle[0]; 7490 vert[6] = fillStyle[1]; 7491 vert[7] = fillStyle[2]; 7492 vert[8] = fillStyle[3]; 7493 // stroke rgba 7494 vert[9] = strokeStyle[0]; 7495 vert[10] = strokeStyle[1]; 7496 vert[11] = strokeStyle[2]; 7497 vert[12] = strokeStyle[3]; 7498 //normals 7499 vert[13] = normalX; 7500 vert[14] = normalY; 7501 vert[15] = normalZ; 7502 } else { 7503 // fill and stroke color 7504 vert[5] = currentFillColor; 7505 vert[6] = currentStrokeColor; 7506 } 7507 7508 vertArray.push(vert); 7509 }; 7510 7511 /* 7512 Draw 3D points created from calls to vertex: 7513 7514 beginShape(POINT); 7515 vertex(x, y, 0); 7516 ... 7517 endShape(); 7518 */ 7519 var point3D = function point3D(vArray, cArray){ 7520 var view = new PMatrix3D(); 7521 view.scale(1, -1, 1); 7522 view.apply(modelView.array()); 7523 view.transpose(); 7524 7525 var proj = new PMatrix3D(); 7526 proj.set(projection); 7527 proj.transpose(); 7528 7529 curContext.useProgram(programObjectUnlitShape); 7530 uniformMatrix(programObjectUnlitShape, "uView", false, view.array()); 7531 uniformMatrix(programObjectUnlitShape, "uProjection", false, proj.array()); 7532 7533 vertexAttribPointer(programObjectUnlitShape, "aVertex", 3, pointBuffer); 7534 curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array(vArray), curContext.STREAM_DRAW); 7535 7536 vertexAttribPointer(programObjectUnlitShape, "aColor", 4, fillColorBuffer); 7537 curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array(cArray), curContext.STREAM_DRAW); 7538 7539 curContext.drawArrays(curContext.POINTS, 0, vArray.length/3); 7540 }; 7541 7542 /* 7543 Draw 3D lines created from calls to beginShape/vertex/endShape 7544 LINES, LINE_LOOP, etc. 7545 */ 7546 var line3D = function line3D(vArray, mode, cArray){ 7547 var ctxMode; 7548 if (mode === "LINES"){ 7549 ctxMode = curContext.LINES; 7550 } 7551 else if(mode === "LINE_LOOP"){ 7552 ctxMode = curContext.LINE_LOOP; 7553 } 7554 else{ 7555 ctxMode = curContext.LINE_STRIP; 7556 } 7557 7558 var view = new PMatrix3D(); 7559 view.scale(1, -1, 1); 7560 view.apply(modelView.array()); 7561 view.transpose(); 7562 7563 var proj = new PMatrix3D(); 7564 proj.set(projection); 7565 proj.transpose(); 7566 7567 curContext.useProgram(programObjectUnlitShape); 7568 uniformMatrix(programObjectUnlitShape, "uView", false, view.array()); 7569 uniformMatrix(programObjectUnlitShape, "uProjection", false, proj.array()); 7570 7571 vertexAttribPointer(programObjectUnlitShape, "aVertex", 3, lineBuffer); 7572 curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array(vArray), curContext.STREAM_DRAW); 7573 7574 vertexAttribPointer(programObjectUnlitShape, "aColor", 4, strokeColorBuffer); 7575 curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array(cArray), curContext.STREAM_DRAW); 7576 7577 curContext.lineWidth(lineWidth); 7578 7579 curContext.drawArrays(ctxMode, 0, vArray.length/3); 7580 }; 7581 7582 var fill3D = function fill3D(vArray, mode, cArray, tArray){ 7583 var ctxMode; 7584 if(mode === "TRIANGLES"){ 7585 ctxMode = curContext.TRIANGLES; 7586 } 7587 else if(mode === "TRIANGLE_FAN"){ 7588 ctxMode = curContext.TRIANGLE_FAN; 7589 } 7590 else{ 7591 ctxMode = curContext.TRIANGLE_STRIP; 7592 } 7593 7594 var view = new PMatrix3D(); 7595 view.scale(1, -1, 1); 7596 view.apply(modelView.array()); 7597 view.transpose(); 7598 7599 var proj = new PMatrix3D(); 7600 proj.set(projection); 7601 proj.transpose(); 7602 7603 curContext.useProgram( programObject3D ); 7604 uniformMatrix( programObject3D, "model", false, [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1] ); 7605 uniformMatrix( programObject3D, "view", false, view.array() ); 7606 uniformMatrix( programObject3D, "projection", false, proj.array() ); 7607 7608 curContext.enable( curContext.POLYGON_OFFSET_FILL ); 7609 curContext.polygonOffset( 1, 1 ); 7610 7611 uniformf(programObject3D, "color", [-1,0,0,0]); 7612 7613 vertexAttribPointer(programObject3D, "Vertex", 3, fillBuffer); 7614 curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array(vArray), curContext.STREAM_DRAW); 7615 7616 vertexAttribPointer(programObject3D, "aColor", 4, fillColorBuffer); 7617 curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array(cArray), curContext.STREAM_DRAW); 7618 7619 // No support for lights....yet 7620 disableVertexAttribPointer(programObject3D, "Normal"); 7621 7622 var i; 7623 7624 if(usingTexture){ 7625 if(curTextureMode === PConstants.IMAGE){ 7626 for(i = 0; i < tArray.length; i += 2){ 7627 tArray[i] = tArray[i]/curTexture.width; 7628 tArray[i+1] /= curTexture.height; 7629 } 7630 } 7631 7632 // hack to handle when users specifies values 7633 // greater than 1.0 for texture coords. 7634 for(i = 0; i < tArray.length; i += 2){ 7635 if( tArray[i+0] > 1.0 ){ tArray[i+0] -= (tArray[i+0] - 1.0);} 7636 if( tArray[i+1] > 1.0 ){ tArray[i+1] -= (tArray[i+1] - 1.0);} 7637 } 7638 7639 uniformi(programObject3D, "usingTexture", usingTexture); 7640 vertexAttribPointer(programObject3D, "aTexture", 2, shapeTexVBO); 7641 curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array(tArray), curContext.STREAM_DRAW); 7642 } 7643 7644 curContext.drawArrays( ctxMode, 0, vArray.length/3 ); 7645 curContext.disable( curContext.POLYGON_OFFSET_FILL ); 7646 }; 7647 7648 p.endShape = function endShape(mode){ 7649 var closeShape = mode === PConstants.CLOSE; 7650 var lineVertArray = []; 7651 var fillVertArray = []; 7652 var colorVertArray = []; 7653 var strokeVertArray = []; 7654 var texVertArray = []; 7655 7656 firstVert = true; 7657 var i, j, k; 7658 var last = vertArray.length - 1; 7659 7660 for (i = 0; i < vertArray.length; i++) { 7661 for (j = 0; j < 3; j++) { 7662 fillVertArray.push(vertArray[i][j]); 7663 } 7664 } 7665 7666 // 5,6,7,8 7667 // R,G,B,A 7668 for (i = 0; i < vertArray.length; i++) { 7669 for (j = 5; j < 9; j++) { 7670 colorVertArray.push(vertArray[i][j]); 7671 } 7672 } 7673 7674 // 9,10,11,12 7675 // R, G, B, A 7676 for (i = 0; i < vertArray.length; i++) { 7677 for (j = 9; j < 13; j++) { 7678 strokeVertArray.push(vertArray[i][j]); 7679 } 7680 } 7681 7682 for (i = 0; i < vertArray.length; i++) { 7683 texVertArray.push(vertArray[i][3]); 7684 texVertArray.push(vertArray[i][4]); 7685 } 7686 7687 if (closeShape) { 7688 fillVertArray.push(vertArray[0][0]); 7689 fillVertArray.push(vertArray[0][1]); 7690 fillVertArray.push(vertArray[0][2]); 7691 7692 for (i = 5; i < 9; i++) { 7693 colorVertArray.push(vertArray[0][i]); 7694 } 7695 7696 for (i = 9; i < 13; i++) { 7697 strokeVertArray.push(vertArray[0][i]); 7698 } 7699 7700 texVertArray.push(vertArray[0][3]); 7701 texVertArray.push(vertArray[0][4]); 7702 } 7703 7704 if (isCurve && curShape === PConstants.POLYGON || isCurve && curShape === undef) { 7705 if (p.use3DContext) { 7706 lineVertArray = fillVertArray; 7707 if (doStroke) { 7708 line3D(lineVertArray, null, strokeVertArray); 7709 } 7710 if (doFill) { 7711 fill3D(fillVertArray, null, colorVertArray); // fill isn't working in 3d curveVertex 7712 } 7713 } else { 7714 if (vertArray.length > 3) { 7715 var b = [], 7716 s = 1 - curTightness; 7717 curContext.beginPath(); 7718 curContext.moveTo(vertArray[1][0], vertArray[1][1]); 7719 /* 7720 * Matrix to convert from Catmull-Rom to cubic Bezier 7721 * where t = curTightness 7722 * |0 1 0 0 | 7723 * |(t-1)/6 1 (1-t)/6 0 | 7724 * |0 (1-t)/6 1 (t-1)/6 | 7725 * |0 0 0 0 | 7726 */ 7727 for (i = 1; (i+2) < vertArray.length; i++) { 7728 b[0] = [vertArray[i][0], vertArray[i][1]]; 7729 b[1] = [vertArray[i][0] + (s * vertArray[i+1][0] - s * vertArray[i-1][0]) / 6, 7730 vertArray[i][1] + (s * vertArray[i+1][1] - s * vertArray[i-1][1]) / 6]; 7731 b[2] = [vertArray[i+1][0] + (s * vertArray[i][0] - s * vertArray[i+2][0]) / 6, 7732 vertArray[i+1][1] + (s * vertArray[i][1] - s * vertArray[i+2][1]) / 6]; 7733 b[3] = [vertArray[i+1][0], vertArray[i+1][1]]; 7734 curContext.bezierCurveTo(b[1][0], b[1][1], b[2][0], b[2][1], b[3][0], b[3][1]); 7735 } 7736 // close the shape 7737 if (closeShape) { 7738 curContext.lineTo(vertArray[0][0], vertArray[0][1]); 7739 } 7740 executeContextFill(); 7741 executeContextStroke(); 7742 curContext.closePath(); 7743 } 7744 } 7745 } else if (isBezier && curShape === PConstants.POLYGON || isBezier && curShape === undef) { 7746 if (p.use3DContext) { 7747 lineVertArray = fillVertArray; 7748 lineVertArray.splice(lineVertArray.length - 3); 7749 strokeVertArray.splice(strokeVertArray.length - 4); 7750 if (doStroke) { 7751 line3D(lineVertArray, null, strokeVertArray); 7752 } 7753 if (doFill) { 7754 fill3D(fillVertArray, "TRIANGLES", colorVertArray); 7755 } 7756 7757 // TODO: Fill not properly working yet, will fix later 7758 /*fillVertArray = []; 7759 colorVertArray = []; 7760 tempArray.reverse(); 7761 for(i = 0; (i+1) < 10; i++){ 7762 for(j = 0; j < 3; j++){ 7763 fillVertArray.push(tempArray[i][j]); 7764 } 7765 for(j = 5; j < 9; j++){ 7766 colorVertArray.push(tempArray[i][j]); 7767 } 7768 for(j = 0; j < 3; j++){ 7769 fillVertArray.push(vertArray[i][j]); 7770 } 7771 for(j = 5; j < 9; j++){ 7772 colorVertArray.push(vertArray[i][j]); 7773 } 7774 for(j = 0; j < 3; j++){ 7775 fillVertArray.push(vertArray[i+1][j]); 7776 } 7777 for(j = 5; j < 9; j++){ 7778 colorVertArray.push(vertArray[i][j]); 7779 } 7780 } 7781 7782 strokeVertArray = []; 7783 for(i = 0; i < tempArray.length/3; i++){ 7784 strokeVertArray.push(255); 7785 strokeVertArray.push(0); 7786 strokeVertArray.push(0); 7787 strokeVertArray.push(255); 7788 } 7789 point3D(tempArray, strokeVertArray);*/ 7790 } else { 7791 curContext.beginPath(); 7792 for (i = 0; i < vertArray.length; i++) { 7793 if (vertArray[i]["isVert"] === true) { //if it is a vertex move to the position 7794 if (vertArray[i]["moveTo"] === true) { 7795 curContext.moveTo(vertArray[i][0], vertArray[i][1]); 7796 } else if (vertArray[i]["moveTo"] === false){ 7797 curContext.lineTo(vertArray[i][0], vertArray[i][1]); 7798 } else { 7799 curContext.moveTo(vertArray[i][0], vertArray[i][1]); 7800 } 7801 } else { //otherwise continue drawing bezier 7802 curContext.bezierCurveTo(vertArray[i][0], vertArray[i][1], vertArray[i][2], vertArray[i][3], vertArray[i][4], vertArray[i][5]); 7803 } 7804 } 7805 // close the shape 7806 if (closeShape) { 7807 curContext.lineTo(vertArray[0][0], vertArray[0][1]); 7808 } 7809 executeContextFill(); 7810 executeContextStroke(); 7811 curContext.closePath(); 7812 } 7813 } else { 7814 if (p.use3DContext) { // 3D context 7815 if (curShape === PConstants.POINTS) { 7816 for (i = 0; i < vertArray.length; i++) { 7817 for (j = 0; j < 3; j++) { 7818 lineVertArray.push(vertArray[i][j]); 7819 } 7820 } 7821 point3D(lineVertArray, strokeVertArray); 7822 } else if (curShape === PConstants.LINES) { 7823 for (i = 0; i < vertArray.length; i++) { 7824 for (j = 0; j < 3; j++) { 7825 lineVertArray.push(vertArray[i][j]); 7826 } 7827 } 7828 for (i = 0; i < vertArray.length; i++) { 7829 for (j = 5; j < 9; j++) { 7830 colorVertArray.push(vertArray[i][j]); 7831 } 7832 } 7833 line3D(lineVertArray, "LINES", strokeVertArray); 7834 } else if (curShape === PConstants.TRIANGLES) { 7835 if (vertArray.length > 2) { 7836 for (i = 0; (i+2) < vertArray.length; i+=3) { 7837 fillVertArray = []; 7838 texVertArray = []; 7839 lineVertArray = []; 7840 colorVertArray = []; 7841 strokeVertArray = []; 7842 for (j = 0; j < 3; j++) { 7843 for (k = 0; k < 3; k++) { 7844 lineVertArray.push(vertArray[i+j][k]); 7845 fillVertArray.push(vertArray[i+j][k]); 7846 } 7847 } 7848 for (j = 0; j < 3; j++) { 7849 for (k = 3; k < 5; k++) { 7850 texVertArray.push(vertArray[i+j][k]); 7851 } 7852 } 7853 for (j = 0; j < 3; j++) { 7854 for (k = 5; k < 9; k++) { 7855 colorVertArray.push(vertArray[i+j][k]); 7856 strokeVertArray.push(vertArray[i+j][k+4]); 7857 } 7858 } 7859 if (doStroke) { 7860 line3D(lineVertArray, "LINE_LOOP", strokeVertArray ); 7861 } 7862 if (doFill || usingTexture) { 7863 fill3D(fillVertArray, "TRIANGLES", colorVertArray, texVertArray); 7864 } 7865 } 7866 } 7867 } else if (curShape === PConstants.TRIANGLE_STRIP) { 7868 if (vertArray.length > 2) { 7869 for (i = 0; (i+2) < vertArray.length; i++) { 7870 lineVertArray = []; 7871 fillVertArray = []; 7872 strokeVertArray = []; 7873 colorVertArray = []; 7874 texVertArray = []; 7875 for (j = 0; j < 3; j++) { 7876 for (k = 0; k < 3; k++) { 7877 lineVertArray.push(vertArray[i+j][k]); 7878 fillVertArray.push(vertArray[i+j][k]); 7879 } 7880 } 7881 for (j = 0; j < 3; j++) { 7882 for (k = 3; k < 5; k++) { 7883 texVertArray.push(vertArray[i+j][k]); 7884 } 7885 } 7886 for (j = 0; j < 3; j++) { 7887 for (k = 5; k < 9; k++) { 7888 strokeVertArray.push(vertArray[i+j][k+4]); 7889 colorVertArray.push(vertArray[i+j][k]); 7890 } 7891 } 7892 7893 if (doFill || usingTexture) { 7894 fill3D(fillVertArray, "TRIANGLE_STRIP", colorVertArray, texVertArray); 7895 } 7896 if (doStroke) { 7897 line3D(lineVertArray, "LINE_LOOP", strokeVertArray); 7898 } 7899 } 7900 } 7901 } else if (curShape === PConstants.TRIANGLE_FAN) { 7902 if (vertArray.length > 2) { 7903 for (i = 0; i < 3; i++) { 7904 for (j = 0; j < 3; j++) { 7905 lineVertArray.push(vertArray[i][j]); 7906 } 7907 } 7908 for (i = 0; i < 3; i++) { 7909 for (j = 9; j < 13; j++) { 7910 strokeVertArray.push(vertArray[i][j]); 7911 } 7912 } 7913 if (doStroke) { 7914 line3D(lineVertArray, "LINE_LOOP", strokeVertArray); 7915 } 7916 7917 for (i = 2; (i+1) < vertArray.length; i++) { 7918 lineVertArray = []; 7919 strokeVertArray = []; 7920 lineVertArray.push(vertArray[0][0]); 7921 lineVertArray.push(vertArray[0][1]); 7922 lineVertArray.push(vertArray[0][2]); 7923 7924 strokeVertArray.push(vertArray[0][9]); 7925 strokeVertArray.push(vertArray[0][10]); 7926 strokeVertArray.push(vertArray[0][11]); 7927 strokeVertArray.push(vertArray[0][12]); 7928 7929 for (j = 0; j < 2; j++) { 7930 for (k = 0; k < 3; k++) { 7931 lineVertArray.push(vertArray[i+j][k]); 7932 } 7933 } 7934 for (j = 0; j < 2; j++) { 7935 for (k = 9; k < 13; k++) { 7936 strokeVertArray.push(vertArray[i+j][k]); 7937 } 7938 } 7939 if (doStroke) { 7940 line3D(lineVertArray, "LINE_STRIP",strokeVertArray); 7941 } 7942 } 7943 if (doFill || usingTexture) { 7944 fill3D(fillVertArray, "TRIANGLE_FAN", colorVertArray, texVertArray); 7945 } 7946 } 7947 } else if (curShape === PConstants.QUADS) { 7948 for (i = 0; (i + 3) < vertArray.length; i+=4) { 7949 lineVertArray = []; 7950 for (j = 0; j < 4; j++) { 7951 for (k = 0; k < 3; k++) { 7952 lineVertArray.push(vertArray[i+j][k]); 7953 } 7954 } 7955 if (doStroke) { 7956 line3D(lineVertArray, "LINE_LOOP",strokeVertArray); 7957 } 7958 7959 if (doFill) { 7960 fillVertArray = []; 7961 colorVertArray = []; 7962 texVertArray = []; 7963 for (j = 0; j < 3; j++) { 7964 fillVertArray.push(vertArray[i][j]); 7965 } 7966 for (j = 5; j < 9; j++) { 7967 colorVertArray.push(vertArray[i][j]); 7968 } 7969 7970 for (j = 0; j < 3; j++) { 7971 fillVertArray.push(vertArray[i+1][j]); 7972 } 7973 for (j = 5; j < 9; j++) { 7974 colorVertArray.push(vertArray[i+1][j]); 7975 } 7976 7977 for (j = 0; j < 3; j++) { 7978 fillVertArray.push(vertArray[i+3][j]); 7979 } 7980 for (j = 5; j < 9; j++) { 7981 colorVertArray.push(vertArray[i+3][j]); 7982 } 7983 7984 for (j = 0; j < 3; j++) { 7985 fillVertArray.push(vertArray[i+2][j]); 7986 } 7987 for (j = 5; j < 9; j++) { 7988 colorVertArray.push(vertArray[i+2][j]); 7989 } 7990 7991 if (usingTexture) { 7992 texVertArray.push(vertArray[i+0][3]); 7993 texVertArray.push(vertArray[i+0][4]); 7994 texVertArray.push(vertArray[i+1][3]); 7995 texVertArray.push(vertArray[i+1][4]); 7996 texVertArray.push(vertArray[i+3][3]); 7997 texVertArray.push(vertArray[i+3][4]); 7998 texVertArray.push(vertArray[i+2][3]); 7999 texVertArray.push(vertArray[i+2][4]); 8000 } 8001 8002 fill3D(fillVertArray, "TRIANGLE_STRIP", colorVertArray, texVertArray); 8003 } 8004 } 8005 } else if (curShape === PConstants.QUAD_STRIP) { 8006 var tempArray = []; 8007 if (vertArray.length > 3) { 8008 for (i = 0; i < 2; i++) { 8009 for (j = 0; j < 3; j++) { 8010 lineVertArray.push(vertArray[i][j]); 8011 } 8012 } 8013 8014 for (i = 0; i < 2; i++) { 8015 for (j = 9; j < 13; j++) { 8016 strokeVertArray.push(vertArray[i][j]); 8017 } 8018 } 8019 8020 line3D(lineVertArray, "LINE_STRIP", strokeVertArray); 8021 if (vertArray.length > 4 && vertArray.length % 2 > 0) { 8022 tempArray = fillVertArray.splice(fillVertArray.length - 3); 8023 vertArray.pop(); 8024 } 8025 for (i = 0; (i+3) < vertArray.length; i+=2) { 8026 lineVertArray = []; 8027 strokeVertArray = []; 8028 for (j = 0; j < 3; j++) { 8029 lineVertArray.push(vertArray[i+1][j]); 8030 } 8031 for (j = 0; j < 3; j++) { 8032 lineVertArray.push(vertArray[i+3][j]); 8033 } 8034 for (j = 0; j < 3; j++) { 8035 lineVertArray.push(vertArray[i+2][j]); 8036 } 8037 for (j = 0; j < 3; j++) { 8038 lineVertArray.push(vertArray[i+0][j]); 8039 } 8040 for (j = 9; j < 13; j++) { 8041 strokeVertArray.push(vertArray[i+1][j]); 8042 } 8043 for (j = 9; j < 13; j++) { 8044 strokeVertArray.push(vertArray[i+3][j]); 8045 } 8046 for (j = 9; j < 13; j++) { 8047 strokeVertArray.push(vertArray[i+2][j]); 8048 } 8049 for (j = 9; j < 13; j++) { 8050 strokeVertArray.push(vertArray[i+0][j]); 8051 } 8052 if (doStroke) { 8053 line3D(lineVertArray, "LINE_STRIP", strokeVertArray); 8054 } 8055 } 8056 8057 if (doFill || usingTexture) { 8058 fill3D(fillVertArray, "TRIANGLE_LIST", colorVertArray, texVertArray); 8059 } 8060 } 8061 } 8062 // If the user didn't specify a type (LINES, TRIANGLES, etc) 8063 else { 8064 // If only one vertex was specified, it must be a point 8065 if (vertArray.length === 1) { 8066 for (j = 0; j < 3; j++) { 8067 lineVertArray.push(vertArray[0][j]); 8068 } 8069 for (j = 9; j < 13; j++) { 8070 strokeVertArray.push(vertArray[0][j]); 8071 } 8072 point3D(lineVertArray,strokeVertArray); 8073 } else { 8074 for (i = 0; i < vertArray.length; i++) { 8075 for (j = 0; j < 3; j++) { 8076 lineVertArray.push(vertArray[i][j]); 8077 } 8078 for (j = 5; j < 9; j++) { 8079 strokeVertArray.push(vertArray[i][j]); 8080 } 8081 } 8082 if (closeShape) { 8083 line3D(lineVertArray, "LINE_LOOP", strokeVertArray); 8084 } else { 8085 line3D(lineVertArray, "LINE_STRIP", strokeVertArray); 8086 } 8087 8088 // fill is ignored if textures are used 8089 if (doFill || usingTexture) { 8090 fill3D(fillVertArray, "TRIANGLE_FAN", colorVertArray, texVertArray); 8091 } 8092 } 8093 } 8094 // everytime beginShape is followed by a call to 8095 // texture(), texturing it turned back on. We do this to 8096 // figure out if the shape should be textured or filled 8097 // with a color. 8098 usingTexture = false; 8099 curContext.useProgram(programObject3D); 8100 uniformi(programObject3D, "usingTexture", usingTexture); 8101 } 8102 8103 // 2D context 8104 else { 8105 if (curShape === PConstants.POINTS) { 8106 for (i = 0; i < vertArray.length; i++) { 8107 if (doStroke) { 8108 p.stroke(vertArray[i][6]); 8109 } 8110 p.point(vertArray[i][0], vertArray[i][1]); 8111 } 8112 } else if (curShape === PConstants.LINES) { 8113 for (i = 0; (i + 1) < vertArray.length; i+=2) { 8114 if (doStroke) { 8115 p.stroke(vertArray[i+1][6]); 8116 } 8117 p.line(vertArray[i][0], vertArray[i][1], vertArray[i+1][0], vertArray[i+1][1]); 8118 } 8119 } else if (curShape === PConstants.TRIANGLES) { 8120 for (i = 0; (i + 2) < vertArray.length; i+=3) { 8121 curContext.beginPath(); 8122 curContext.moveTo(vertArray[i][0], vertArray[i][1]); 8123 curContext.lineTo(vertArray[i+1][0], vertArray[i+1][1]); 8124 curContext.lineTo(vertArray[i+2][0], vertArray[i+2][1]); 8125 curContext.lineTo(vertArray[i][0], vertArray[i][1]); 8126 8127 if (doFill) { 8128 p.fill(vertArray[i+2][5]); 8129 executeContextFill(); 8130 } 8131 if (doStroke) { 8132 p.stroke(vertArray[i+2][6]); 8133 executeContextStroke(); 8134 } 8135 8136 curContext.closePath(); 8137 } 8138 } else if (curShape === PConstants.TRIANGLE_STRIP) { 8139 for (i = 0; (i+1) < vertArray.length; i++) { 8140 curContext.beginPath(); 8141 curContext.moveTo(vertArray[i+1][0], vertArray[i+1][1]); 8142 curContext.lineTo(vertArray[i][0], vertArray[i][1]); 8143 8144 if (doStroke) { 8145 p.stroke(vertArray[i+1][6]); 8146 } 8147 if (doFill) { 8148 p.fill(vertArray[i+1][5]); 8149 } 8150 8151 if (i + 2 < vertArray.length) { 8152 curContext.lineTo(vertArray[i+2][0], vertArray[i+2][1]); 8153 if (doStroke) { 8154 p.stroke(vertArray[i+2][6]); 8155 } 8156 if (doFill) { 8157 p.fill(vertArray[i+2][5]); 8158 } 8159 } 8160 executeContextFill(); 8161 executeContextStroke(); 8162 curContext.closePath(); 8163 } 8164 } else if (curShape === PConstants.TRIANGLE_FAN) { 8165 if (vertArray.length > 2) { 8166 curContext.beginPath(); 8167 curContext.moveTo(vertArray[0][0], vertArray[0][1]); 8168 curContext.lineTo(vertArray[1][0], vertArray[1][1]); 8169 curContext.lineTo(vertArray[2][0], vertArray[2][1]); 8170 8171 if (doFill) { 8172 p.fill(vertArray[2][5]); 8173 executeContextFill(); 8174 } 8175 if (doStroke) { 8176 p.stroke(vertArray[2][6]); 8177 executeContextStroke(); 8178 } 8179 8180 curContext.closePath(); 8181 for (i = 3; i < vertArray.length; i++) { 8182 curContext.beginPath(); 8183 curContext.moveTo(vertArray[0][0], vertArray[0][1]); 8184 curContext.lineTo(vertArray[i-1][0], vertArray[i-1][1]); 8185 curContext.lineTo(vertArray[i][0], vertArray[i][1]); 8186 8187 if (doFill) { 8188 p.fill(vertArray[i][5]); 8189 executeContextFill(); 8190 } 8191 if (doStroke) { 8192 p.stroke(vertArray[i][6]); 8193 executeContextStroke(); 8194 } 8195 8196 curContext.closePath(); 8197 } 8198 } 8199 } else if (curShape === PConstants.QUADS) { 8200 for (i = 0; (i + 3) < vertArray.length; i+=4) { 8201 curContext.beginPath(); 8202 curContext.moveTo(vertArray[i][0], vertArray[i][1]); 8203 for (j = 1; j < 4; j++) { 8204 curContext.lineTo(vertArray[i+j][0], vertArray[i+j][1]); 8205 } 8206 curContext.lineTo(vertArray[i][0], vertArray[i][1]); 8207 8208 if (doFill) { 8209 p.fill(vertArray[i+3][5]); 8210 executeContextFill(); 8211 } 8212 if (doStroke) { 8213 p.stroke(vertArray[i+3][6]); 8214 executeContextStroke(); 8215 } 8216 8217 curContext.closePath(); 8218 } 8219 } else if (curShape === PConstants.QUAD_STRIP) { 8220 if (vertArray.length > 3) { 8221 for (i = 0; (i+1) < vertArray.length; i+=2) { 8222 curContext.beginPath(); 8223 if (i+3 < vertArray.length) { 8224 curContext.moveTo(vertArray[i+2][0], vertArray[i+2][1]); 8225 curContext.lineTo(vertArray[i][0], vertArray[i][1]); 8226 curContext.lineTo(vertArray[i+1][0], vertArray[i+1][1]); 8227 curContext.lineTo(vertArray[i+3][0], vertArray[i+3][1]); 8228 8229 if (doFill) { 8230 p.fill(vertArray[i+3][5]); 8231 } 8232 if (doStroke) { 8233 p.stroke(vertArray[i+3][6]); 8234 } 8235 } else { 8236 curContext.moveTo(vertArray[i][0], vertArray[i][1]); 8237 curContext.lineTo(vertArray[i+1][0], vertArray[i+1][1]); 8238 } 8239 executeContextFill(); 8240 executeContextStroke(); 8241 curContext.closePath(); 8242 } 8243 } 8244 } else { 8245 curContext.beginPath(); 8246 curContext.moveTo(vertArray[0][0], vertArray[0][1]); 8247 for (i = 1; i < vertArray.length; i++) { 8248 if (vertArray[i]["isVert"] === true ) { //if it is a vertex move to the position 8249 if (vertArray[i]["moveTo"] === true) { 8250 curContext.moveTo(vertArray[i][0], vertArray[i][1]); 8251 } else if (vertArray[i]["moveTo"] === false){ 8252 curContext.lineTo(vertArray[i][0], vertArray[i][1]); 8253 } else { 8254 curContext.lineTo(vertArray[i][0], vertArray[i][1]); 8255 } 8256 } 8257 } 8258 if (closeShape) { 8259 curContext.lineTo(vertArray[0][0], vertArray[0][1]); 8260 } 8261 executeContextFill(); 8262 executeContextStroke(); 8263 curContext.closePath(); 8264 } 8265 } 8266 } 8267 isCurve = false; 8268 isBezier = false; 8269 curveVertArray = []; 8270 curveVertCount = 0; 8271 }; 8272 8273 //used by both curveDetail and bezierDetail 8274 var splineForward = function(segments, matrix) { 8275 var f = 1.0 / segments; 8276 var ff = f * f; 8277 var fff = ff * f; 8278 8279 matrix.set(0, 0, 0, 1, fff, ff, f, 0, 6 * fff, 2 * ff, 0, 0, 6 * fff, 0, 0, 0); 8280 }; 8281 8282 //internal curveInit 8283 //used by curveDetail, curveTightness 8284 var curveInit = function() { 8285 // allocate only if/when used to save startup time 8286 if (!curveDrawMatrix) { 8287 curveBasisMatrix = new PMatrix3D(); 8288 curveDrawMatrix = new PMatrix3D(); 8289 curveInited = true; 8290 } 8291 8292 var s = curTightness; 8293 curveBasisMatrix.set(((s - 1) / 2).toFixed(2), ((s + 3) / 2).toFixed(2), 8294 ((-3 - s) / 2).toFixed(2), ((1 - s) / 2).toFixed(2), 8295 (1 - s), ((-5 - s) / 2).toFixed(2), (s + 2), ((s - 1) / 2).toFixed(2), 8296 ((s - 1) / 2).toFixed(2), 0, ((1 - s) / 2).toFixed(2), 0, 0, 1, 0, 0); 8297 8298 splineForward(curveDet, curveDrawMatrix); 8299 8300 if (!bezierBasisInverse) { 8301 //bezierBasisInverse = bezierBasisMatrix.get(); 8302 //bezierBasisInverse.invert(); 8303 curveToBezierMatrix = new PMatrix3D(); 8304 } 8305 8306 // TODO only needed for PGraphicsJava2D? if so, move it there 8307 // actually, it's generally useful for other renderers, so keep it 8308 // or hide the implementation elsewhere. 8309 curveToBezierMatrix.set(curveBasisMatrix); 8310 curveToBezierMatrix.preApply(bezierBasisInverse); 8311 8312 // multiply the basis and forward diff matrices together 8313 // saves much time since this needn't be done for each curve 8314 curveDrawMatrix.apply(curveBasisMatrix); 8315 }; 8316 8317 p.bezierVertex = function bezierVertex() { 8318 isBezier = true; 8319 var vert = []; 8320 if (firstVert) { 8321 throw ("vertex() must be used at least once before calling bezierVertex()"); 8322 } else { 8323 if (arguments.length === 9) { 8324 if (p.use3DContext) { 8325 if (bezierDrawMatrix === undef) { 8326 bezierDrawMatrix = new PMatrix3D(); 8327 } 8328 // setup matrix for forward differencing to speed up drawing 8329 var lastPoint = vertArray.length - 1; 8330 splineForward( bezDetail, bezierDrawMatrix ); 8331 bezierDrawMatrix.apply( bezierBasisMatrix ); 8332 var draw = bezierDrawMatrix.array(); 8333 var x1 = vertArray[lastPoint][0], 8334 y1 = vertArray[lastPoint][1], 8335 z1 = vertArray[lastPoint][2]; 8336 var xplot1 = draw[4] * x1 + draw[5] * arguments[0] + draw[6] * arguments[3] + draw[7] * arguments[6]; 8337 var xplot2 = draw[8] * x1 + draw[9] * arguments[0] + draw[10]* arguments[3] + draw[11]* arguments[6]; 8338 var xplot3 = draw[12]* x1 + draw[13]* arguments[0] + draw[14]* arguments[3] + draw[15]* arguments[6]; 8339 8340 var yplot1 = draw[4] * y1 + draw[5] * arguments[1] + draw[6] * arguments[4] + draw[7] * arguments[7]; 8341 var yplot2 = draw[8] * y1 + draw[9] * arguments[1] + draw[10]* arguments[4] + draw[11]* arguments[7]; 8342 var yplot3 = draw[12]* y1 + draw[13]* arguments[1] + draw[14]* arguments[4] + draw[15]* arguments[7]; 8343 8344 var zplot1 = draw[4] * z1 + draw[5] * arguments[2] + draw[6] * arguments[5] + draw[7] * arguments[8]; 8345 var zplot2 = draw[8] * z1 + draw[9] * arguments[2] + draw[10]* arguments[5] + draw[11]* arguments[8]; 8346 var zplot3 = draw[12]* z1 + draw[13]* arguments[2] + draw[14]* arguments[5] + draw[15]* arguments[8]; 8347 for (var j = 0; j < bezDetail; j++) { 8348 x1 += xplot1; xplot1 += xplot2; xplot2 += xplot3; 8349 y1 += yplot1; yplot1 += yplot2; yplot2 += yplot3; 8350 z1 += zplot1; zplot1 += zplot2; zplot2 += zplot3; 8351 p.vertex(x1, y1, z1); 8352 } 8353 p.vertex(arguments[6], arguments[7], arguments[8]); 8354 } 8355 } else { 8356 for (var i = 0; i < arguments.length; i++) { 8357 vert[i] = arguments[i]; 8358 } 8359 vertArray.push(vert); 8360 vertArray[vertArray.length -1]["isVert"] = false; 8361 } 8362 } 8363 }; 8364 8365 // texImage2D function changed http://www.khronos.org/webgl/public-mailing-list/archives/1007/msg00034.html 8366 // This method tries the new argument pattern first and falls back to the old version 8367 var executeTexImage2D = function () { 8368 var canvas2d = document.createElement('canvas'); 8369 8370 try { // new way. 8371 curContext.texImage2D(curContext.TEXTURE_2D, 0, curContext.RGBA, curContext.RGBA, curContext.UNSIGNED_BYTE, canvas2d); 8372 executeTexImage2D = function(texture) { 8373 curContext.texImage2D(curContext.TEXTURE_2D, 0, curContext.RGBA, curContext.RGBA, curContext.UNSIGNED_BYTE, texture); 8374 }; 8375 } catch (e) { 8376 executeTexImage2D = function(texture) { 8377 curContext.texImage2D(curContext.TEXTURE_2D, 0, texture, false); 8378 }; 8379 } 8380 8381 executeTexImage2D.apply(this, arguments); 8382 }; 8383 8384 p.texture = function(pimage) { 8385 if (pimage.localName === "canvas") { 8386 curContext.bindTexture(curContext.TEXTURE_2D, canTex); 8387 executeTexImage2D(pimage); 8388 curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_MAG_FILTER, curContext.LINEAR); 8389 curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_MIN_FILTER, curContext.LINEAR); 8390 curContext.generateMipmap(curContext.TEXTURE_2D); 8391 } else if (!pimage.__texture) { 8392 var texture = curContext.createTexture(); 8393 pimage.__texture = texture; 8394 8395 var cvs = document.createElement('canvas'); 8396 cvs.width = pimage.width; 8397 cvs.height = pimage.height; 8398 var ctx = cvs.getContext('2d'); 8399 var textureImage = ctx.createImageData(cvs.width, cvs.height); 8400 8401 var imgData = pimage.toImageData(); 8402 8403 for (var i = 0; i < cvs.width; i += 1) { 8404 for (var j = 0; j < cvs.height; j += 1) { 8405 var index = (j * cvs.width + i) * 4; 8406 textureImage.data[index + 0] = imgData.data[index + 0]; 8407 textureImage.data[index + 1] = imgData.data[index + 1]; 8408 textureImage.data[index + 2] = imgData.data[index + 2]; 8409 textureImage.data[index + 3] = 255; 8410 } 8411 } 8412 8413 ctx.putImageData(textureImage, 0, 0); 8414 pimage.__cvs = cvs; 8415 8416 curContext.bindTexture(curContext.TEXTURE_2D, pimage.__texture); 8417 curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_MIN_FILTER, curContext.LINEAR_MIPMAP_LINEAR); 8418 curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_MAG_FILTER, curContext.LINEAR); 8419 curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_WRAP_T, curContext.CLAMP_TO_EDGE); 8420 curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_WRAP_S, curContext.CLAMP_TO_EDGE); 8421 executeTexImage2D(pimage.__cvs); 8422 curContext.generateMipmap(curContext.TEXTURE_2D); 8423 } else { 8424 curContext.bindTexture(curContext.TEXTURE_2D, pimage.__texture); 8425 } 8426 8427 curTexture.width = pimage.width; 8428 curTexture.height = pimage.height; 8429 usingTexture = true; 8430 curContext.useProgram(programObject3D); 8431 uniformi(programObject3D, "usingTexture", usingTexture); 8432 }; 8433 8434 p.textureMode = function(mode){ 8435 curTextureMode = mode; 8436 }; 8437 8438 var curveVertexSegment = function(x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4) { 8439 var x0 = x2; 8440 var y0 = y2; 8441 var z0 = z2; 8442 8443 var draw = curveDrawMatrix.array(); 8444 8445 var xplot1 = draw[4] * x1 + draw[5] * x2 + draw[6] * x3 + draw[7] * x4; 8446 var xplot2 = draw[8] * x1 + draw[9] * x2 + draw[10] * x3 + draw[11] * x4; 8447 var xplot3 = draw[12] * x1 + draw[13] * x2 + draw[14] * x3 + draw[15] * x4; 8448 8449 var yplot1 = draw[4] * y1 + draw[5] * y2 + draw[6] * y3 + draw[7] * y4; 8450 var yplot2 = draw[8] * y1 + draw[9] * y2 + draw[10] * y3 + draw[11] * y4; 8451 var yplot3 = draw[12] * y1 + draw[13] * y2 + draw[14] * y3 + draw[15] * y4; 8452 8453 var zplot1 = draw[4] * z1 + draw[5] * z2 + draw[6] * z3 + draw[7] * z4; 8454 var zplot2 = draw[8] * z1 + draw[9] * z2 + draw[10] * z3 + draw[11] * z4; 8455 var zplot3 = draw[12] * z1 + draw[13] * z2 + draw[14] * z3 + draw[15] * z4; 8456 8457 p.vertex(x0, y0, z0); 8458 for (var j = 0; j < curveDet; j++) { 8459 x0 += xplot1; xplot1 += xplot2; xplot2 += xplot3; 8460 y0 += yplot1; yplot1 += yplot2; yplot2 += yplot3; 8461 z0 += zplot1; zplot1 += zplot2; zplot2 += zplot3; 8462 p.vertex(x0, y0, z0); 8463 } 8464 }; 8465 8466 p.curveVertex = function(x, y, z) { 8467 isCurve = true; 8468 if(p.use3DContext){ 8469 if (!curveInited){ 8470 curveInit(); 8471 } 8472 var vert = []; 8473 vert[0] = x; 8474 vert[1] = y; 8475 vert[2] = z; 8476 curveVertArray.push(vert); 8477 curveVertCount++; 8478 8479 if (curveVertCount > 3){ 8480 curveVertexSegment( curveVertArray[curveVertCount-4][0], 8481 curveVertArray[curveVertCount-4][1], 8482 curveVertArray[curveVertCount-4][2], 8483 curveVertArray[curveVertCount-3][0], 8484 curveVertArray[curveVertCount-3][1], 8485 curveVertArray[curveVertCount-3][2], 8486 curveVertArray[curveVertCount-2][0], 8487 curveVertArray[curveVertCount-2][1], 8488 curveVertArray[curveVertCount-2][2], 8489 curveVertArray[curveVertCount-1][0], 8490 curveVertArray[curveVertCount-1][1], 8491 curveVertArray[curveVertCount-1][2] ); 8492 } 8493 } 8494 else{ 8495 p.vertex(x, y, z); 8496 } 8497 }; 8498 8499 p.curve = function curve() { 8500 if (arguments.length === 8) // curve(x1, y1, x2, y2, x3, y3, x4, y4) 8501 { 8502 p.beginShape(); 8503 p.curveVertex(arguments[0], arguments[1]); 8504 p.curveVertex(arguments[2], arguments[3]); 8505 p.curveVertex(arguments[4], arguments[5]); 8506 p.curveVertex(arguments[6], arguments[7]); 8507 p.endShape(); 8508 } else { // curve( x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4); 8509 if (p.use3DContext) { 8510 p.beginShape(); 8511 p.curveVertex(arguments[0], arguments[1], arguments[2]); 8512 p.curveVertex(arguments[3], arguments[4], arguments[5]); 8513 p.curveVertex(arguments[6], arguments[7], arguments[8]); 8514 p.curveVertex(arguments[9], arguments[10], arguments[11]); 8515 p.endShape(); 8516 } 8517 } 8518 }; 8519 8520 p.curveTightness = function(tightness) { 8521 curTightness = tightness; 8522 }; 8523 8524 p.curveDetail = function curveDetail( detail ) { 8525 curveDet = detail; 8526 curveInit(); 8527 }; 8528 8529 p.rectMode = function rectMode(aRectMode) { 8530 curRectMode = aRectMode; 8531 }; 8532 8533 p.imageMode = function(mode) { 8534 switch (mode) { 8535 case PConstants.CORNER: 8536 imageModeConvert = imageModeCorner; 8537 break; 8538 case PConstants.CORNERS: 8539 imageModeConvert = imageModeCorners; 8540 break; 8541 case PConstants.CENTER: 8542 imageModeConvert = imageModeCenter; 8543 break; 8544 default: 8545 throw "Invalid imageMode"; 8546 } 8547 }; 8548 8549 p.ellipseMode = function ellipseMode(aEllipseMode) { 8550 curEllipseMode = aEllipseMode; 8551 }; 8552 8553 p.arc = function arc(x, y, width, height, start, stop) { 8554 if (width <= 0) { 8555 return; 8556 } 8557 8558 if (curEllipseMode === PConstants.CORNER) { 8559 x += width / 2; 8560 y += height / 2; 8561 } 8562 8563 curContext.moveTo(x, y); 8564 curContext.beginPath(); 8565 curContext.arc(x, y, curEllipseMode === PConstants.CENTER_RADIUS ? width : width / 2, start, stop, false); 8566 8567 executeContextStroke(); 8568 curContext.lineTo(x, y); 8569 8570 executeContextFill(); 8571 curContext.closePath(); 8572 }; 8573 8574 p.line = function line() { 8575 var x1, y1, z1, x2, y2, z2; 8576 8577 if (p.use3DContext) { 8578 if (arguments.length === 6) { 8579 x1 = arguments[0]; 8580 y1 = arguments[1]; 8581 z1 = arguments[2]; 8582 x2 = arguments[3]; 8583 y2 = arguments[4]; 8584 z2 = arguments[5]; 8585 } else if (arguments.length === 4) { 8586 x1 = arguments[0]; 8587 y1 = arguments[1]; 8588 z1 = 0; 8589 x2 = arguments[2]; 8590 y2 = arguments[3]; 8591 z2 = 0; 8592 } 8593 8594 var lineVerts = [x1, y1, z1, x2, y2, z2]; 8595 8596 var view = new PMatrix3D(); 8597 view.scale(1, -1, 1); 8598 view.apply(modelView.array()); 8599 view.transpose(); 8600 8601 var proj = new PMatrix3D(); 8602 proj.set(projection); 8603 proj.transpose(); 8604 8605 if (lineWidth > 0 && doStroke) { 8606 curContext.useProgram(programObject2D); 8607 8608 uniformMatrix(programObject2D, "model", false, [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1]); 8609 uniformMatrix(programObject2D, "view", false, view.array()); 8610 uniformMatrix(programObject2D, "projection", false, proj.array()); 8611 8612 uniformf(programObject2D, "color", strokeStyle); 8613 uniformi(programObject2D, "picktype", 0); 8614 8615 curContext.lineWidth(lineWidth); 8616 8617 vertexAttribPointer(programObject2D, "Vertex", 3, lineBuffer); 8618 disableVertexAttribPointer(programObject2D, "aTextureCoord"); 8619 8620 curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array(lineVerts), curContext.STREAM_DRAW); 8621 curContext.drawArrays(curContext.LINES, 0, 2); 8622 } 8623 } else { 8624 x1 = arguments[0]; 8625 y1 = arguments[1]; 8626 x2 = arguments[2]; 8627 y2 = arguments[3]; 8628 8629 // if line is parallel to axis and lineWidth is less than 1px, trying to do it "crisp" 8630 if ((x1 === x2 || y1 === y2) && lineWidth <= 1.0 && doStroke && curSketch.options.crispLines) { 8631 var temp; 8632 if(x1 === x2) { 8633 if(y1 > y2) { temp = y1; y1 = y2; y2 = temp; } 8634 for(var y=y1;y<=y2;++y) { 8635 p.set(x1, y, currentStrokeColor); 8636 } 8637 } else { 8638 if(x1 > x2) { temp = x1; x1 = x2; x2 = temp; } 8639 for(var x=x1;x<=x2;++x) { 8640 p.set(x, y1, currentStrokeColor); 8641 } 8642 } 8643 return; 8644 } 8645 8646 if (doStroke) { 8647 curContext.beginPath(); 8648 curContext.moveTo(x1 || 0, y1 || 0); 8649 curContext.lineTo(x2 || 0, y2 || 0); 8650 executeContextStroke(); 8651 curContext.closePath(); 8652 } 8653 } 8654 }; 8655 8656 p.bezier = function bezier() { 8657 if( arguments.length === 8 && !p.use3DContext ){ 8658 p.beginShape(); 8659 p.vertex( arguments[0], arguments[1] ); 8660 p.bezierVertex( arguments[2], arguments[3], 8661 arguments[4], arguments[5], 8662 arguments[6], arguments[7] ); 8663 p.endShape(); 8664 } 8665 else if( arguments.length === 12 && p.use3DContext ){ 8666 p.beginShape(); 8667 p.vertex( arguments[0], arguments[1], arguments[2] ); 8668 p.bezierVertex( arguments[3], arguments[4], arguments[5], 8669 arguments[6], arguments[7], arguments[8], 8670 arguments[9], arguments[10], arguments[11] ); 8671 p.endShape(); 8672 } 8673 else { 8674 throw("Please use the proper parameters!"); 8675 } 8676 }; 8677 p.bezierDetail = function bezierDetail( detail ){ 8678 bezDetail = detail; 8679 }; 8680 8681 p.bezierPoint = function bezierPoint(a, b, c, d, t) { 8682 return (1 - t) * (1 - t) * (1 - t) * a + 3 * (1 - t) * (1 - t) * t * b + 3 * (1 - t) * t * t * c + t * t * t * d; 8683 }; 8684 8685 p.bezierTangent = function bezierTangent(a, b, c, d, t) { 8686 return (3 * t * t * (-a + 3 * b - 3 * c + d) + 6 * t * (a - 2 * b + c) + 3 * (-a + b)); 8687 }; 8688 8689 p.curvePoint = function curvePoint(a, b, c, d, t) { 8690 return 0.5 * ((2 * b) + (-a + c) * t + (2 * a - 5 * b + 4 * c - d) * t * t + (-a + 3 * b - 3 * c + d) * t * t * t); 8691 }; 8692 8693 p.curveTangent = function curveTangent(a, b, c, d, t) { 8694 return 0.5 * ((-a + c) + 2 * (2 * a - 5 * b + 4 * c - d) * t + 3 * (-a + 3 * b - 3 * c + d) * t * t); 8695 }; 8696 8697 p.triangle = function triangle(x1, y1, x2, y2, x3, y3) { 8698 p.beginShape(PConstants.TRIANGLES); 8699 p.vertex(x1, y1, 0); 8700 p.vertex(x2, y2, 0); 8701 p.vertex(x3, y3, 0); 8702 p.endShape(); 8703 }; 8704 8705 p.quad = function quad(x1, y1, x2, y2, x3, y3, x4, y4) { 8706 p.beginShape(PConstants.QUADS); 8707 p.vertex(x1, y1, 0); 8708 p.vertex(x2, y2, 0); 8709 p.vertex(x3, y3, 0); 8710 p.vertex(x4, y4, 0); 8711 p.endShape(); 8712 }; 8713 8714 p.rect = function rect(x, y, width, height) { 8715 if (p.use3DContext) { 8716 // Modeling transformation 8717 var model = new PMatrix3D(); 8718 model.translate(x, y, 0); 8719 model.scale(width, height, 1); 8720 model.transpose(); 8721 8722 // viewing transformation needs to have Y flipped 8723 // becuase that's what Processing does. 8724 var view = new PMatrix3D(); 8725 view.scale(1, -1, 1); 8726 view.apply(modelView.array()); 8727 view.transpose(); 8728 8729 var proj = new PMatrix3D(); 8730 proj.set(projection); 8731 proj.transpose(); 8732 8733 if (lineWidth > 0 && doStroke) { 8734 curContext.useProgram(programObject2D); 8735 uniformMatrix(programObject2D, "model", false, model.array()); 8736 uniformMatrix(programObject2D, "view", false, view.array()); 8737 uniformMatrix(programObject2D, "projection", false, proj.array()); 8738 8739 uniformf(programObject2D, "color", strokeStyle); 8740 uniformi(programObject2D, "picktype", 0); 8741 8742 vertexAttribPointer(programObject2D, "Vertex", 3, rectBuffer); 8743 disableVertexAttribPointer(programObject2D, "aTextureCoord"); 8744 8745 curContext.lineWidth(lineWidth); 8746 curContext.drawArrays(curContext.LINE_LOOP, 0, rectVerts.length / 3); 8747 } 8748 8749 if (doFill) { 8750 curContext.useProgram(programObject3D); 8751 uniformMatrix(programObject3D, "model", false, model.array()); 8752 uniformMatrix(programObject3D, "view", false, view.array()); 8753 uniformMatrix(programObject3D, "projection", false, proj.array()); 8754 8755 // fix stitching problems. (lines get occluded by triangles 8756 // since they share the same depth values). This is not entirely 8757 // working, but it's a start for drawing the outline. So 8758 // developers can start playing around with styles. 8759 curContext.enable(curContext.POLYGON_OFFSET_FILL); 8760 curContext.polygonOffset(1, 1); 8761 8762 uniformf(programObject3D, "color", fillStyle); 8763 8764 var v = new PMatrix3D(); 8765 v.set(view); 8766 8767 var m = new PMatrix3D(); 8768 m.set(model); 8769 8770 v.mult(m); 8771 8772 var normalMatrix = new PMatrix3D(); 8773 normalMatrix.set(v); 8774 normalMatrix.invert(); 8775 normalMatrix.transpose(); 8776 8777 uniformMatrix(programObject3D, "normalTransform", false, normalMatrix.array()); 8778 8779 vertexAttribPointer(programObject3D, "Vertex", 3, rectBuffer); 8780 vertexAttribPointer(programObject3D, "Normal", 3, rectNormBuffer); 8781 8782 curContext.drawArrays(curContext.TRIANGLE_FAN, 0, rectVerts.length / 3); 8783 curContext.disable(curContext.POLYGON_OFFSET_FILL); 8784 } 8785 } 8786 else{ 8787 if (!width && !height) { 8788 return; 8789 } 8790 8791 // if only stroke is enabled, do it "crisp" 8792 if (doStroke && !doFill && lineWidth <= 1.0 && curSketch.options.crispLines) { 8793 var i, x2 = x + width - 1, y2 = y + height - 1; 8794 for(i=0;i<width;++i) { 8795 p.set(x + i, y, currentStrokeColor); 8796 p.set(x + i, y2, currentStrokeColor); 8797 } 8798 for(i=0;i<height;++i) { 8799 p.set(x, y + i, currentStrokeColor); 8800 p.set(x2, y + i, currentStrokeColor); 8801 } 8802 return; 8803 } 8804 8805 curContext.beginPath(); 8806 8807 var offsetStart = 0; 8808 var offsetEnd = 0; 8809 8810 if (curRectMode === PConstants.CORNERS) { 8811 width -= x; 8812 height -= y; 8813 } 8814 8815 if (curRectMode === PConstants.RADIUS) { 8816 width *= 2; 8817 height *= 2; 8818 } 8819 8820 if (curRectMode === PConstants.CENTER || curRectMode === PConstants.RADIUS) { 8821 x -= width / 2; 8822 y -= height / 2; 8823 } 8824 8825 curContext.rect( 8826 Math.round(x) - offsetStart, Math.round(y) - offsetStart, Math.round(width) + offsetEnd, Math.round(height) + offsetEnd); 8827 8828 executeContextFill(); 8829 executeContextStroke(); 8830 8831 curContext.closePath(); 8832 } 8833 }; 8834 8835 p.ellipse = function ellipse(x, y, width, height) { 8836 x = x || 0; 8837 y = y || 0; 8838 8839 if (width <= 0 && height <= 0) { 8840 return; 8841 } 8842 8843 if (curEllipseMode === PConstants.RADIUS) { 8844 width *= 2; 8845 height *= 2; 8846 } 8847 8848 if (curEllipseMode === PConstants.CORNERS) { 8849 width = width - x; 8850 height = height - y; 8851 } 8852 8853 if (curEllipseMode === PConstants.CORNER || curEllipseMode === PConstants.CORNERS) { 8854 x += width / 2; 8855 y += height / 2; 8856 } 8857 8858 var offsetStart = 0; 8859 8860 // Shortcut for drawing a 2D circle 8861 if ((!p.use3DContext) && (width === height)) { 8862 curContext.beginPath(); 8863 curContext.arc(x - offsetStart, y - offsetStart, width / 2, 0, PConstants.TWO_PI, false); 8864 executeContextFill(); 8865 executeContextStroke(); 8866 curContext.closePath(); 8867 } 8868 else { 8869 var w = width / 2, 8870 h = height / 2, 8871 C = 0.5522847498307933; 8872 var c_x = C * w, 8873 c_y = C * h; 8874 8875 if(!p.use3DContext){ 8876 // TODO: Audit 8877 p.beginShape(); 8878 p.vertex(x + w, y); 8879 p.bezierVertex(x + w, y - c_y, x + c_x, y - h, x, y - h); 8880 p.bezierVertex(x - c_x, y - h, x - w, y - c_y, x - w, y); 8881 p.bezierVertex(x - w, y + c_y, x - c_x, y + h, x, y + h); 8882 p.bezierVertex(x + c_x, y + h, x + w, y + c_y, x + w, y); 8883 p.endShape(); 8884 } 8885 else{ 8886 p.beginShape(); 8887 p.vertex(x + w, y); 8888 p.bezierVertex(x + w, y - c_y, 0, x + c_x, y - h, 0, x, y - h, 0); 8889 p.bezierVertex(x - c_x, y - h, 0, x - w, y - c_y, 0, x - w, y, 0); 8890 p.bezierVertex(x - w, y + c_y, 0, x - c_x, y + h, 0, x, y + h, 0); 8891 p.bezierVertex(x + c_x, y + h, 0, x + w, y + c_y, 0, x + w, y, 0); 8892 p.endShape(); 8893 8894 //temporary workaround to not working fills for bezier -- will fix later 8895 var xAv = 0, yAv = 0, i, j; 8896 for(i = 0; i < vertArray.length; i++){ 8897 xAv += vertArray[i][0]; 8898 yAv += vertArray[i][1]; 8899 } 8900 xAv /= vertArray.length; 8901 yAv /= vertArray.length; 8902 var vert = [], 8903 fillVertArray = [], 8904 colorVertArray = []; 8905 vert[0] = xAv; 8906 vert[1] = yAv; 8907 vert[2] = 0; 8908 vert[3] = 0; 8909 vert[4] = 0; 8910 vert[5] = fillStyle[0]; 8911 vert[6] = fillStyle[1]; 8912 vert[7] = fillStyle[2]; 8913 vert[8] = fillStyle[3]; 8914 vert[9] = strokeStyle[0]; 8915 vert[10] = strokeStyle[1]; 8916 vert[11] = strokeStyle[2]; 8917 vert[12] = strokeStyle[3]; 8918 vert[13] = normalX; 8919 vert[14] = normalY; 8920 vert[15] = normalZ; 8921 vertArray.unshift(vert); 8922 for(i = 0; i < vertArray.length; i++){ 8923 for(j = 0; j < 3; j++){ 8924 fillVertArray.push(vertArray[i][j]); 8925 } 8926 for(j = 5; j < 9; j++){ 8927 colorVertArray.push(vertArray[i][j]); 8928 } 8929 } 8930 fill3D(fillVertArray, "TRIANGLE_FAN", colorVertArray); 8931 } 8932 } 8933 }; 8934 8935 p.normal = function normal(nx, ny, nz) { 8936 if (arguments.length !== 3 || !(typeof nx === "number" && typeof ny === "number" && typeof nz === "number")) { 8937 throw "normal() requires three numeric arguments."; 8938 } 8939 8940 normalX = nx; 8941 normalY = ny; 8942 normalZ = nz; 8943 8944 if (curShape !== 0) { 8945 if (normalMode === PConstants.NORMAL_MODE_AUTO) { 8946 normalMode = PConstants.NORMAL_MODE_SHAPE; 8947 } else if (normalMode === PConstants.NORMAL_MODE_SHAPE) { 8948 normalMode = PConstants.NORMAL_MODE_VERTEX; 8949 } 8950 } 8951 }; 8952 8953 //////////////////////////////////////////////////////////////////////////// 8954 // Raster drawing functions 8955 //////////////////////////////////////////////////////////////////////////// 8956 8957 p.save = function save(file, img) { 8958 // file is unused at the moment 8959 // may implement this differently in later release 8960 if (img !== undef) { 8961 return window.open(img.toDataURL(),"_blank"); 8962 } else { 8963 return window.open(p.externals.canvas.toDataURL(),"_blank"); 8964 } 8965 }; 8966 8967 var utilityContext2d = document.createElement("canvas").getContext("2d"); 8968 8969 var canvasDataCache = [undef, undef, undef]; // we need three for now 8970 8971 function getCanvasData(obj, w, h) { 8972 var canvasData = canvasDataCache.shift(); 8973 8974 if (canvasData === undef) { 8975 canvasData = {}; 8976 canvasData.canvas = document.createElement("canvas"); 8977 canvasData.context = canvasData.canvas.getContext('2d'); 8978 } 8979 8980 canvasDataCache.push(canvasData); 8981 8982 var canvas = canvasData.canvas, context = canvasData.context, 8983 width = w || obj.width, height = h || obj.height; 8984 8985 canvas.width = width; 8986 canvas.height = height; 8987 8988 if (!obj) { 8989 context.clearRect(0, 0, width, height); 8990 } else if ("data" in obj) { // ImageData 8991 context.putImageData(obj, 0, 0); 8992 } else { 8993 context.clearRect(0, 0, width, height); 8994 context.drawImage(obj, 0, 0, width, height); 8995 } 8996 return canvasData; 8997 } 8998 8999 var PImage = function PImage(aWidth, aHeight, aFormat) { 9000 this.get = function(x, y, w, h) { 9001 if (!arguments.length) { 9002 return p.get(this); 9003 } else if (arguments.length === 2) { 9004 return p.get(x, y, this); 9005 } else if (arguments.length === 4) { 9006 return p.get(x, y, w, h, this); 9007 } 9008 }; 9009 9010 this.set = function(x, y, c) { 9011 p.set(x, y, c, this); 9012 }; 9013 9014 this.blend = function(srcImg, x, y, width, height, dx, dy, dwidth, dheight, MODE) { 9015 if (arguments.length === 9) { 9016 p.blend(this, srcImg, x, y, width, height, dx, dy, dwidth, dheight, this); 9017 } else if (arguments.length === 10) { 9018 p.blend(srcImg, x, y, width, height, dx, dy, dwidth, dheight, MODE, this); 9019 } 9020 }; 9021 9022 this.copy = function(srcImg, sx, sy, swidth, sheight, dx, dy, dwidth, dheight) { 9023 if (arguments.length === 8) { 9024 p.blend(this, srcImg, sx, sy, swidth, sheight, dx, dy, dwidth, PConstants.REPLACE, this); 9025 } else if (arguments.length === 9) { 9026 p.blend(srcImg, sx, sy, swidth, sheight, dx, dy, dwidth, dheight, PConstants.REPLACE, this); 9027 } 9028 }; 9029 9030 this.filter = function(mode, param) { 9031 if (arguments.length === 2) { 9032 p.filter(mode, param, this); 9033 } else if (arguments.length === 1) { 9034 // no param specified, send null to show its invalid 9035 p.filter(mode, null, this); 9036 } 9037 }; 9038 9039 this.save = function(file){ 9040 p.save(file,this); 9041 }; 9042 9043 this.resize = function(w, h) { 9044 if (this.isRemote) { // Remote images cannot access imageData 9045 throw "Image is loaded remotely. Cannot resize."; 9046 } else { 9047 if (this.width !== 0 || this.height !== 0) { 9048 // make aspect ratio if w or h is 0 9049 if (w === 0 && h !== 0) { 9050 w = this.width / this.height * h; 9051 } else if (h === 0 && w !== 0) { 9052 h = w / (this.width / this.height); 9053 } 9054 // put 'this.imageData' into a new canvas 9055 var canvas = getCanvasData(this.imageData).canvas; 9056 // pull imageData object out of canvas into ImageData object 9057 var imageData = getCanvasData(canvas, w, h).context.getImageData(0, 0, w, h); 9058 // set this as new pimage 9059 this.fromImageData(imageData); 9060 } 9061 } 9062 }; 9063 9064 this.mask = function(mask) { 9065 this.__mask = undef; 9066 9067 if (mask instanceof PImage) { 9068 if (mask.width === this.width && mask.height === this.height) { 9069 this.__mask = mask; 9070 } else { 9071 throw "mask must have the same dimensions as PImage."; 9072 } 9073 } else if (typeof mask === "object" && mask.constructor === Array) { // this is a pixel array 9074 // mask pixel array needs to be the same length as this.pixels 9075 // how do we update this for 0.9 this.imageData holding pixels ^^ 9076 // mask.constructor ? and this.pixels.length = this.imageData.data.length instead ? 9077 if (this.pixels.length === mask.length) { 9078 this.__mask = mask; 9079 } else { 9080 throw "mask array must be the same length as PImage pixels array."; 9081 } 9082 } 9083 }; 9084 9085 // handle the sketch code for pixels[] and pixels.length 9086 // parser code converts pixels[] to getPixels() 9087 // or setPixels(), .length becomes getLength() 9088 this.pixels = { 9089 getLength: (function(aImg) { 9090 if (aImg.isRemote) { // Remote images cannot access imageData 9091 throw "Image is loaded remotely. Cannot get length."; 9092 } else { 9093 return function() { 9094 return aImg.imageData.data.length ? aImg.imageData.data.length/4 : 0; 9095 }; 9096 } 9097 }(this)), 9098 getPixel: (function(aImg) { 9099 if (aImg.isRemote) { // Remote images cannot access imageData 9100 throw "Image is loaded remotely. Cannot get pixels."; 9101 } else { 9102 return function(i) { 9103 var offset = i*4; 9104 return p.color.toInt(aImg.imageData.data[offset], aImg.imageData.data[offset+1], 9105 aImg.imageData.data[offset+2], aImg.imageData.data[offset+3]); 9106 }; 9107 } 9108 }(this)), 9109 setPixel: (function(aImg) { 9110 if (aImg.isRemote) { // Remote images cannot access imageData 9111 throw "Image is loaded remotely. Cannot set pixel."; 9112 } else { 9113 return function(i,c) { 9114 var offset = i*4; 9115 aImg.imageData.data[offset+0] = (c & PConstants.RED_MASK) >>> 16; 9116 aImg.imageData.data[offset+1] = (c & PConstants.GREEN_MASK) >>> 8; 9117 aImg.imageData.data[offset+2] = (c & PConstants.BLUE_MASK); 9118 aImg.imageData.data[offset+3] = (c & PConstants.ALPHA_MASK) >>> 24; 9119 }; 9120 } 9121 }(this)), 9122 set: function(arr) { 9123 if (this.isRemote) { // Remote images cannot access imageData 9124 throw "Image is loaded remotely. Cannot set pixels."; 9125 } else { 9126 for (var i = 0, aL = arr.length; i < aL; i++) { 9127 this.setPixel(i, arr[i]); 9128 } 9129 } 9130 } 9131 }; 9132 9133 // These are intentionally left blank for PImages, we work live with pixels and draw as necessary 9134 this.loadPixels = function() {}; 9135 9136 this.updatePixels = function() {}; 9137 9138 this.toImageData = function() { 9139 if (this.isRemote) { // Remote images cannot access imageData, send source image instead 9140 return this.sourceImg; 9141 } else { 9142 var canvasData = getCanvasData(this.imageData); 9143 return canvasData.context.getImageData(0, 0, this.width, this.height); 9144 } 9145 }; 9146 9147 this.toDataURL = function() { 9148 if (this.isRemote) { // Remote images cannot access imageData 9149 throw "Image is loaded remotely. Cannot create dataURI."; 9150 } else { 9151 var canvasData = getCanvasData(this.imageData); 9152 return canvasData.canvas.toDataURL(); 9153 } 9154 }; 9155 9156 this.fromImageData = function(canvasImg) { 9157 this.width = canvasImg.width; 9158 this.height = canvasImg.height; 9159 this.imageData = canvasImg; 9160 // changed for 0.9 9161 this.format = PConstants.ARGB; 9162 }; 9163 9164 this.fromHTMLImageData = function(htmlImg) { 9165 // convert an <img> to a PImage 9166 var canvasData = getCanvasData(htmlImg); 9167 try { 9168 var imageData = canvasData.context.getImageData(0, 0, htmlImg.width, htmlImg.height); 9169 this.fromImageData(imageData); 9170 } catch(e) { 9171 if (htmlImg.width && htmlImg.height) { 9172 this.isRemote = true; 9173 this.width = htmlImg.width; 9174 this.height = htmlImg.height; 9175 } 9176 } 9177 this.sourceImg = htmlImg; 9178 }; 9179 9180 if (arguments.length === 1) { 9181 // convert an <img> to a PImage 9182 this.fromHTMLImageData(arguments[0]); 9183 } else if (arguments.length === 2 || arguments.length === 3) { 9184 this.width = aWidth || 1; 9185 this.height = aHeight || 1; 9186 this.imageData = utilityContext2d.createImageData(this.width, this.height); 9187 this.format = (aFormat === PConstants.ARGB || aFormat === PConstants.ALPHA) ? aFormat : PConstants.RGB; 9188 } else { 9189 this.width = 0; 9190 this.height = 0; 9191 this.imageData = utilityContext2d.createImageData(1, 1); 9192 this.format = PConstants.ARGB; 9193 } 9194 }; 9195 9196 p.PImage = PImage; 9197 9198 p.createImage = function createImage(w, h, mode) { 9199 return new PImage(w,h,mode); 9200 }; 9201 9202 // Loads an image for display. Type is an extension. Callback is fired on load. 9203 p.loadImage = function loadImage(file, type, callback) { 9204 // if type is specified add it with a . to file to make the filename 9205 if (type) { 9206 file = file + "." + type; 9207 } 9208 // if image is in the preloader cache return a new PImage 9209 if (curSketch.imageCache.images[file]) { 9210 return new PImage(curSketch.imageCache.images[file]); 9211 } 9212 // else aysnc load it 9213 else { 9214 var pimg = new PImage(0, 0, PConstants.ARGB); 9215 var img = document.createElement('img'); 9216 9217 pimg.sourceImg = img; 9218 9219 img.onload = (function(aImage, aPImage, aCallback) { 9220 var image = aImage; 9221 var pimg = aPImage; 9222 var callback = aCallback; 9223 return function() { 9224 // change the <img> object into a PImage now that its loaded 9225 pimg.fromHTMLImageData(image); 9226 pimg.loaded = true; 9227 if (callback) { 9228 callback(); 9229 } 9230 }; 9231 }(img, pimg, callback)); 9232 9233 img.src = file; // needs to be called after the img.onload function is declared or it wont work in opera 9234 return pimg; 9235 } 9236 }; 9237 9238 // async loading of large images, same functionality as loadImage above 9239 p.requestImage = p.loadImage; 9240 9241 function get$0() { 9242 //return a PImage of curContext 9243 var c = new PImage(p.width, p.height, PConstants.RGB); 9244 c.fromImageData(curContext.getImageData(0, 0, p.width, p.height)); 9245 return c; 9246 } 9247 function get$2(x,y) { 9248 var data; 9249 // return the color at x,y (int) of curContext 9250 // create a PImage object of size 1x1 and return the int of the pixels array element 0 9251 if (x < p.width && x >= 0 && y >= 0 && y < p.height) { 9252 if(isContextReplaced) { 9253 var offset = ((0|x) + p.width * (0|y))*4; 9254 data = p.imageData.data; 9255 return p.color.toInt(data[offset], data[offset+1], 9256 data[offset+2], data[offset+3]); 9257 } 9258 // x,y is inside canvas space 9259 data = curContext.getImageData(0|x, 0|y, 1, 1).data; 9260 // changed for 0.9 9261 return p.color.toInt(data[0], data[1], data[2], data[3]); 9262 } else { 9263 // x,y is outside image return transparent black 9264 return 0; 9265 } 9266 } 9267 function get$3(x,y,img) { 9268 if (img.isRemote) { // Remote images cannot access imageData 9269 throw "Image is loaded remotely. Cannot get x,y."; 9270 } else { 9271 // PImage.get(x,y) was called, return the color (int) at x,y of img 9272 // changed in 0.9 9273 var offset = y * img.width * 4 + (x * 4); 9274 return p.color.toInt(img.imageData.data[offset], 9275 img.imageData.data[offset + 1], 9276 img.imageData.data[offset + 2], 9277 img.imageData.data[offset + 3]); 9278 } 9279 } 9280 function get$4(x, y, w, h) { 9281 // return a PImage of w and h from cood x,y of curContext 9282 var c = new PImage(w, h, PConstants.RGB); 9283 c.fromImageData(curContext.getImageData(x, y, w, h)); 9284 return c; 9285 } 9286 function get$5(x, y, w, h, img) { 9287 if (img.isRemote) { // Remote images cannot access imageData 9288 throw "Image is loaded remotely. Cannot get x,y,w,h."; 9289 } else { 9290 // PImage.get(x,y,w,h) was called, return x,y,w,h PImage of img 9291 // changed for 0.9, offset start point needs to be *4 9292 var start = y * img.width * 4 + (x*4); 9293 var end = (y + h) * img.width * 4 + ((x + w) * 4); 9294 var c = new PImage(w, h, PConstants.RGB); 9295 for (var i = start, j = 0; i < end; i++, j++) { 9296 // changed in 0.9 9297 c.imageData.data[j] = img.imageData.data[i]; 9298 if ((j+1) % (w*4) === 0) { 9299 //completed one line, increment i by offset 9300 i += (img.width - w) * 4; 9301 } 9302 } 9303 return c; 9304 } 9305 } 9306 9307 // Gets a single pixel or block of pixels from the current Canvas Context or a PImage 9308 p.get = function get(x, y, w, h, img) { 9309 // for 0 2 and 4 arguments use curContext, otherwise PImage.get was called 9310 if (arguments.length === 2) { 9311 return get$2(x, y); 9312 } else if (arguments.length === 0) { 9313 return get$0(); 9314 } else if (arguments.length === 5) { 9315 return get$5(x, y, w, h, img); 9316 } else if (arguments.length === 4) { 9317 return get$4(x, y, w, h); 9318 } else if (arguments.length === 3) { 9319 return get$3(x, y, w); 9320 } else if (arguments.length === 1) { 9321 // PImage.get() was called, return the PImage 9322 return x; 9323 } 9324 }; 9325 9326 // Creates a new Processing instance and passes it back for... processing 9327 p.createGraphics = function createGraphics(w, h, render) { 9328 var canvas = document.createElement("canvas"); 9329 var pg = new Processing(canvas); 9330 pg.size(w, h, render); 9331 pg.canvas = canvas; 9332 //Processing.addInstance(pg); // TODO: this function does not exist in this scope 9333 return pg; 9334 }; 9335 9336 // pixels caching 9337 function resetContext() { 9338 if(isContextReplaced) { 9339 curContext = originalContext; 9340 isContextReplaced = false; 9341 9342 p.updatePixels(); 9343 } 9344 } 9345 function SetPixelContextWrapper() { 9346 function wrapFunction(newContext, name) { 9347 function wrapper() { 9348 resetContext(); 9349 curContext[name].apply(curContext, arguments); 9350 } 9351 newContext[name] = wrapper; 9352 } 9353 function wrapProperty(newContext, name) { 9354 function getter() { 9355 resetContext(); 9356 return curContext[name]; 9357 } 9358 function setter(value) { 9359 resetContext(); 9360 curContext[name] = value; 9361 } 9362 p.defineProperty(newContext, name, { get: getter, set: setter }); 9363 } 9364 for(var n in curContext) { 9365 if(typeof curContext[n] === 'function') { 9366 wrapFunction(this, n); 9367 } else { 9368 wrapProperty(this, n); 9369 } 9370 } 9371 } 9372 function replaceContext() { 9373 if(isContextReplaced) { 9374 return; 9375 } 9376 p.loadPixels(); 9377 if(proxyContext === null) { 9378 originalContext = curContext; 9379 proxyContext = new SetPixelContextWrapper(); 9380 } 9381 isContextReplaced = true; 9382 curContext = proxyContext; 9383 setPixelsCached = 0; 9384 } 9385 9386 function set$3(x, y, c) { 9387 if (x < p.width && x >= 0 && y >= 0 && y < p.height) { 9388 replaceContext(); 9389 p.pixels.setPixel((0|x)+p.width*(0|y), c); 9390 if(++setPixelsCached > maxPixelsCached) { 9391 resetContext(); 9392 } 9393 } 9394 } 9395 function set$4(x, y, obj, img) { 9396 if (img.isRemote) { // Remote images cannot access imageData 9397 throw "Image is loaded remotely. Cannot set x,y."; 9398 } else { 9399 var c = p.color.toArray(obj); 9400 var offset = y * img.width * 4 + (x*4); 9401 var data = img.imageData.data; 9402 data[offset] = c[0]; 9403 data[offset+1] = c[1]; 9404 data[offset+2] = c[2]; 9405 data[offset+3] = c[3]; 9406 } 9407 } 9408 // Paints a pixel array into the canvas 9409 p.set = function set(x, y, obj, img) { 9410 var color, oldFill; 9411 if (arguments.length === 3) { 9412 // called p.set(), was it with a color or a img ? 9413 if (typeof obj === "number") { 9414 set$3(x, y, obj); 9415 } else if (obj instanceof PImage) { 9416 p.image(obj, x, y); 9417 } 9418 } else if (arguments.length === 4) { 9419 // PImage.set(x,y,c) was called, set coordinate x,y color to c of img 9420 set$4(x, y, obj, img); 9421 } 9422 }; 9423 p.imageData = {}; 9424 9425 // handle the sketch code for pixels[] 9426 // parser code converts pixels[] to getPixels() 9427 // or setPixels(), .length becomes getLength() 9428 p.pixels = { 9429 getLength: function() { return p.imageData.data.length ? p.imageData.data.length/4 : 0; }, 9430 getPixel: function(i) { 9431 var offset = i*4; 9432 return (p.imageData.data[offset+3] << 24) & 0xff000000 | 9433 (p.imageData.data[offset+0] << 16) & 0x00ff0000 | 9434 (p.imageData.data[offset+1] << 8) & 0x0000ff00 | 9435 p.imageData.data[offset+2] & 0x000000ff; 9436 }, 9437 setPixel: function(i,c) { 9438 var offset = i*4; 9439 p.imageData.data[offset+0] = (c & 0x00ff0000) >>> 16; // RED_MASK 9440 p.imageData.data[offset+1] = (c & 0x0000ff00) >>> 8; // GREEN_MASK 9441 p.imageData.data[offset+2] = (c & 0x000000ff); // BLUE_MASK 9442 p.imageData.data[offset+3] = (c & 0xff000000) >>> 24; // ALPHA_MASK 9443 }, 9444 set: function(arr) { 9445 for (var i = 0, aL = arr.length; i < aL; i++) { 9446 this.setPixel(i, arr[i]); 9447 } 9448 } 9449 }; 9450 9451 // Gets a 1-Dimensional pixel array from Canvas 9452 p.loadPixels = function() { 9453 // changed in 0.9 9454 p.imageData = curContext.getImageData(0, 0, p.width, p.height); 9455 }; 9456 9457 // Draws a 1-Dimensional pixel array to Canvas 9458 p.updatePixels = function() { 9459 // changed in 0.9 9460 if (p.imageData) { 9461 curContext.putImageData(p.imageData, 0, 0); 9462 } 9463 }; 9464 9465 p.hint = function hint(which) { 9466 if (which === PConstants.DISABLE_DEPTH_TEST) { 9467 curContext.disable(curContext.DEPTH_TEST); 9468 curContext.depthMask(false); 9469 curContext.clear(curContext.DEPTH_BUFFER_BIT); 9470 } 9471 else if (which === PConstants.ENABLE_DEPTH_TEST) { 9472 curContext.enable(curContext.DEPTH_TEST); 9473 curContext.depthMask(true); 9474 } 9475 }; 9476 9477 // Draw an image or a color to the background 9478 p.background = function background() { 9479 var color, a, img; 9480 // background params are either a color or a PImage 9481 if (typeof arguments[0] === 'number') { 9482 color = p.color.apply(this, arguments); 9483 9484 // override alpha value, processing ignores the alpha for background color 9485 if (!curSketch.options.isTransparent) { 9486 color = color | PConstants.ALPHA_MASK; 9487 } 9488 } else if (arguments.length === 1 && arguments[0] instanceof PImage) { 9489 img = arguments[0]; 9490 9491 if (!img.pixels || img.width !== p.width || img.height !== p.height) { 9492 throw "Background image must be the same dimensions as the canvas."; 9493 } 9494 } else { 9495 throw "Incorrect background parameters."; 9496 } 9497 9498 if (p.use3DContext) { 9499 if (color !== undef) { 9500 var c = p.color.toGLArray(color); 9501 refreshBackground = function() { 9502 curContext.clearColor(c[0], c[1], c[2], c[3]); 9503 curContext.clear(curContext.COLOR_BUFFER_BIT | curContext.DEPTH_BUFFER_BIT); 9504 }; 9505 } else { 9506 // Handle image background for 3d context. not done yet. 9507 refreshBackground = function() {}; 9508 } 9509 } else { // 2d context 9510 if (color !== undef) { 9511 refreshBackground = function() { 9512 if (curSketch.options.isTransparent) { 9513 curContext.clearRect(0,0, p.width, p.height); 9514 } 9515 curContext.fillStyle = p.color.toString(color); 9516 curContext.fillRect(0, 0, p.width, p.height); 9517 isFillDirty = true; 9518 }; 9519 } else { 9520 refreshBackground = function() { 9521 p.image(img, 0, 0); 9522 }; 9523 } 9524 } 9525 refreshBackground(); 9526 }; 9527 9528 // Draws an image to the Canvas 9529 p.image = function image(img, x, y, w, h) { 9530 if (img.width > 0) { 9531 var wid = w || img.width; 9532 var hgt = h || img.height; 9533 if (p.use3DContext) { 9534 p.beginShape(p.QUADS); 9535 p.texture(img.externals.canvas); 9536 p.vertex(x, y, 0, 0, 0); 9537 p.vertex(x, y+hgt, 0, 0, hgt); 9538 p.vertex(x+wid, y+hgt, 0, wid, hgt); 9539 p.vertex(x+wid, y, 0, wid, 0); 9540 p.endShape(); 9541 } else { 9542 var bounds = imageModeConvert(x || 0, y || 0, w || img.width, h || img.height, arguments.length < 4); 9543 var obj = img.toImageData(); 9544 9545 if (img.__mask) { 9546 var j, size; 9547 if (img.__mask instanceof PImage) { 9548 var objMask = img.__mask.toImageData(); 9549 for (j = 2, size = img.width * img.height * 4; j < size; j += 4) { 9550 // using it as an alpha channel 9551 obj.data[j + 1] = objMask.data[j]; 9552 // but only the blue color channel 9553 } 9554 } else { 9555 for (j = 0, size = img.__mask.length; j < size; ++j) { 9556 obj.data[(j << 2) + 3] = img.__mask[j]; 9557 } 9558 } 9559 } 9560 9561 // draw the image 9562 curTint(obj); 9563 9564 curContext.drawImage(getCanvasData(obj).canvas, 0, 0, img.width, img.height, bounds.x, bounds.y, bounds.w, bounds.h); 9565 } 9566 } 9567 }; 9568 9569 // Clears a rectangle in the Canvas element or the whole Canvas 9570 p.clear = function clear(x, y, width, height) { 9571 if (arguments.length === 0) { 9572 curContext.clearRect(0, 0, p.width, p.height); 9573 } else { 9574 curContext.clearRect(x, y, width, height); 9575 } 9576 }; 9577 9578 p.tint = function tint() { 9579 var tintColor = p.color.apply(this, arguments); 9580 var r = p.red(tintColor) / colorModeX; 9581 var g = p.green(tintColor) / colorModeY; 9582 var b = p.blue(tintColor) / colorModeZ; 9583 var a = p.alpha(tintColor) / colorModeA; 9584 9585 curTint = function(obj) { 9586 var data = obj.data, 9587 length = 4 * obj.width * obj.height; 9588 for (var i = 0; i < length;) { 9589 data[i++] *= r; 9590 data[i++] *= g; 9591 data[i++] *= b; 9592 data[i++] *= a; 9593 } 9594 }; 9595 }; 9596 9597 p.noTint = function noTint() { 9598 curTint = function() {}; 9599 }; 9600 9601 p.copy = function copy(src, sx, sy, sw, sh, dx, dy, dw, dh) { 9602 if (arguments.length === 8) { 9603 // shift everything, and introduce p 9604 dh = dw; 9605 dw = dy; 9606 dy = dx; 9607 dx = sh; 9608 sh = sw; 9609 sw = sy; 9610 sy = sx; 9611 sx = src; 9612 src = p; 9613 } 9614 p.blend(src, sx, sy, sw, sh, dx, dy, dw, dh, PConstants.REPLACE); 9615 }; 9616 9617 p.blend = function blend(src, sx, sy, sw, sh, dx, dy, dw, dh, mode, pimgdest) { 9618 if (arguments.length === 9) { 9619 // shift everything, and introduce p 9620 mode = dh; 9621 dh = dw; 9622 dw = dy; 9623 dy = dx; 9624 dx = sh; 9625 sh = sw; 9626 sw = sy; 9627 sy = sx; 9628 sx = src; 9629 src = p; 9630 } 9631 9632 var sx2 = sx + sw; 9633 var sy2 = sy + sh; 9634 var dx2 = dx + dw; 9635 var dy2 = dy + dh; 9636 var dest; 9637 if (src.isRemote) { // Remote images cannot access imageData 9638 throw "Image is loaded remotely. Cannot blend image."; 9639 } else { 9640 // check if pimgdest is there and pixels, if so this was a call from pimg.blend 9641 if (arguments.length === 10 || arguments.length === 9) { 9642 p.loadPixels(); 9643 dest = p; 9644 } else if (arguments.length === 11 && pimgdest && pimgdest.imageData) { 9645 dest = pimgdest; 9646 } 9647 if (src === p) { 9648 if (p.intersect(sx, sy, sx2, sy2, dx, dy, dx2, dy2)) { 9649 p.blit_resize(p.get(sx, sy, sx2 - sx, sy2 - sy), 0, 0, sx2 - sx - 1, sy2 - sy - 1, 9650 dest.imageData.data, dest.width, dest.height, dx, dy, dx2, dy2, mode); 9651 } else { 9652 // same as below, except skip the loadPixels() because it'd be redundant 9653 p.blit_resize(src, sx, sy, sx2, sy2, dest.imageData.data, dest.width, dest.height, dx, dy, dx2, dy2, mode); 9654 } 9655 } else { 9656 src.loadPixels(); 9657 p.blit_resize(src, sx, sy, sx2, sy2, dest.imageData.data, dest.width, dest.height, dx, dy, dx2, dy2, mode); 9658 } 9659 if (arguments.length === 10) { 9660 p.updatePixels(); 9661 } 9662 } 9663 }; 9664 9665 // helper function for filter() 9666 var buildBlurKernel = function buildBlurKernel(r) { 9667 var radius = p.floor(r * 3.5), i, radiusi; 9668 radius = (radius < 1) ? 1 : ((radius < 248) ? radius : 248); 9669 if (p.shared.blurRadius !== radius) { 9670 p.shared.blurRadius = radius; 9671 p.shared.blurKernelSize = 1 + (p.shared.blurRadius<<1); 9672 p.shared.blurKernel = new Array(p.shared.blurKernelSize); 9673 // init blurKernel 9674 for (i = 0; i < p.shared.blurKernelSize; i++) { 9675 p.shared.blurKernel[i] = 0; 9676 } 9677 9678 for (i = 1, radiusi = radius - 1; i < radius; i++) { 9679 p.shared.blurKernel[radius+i] = p.shared.blurKernel[radiusi] = radiusi * radiusi; 9680 } 9681 p.shared.blurKernel[radius] = radius * radius; 9682 } 9683 }; 9684 9685 var blurARGB = function blurARGB(r, aImg) { 9686 var sum, cr, cg, cb, ca, c, m; 9687 var read, ri, ym, ymi, bk0; 9688 var wh = aImg.pixels.getLength(); 9689 var r2 = new Array(wh); 9690 var g2 = new Array(wh); 9691 var b2 = new Array(wh); 9692 var a2 = new Array(wh); 9693 var yi = 0; 9694 var x, y, i; 9695 9696 buildBlurKernel(r); 9697 9698 for (y = 0; y < aImg.height; y++) { 9699 for (x = 0; x < aImg.width; x++) { 9700 cb = cg = cr = ca = sum = 0; 9701 read = x - p.shared.blurRadius; 9702 if (read<0) { 9703 bk0 = -read; 9704 read = 0; 9705 } else { 9706 if (read >= aImg.width) { 9707 break; 9708 } 9709 bk0=0; 9710 } 9711 for (i = bk0; i < p.shared.blurKernelSize; i++) { 9712 if (read >= aImg.width) { 9713 break; 9714 } 9715 c = aImg.pixels.getPixel(read + yi); 9716 m = p.shared.blurKernel[i]; 9717 ca += m * ((c & PConstants.ALPHA_MASK) >>> 24); 9718 cr += m * ((c & PConstants.RED_MASK) >> 16); 9719 cg += m * ((c & PConstants.GREEN_MASK) >> 8); 9720 cb += m * (c & PConstants.BLUE_MASK); 9721 sum += m; 9722 read++; 9723 } 9724 ri = yi + x; 9725 a2[ri] = ca / sum; 9726 r2[ri] = cr / sum; 9727 g2[ri] = cg / sum; 9728 b2[ri] = cb / sum; 9729 } 9730 yi += aImg.width; 9731 } 9732 9733 yi = 0; 9734 ym = -p.shared.blurRadius; 9735 ymi = ym*aImg.width; 9736 9737 for (y = 0; y < aImg.height; y++) { 9738 for (x = 0; x < aImg.width; x++) { 9739 cb = cg = cr = ca = sum = 0; 9740 if (ym<0) { 9741 bk0 = ri = -ym; 9742 read = x; 9743 } else { 9744 if (ym >= aImg.height) { 9745 break; 9746 } 9747 bk0 = 0; 9748 ri = ym; 9749 read = x + ymi; 9750 } 9751 for (i = bk0; i < p.shared.blurKernelSize; i++) { 9752 if (ri >= aImg.height) { 9753 break; 9754 } 9755 m = p.shared.blurKernel[i]; 9756 ca += m * a2[read]; 9757 cr += m * r2[read]; 9758 cg += m * g2[read]; 9759 cb += m * b2[read]; 9760 sum += m; 9761 ri++; 9762 read += aImg.width; 9763 } 9764 aImg.pixels.setPixel(x+yi, ((ca/sum)<<24 | (cr/sum)<<16 | (cg/sum)<<8 | (cb/sum))); 9765 } 9766 yi += aImg.width; 9767 ymi += aImg.width; 9768 ym++; 9769 } 9770 }; 9771 9772 // helper funtion for ERODE and DILATE modes of filter() 9773 var dilate = function dilate(isInverted, aImg) { 9774 var currIdx = 0; 9775 var maxIdx = aImg.pixels.getLength(); 9776 var out = new Array(maxIdx); 9777 var currRowIdx, maxRowIdx, colOrig, colOut, currLum; 9778 var idxRight, idxLeft, idxUp, idxDown, 9779 colRight, colLeft, colUp, colDown, 9780 lumRight, lumLeft, lumUp, lumDown; 9781 9782 if (!isInverted) { 9783 // erosion (grow light areas) 9784 while (currIdx<maxIdx) { 9785 currRowIdx = currIdx; 9786 maxRowIdx = currIdx + aImg.width; 9787 while (currIdx < maxRowIdx) { 9788 colOrig = colOut = aImg.pixels.getPixel(currIdx); 9789 idxLeft = currIdx - 1; 9790 idxRight = currIdx + 1; 9791 idxUp = currIdx - aImg.width; 9792 idxDown = currIdx + aImg.width; 9793 if (idxLeft < currRowIdx) { 9794 idxLeft = currIdx; 9795 } 9796 if (idxRight >= maxRowIdx) { 9797 idxRight = currIdx; 9798 } 9799 if (idxUp < 0) { 9800 idxUp = 0; 9801 } 9802 if (idxDown >= maxIdx) { 9803 idxDown = currIdx; 9804 } 9805 colUp = aImg.pixels.getPixel(idxUp); 9806 colLeft = aImg.pixels.getPixel(idxLeft); 9807 colDown = aImg.pixels.getPixel(idxDown); 9808 colRight = aImg.pixels.getPixel(idxRight); 9809 9810 // compute luminance 9811 currLum = 77*(colOrig>>16&0xff) + 151*(colOrig>>8&0xff) + 28*(colOrig&0xff); 9812 lumLeft = 77*(colLeft>>16&0xff) + 151*(colLeft>>8&0xff) + 28*(colLeft&0xff); 9813 lumRight = 77*(colRight>>16&0xff) + 151*(colRight>>8&0xff) + 28*(colRight&0xff); 9814 lumUp = 77*(colUp>>16&0xff) + 151*(colUp>>8&0xff) + 28*(colUp&0xff); 9815 lumDown = 77*(colDown>>16&0xff) + 151*(colDown>>8&0xff) + 28*(colDown&0xff); 9816 9817 if (lumLeft > currLum) { 9818 colOut = colLeft; 9819 currLum = lumLeft; 9820 } 9821 if (lumRight > currLum) { 9822 colOut = colRight; 9823 currLum = lumRight; 9824 } 9825 if (lumUp > currLum) { 9826 colOut = colUp; 9827 currLum = lumUp; 9828 } 9829 if (lumDown > currLum) { 9830 colOut = colDown; 9831 currLum = lumDown; 9832 } 9833 out[currIdx++] = colOut; 9834 } 9835 } 9836 } else { 9837 // dilate (grow dark areas) 9838 while (currIdx < maxIdx) { 9839 currRowIdx = currIdx; 9840 maxRowIdx = currIdx + aImg.width; 9841 while (currIdx < maxRowIdx) { 9842 colOrig = colOut = aImg.pixels.getPixel(currIdx); 9843 idxLeft = currIdx - 1; 9844 idxRight = currIdx + 1; 9845 idxUp = currIdx - aImg.width; 9846 idxDown = currIdx + aImg.width; 9847 if (idxLeft < currRowIdx) { 9848 idxLeft = currIdx; 9849 } 9850 if (idxRight >= maxRowIdx) { 9851 idxRight = currIdx; 9852 } 9853 if (idxUp < 0) { 9854 idxUp = 0; 9855 } 9856 if (idxDown >= maxIdx) { 9857 idxDown = currIdx; 9858 } 9859 colUp = aImg.pixels.getPixel(idxUp); 9860 colLeft = aImg.pixels.getPixel(idxLeft); 9861 colDown = aImg.pixels.getPixel(idxDown); 9862 colRight = aImg.pixels.getPixel(idxRight); 9863 9864 // compute luminance 9865 currLum = 77*(colOrig>>16&0xff) + 151*(colOrig>>8&0xff) + 28*(colOrig&0xff); 9866 lumLeft = 77*(colLeft>>16&0xff) + 151*(colLeft>>8&0xff) + 28*(colLeft&0xff); 9867 lumRight = 77*(colRight>>16&0xff) + 151*(colRight>>8&0xff) + 28*(colRight&0xff); 9868 lumUp = 77*(colUp>>16&0xff) + 151*(colUp>>8&0xff) + 28*(colUp&0xff); 9869 lumDown = 77*(colDown>>16&0xff) + 151*(colDown>>8&0xff) + 28*(colDown&0xff); 9870 9871 if (lumLeft < currLum) { 9872 colOut = colLeft; 9873 currLum = lumLeft; 9874 } 9875 if (lumRight < currLum) { 9876 colOut = colRight; 9877 currLum = lumRight; 9878 } 9879 if (lumUp < currLum) { 9880 colOut = colUp; 9881 currLum = lumUp; 9882 } 9883 if (lumDown < currLum) { 9884 colOut = colDown; 9885 currLum = lumDown; 9886 } 9887 out[currIdx++]=colOut; 9888 } 9889 } 9890 } 9891 aImg.pixels.set(out); 9892 //p.arraycopy(out,0,pixels,0,maxIdx); 9893 }; 9894 9895 p.filter = function filter(kind, param, aImg){ 9896 var img, col, lum, i; 9897 9898 if (arguments.length === 3) { 9899 aImg.loadPixels(); 9900 img = aImg; 9901 } else { 9902 p.loadPixels(); 9903 img = p; 9904 } 9905 9906 if (param === undef) { 9907 param = null; 9908 } 9909 if (img.isRemote) { // Remote images cannot access imageData 9910 throw "Image is loaded remotely. Cannot filter image."; 9911 } else { 9912 // begin filter process 9913 var imglen = img.pixels.getLength(); 9914 switch (kind) { 9915 case PConstants.BLUR: 9916 var radius = param || 1; // if no param specified, use 1 (default for p5) 9917 blurARGB(radius, img); 9918 break; 9919 9920 case PConstants.GRAY: 9921 if (img.format === PConstants.ALPHA) { //trouble 9922 // for an alpha image, convert it to an opaque grayscale 9923 for (i = 0; i < imglen; i++) { 9924 col = 255 - img.pixels.getPixel(i); 9925 img.pixels.setPixel(i,(0xff000000 | (col << 16) | (col << 8) | col)); 9926 } 9927 img.format = PConstants.RGB; //trouble 9928 } else { 9929 for (i = 0; i < imglen; i++) { 9930 col = img.pixels.getPixel(i); 9931 lum = (77*(col>>16&0xff) + 151*(col>>8&0xff) + 28*(col&0xff))>>8; 9932 img.pixels.setPixel(i,((col & PConstants.ALPHA_MASK) | lum<<16 | lum<<8 | lum)); 9933 } 9934 } 9935 break; 9936 9937 case PConstants.INVERT: 9938 for (i = 0; i < imglen; i++) { 9939 img.pixels.setPixel(i, (img.pixels.getPixel(i) ^ 0xffffff)); 9940 } 9941 break; 9942 9943 case PConstants.POSTERIZE: 9944 if (param === null) { 9945 throw "Use filter(POSTERIZE, int levels) instead of filter(POSTERIZE)"; 9946 } 9947 var levels = p.floor(param); 9948 if ((levels < 2) || (levels > 255)) { 9949 throw "Levels must be between 2 and 255 for filter(POSTERIZE, levels)"; 9950 } 9951 var levels1 = levels - 1; 9952 for (i = 0; i < imglen; i++) { 9953 var rlevel = (img.pixels.getPixel(i) >> 16) & 0xff; 9954 var glevel = (img.pixels.getPixel(i) >> 8) & 0xff; 9955 var blevel = img.pixels.getPixel(i) & 0xff; 9956 rlevel = (((rlevel * levels) >> 8) * 255) / levels1; 9957 glevel = (((glevel * levels) >> 8) * 255) / levels1; 9958 blevel = (((blevel * levels) >> 8) * 255) / levels1; 9959 img.pixels.setPixel(i, ((0xff000000 & img.pixels.getPixel(i)) | (rlevel << 16) | (glevel << 8) | blevel)); 9960 } 9961 break; 9962 9963 case PConstants.OPAQUE: 9964 for (i = 0; i < imglen; i++) { 9965 img.pixels.setPixel(i, (img.pixels.getPixel(i) | 0xff000000)); 9966 } 9967 img.format = PConstants.RGB; //trouble 9968 break; 9969 9970 case PConstants.THRESHOLD: 9971 if (param === null) { 9972 param = 0.5; 9973 } 9974 if ((param < 0) || (param > 1)) { 9975 throw "Level must be between 0 and 1 for filter(THRESHOLD, level)"; 9976 } 9977 var thresh = p.floor(param * 255); 9978 for (i = 0; i < imglen; i++) { 9979 var max = p.max((img.pixels.getPixel(i) & PConstants.RED_MASK) >> 16, p.max((img.pixels.getPixel(i) & PConstants.GREEN_MASK) >> 8, (img.pixels.getPixel(i) & PConstants.BLUE_MASK))); 9980 img.pixels.setPixel(i, ((img.pixels.getPixel(i) & PConstants.ALPHA_MASK) | ((max < thresh) ? 0x000000 : 0xffffff))); 9981 } 9982 break; 9983 9984 case PConstants.ERODE: 9985 dilate(true, img); 9986 break; 9987 9988 case PConstants.DILATE: 9989 dilate(false, img); 9990 break; 9991 } 9992 img.updatePixels(); 9993 } 9994 }; 9995 9996 9997 // shared variables for blit_resize(), filter_new_scanline(), filter_bilinear(), filter() 9998 // change this in the future to not be exposed to p 9999 p.shared = { 10000 fracU: 0, 10001 ifU: 0, 10002 fracV: 0, 10003 ifV: 0, 10004 u1: 0, 10005 u2: 0, 10006 v1: 0, 10007 v2: 0, 10008 sX: 0, 10009 sY: 0, 10010 iw: 0, 10011 iw1: 0, 10012 ih1: 0, 10013 ul: 0, 10014 ll: 0, 10015 ur: 0, 10016 lr: 0, 10017 cUL: 0, 10018 cLL: 0, 10019 cUR: 0, 10020 cLR: 0, 10021 srcXOffset: 0, 10022 srcYOffset: 0, 10023 r: 0, 10024 g: 0, 10025 b: 0, 10026 a: 0, 10027 srcBuffer: null, 10028 blurRadius: 0, 10029 blurKernelSize: 0, 10030 blurKernel: null 10031 }; 10032 10033 p.intersect = function intersect(sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2) { 10034 var sw = sx2 - sx1 + 1; 10035 var sh = sy2 - sy1 + 1; 10036 var dw = dx2 - dx1 + 1; 10037 var dh = dy2 - dy1 + 1; 10038 if (dx1 < sx1) { 10039 dw += dx1 - sx1; 10040 if (dw > sw) { 10041 dw = sw; 10042 } 10043 } else { 10044 var w = sw + sx1 - dx1; 10045 if (dw > w) { 10046 dw = w; 10047 } 10048 } 10049 if (dy1 < sy1) { 10050 dh += dy1 - sy1; 10051 if (dh > sh) { 10052 dh = sh; 10053 } 10054 } else { 10055 var h = sh + sy1 - dy1; 10056 if (dh > h) { 10057 dh = h; 10058 } 10059 } 10060 return ! (dw <= 0 || dh <= 0); 10061 }; 10062 10063 p.filter_new_scanline = function filter_new_scanline() { 10064 p.shared.sX = p.shared.srcXOffset; 10065 p.shared.fracV = p.shared.srcYOffset & PConstants.PREC_MAXVAL; 10066 p.shared.ifV = PConstants.PREC_MAXVAL - p.shared.fracV; 10067 p.shared.v1 = (p.shared.srcYOffset >> PConstants.PRECISIONB) * p.shared.iw; 10068 p.shared.v2 = Math.min((p.shared.srcYOffset >> PConstants.PRECISIONB) + 1, p.shared.ih1) * p.shared.iw; 10069 }; 10070 10071 p.filter_bilinear = function filter_bilinear() { 10072 p.shared.fracU = p.shared.sX & PConstants.PREC_MAXVAL; 10073 p.shared.ifU = PConstants.PREC_MAXVAL - p.shared.fracU; 10074 p.shared.ul = (p.shared.ifU * p.shared.ifV) >> PConstants.PRECISIONB; 10075 p.shared.ll = (p.shared.ifU * p.shared.fracV) >> PConstants.PRECISIONB; 10076 p.shared.ur = (p.shared.fracU * p.shared.ifV) >> PConstants.PRECISIONB; 10077 p.shared.lr = (p.shared.fracU * p.shared.fracV) >> PConstants.PRECISIONB; 10078 p.shared.u1 = (p.shared.sX >> PConstants.PRECISIONB); 10079 p.shared.u2 = Math.min(p.shared.u1 + 1, p.shared.iw1); 10080 // get color values of the 4 neighbouring texels 10081 // changed for 0.9 10082 var cULoffset = (p.shared.v1 + p.shared.u1) * 4; 10083 var cURoffset = (p.shared.v1 + p.shared.u2) * 4; 10084 var cLLoffset = (p.shared.v2 + p.shared.u1) * 4; 10085 var cLRoffset = (p.shared.v2 + p.shared.u2) * 4; 10086 p.shared.cUL = p.color.toInt(p.shared.srcBuffer[cULoffset], p.shared.srcBuffer[cULoffset+1], 10087 p.shared.srcBuffer[cULoffset+2], p.shared.srcBuffer[cULoffset+3]); 10088 p.shared.cUR = p.color.toInt(p.shared.srcBuffer[cURoffset], p.shared.srcBuffer[cURoffset+1], 10089 p.shared.srcBuffer[cURoffset+2], p.shared.srcBuffer[cURoffset+3]); 10090 p.shared.cLL = p.color.toInt(p.shared.srcBuffer[cLLoffset], p.shared.srcBuffer[cLLoffset+1], 10091 p.shared.srcBuffer[cLLoffset+2], p.shared.srcBuffer[cLLoffset+3]); 10092 p.shared.cLR = p.color.toInt(p.shared.srcBuffer[cLRoffset], p.shared.srcBuffer[cLRoffset+1], 10093 p.shared.srcBuffer[cLRoffset+2], p.shared.srcBuffer[cLRoffset+3]); 10094 p.shared.r = ((p.shared.ul * ((p.shared.cUL & PConstants.RED_MASK) >> 16) + p.shared.ll * 10095 ((p.shared.cLL & PConstants.RED_MASK) >> 16) + p.shared.ur * ((p.shared.cUR & PConstants.RED_MASK) >> 16) + 10096 p.shared.lr * ((p.shared.cLR & PConstants.RED_MASK) >> 16)) << PConstants.PREC_RED_SHIFT) & PConstants.RED_MASK; 10097 p.shared.g = ((p.shared.ul * (p.shared.cUL & PConstants.GREEN_MASK) + p.shared.ll * (p.shared.cLL & PConstants.GREEN_MASK) + 10098 p.shared.ur * (p.shared.cUR & PConstants.GREEN_MASK) + p.shared.lr * 10099 (p.shared.cLR & PConstants.GREEN_MASK)) >>> PConstants.PRECISIONB) & PConstants.GREEN_MASK; 10100 p.shared.b = (p.shared.ul * (p.shared.cUL & PConstants.BLUE_MASK) + p.shared.ll * (p.shared.cLL & PConstants.BLUE_MASK) + 10101 p.shared.ur * (p.shared.cUR & PConstants.BLUE_MASK) + p.shared.lr * (p.shared.cLR & PConstants.BLUE_MASK)) >>> PConstants.PRECISIONB; 10102 p.shared.a = ((p.shared.ul * ((p.shared.cUL & PConstants.ALPHA_MASK) >>> 24) + p.shared.ll * 10103 ((p.shared.cLL & PConstants.ALPHA_MASK) >>> 24) + p.shared.ur * ((p.shared.cUR & PConstants.ALPHA_MASK) >>> 24) + 10104 p.shared.lr * ((p.shared.cLR & PConstants.ALPHA_MASK) >>> 24)) << PConstants.PREC_ALPHA_SHIFT) & PConstants.ALPHA_MASK; 10105 return p.shared.a | p.shared.r | p.shared.g | p.shared.b; 10106 }; 10107 10108 p.blit_resize = function blit_resize(img, srcX1, srcY1, srcX2, srcY2, destPixels, 10109 screenW, screenH, destX1, destY1, destX2, destY2, mode) { 10110 var x, y; // iterator vars 10111 if (srcX1 < 0) { 10112 srcX1 = 0; 10113 } 10114 if (srcY1 < 0) { 10115 srcY1 = 0; 10116 } 10117 if (srcX2 >= img.width) { 10118 srcX2 = img.width - 1; 10119 } 10120 if (srcY2 >= img.height) { 10121 srcY2 = img.height - 1; 10122 } 10123 var srcW = srcX2 - srcX1; 10124 var srcH = srcY2 - srcY1; 10125 var destW = destX2 - destX1; 10126 var destH = destY2 - destY1; 10127 var smooth = true; // may as well go with the smoothing these days 10128 if (!smooth) { 10129 srcW++; 10130 srcH++; 10131 } 10132 if (destW <= 0 || destH <= 0 || srcW <= 0 || srcH <= 0 || destX1 >= screenW || 10133 destY1 >= screenH || srcX1 >= img.width || srcY1 >= img.height) { 10134 return; 10135 } 10136 var dx = Math.floor(srcW / destW * PConstants.PRECISIONF); 10137 var dy = Math.floor(srcH / destH * PConstants.PRECISIONF); 10138 p.shared.srcXOffset = Math.floor(destX1 < 0 ? -destX1 * dx : srcX1 * PConstants.PRECISIONF); 10139 p.shared.srcYOffset = Math.floor(destY1 < 0 ? -destY1 * dy : srcY1 * PConstants.PRECISIONF); 10140 if (destX1 < 0) { 10141 destW += destX1; 10142 destX1 = 0; 10143 } 10144 if (destY1 < 0) { 10145 destH += destY1; 10146 destY1 = 0; 10147 } 10148 destW = Math.min(destW, screenW - destX1); 10149 destH = Math.min(destH, screenH - destY1); 10150 // changed in 0.9, TODO 10151 var destOffset = destY1 * screenW + destX1; 10152 var destColor; 10153 p.shared.srcBuffer = img.imageData.data; 10154 if (smooth) { 10155 // use bilinear filtering 10156 p.shared.iw = img.width; 10157 p.shared.iw1 = img.width - 1; 10158 p.shared.ih1 = img.height - 1; 10159 switch (mode) { 10160 case PConstants.BLEND: 10161 for (y = 0; y < destH; y++) { 10162 p.filter_new_scanline(); 10163 for (x = 0; x < destW; x++) { 10164 // changed for 0.9 10165 destColor = p.color.toInt(destPixels[(destOffset + x) * 4], 10166 destPixels[((destOffset + x) * 4) + 1], 10167 destPixels[((destOffset + x) * 4) + 2], 10168 destPixels[((destOffset + x) * 4) + 3]); 10169 destColor = p.color.toArray(p.modes.blend(destColor, p.filter_bilinear())); 10170 //destPixels[destOffset + x] = p.modes.blend(destPixels[destOffset + x], p.filter_bilinear()); 10171 destPixels[(destOffset + x) * 4] = destColor[0]; 10172 destPixels[(destOffset + x) * 4 + 1] = destColor[1]; 10173 destPixels[(destOffset + x) * 4 + 2] = destColor[2]; 10174 destPixels[(destOffset + x) * 4 + 3] = destColor[3]; 10175 p.shared.sX += dx; 10176 } 10177 destOffset += screenW; 10178 p.shared.srcYOffset += dy; 10179 } 10180 break; 10181 case PConstants.ADD: 10182 for (y = 0; y < destH; y++) { 10183 p.filter_new_scanline(); 10184 for (x = 0; x < destW; x++) { 10185 // changed for 0.9 10186 destColor = p.color.toInt(destPixels[(destOffset + x) * 4], 10187 destPixels[((destOffset + x) * 4) + 1], 10188 destPixels[((destOffset + x) * 4) + 2], 10189 destPixels[((destOffset + x) * 4) + 3]); 10190 destColor = p.color.toArray(p.modes.add(destColor, p.filter_bilinear())); 10191 destColor = p.color.toArray(p.modes.add(destColor, p.filter_bilinear())); 10192 //destPixels[destOffset + x] = p.modes.add(destPixels[destOffset + x], p.filter_bilinear()); 10193 destPixels[(destOffset + x) * 4] = destColor[0]; 10194 destPixels[(destOffset + x) * 4 + 1] = destColor[1]; 10195 destPixels[(destOffset + x) * 4 + 2] = destColor[2]; 10196 destPixels[(destOffset + x) * 4 + 3] = destColor[3]; 10197 p.shared.sX += dx; 10198 } 10199 destOffset += screenW; 10200 p.shared.srcYOffset += dy; 10201 } 10202 break; 10203 case PConstants.SUBTRACT: 10204 for (y = 0; y < destH; y++) { 10205 p.filter_new_scanline(); 10206 for (x = 0; x < destW; x++) { 10207 // changed for 0.9 10208 destColor = p.color.toInt(destPixels[(destOffset + x) * 4], 10209 destPixels[((destOffset + x) * 4) + 1], 10210 destPixels[((destOffset + x) * 4) + 2], 10211 destPixels[((destOffset + x) * 4) + 3]); 10212 destColor = p.color.toArray(p.modes.subtract(destColor, p.filter_bilinear())); 10213 //destPixels[destOffset + x] = p.modes.subtract(destPixels[destOffset + x], p.filter_bilinear()); 10214 destPixels[(destOffset + x) * 4] = destColor[0]; 10215 destPixels[(destOffset + x) * 4 + 1] = destColor[1]; 10216 destPixels[(destOffset + x) * 4 + 2] = destColor[2]; 10217 destPixels[(destOffset + x) * 4 + 3] = destColor[3]; 10218 p.shared.sX += dx; 10219 } 10220 destOffset += screenW; 10221 p.shared.srcYOffset += dy; 10222 } 10223 break; 10224 case PConstants.LIGHTEST: 10225 for (y = 0; y < destH; y++) { 10226 p.filter_new_scanline(); 10227 for (x = 0; x < destW; x++) { 10228 // changed for 0.9 10229 destColor = p.color.toInt(destPixels[(destOffset + x) * 4], 10230 destPixels[((destOffset + x) * 4) + 1], 10231 destPixels[((destOffset + x) * 4) + 2], 10232 destPixels[((destOffset + x) * 4) + 3]); 10233 destColor = p.color.toArray(p.modes.lightest(destColor, p.filter_bilinear())); 10234 //destPixels[destOffset + x] = p.modes.lightest(destPixels[destOffset + x], p.filter_bilinear()); 10235 destPixels[(destOffset + x) * 4] = destColor[0]; 10236 destPixels[(destOffset + x) * 4 + 1] = destColor[1]; 10237 destPixels[(destOffset + x) * 4 + 2] = destColor[2]; 10238 destPixels[(destOffset + x) * 4 + 3] = destColor[3]; 10239 p.shared.sX += dx; 10240 } 10241 destOffset += screenW; 10242 p.shared.srcYOffset += dy; 10243 } 10244 break; 10245 case PConstants.DARKEST: 10246 for (y = 0; y < destH; y++) { 10247 p.filter_new_scanline(); 10248 for (x = 0; x < destW; x++) { 10249 // changed for 0.9 10250 destColor = p.color.toInt(destPixels[(destOffset + x) * 4], 10251 destPixels[((destOffset + x) * 4) + 1], 10252 destPixels[((destOffset + x) * 4) + 2], 10253 destPixels[((destOffset + x) * 4) + 3]); 10254 destColor = p.color.toArray(p.modes.darkest(destColor, p.filter_bilinear())); 10255 //destPixels[destOffset + x] = p.modes.darkest(destPixels[destOffset + x], p.filter_bilinear()); 10256 destPixels[(destOffset + x) * 4] = destColor[0]; 10257 destPixels[(destOffset + x) * 4 + 1] = destColor[1]; 10258 destPixels[(destOffset + x) * 4 + 2] = destColor[2]; 10259 destPixels[(destOffset + x) * 4 + 3] = destColor[3]; 10260 p.shared.sX += dx; 10261 } 10262 destOffset += screenW; 10263 p.shared.srcYOffset += dy; 10264 } 10265 break; 10266 case PConstants.REPLACE: 10267 for (y = 0; y < destH; y++) { 10268 p.filter_new_scanline(); 10269 for (x = 0; x < destW; x++) { 10270 // changed for 0.9 10271 destColor = p.color.toInt(destPixels[(destOffset + x) * 4], 10272 destPixels[((destOffset + x) * 4) + 1], 10273 destPixels[((destOffset + x) * 4) + 2], 10274 destPixels[((destOffset + x) * 4) + 3]); 10275 destColor = p.color.toArray(p.filter_bilinear()); 10276 //destPixels[destOffset + x] = p.filter_bilinear(); 10277 destPixels[(destOffset + x) * 4] = destColor[0]; 10278 destPixels[(destOffset + x) * 4 + 1] = destColor[1]; 10279 destPixels[(destOffset + x) * 4 + 2] = destColor[2]; 10280 destPixels[(destOffset + x) * 4 + 3] = destColor[3]; 10281 p.shared.sX += dx; 10282 } 10283 destOffset += screenW; 10284 p.shared.srcYOffset += dy; 10285 } 10286 break; 10287 case PConstants.DIFFERENCE: 10288 for (y = 0; y < destH; y++) { 10289 p.filter_new_scanline(); 10290 for (x = 0; x < destW; x++) { 10291 // changed for 0.9 10292 destColor = p.color.toInt(destPixels[(destOffset + x) * 4], 10293 destPixels[((destOffset + x) * 4) + 1], 10294 destPixels[((destOffset + x) * 4) + 2], 10295 destPixels[((destOffset + x) * 4) + 3]); 10296 destColor = p.color.toArray(p.modes.difference(destColor, p.filter_bilinear())); 10297 //destPixels[destOffset + x] = p.modes.difference(destPixels[destOffset + x], p.filter_bilinear()); 10298 destPixels[(destOffset + x) * 4] = destColor[0]; 10299 destPixels[(destOffset + x) * 4 + 1] = destColor[1]; 10300 destPixels[(destOffset + x) * 4 + 2] = destColor[2]; 10301 destPixels[(destOffset + x) * 4 + 3] = destColor[3]; 10302 p.shared.sX += dx; 10303 } 10304 destOffset += screenW; 10305 p.shared.srcYOffset += dy; 10306 } 10307 break; 10308 case PConstants.EXCLUSION: 10309 for (y = 0; y < destH; y++) { 10310 p.filter_new_scanline(); 10311 for (x = 0; x < destW; x++) { 10312 // changed for 0.9 10313 destColor = p.color.toInt(destPixels[(destOffset + x) * 4], 10314 destPixels[((destOffset + x) * 4) + 1], 10315 destPixels[((destOffset + x) * 4) + 2], 10316 destPixels[((destOffset + x) * 4) + 3]); 10317 destColor = p.color.toArray(p.modes.exclusion(destColor, p.filter_bilinear())); 10318 //destPixels[destOffset + x] = p.modes.exclusion(destPixels[destOffset + x], p.filter_bilinear()); 10319 destPixels[(destOffset + x) * 4] = destColor[0]; 10320 destPixels[(destOffset + x) * 4 + 1] = destColor[1]; 10321 destPixels[(destOffset + x) * 4 + 2] = destColor[2]; 10322 destPixels[(destOffset + x) * 4 + 3] = destColor[3]; 10323 p.shared.sX += dx; 10324 } 10325 destOffset += screenW; 10326 p.shared.srcYOffset += dy; 10327 } 10328 break; 10329 case PConstants.MULTIPLY: 10330 for (y = 0; y < destH; y++) { 10331 p.filter_new_scanline(); 10332 for (x = 0; x < destW; x++) { 10333 // changed for 0.9 10334 destColor = p.color.toInt(destPixels[(destOffset + x) * 4], 10335 destPixels[((destOffset + x) * 4) + 1], 10336 destPixels[((destOffset + x) * 4) + 2], 10337 destPixels[((destOffset + x) * 4) + 3]); 10338 destColor = p.color.toArray(p.modes.multiply(destColor, p.filter_bilinear())); 10339 //destPixels[destOffset + x] = p.modes.multiply(destPixels[destOffset + x], p.filter_bilinear()); 10340 destPixels[(destOffset + x) * 4] = destColor[0]; 10341 destPixels[(destOffset + x) * 4 + 1] = destColor[1]; 10342 destPixels[(destOffset + x) * 4 + 2] = destColor[2]; 10343 destPixels[(destOffset + x) * 4 + 3] = destColor[3]; 10344 p.shared.sX += dx; 10345 } 10346 destOffset += screenW; 10347 p.shared.srcYOffset += dy; 10348 } 10349 break; 10350 case PConstants.SCREEN: 10351 for (y = 0; y < destH; y++) { 10352 p.filter_new_scanline(); 10353 for (x = 0; x < destW; x++) { 10354 // changed for 0.9 10355 destColor = p.color.toInt(destPixels[(destOffset + x) * 4], 10356 destPixels[((destOffset + x) * 4) + 1], 10357 destPixels[((destOffset + x) * 4) + 2], 10358 destPixels[((destOffset + x) * 4) + 3]); 10359 destColor = p.color.toArray(p.modes.screen(destColor, p.filter_bilinear())); 10360 //destPixels[destOffset + x] = p.modes.screen(destPixels[destOffset + x], p.filter_bilinear()); 10361 destPixels[(destOffset + x) * 4] = destColor[0]; 10362 destPixels[(destOffset + x) * 4 + 1] = destColor[1]; 10363 destPixels[(destOffset + x) * 4 + 2] = destColor[2]; 10364 destPixels[(destOffset + x) * 4 + 3] = destColor[3]; 10365 p.shared.sX += dx; 10366 } 10367 destOffset += screenW; 10368 p.shared.srcYOffset += dy; 10369 } 10370 break; 10371 case PConstants.OVERLAY: 10372 for (y = 0; y < destH; y++) { 10373 p.filter_new_scanline(); 10374 for (x = 0; x < destW; x++) { 10375 // changed for 0.9 10376 destColor = p.color.toInt(destPixels[(destOffset + x) * 4], 10377 destPixels[((destOffset + x) * 4) + 1], 10378 destPixels[((destOffset + x) * 4) + 2], 10379 destPixels[((destOffset + x) * 4) + 3]); 10380 destColor = p.color.toArray(p.modes.overlay(destColor, p.filter_bilinear())); 10381 //destPixels[destOffset + x] = p.modes.overlay(destPixels[destOffset + x], p.filter_bilinear()); 10382 destPixels[(destOffset + x) * 4] = destColor[0]; 10383 destPixels[(destOffset + x) * 4 + 1] = destColor[1]; 10384 destPixels[(destOffset + x) * 4 + 2] = destColor[2]; 10385 destPixels[(destOffset + x) * 4 + 3] = destColor[3]; 10386 p.shared.sX += dx; 10387 } 10388 destOffset += screenW; 10389 p.shared.srcYOffset += dy; 10390 } 10391 break; 10392 case PConstants.HARD_LIGHT: 10393 for (y = 0; y < destH; y++) { 10394 p.filter_new_scanline(); 10395 for (x = 0; x < destW; x++) { 10396 // changed for 0.9 10397 destColor = p.color.toInt(destPixels[(destOffset + x) * 4], 10398 destPixels[((destOffset + x) * 4) + 1], 10399 destPixels[((destOffset + x) * 4) + 2], 10400 destPixels[((destOffset + x) * 4) + 3]); 10401 destColor = p.color.toArray(p.modes.hard_light(destColor, p.filter_bilinear())); 10402 //destPixels[destOffset + x] = p.modes.hard_light(destPixels[destOffset + x], p.filter_bilinear()); 10403 destPixels[(destOffset + x) * 4] = destColor[0]; 10404 destPixels[(destOffset + x) * 4 + 1] = destColor[1]; 10405 destPixels[(destOffset + x) * 4 + 2] = destColor[2]; 10406 destPixels[(destOffset + x) * 4 + 3] = destColor[3]; 10407 p.shared.sX += dx; 10408 } 10409 destOffset += screenW; 10410 p.shared.srcYOffset += dy; 10411 } 10412 break; 10413 case PConstants.SOFT_LIGHT: 10414 for (y = 0; y < destH; y++) { 10415 p.filter_new_scanline(); 10416 for (x = 0; x < destW; x++) { 10417 // changed for 0.9 10418 destColor = p.color.toInt(destPixels[(destOffset + x) * 4], 10419 destPixels[((destOffset + x) * 4) + 1], 10420 destPixels[((destOffset + x) * 4) + 2], 10421 destPixels[((destOffset + x) * 4) + 3]); 10422 destColor = p.color.toArray(p.modes.soft_light(destColor, p.filter_bilinear())); 10423 //destPixels[destOffset + x] = p.modes.soft_light(destPixels[destOffset + x], p.filter_bilinear()); 10424 destPixels[(destOffset + x) * 4] = destColor[0]; 10425 destPixels[(destOffset + x) * 4 + 1] = destColor[1]; 10426 destPixels[(destOffset + x) * 4 + 2] = destColor[2]; 10427 destPixels[(destOffset + x) * 4 + 3] = destColor[3]; 10428 p.shared.sX += dx; 10429 } 10430 destOffset += screenW; 10431 p.shared.srcYOffset += dy; 10432 } 10433 break; 10434 case PConstants.DODGE: 10435 for (y = 0; y < destH; y++) { 10436 p.filter_new_scanline(); 10437 for (x = 0; x < destW; x++) { 10438 // changed for 0.9 10439 destColor = p.color.toInt(destPixels[(destOffset + x) * 4], 10440 destPixels[((destOffset + x) * 4) + 1], 10441 destPixels[((destOffset + x) * 4) + 2], 10442 destPixels[((destOffset + x) * 4) + 3]); 10443 destColor = p.color.toArray(p.modes.dodge(destColor, p.filter_bilinear())); 10444 //destPixels[destOffset + x] = p.modes.dodge(destPixels[destOffset + x], p.filter_bilinear()); 10445 destPixels[(destOffset + x) * 4] = destColor[0]; 10446 destPixels[(destOffset + x) * 4 + 1] = destColor[1]; 10447 destPixels[(destOffset + x) * 4 + 2] = destColor[2]; 10448 destPixels[(destOffset + x) * 4 + 3] = destColor[3]; 10449 p.shared.sX += dx; 10450 } 10451 destOffset += screenW; 10452 p.shared.srcYOffset += dy; 10453 } 10454 break; 10455 case PConstants.BURN: 10456 for (y = 0; y < destH; y++) { 10457 p.filter_new_scanline(); 10458 for (x = 0; x < destW; x++) { 10459 // changed for 0.9 10460 destColor = p.color.toInt(destPixels[(destOffset + x) * 4], 10461 destPixels[((destOffset + x) * 4) + 1], 10462 destPixels[((destOffset + x) * 4) + 2], 10463 destPixels[((destOffset + x) * 4) + 3]); 10464 destColor = p.color.toArray(p.modes.burn(destColor, p.filter_bilinear())); 10465 //destPixels[destOffset + x] = p.modes.burn(destPixels[destOffset + x], p.filter_bilinear()); 10466 destPixels[(destOffset + x) * 4] = destColor[0]; 10467 destPixels[(destOffset + x) * 4 + 1] = destColor[1]; 10468 destPixels[(destOffset + x) * 4 + 2] = destColor[2]; 10469 destPixels[(destOffset + x) * 4 + 3] = destColor[3]; 10470 p.shared.sX += dx; 10471 } 10472 destOffset += screenW; 10473 p.shared.srcYOffset += dy; 10474 } 10475 break; 10476 } 10477 } 10478 }; 10479 10480 //////////////////////////////////////////////////////////////////////////// 10481 // Font handling 10482 //////////////////////////////////////////////////////////////////////////// 10483 10484 // Loads a font from an SVG or Canvas API 10485 p.loadFont = function loadFont(name) { 10486 if (name.indexOf(".svg") === -1) { 10487 return { 10488 name: "\"" + name + "\", sans-serif", 10489 origName: name, 10490 width: function(str) { 10491 if ("measureText" in curContext) { 10492 return curContext.measureText(typeof str === "number" ? String.fromCharCode(str) : str).width / curTextSize; 10493 } else if ("mozMeasureText" in curContext) { 10494 return curContext.mozMeasureText(typeof str === "number" ? String.fromCharCode(str) : str) / curTextSize; 10495 } else { 10496 return 0; 10497 } 10498 } 10499 }; 10500 } else { 10501 // If the font is a glyph, calculate by SVG table 10502 var font = p.loadGlyphs(name); 10503 10504 return { 10505 name: name, 10506 glyph: true, 10507 units_per_em: font.units_per_em, 10508 horiz_adv_x: 1 / font.units_per_em * font.horiz_adv_x, 10509 ascent: font.ascent, 10510 descent: font.descent, 10511 width: function(str) { 10512 var width = 0; 10513 var len = str.length; 10514 for (var i = 0; i < len; i++) { 10515 try { 10516 width += parseFloat(p.glyphLook(p.glyphTable[name], str[i]).horiz_adv_x); 10517 } 10518 catch(e) { 10519 Processing.debug(e); 10520 } 10521 } 10522 return width / p.glyphTable[name].units_per_em; 10523 } 10524 }; 10525 } 10526 }; 10527 10528 p.createFont = function(name, size, smooth, charset) { 10529 if (arguments.length === 2) { 10530 p.textSize(size); 10531 return p.loadFont(name); 10532 } else if (arguments.length === 3) { 10533 // smooth: true for an antialiased font, false for aliased 10534 p.textSize(size); 10535 return p.loadFont(name); 10536 } else if (arguments.length === 4) { 10537 // charset: char array containing characters to be generated 10538 p.textSize(size); 10539 return p.loadFont(name); 10540 } else { 10541 throw("incorrent number of parameters for createFont"); 10542 } 10543 }; 10544 10545 // Sets a 'current font' for use 10546 p.textFont = function textFont(name, size) { 10547 curTextFont = name; 10548 p.textSize(size); 10549 }; 10550 10551 // Sets the font size 10552 p.textSize = function textSize(size) { 10553 if (size) { 10554 curTextSize = size; 10555 } 10556 }; 10557 10558 p.textAlign = function textAlign() { 10559 if(arguments.length === 1) { 10560 horizontalTextAlignment = arguments[0]; 10561 } else if(arguments.length === 2) { 10562 horizontalTextAlignment = arguments[0]; 10563 verticalTextAlignment = arguments[1]; 10564 } 10565 }; 10566 10567 p.textWidth = function textWidth(str) { 10568 if (p.use3DContext) { 10569 if (textcanvas === undef) { 10570 textcanvas = document.createElement("canvas"); 10571 } 10572 var oldContext = curContext; 10573 curContext = textcanvas.getContext("2d"); 10574 curContext.font = curContext.mozTextStyle = curTextSize + "px " + curTextFont.name; 10575 if ("fillText" in curContext) { 10576 textcanvas.width = curContext.measureText(str).width; 10577 } else if ("mozDrawText" in curContext) { 10578 textcanvas.width = curContext.mozMeasureText(str); 10579 } 10580 curContext = oldContext; 10581 return textcanvas.width; 10582 } else { 10583 curContext.font = curTextSize + "px " + curTextFont.name; 10584 if ("fillText" in curContext) { 10585 return curContext.measureText(str).width; 10586 } else if ("mozDrawText" in curContext) { 10587 return curContext.mozMeasureText(str); 10588 } 10589 } 10590 }; 10591 10592 p.textAscent = (function() { 10593 var oldTextSize = undef, 10594 oldTextFont = undef, 10595 ascent = undef, 10596 graphics = undef; 10597 return function textAscent() { 10598 // if text size or font has changed, recalculate ascent value 10599 if (oldTextFont !== curTextFont || oldTextSize !== curTextSize) { 10600 // store current size and font 10601 oldTextFont = curTextFont; 10602 oldTextSize = curTextSize; 10603 10604 var found = false, 10605 character = "k", 10606 colour = p.color(0), 10607 top = 0, 10608 bottom = curTextSize, 10609 yLoc = curTextSize/2; 10610 10611 // setup off screen image to write and measure text from 10612 if (graphics !== undef) { 10613 graphics.size(curTextSize, curTextSize); 10614 } else { 10615 graphics = p.createGraphics(curTextSize, curTextSize); 10616 } 10617 graphics.background(0); 10618 graphics.fill(255); 10619 graphics.textFont(curTextFont, curTextSize); 10620 graphics.text(character, 0, curTextSize); 10621 10622 // binary search for highest pixel 10623 while(yLoc !== bottom) { 10624 for (var xLoc = 0; xLoc < curTextSize; xLoc++) { 10625 if (graphics.get(xLoc, yLoc) !== colour) { 10626 found = true; 10627 xLoc = curTextSize; 10628 } 10629 } 10630 if (found) { 10631 // y-- 10632 bottom = yLoc; 10633 found = false; 10634 } else { 10635 // y++ 10636 top = yLoc; 10637 } 10638 yLoc = Math.ceil((bottom + top)/2); 10639 } 10640 ascent = ((curTextSize-1) - yLoc) + 1; 10641 return ascent; 10642 } else { // text size and font have not changed since last time 10643 return ascent; 10644 } 10645 }; 10646 }()); 10647 10648 p.textDescent = (function() { 10649 var oldTextSize = undef, 10650 oldTextFont = undef, 10651 descent = undef, 10652 graphics = undef; 10653 return function textDescent() { 10654 // if text size or font has changed, recalculate descent value 10655 if (oldTextFont !== curTextFont || oldTextSize !== curTextSize) { 10656 // store current size and font 10657 oldTextFont = curTextFont; 10658 oldTextSize = curTextSize; 10659 10660 var found = false, 10661 character = "p", 10662 colour = p.color(0), 10663 top = 0, 10664 bottom = curTextSize, 10665 yLoc = curTextSize/2; 10666 10667 // setup off screen image to write and measure text from 10668 if (graphics !== undef) { 10669 graphics.size(curTextSize, curTextSize); 10670 } else { 10671 graphics = p.createGraphics(curTextSize, curTextSize); 10672 } 10673 graphics.background(0); 10674 graphics.fill(255); 10675 graphics.textFont(curTextFont, curTextSize); 10676 graphics.text(character, 0, 0); 10677 10678 // binary search for lowest pixel 10679 while(yLoc !== bottom) { 10680 for (var xLoc = 0; xLoc < curTextSize; xLoc++) { 10681 if (graphics.get(xLoc, yLoc) !== colour) { 10682 found = true; 10683 xLoc = curTextSize; 10684 } 10685 } 10686 if (found) { 10687 // y++ 10688 top = yLoc; 10689 found = false; 10690 } else { 10691 // y-- 10692 bottom = yLoc; 10693 } 10694 yLoc = Math.ceil((bottom + top)/2); 10695 } 10696 descent = yLoc + 1; 10697 return descent; 10698 } else { // text size and font have not changed since last time 10699 return descent; 10700 } 10701 }; 10702 }()); 10703 10704 // A lookup table for characters that can not be referenced by Object 10705 p.glyphLook = function glyphLook(font, chr) { 10706 try { 10707 switch (chr) { 10708 case "1": 10709 return font.one; 10710 case "2": 10711 return font.two; 10712 case "3": 10713 return font.three; 10714 case "4": 10715 return font.four; 10716 case "5": 10717 return font.five; 10718 case "6": 10719 return font.six; 10720 case "7": 10721 return font.seven; 10722 case "8": 10723 return font.eight; 10724 case "9": 10725 return font.nine; 10726 case "0": 10727 return font.zero; 10728 case " ": 10729 return font.space; 10730 case "$": 10731 return font.dollar; 10732 case "!": 10733 return font.exclam; 10734 case '"': 10735 return font.quotedbl; 10736 case "#": 10737 return font.numbersign; 10738 case "%": 10739 return font.percent; 10740 case "&": 10741 return font.ampersand; 10742 case "'": 10743 return font.quotesingle; 10744 case "(": 10745 return font.parenleft; 10746 case ")": 10747 return font.parenright; 10748 case "*": 10749 return font.asterisk; 10750 case "+": 10751 return font.plus; 10752 case ",": 10753 return font.comma; 10754 case "-": 10755 return font.hyphen; 10756 case ".": 10757 return font.period; 10758 case "/": 10759 return font.slash; 10760 case "_": 10761 return font.underscore; 10762 case ":": 10763 return font.colon; 10764 case ";": 10765 return font.semicolon; 10766 case "<": 10767 return font.less; 10768 case "=": 10769 return font.equal; 10770 case ">": 10771 return font.greater; 10772 case "?": 10773 return font.question; 10774 case "@": 10775 return font.at; 10776 case "[": 10777 return font.bracketleft; 10778 case "\\": 10779 return font.backslash; 10780 case "]": 10781 return font.bracketright; 10782 case "^": 10783 return font.asciicircum; 10784 case "`": 10785 return font.grave; 10786 case "{": 10787 return font.braceleft; 10788 case "|": 10789 return font.bar; 10790 case "}": 10791 return font.braceright; 10792 case "~": 10793 return font.asciitilde; 10794 // If the character is not 'special', access it by object reference 10795 default: 10796 return font[chr]; 10797 } 10798 } catch(e) { 10799 Processing.debug(e); 10800 } 10801 }; 10802 10803 function toP5String(obj) { 10804 if(obj instanceof String) { 10805 return obj; 10806 } else if(typeof obj === 'number') { 10807 // check if an int 10808 if(obj === (0 | obj)) { 10809 return obj.toString(); 10810 } else { 10811 return p.nf(obj, 0, 3); 10812 } 10813 } else if(obj === null || obj === undef) { 10814 return ""; 10815 } else { 10816 return obj.toString(); 10817 } 10818 } 10819 10820 // Print some text to the Canvas 10821 function text$line(str, x, y, z, align) { 10822 var textWidth = 0, xOffset = 0; 10823 // If the font is a standard Canvas font... 10824 if (!curTextFont.glyph) { 10825 if (str && ("fillText" in curContext || "mozDrawText" in curContext)) { 10826 curContext.font = curContext.mozTextStyle = curTextSize + "px " + curTextFont.name; 10827 10828 if (isFillDirty) { 10829 curContext.fillStyle = p.color.toString(currentFillColor); 10830 isFillDirty = false; 10831 } 10832 10833 // horizontal offset/alignment 10834 if(align === PConstants.RIGHT || align === PConstants.CENTER) { 10835 if ("fillText" in curContext) { 10836 textWidth = curContext.measureText(str).width; 10837 } else if ("mozDrawText" in curContext) { 10838 textWidth = curContext.mozMeasureText(str); 10839 } 10840 10841 if(align === PConstants.RIGHT) { 10842 xOffset = -textWidth; 10843 } else { // if(align === PConstants.CENTER) 10844 xOffset = -textWidth/2; 10845 } 10846 } 10847 10848 if ("fillText" in curContext) { 10849 curContext.fillText(str, x+xOffset, y); 10850 } else if ("mozDrawText" in curContext) { 10851 saveContext(); 10852 curContext.translate(x+xOffset, y); 10853 curContext.mozDrawText(str); 10854 restoreContext(); 10855 } 10856 } 10857 } else { 10858 // If the font is a Batik SVG font... 10859 var font = p.glyphTable[curTextFont.name]; 10860 saveContext(); 10861 curContext.translate(x, y + curTextSize); 10862 10863 // horizontal offset/alignment 10864 if(align === PConstants.RIGHT || align === PConstants.CENTER) { 10865 textWidth = font.width(str); 10866 10867 if(align === PConstants.RIGHT) { 10868 xOffset = -textWidth; 10869 } else { // if(align === PConstants.CENTER) 10870 xOffset = -textWidth/2; 10871 } 10872 } 10873 10874 var upem = font.units_per_em, 10875 newScale = 1 / upem * curTextSize; 10876 10877 curContext.scale(newScale, newScale); 10878 10879 for (var i=0, len=str.length; i < len; i++) { 10880 // Test character against glyph table 10881 try { 10882 p.glyphLook(font, str[i]).draw(); 10883 } catch(e) { 10884 Processing.debug(e); 10885 } 10886 } 10887 restoreContext(); 10888 } 10889 } 10890 10891 function text$line$3d(str, x, y, z, align) { 10892 // handle case for 3d text 10893 if (textcanvas === undef) { 10894 textcanvas = document.createElement("canvas"); 10895 } 10896 var oldContext = curContext; 10897 curContext = textcanvas.getContext("2d"); 10898 curContext.font = curContext.mozTextStyle = curTextSize + "px " + curTextFont.name; 10899 var textWidth = 0; 10900 if ("fillText" in curContext) { 10901 textWidth = curContext.measureText(str).width; 10902 } else if ("mozDrawText" in curContext) { 10903 textWidth = curContext.mozMeasureText(str); 10904 } 10905 textcanvas.width = textWidth; 10906 textcanvas.height = curTextSize; 10907 curContext = textcanvas.getContext("2d"); // refreshes curContext 10908 curContext.font = curContext.mozTextStyle = curTextSize + "px " + curTextFont.name; 10909 curContext.textBaseline="top"; 10910 10911 // paint on 2D canvas 10912 text$line(str,0,0,0,PConstants.LEFT); 10913 10914 // use it as a texture 10915 var aspect = textcanvas.width/textcanvas.height; 10916 curContext = oldContext; 10917 10918 executeTexImage2D(textcanvas); 10919 curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_MAG_FILTER, curContext.LINEAR); 10920 curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_MIN_FILTER, curContext.LINEAR_MIPMAP_LINEAR); 10921 curContext.generateMipmap(curContext.TEXTURE_2D); 10922 10923 // horizontal offset/alignment 10924 var xOffset = 0; 10925 if (align === PConstants.RIGHT) { 10926 xOffset = -textWidth; 10927 } else if(align === PConstants.CENTER) { 10928 xOffset = -textWidth/2; 10929 } 10930 var model = new PMatrix3D(); 10931 var scalefactor = curTextSize * 0.5; 10932 model.translate(x+xOffset-scalefactor/2, y-scalefactor, z); 10933 model.scale(-aspect*scalefactor, -scalefactor, scalefactor); 10934 model.translate(-1, -1, -1); 10935 model.transpose(); 10936 10937 var view = new PMatrix3D(); 10938 view.scale(1, -1, 1); 10939 view.apply(modelView.array()); 10940 view.transpose(); 10941 10942 var proj = new PMatrix3D(); 10943 proj.set(projection); 10944 proj.transpose(); 10945 10946 curContext.useProgram(programObject2D); 10947 vertexAttribPointer(programObject2D, "Vertex", 3, textBuffer); 10948 vertexAttribPointer(programObject2D, "aTextureCoord", 2, textureBuffer); 10949 uniformi(programObject2D, "uSampler", [0]); 10950 uniformi(programObject2D, "picktype", 1); 10951 uniformMatrix(programObject2D, "model", false, model.array()); 10952 uniformMatrix(programObject2D, "view", false, view.array()); 10953 uniformMatrix(programObject2D, "projection", false, proj.array()); 10954 uniformf(programObject2D, "color", fillStyle); 10955 curContext.bindBuffer(curContext.ELEMENT_ARRAY_BUFFER, indexBuffer); 10956 curContext.drawElements(curContext.TRIANGLES, 6, curContext.UNSIGNED_SHORT, 0); 10957 } 10958 10959 function text$4(str, x, y, z) { 10960 var lineFunction = p.use3DContext ? text$line$3d : text$line; 10961 var lines, linesCount; 10962 if(str.indexOf('\n') < 0) { 10963 lines = [str]; 10964 linesCount = 1; 10965 } else { 10966 lines = str.split(/\r?\n/g); 10967 linesCount = lines.length; 10968 } 10969 // handle text line-by-line 10970 10971 var yOffset; 10972 if(verticalTextAlignment === PConstants.TOP) { 10973 yOffset = (1-baselineOffset) * curTextSize; 10974 } else if(verticalTextAlignment === PConstants.CENTER) { 10975 yOffset = (1-baselineOffset - linesCount/2) * curTextSize; 10976 } else if(verticalTextAlignment === PConstants.BOTTOM) { 10977 yOffset = (1-baselineOffset - linesCount) * curTextSize; 10978 } else { // if(verticalTextAlignment === PConstants.BASELINE) { 10979 yOffset = (1 - linesCount) * curTextSize; 10980 } 10981 for(var i=0;i<linesCount;++i) { 10982 var line = lines[i]; 10983 lineFunction(line, x, y + yOffset, z, horizontalTextAlignment); 10984 yOffset += curTextSize; 10985 } 10986 } 10987 10988 function text$6(str, x, y, width, height, z) { 10989 if (str.length === 0) { // is empty string 10990 return; 10991 } 10992 if(curTextSize > height) { // is text height larger than box 10993 return; 10994 } 10995 10996 var spaceMark = -1; 10997 var start = 0; 10998 var lineWidth = 0; 10999 var textboxWidth = width; 11000 11001 var yOffset = 0; 11002 11003 curContext.font = curTextSize + "px " + curTextFont.name; 11004 11005 var drawCommands = []; 11006 var hadSpaceBefore = false; 11007 for (var j=0, len=str.length; j < len; j++) { 11008 var currentChar = str[j]; 11009 var letterWidth; 11010 11011 if ("fillText" in curContext) { 11012 letterWidth = curContext.measureText(currentChar).width; 11013 } else if ("mozDrawText" in curContext) { 11014 letterWidth = curContext.mozMeasureText(currentChar); 11015 } 11016 11017 if (currentChar !== "\n" && (currentChar === " " || (hadSpaceBefore && str[j + 1] === " ") || 11018 lineWidth + 2 * letterWidth < textboxWidth)) { // check a line of text 11019 if (currentChar === " ") { 11020 spaceMark = j; 11021 } 11022 lineWidth += letterWidth; 11023 } else { // draw a line of text 11024 if (start === spaceMark + 1) { // in case a whole line without a space 11025 spaceMark = j; 11026 } 11027 11028 if (str[j] === "\n") { 11029 drawCommands.push({text:str.substring(start, j), width: lineWidth, offset: yOffset}); 11030 start = j + 1; 11031 } else { 11032 drawCommands.push({text:str.substring(start, spaceMark + 1), width: lineWidth, offset: yOffset}); 11033 start = spaceMark + 1; 11034 } 11035 yOffset += curTextSize; 11036 11037 lineWidth = 0; 11038 j = start - 1; 11039 } 11040 hadSpaceBefore = currentChar === " "; 11041 } // for (var j= 11042 11043 if (start < len) { // draw the last line 11044 drawCommands.push({text:str.substring(start), width: lineWidth, offset: yOffset}); 11045 yOffset += curTextSize; 11046 } 11047 11048 // actual draw 11049 var lineFunction = p.use3DContext ? text$line$3d : text$line; 11050 var xOffset = 0; 11051 if (horizontalTextAlignment === PConstants.CENTER) { 11052 xOffset = width / 2; 11053 } else if (horizontalTextAlignment === PConstants.RIGHT) { 11054 xOffset = width; 11055 } 11056 11057 // offsets for alignment 11058 var boxYOffset1 = (1-baselineOffset) * curTextSize, boxYOffset2 = 0; 11059 if (verticalTextAlignment === PConstants.BOTTOM) { 11060 boxYOffset2 = height-yOffset; 11061 } else if (verticalTextAlignment === PConstants.CENTER) { 11062 boxYOffset2 = (height-yOffset) / 2; 11063 } 11064 11065 for (var il=0,ll=drawCommands.length; il<ll; ++il) { 11066 var command = drawCommands[il]; 11067 if (command.offset + boxYOffset2 < 0) { 11068 continue; // skip if not inside box yet 11069 } 11070 if (command.offset + boxYOffset2 + curTextSize > height) { 11071 break; // stop if no enough space for one more line draw 11072 } 11073 lineFunction(command.text, x + xOffset, y + command.offset + boxYOffset1 + boxYOffset2, z, horizontalTextAlignment); 11074 } 11075 } 11076 11077 p.text = function text() { 11078 if (tMode === PConstants.SCREEN) { // TODO: 3D Screen not working yet due to 3D not working in textAscent 11079 p.pushMatrix(); 11080 p.resetMatrix(); 11081 var asc = p.textAscent(); 11082 var des = p.textDescent(); 11083 var tWidth = p.textWidth(arguments[0]); 11084 var tHeight = asc + des; 11085 var font = p.loadFont(curTextFont.origName); 11086 var hud = p.createGraphics(tWidth, tHeight); 11087 hud.beginDraw(); 11088 hud.fill(currentFillColor); 11089 hud.opaque = false; 11090 hud.background(0, 0, 0, 0); 11091 hud.textFont(font); 11092 hud.textSize(curTextSize); 11093 hud.text(arguments[0], 0, asc); 11094 hud.endDraw(); 11095 if (arguments.length === 5 || arguments.length === 6) { 11096 p.image(hud, arguments[1], arguments[2]-asc, arguments[3], arguments[4]); 11097 } else { 11098 p.image(hud, arguments[1], arguments[2]-asc); 11099 } 11100 p.popMatrix(); 11101 } 11102 else if (tMode === PConstants.SHAPE) { 11103 // TODO: requires beginRaw function 11104 } else { 11105 if (arguments.length === 3) { // for text( str, x, y) 11106 text$4(toP5String(arguments[0]), arguments[1], arguments[2], 0); 11107 } else if (arguments.length === 4) { // for text( str, x, y, z) 11108 text$4(toP5String(arguments[0]), arguments[1], arguments[2], arguments[3]); 11109 } else if (arguments.length === 5) { // for text( str, x, y , width, height) 11110 text$6(toP5String(arguments[0]), arguments[1], arguments[2], arguments[3], arguments[4], 0); 11111 } else if (arguments.length === 6) { // for text( stringdata, x, y , width, height, z) 11112 text$6(toP5String(arguments[0]), arguments[1], arguments[2], arguments[3], arguments[4], arguments[5]); 11113 } 11114 } 11115 }; 11116 11117 p.textMode = function textMode(mode){ 11118 tMode = mode; 11119 }; 11120 11121 // Load Batik SVG Fonts and parse to pre-def objects for quick rendering 11122 p.loadGlyphs = function loadGlyph(url) { 11123 var x, y, cx, cy, nx, ny, d, a, lastCom, lenC, horiz_adv_x, getXY = '[0-9\\-]+', path; 11124 11125 // Return arrays of SVG commands and coords 11126 // get this to use p.matchAll() - will need to work around the lack of null return 11127 var regex = function regex(needle, hay) { 11128 var i = 0, 11129 results = [], 11130 latest, regexp = new RegExp(needle, "g"); 11131 latest = results[i] = regexp.exec(hay); 11132 while (latest) { 11133 i++; 11134 latest = results[i] = regexp.exec(hay); 11135 } 11136 return results; 11137 }; 11138 11139 var buildPath = function buildPath(d) { 11140 var c = regex("[A-Za-z][0-9\\- ]+|Z", d); 11141 11142 // Begin storing path object 11143 path = "var path={draw:function(){saveContext();curContext.beginPath();"; 11144 11145 x = 0; 11146 y = 0; 11147 cx = 0; 11148 cy = 0; 11149 nx = 0; 11150 ny = 0; 11151 d = 0; 11152 a = 0; 11153 lastCom = ""; 11154 lenC = c.length - 1; 11155 11156 // Loop through SVG commands translating to canvas eqivs functions in path object 11157 for (var j = 0; j < lenC; j++) { 11158 var com = c[j][0], xy = regex(getXY, com); 11159 11160 switch (com[0]) { 11161 case "M": 11162 //curContext.moveTo(x,-y); 11163 x = parseFloat(xy[0][0]); 11164 y = parseFloat(xy[1][0]); 11165 path += "curContext.moveTo(" + x + "," + (-y) + ");"; 11166 break; 11167 11168 case "L": 11169 //curContext.lineTo(x,-y); 11170 x = parseFloat(xy[0][0]); 11171 y = parseFloat(xy[1][0]); 11172 path += "curContext.lineTo(" + x + "," + (-y) + ");"; 11173 break; 11174 11175 case "H": 11176 //curContext.lineTo(x,-y) 11177 x = parseFloat(xy[0][0]); 11178 path += "curContext.lineTo(" + x + "," + (-y) + ");"; 11179 break; 11180 11181 case "V": 11182 //curContext.lineTo(x,-y); 11183 y = parseFloat(xy[0][0]); 11184 path += "curContext.lineTo(" + x + "," + (-y) + ");"; 11185 break; 11186 11187 case "T": 11188 //curContext.quadraticCurveTo(cx,-cy,nx,-ny); 11189 nx = parseFloat(xy[0][0]); 11190 ny = parseFloat(xy[1][0]); 11191 11192 if (lastCom === "Q" || lastCom === "T") { 11193 d = Math.sqrt(Math.pow(x - cx, 2) + Math.pow(cy - y, 2)); 11194 a = Math.PI + Math.atan2(cx - x, cy - y); 11195 cx = x + (Math.sin(a) * (d)); 11196 cy = y + (Math.cos(a) * (d)); 11197 } else { 11198 cx = x; 11199 cy = y; 11200 } 11201 11202 path += "curContext.quadraticCurveTo(" + cx + "," + (-cy) + "," + nx + "," + (-ny) + ");"; 11203 x = nx; 11204 y = ny; 11205 break; 11206 11207 case "Q": 11208 //curContext.quadraticCurveTo(cx,-cy,nx,-ny); 11209 cx = parseFloat(xy[0][0]); 11210 cy = parseFloat(xy[1][0]); 11211 nx = parseFloat(xy[2][0]); 11212 ny = parseFloat(xy[3][0]); 11213 path += "curContext.quadraticCurveTo(" + cx + "," + (-cy) + "," + nx + "," + (-ny) + ");"; 11214 x = nx; 11215 y = ny; 11216 break; 11217 11218 case "Z": 11219 //curContext.closePath(); 11220 path += "curContext.closePath();"; 11221 break; 11222 } 11223 lastCom = com[0]; 11224 } 11225 11226 path += "executeContextFill();executeContextStroke();"; 11227 path += "restoreContext();"; 11228 path += "curContext.translate(" + horiz_adv_x + ",0);"; 11229 path += "}}"; 11230 11231 return path; 11232 }; 11233 11234 // Parse SVG font-file into block of Canvas commands 11235 var parseSVGFont = function parseSVGFontse(svg) { 11236 // Store font attributes 11237 var font = svg.getElementsByTagName("font"); 11238 p.glyphTable[url].horiz_adv_x = font[0].getAttribute("horiz-adv-x"); 11239 11240 var font_face = svg.getElementsByTagName("font-face")[0]; 11241 p.glyphTable[url].units_per_em = parseFloat(font_face.getAttribute("units-per-em")); 11242 p.glyphTable[url].ascent = parseFloat(font_face.getAttribute("ascent")); 11243 p.glyphTable[url].descent = parseFloat(font_face.getAttribute("descent")); 11244 11245 var glyph = svg.getElementsByTagName("glyph"), 11246 len = glyph.length; 11247 11248 // Loop through each glyph in the SVG 11249 for (var i = 0; i < len; i++) { 11250 // Store attributes for this glyph 11251 var unicode = glyph[i].getAttribute("unicode"); 11252 var name = glyph[i].getAttribute("glyph-name"); 11253 horiz_adv_x = glyph[i].getAttribute("horiz-adv-x"); 11254 if (horiz_adv_x === null) { 11255 horiz_adv_x = p.glyphTable[url].horiz_adv_x; 11256 } 11257 d = glyph[i].getAttribute("d"); 11258 // Split path commands in glpyh 11259 if (d !== undef) { 11260 path = buildPath(d); 11261 eval(path); 11262 // Store glyph data to table object 11263 p.glyphTable[url][name] = { 11264 name: name, 11265 unicode: unicode, 11266 horiz_adv_x: horiz_adv_x, 11267 draw: path.draw 11268 }; 11269 } 11270 } // finished adding glyphs to table 11271 }; 11272 11273 // Load and parse Batik SVG font as XML into a Processing Glyph object 11274 var loadXML = function loadXML() { 11275 var xmlDoc; 11276 11277 try { 11278 xmlDoc = document.implementation.createDocument("", "", null); 11279 } 11280 catch(e_fx_op) { 11281 Processing.debug(e_fx_op.message); 11282 return; 11283 } 11284 11285 try { 11286 xmlDoc.async = false; 11287 xmlDoc.load(url); 11288 parseSVGFont(xmlDoc.getElementsByTagName("svg")[0]); 11289 } 11290 catch(e_sf_ch) { 11291 // Google Chrome, Safari etc. 11292 Processing.debug(e_sf_ch); 11293 try { 11294 var xmlhttp = new window.XMLHttpRequest(); 11295 xmlhttp.open("GET", url, false); 11296 xmlhttp.send(null); 11297 parseSVGFont(xmlhttp.responseXML.documentElement); 11298 } 11299 catch(e) { 11300 Processing.debug(e_sf_ch); 11301 } 11302 } 11303 }; 11304 11305 // Create a new object in glyphTable to store this font 11306 p.glyphTable[url] = {}; 11307 11308 // Begin loading the Batik SVG font... 11309 loadXML(url); 11310 11311 // Return the loaded font for attribute grabbing 11312 return p.glyphTable[url]; 11313 }; 11314 11315 //////////////////////////////////////////////////////////////////////////// 11316 // Class methods 11317 //////////////////////////////////////////////////////////////////////////// 11318 11319 p.extendClass = function extendClass(subClass, baseClass) { 11320 function extendGetterSetter(propertyName) { 11321 p.defineProperty(subClass, propertyName, { 11322 get: function() { 11323 return baseClass[propertyName]; 11324 }, 11325 set: function(v) { 11326 baseClass[propertyName]=v; 11327 } 11328 }); 11329 } 11330 11331 for (var propertyName in baseClass) { 11332 if (subClass[propertyName] === undef) { 11333 if (typeof baseClass[propertyName] === 'function') { 11334 subClass[propertyName] = baseClass[propertyName]; 11335 } else { 11336 extendGetterSetter(propertyName); 11337 } 11338 } 11339 } 11340 }; 11341 11342 p.addMethod = function addMethod(object, name, fn, superAccessor) { 11343 if (object[name]) { 11344 var args = fn.length, 11345 oldfn = object[name]; 11346 11347 object[name] = function() { 11348 if (arguments.length === args) { 11349 return fn.apply(this, arguments); 11350 } else { 11351 return oldfn.apply(this, arguments); 11352 } 11353 }; 11354 } else { 11355 object[name] = fn; 11356 } 11357 }; 11358 11359 ////////////////////////////////////////////////////////////////////////// 11360 // Event handling 11361 ////////////////////////////////////////////////////////////////////////// 11362 11363 function attach(elem, type, fn) { 11364 if (elem.addEventListener) { 11365 elem.addEventListener(type, fn, false); 11366 } else { 11367 elem.attachEvent("on" + type, fn); 11368 } 11369 eventHandlers.push([elem, type, fn]); 11370 } 11371 11372 attach(curElement, "mousemove", function(e) { 11373 var element = curElement, offsetX = 0, offsetY = 0; 11374 11375 p.pmouseX = p.mouseX; 11376 p.pmouseY = p.mouseY; 11377 11378 if (element.offsetParent) { 11379 do { 11380 offsetX += element.offsetLeft; 11381 offsetY += element.offsetTop; 11382 } while ((element = element.offsetParent)); 11383 } 11384 11385 // Add padding and border style widths to offset 11386 offsetX += stylePaddingLeft; 11387 offsetY += stylePaddingTop; 11388 11389 offsetX += styleBorderLeft; 11390 offsetY += styleBorderTop; 11391 11392 // Dropping support for IE clientX and clientY, switching to pageX and pageY so we don't have to calculate scroll offset. 11393 // Removed in ticket #184. See rev: 2f106d1c7017fed92d045ba918db47d28e5c16f4 11394 p.mouseX = e.pageX - offsetX; 11395 p.mouseY = e.pageY - offsetY; 11396 11397 if (typeof p.mouseMoved === "function" && !p.__mousePressed) { 11398 p.mouseMoved(); 11399 } 11400 if (typeof p.mouseDragged === "function" && p.__mousePressed) { 11401 p.mouseDragged(); 11402 p.mouseDragging = true; 11403 } 11404 }); 11405 11406 attach(curElement, "mouseout", function(e) { 11407 }); 11408 11409 attach(curElement, "mousedown", function(e) { 11410 p.__mousePressed = true; 11411 p.mouseDragging = false; 11412 switch (e.which) { 11413 case 1: 11414 p.mouseButton = PConstants.LEFT; 11415 break; 11416 case 2: 11417 p.mouseButton = PConstants.CENTER; 11418 break; 11419 case 3: 11420 p.mouseButton = PConstants.RIGHT; 11421 break; 11422 } 11423 11424 if (typeof p.mousePressed === "function") { 11425 p.mousePressed(); 11426 } 11427 }); 11428 11429 attach(curElement, "mouseup", function(e) { 11430 p.__mousePressed = false; 11431 11432 if (typeof p.mouseClicked === "function" && !p.mouseDragging) { 11433 p.mouseClicked(); 11434 } 11435 11436 if (typeof p.mouseReleased === "function") { 11437 p.mouseReleased(); 11438 } 11439 }); 11440 11441 var mouseWheelHandler = function(e) { 11442 var delta = 0; 11443 11444 if (e.wheelDelta) { 11445 delta = e.wheelDelta / 120; 11446 if (window.opera) { 11447 delta = -delta; 11448 } 11449 } else if (e.detail) { 11450 delta = -e.detail / 3; 11451 } 11452 11453 p.mouseScroll = delta; 11454 11455 if (delta && typeof p.mouseScrolled === 'function') { 11456 p.mouseScrolled(); 11457 } 11458 }; 11459 11460 // Support Gecko and non-Gecko scroll events 11461 attach(document, 'DOMMouseScroll', mouseWheelHandler); 11462 attach(document, 'mousewheel', mouseWheelHandler); 11463 11464 ////////////////////////////////////////////////////////////////////////// 11465 // Keyboard Events 11466 ////////////////////////////////////////////////////////////////////////// 11467 11468 function keyCodeMap(code, shift) { 11469 // Letters 11470 if (code >= 65 && code <= 90) { // A-Z 11471 // Keys return ASCII for upcased letters. 11472 // Convert to downcase if shiftKey is not pressed. 11473 if (shift) { 11474 return code; 11475 } 11476 else { 11477 return code + 32; 11478 } 11479 } 11480 11481 // Numbers and their shift-symbols 11482 else if (code >= 48 && code <= 57) { // 0-9 11483 if (shift) { 11484 switch (code) { 11485 case 49: 11486 return 33; // ! 11487 case 50: 11488 return 64; // @ 11489 case 51: 11490 return 35; // # 11491 case 52: 11492 return 36; // $ 11493 case 53: 11494 return 37; // % 11495 case 54: 11496 return 94; // ^ 11497 case 55: 11498 return 38; // & 11499 case 56: 11500 return 42; // * 11501 case 57: 11502 return 40; // ( 11503 case 48: 11504 return 41; // ) 11505 } 11506 } 11507 } 11508 11509 // Coded keys 11510 else if (codedKeys.indexOf(code) >= 0) { // SHIFT, CONTROL, ALT, LEFT, RIGHT, UP, DOWN 11511 p.keyCode = code; 11512 return PConstants.CODED; 11513 } 11514 11515 // Symbols and their shift-symbols 11516 else { 11517 if (shift) { 11518 switch (code) { 11519 case 107: 11520 return 43; // + 11521 case 219: 11522 return 123; // { 11523 case 221: 11524 return 125; // } 11525 case 222: 11526 return 34; // " 11527 } 11528 } else { 11529 switch (code) { 11530 case 188: 11531 return 44; // , 11532 case 109: 11533 return 45; // - 11534 case 190: 11535 return 46; // . 11536 case 191: 11537 return 47; // / 11538 case 192: 11539 return 96; // ~ 11540 case 219: 11541 return 91; // [ 11542 case 220: 11543 return 92; // \ 11544 case 221: 11545 return 93; // ] 11546 case 222: 11547 return 39; // ' 11548 } 11549 } 11550 } 11551 return code; 11552 } 11553 11554 attach(document, "keydown", function(e) { 11555 p.__keyPressed = true; 11556 p.keyCode = null; 11557 p.key = keyCodeMap(e.keyCode, e.shiftKey); 11558 11559 if (typeof p.keyPressed === "function") { 11560 p.keyPressed(); 11561 } 11562 }); 11563 11564 attach(document, "keyup", function(e) { 11565 p.keyCode = null; 11566 p.key = keyCodeMap(e.keyCode, e.shiftKey); 11567 11568 //TODO: This needs to only be made false if all keys have been released. 11569 p.__keyPressed = false; 11570 11571 if (typeof p.keyReleased === "function") { 11572 p.keyReleased(); 11573 } 11574 }); 11575 11576 attach(document, "keypress", function (e) { 11577 // In Firefox, e.keyCode is not currently set with keypress. 11578 // 11579 // keypress will always happen after a keydown, so p.keyCode and p.key 11580 // should remain correct. Some browsers (chrome) refire keydown when 11581 // key repeats happen, others (firefox) don't. Either way keyCode and 11582 // key should remain correct. 11583 11584 if (p.keyTyped) { 11585 p.keyTyped(); 11586 } 11587 }); 11588 11589 // Place-holder for debugging function 11590 Processing.debug = function(e) {}; 11591 11592 // Get the DOM element if string was passed 11593 if (typeof curElement === "string") { 11594 curElement = document.getElementById(curElement); 11595 } 11596 11597 // Send aCode Processing syntax to be converted to JavaScript 11598 if (aCode) { 11599 if(aCode instanceof Processing.Sketch) { 11600 // Use sketch as is 11601 curSketch = aCode; 11602 } else if(typeof aCode === "function") { 11603 // Wrap function with default sketch parameters 11604 curSketch = new Processing.Sketch(aCode); 11605 } else { 11606 // Compile the code 11607 curSketch = Processing.compile(aCode); 11608 } 11609 11610 // Expose internal field for diagnostics and testing 11611 p.externals.sketch = curSketch; 11612 11613 p.use3DContext = curSketch.use3DContext; 11614 11615 if ("mozOpaque" in curElement) { 11616 curElement.mozOpaque = !curSketch.options.isTransparent; 11617 } 11618 11619 // Initialize the onfocus and onblur event handler externals 11620 if (curSketch.options.pauseOnBlur) { 11621 p.externals.onfocus = function() { 11622 if (doLoop) { 11623 p.loop(); 11624 } 11625 }; 11626 11627 p.externals.onblur = function() { 11628 if (doLoop && loopStarted) { 11629 p.noLoop(); 11630 doLoop = true; // make sure to keep this true after the noLoop call 11631 } 11632 }; 11633 } 11634 11635 if (!curSketch.use3DContext) { 11636 // Setup default 2d canvas context. 11637 curContext = curElement.getContext('2d'); 11638 11639 // Externalize the default context 11640 p.externals.context = curContext; 11641 11642 modelView = new PMatrix2D(); 11643 11644 // Canvas has trouble rendering single pixel stuff on whole-pixel 11645 // counts, so we slightly offset it (this is super lame). 11646 curContext.translate(0.5, 0.5); 11647 11648 curContext.lineCap = 'round'; 11649 11650 // Set default stroke and fill color 11651 p.stroke(0); 11652 p.fill(255); 11653 p.noSmooth(); 11654 p.disableContextMenu(); 11655 } 11656 11657 // Step through the libraries that were attached at doc load... 11658 for (var i in Processing.lib) { 11659 if (Processing.lib.hasOwnProperty(i)) { 11660 // Init the libraries in the context of this p_instance 11661 Processing.lib[i].call(this); 11662 } 11663 } 11664 11665 var executeSketch = function(processing) { 11666 // Don't start until all specified images and fonts in the cache are preloaded 11667 if (!curSketch.imageCache.pending && curSketch.fonts.pending()) { 11668 curSketch.attach(processing, PConstants); 11669 11670 // Run void setup() 11671 if (processing.setup) { 11672 processing.setup(); 11673 } 11674 11675 // some pixels can be cached, flushing 11676 resetContext(); 11677 11678 if (processing.draw) { 11679 if (!doLoop) { 11680 processing.redraw(); 11681 } else { 11682 processing.loop(); 11683 } 11684 } 11685 } else { 11686 window.setTimeout(function() { executeSketch(processing); }, 10); 11687 } 11688 }; 11689 11690 // The parser adds custom methods to the processing context 11691 // this renames p to processing so these methods will run 11692 executeSketch(p); 11693 } else { 11694 // No executable sketch was specified 11695 // or called via createGraphics 11696 curSketch = new Processing.Sketch(); 11697 curSketch.options.isTransparent = true; 11698 } 11699 11700 }; 11701 11702 // Processing global methods and constants for the parser 11703 function getGlobalMembers() { 11704 var names = [ /* this code is generated by jsglobals.js */ 11705 "abs", "acos", "alpha", "ambient", "ambientLight", "append", "applyMatrix", 11706 "arc", "arrayCopy", "ArrayList", "asin", "atan", "atan2", "background", 11707 "beginCamera", "beginDraw", "beginShape", "bezier", "bezierDetail", 11708 "bezierPoint", "bezierTangent", "bezierVertex", "binary", "blend", 11709 "blendColor", "blit_resize", "blue", "boolean", "box", "breakShape", 11710 "brightness", "byte", "camera", "ceil", "char", "Character", "clear", 11711 "color", "colorMode", "concat", "console", "constrain", "copy", "cos", 11712 "createFont", "createGraphics", "createImage", "cursor", "curve", 11713 "curveDetail", "curvePoint", "curveTangent", "curveTightness", 11714 "curveVertex", "day", "defaultColor", "degrees", "directionalLight", 11715 "disableContextMenu", "dist", "draw", "ellipse", "ellipseMode", "emissive", 11716 "enableContextMenu", "endCamera", "endDraw", "endShape", "exit", "exp", 11717 "expand", "externals", "fill", "filter", "filter_bilinear", 11718 "filter_new_scanline", "float", "floor", "focused", "frameCount", 11719 "frameRate", "frustum", "get", "glyphLook", "glyphTable", "green", 11720 "HashMap", "height", "hex", "hint", "hour", "hue", "image", "imageMode", 11721 "Import", "int", "intersect", "join", "key", "keyCode", "keyPressed", 11722 "keyReleased", "keyTyped", "lerp", "lerpColor", "lightFalloff", "lights", 11723 "lightSpecular", "line", "link", "loadBytes", "loadFont", "loadGlyphs", 11724 "loadImage", "loadPixels", "loadShape", "loadStrings", "log", "loop", 11725 "mag", "map", "match", "matchAll", "max", "millis", "min", "minute", "mix", 11726 "modelX", "modelY", "modelZ", "modes", "month", "mouseButton", 11727 "mouseClicked", "mouseDragged", "mouseMoved", "mousePressed", 11728 "mouseReleased", "mouseScroll", "mouseScrolled", "mouseX", "mouseY", 11729 "name", "nf", "nfc", "nfp", "nfs", "noCursor", "noFill", "noise", 11730 "noiseDetail", "noiseSeed", "noLights", "noLoop", "norm", "normal", 11731 "noSmooth", "noStroke", "noTint", "ortho", "peg", "perspective", "PImage", 11732 "pixels", "PMatrix2D", "PMatrix3D", "PMatrixStack", "pmouseX", "pmouseY", 11733 "point", "pointLight", "popMatrix", "popStyle", "pow", "print", 11734 "printCamera", "println", "printMatrix", "printProjection", "PShape", 11735 "pushMatrix", "pushStyle", "PVector", "quad", "radians", "random", 11736 "Random", "randomSeed", "rect", "rectMode", "red", "redraw", 11737 "requestImage", "resetMatrix", "reverse", "rotate", "rotateX", "rotateY", 11738 "rotateZ", "round", "saturation", "save", "saveStrings", "scale", 11739 "screenX", "screenY", "screenZ", "second", "set", "setup", "shape", 11740 "shapeMode", "shared", "shininess", "shorten", "sin", "size", "smooth", 11741 "sort", "specular", "sphere", "sphereDetail", "splice", "split", 11742 "splitTokens", "spotLight", "sq", "sqrt", "status", "str", "stroke", 11743 "strokeCap", "strokeJoin", "strokeWeight", "subset", "tan", "text", 11744 "textAlign", "textAscent", "textDescent", "textFont", "textMode", 11745 "textSize", "texture", "textureMode", "textWidth", "tint", "translate", 11746 "triangle", "trim", "unbinary", "unhex", "updatePixels", "use3DContext", 11747 "vertex", "width", "XMLElement", "year", "__frameRate", "__keyPressed", 11748 "__mousePressed"]; 11749 11750 var members = {}; 11751 var i, l; 11752 for (i = 0, l = names.length; i < l ; ++i) { 11753 members[names[i]] = null; 11754 } 11755 for (var lib in Processing.lib) { 11756 if (Processing.lib.hasOwnProperty(lib)) { 11757 if (Processing.lib[lib].exports) { 11758 var exportedNames = Processing.lib[lib].exports; 11759 for (i = 0, l = exportedNames.length; i < l; ++i) { 11760 members[exportedNames[i]] = null; 11761 } 11762 } 11763 } 11764 } 11765 return members; 11766 } 11767 11768 // Parser starts 11769 function parseProcessing(code) { 11770 var globalMembers = getGlobalMembers(); 11771 11772 function splitToAtoms(code) { 11773 var atoms = []; 11774 var items = code.split(/([\{\[\(\)\]\}])/); 11775 var result = items[0]; 11776 11777 var stack = []; 11778 for(var i=1; i < items.length; i += 2) { 11779 var item = items[i]; 11780 if(item === '[' || item === '{' || item === '(') { 11781 stack.push(result); result = item; 11782 } else if(item === ']' || item === '}' || item === ')') { 11783 var kind = item === '}' ? 'A' : item === ')' ? 'B' : 'C'; 11784 var index = atoms.length; atoms.push(result + item); 11785 result = stack.pop() + '"' + kind + (index + 1) + '"'; 11786 } 11787 result += items[i + 1]; 11788 } 11789 atoms.unshift(result); 11790 return atoms; 11791 } 11792 11793 function injectStrings(code, strings) { 11794 return code.replace(/'(\d+)'/g, function(all, index) { 11795 var val = strings[index]; 11796 if(val.charAt(0) === "/") { 11797 return val; 11798 } else { 11799 return (/^'((?:[^'\\\n])|(?:\\.[0-9A-Fa-f]*))'$/).test(val) ? "(new processing.Character(" + val + "))" : val; 11800 } 11801 }); 11802 } 11803 11804 function trimSpaces(string) { 11805 var m1 = /^\s*/.exec(string), result; 11806 if(m1[0].length === string.length) { 11807 result = {left: m1[0], middle: "", right: ""}; 11808 } else { 11809 var m2 = /\s*$/.exec(string); 11810 result = {left: m1[0], middle: string.substring(m1[0].length, m2.index), right: m2[0]}; 11811 } 11812 result.untrim = function(t) { return this.left + t + this.right; }; 11813 return result; 11814 } 11815 11816 function trim(string) { 11817 return string.replace(/^\s+/,'').replace(/\s+$/,''); 11818 } 11819 11820 function appendToLookupTable(table, array) { 11821 for(var i=0,l=array.length;i<l;++i) { 11822 table[array[i]] = null; 11823 } 11824 return table; 11825 } 11826 11827 function isLookupTableEmpty(table) { 11828 for(var i in table) { 11829 if(table.hasOwnProperty(i)) { 11830 return false; 11831 } 11832 } 11833 return true; 11834 } 11835 11836 function getAtomIndex(templ) { return templ.substring(2, templ.length - 1); } 11837 11838 var codeWoExtraCr = code.replace(/\r\n?|\n\r/g, "\n"); 11839 11840 var strings = []; 11841 var codeWoStrings = codeWoExtraCr.replace(/("(?:[^"\\\n]|\\.)*")|('(?:[^'\\\n]|\\.)*')|(([\[\(=|&!\^:?]\s*)(\/(?![*\/])(?:[^\/\\\n]|\\.)*\/[gim]*)\b)|(\/\/[^\n]*\n)|(\/\*(?:(?!\*\/)(?:.|\n))*\*\/)/g, 11842 function(all, quoted, aposed, regexCtx, prefix, regex, singleComment, comment) { 11843 var index; 11844 if(quoted || aposed) { // replace strings 11845 index = strings.length; strings.push(all); 11846 return "'" + index + "'"; 11847 } else if(regexCtx) { // replace RegExps 11848 index = strings.length; strings.push(regex); 11849 return prefix + "'" + index + "'"; 11850 } else { // kill comments 11851 return comment !== "" ? " " : "\n"; 11852 } 11853 }); 11854 11855 var atoms = splitToAtoms(codeWoStrings); 11856 var replaceContext; 11857 var declaredClasses = {}, currentClassId, classIdSeed = 0; 11858 11859 function addAtom(text, type) { 11860 var lastIndex = atoms.length; 11861 atoms.push(text); 11862 return '"' + type + lastIndex + '"'; 11863 } 11864 11865 function generateClassId() { 11866 return "class" + (++classIdSeed); 11867 } 11868 11869 function appendClass(class_, classId, scopeId) { 11870 class_.classId = classId; 11871 class_.scopeId = scopeId; 11872 declaredClasses[classId] = class_; 11873 } 11874 11875 // function defined below 11876 var transformClassBody, transformStatementsBlock, transformStatements, transformMain, transformExpression; 11877 11878 var classesRegex = /\b((?:(?:public|private|final|protected|static|abstract)\s+)*)(class|interface)\s+([A-Za-z_$][\w$]*\b)(\s+extends\s+[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*\b)?(\s+implements\s+[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*(?:\s*,\s*[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*\b)*)?\s*("A\d+")/g; 11879 var methodsRegex = /\b((?:(?:public|private|final|protected|static|abstract)\s+)*)((?!(?:else|new|return|throw|function|public|private|protected)\b)[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*(?:\s*"C\d+")*)\s*([A-Za-z_$][\w$]*\b)\s*("B\d+")(\s*throws\s+[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*(?:\s*,\s*[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*)*)?\s*("A\d+"|;)/g; 11880 var fieldTest = /^((?:(?:public|private|final|protected|static)\s+)*)((?!(?:else|new|return|throw)\b)[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*(?:\s*"C\d+")*)\s*([A-Za-z_$][\w$]*\b)\s*(?:"C\d+"\s*)*([=,]|$)/; 11881 var cstrsRegex = /\b((?:(?:public|private|final|protected|static|abstract)\s+)*)((?!(?:new|return|throw)\b)[A-Za-z_$][\w$]*\b)\s*("B\d+")(\s*throws\s+[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*(?:\s*,\s*[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*)*)?\s*("A\d+")/g; 11882 var attrAndTypeRegex = /^((?:(?:public|private|final|protected|static)\s+)*)((?!(?:new|return|throw)\b)[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*(?:\s*"C\d+")*)\s*/; 11883 var functionsRegex = /\bfunction(?:\s+([A-Za-z_$][\w$]*))?\s*("B\d+")\s*("A\d+")/g; 11884 11885 function extractClassesAndMethods(code) { 11886 var s = code; 11887 s = s.replace(classesRegex, function(all) { 11888 return addAtom(all, 'E'); 11889 }); 11890 s = s.replace(methodsRegex, function(all) { 11891 return addAtom(all, 'D'); 11892 }); 11893 s = s.replace(functionsRegex, function(all) { 11894 return addAtom(all, 'H'); 11895 }); 11896 return s; 11897 } 11898 11899 function extractConstructors(code, className) { 11900 var result = code.replace(cstrsRegex, function(all, attr, name, params, throws_, body) { 11901 if(name !== className) { 11902 return all; 11903 } else { 11904 return addAtom(all, 'G'); 11905 } 11906 }); 11907 return result; 11908 } 11909 11910 function AstParam(name) { 11911 this.name = name; 11912 } 11913 AstParam.prototype.toString = function() { 11914 return this.name; 11915 }; 11916 function AstParams(params) { 11917 this.params = params; 11918 } 11919 AstParams.prototype.getNames = function() { 11920 var names = []; 11921 for(var i=0,l=this.params.length;i<l;++i) { 11922 names.push(this.params[i].name); 11923 } 11924 return names; 11925 }; 11926 AstParams.prototype.toString = function() { 11927 if(this.params.length === 0) { 11928 return "()"; 11929 } 11930 var result = "("; 11931 for(var i=0,l=this.params.length;i<l;++i) { 11932 result += this.params[i] + ", "; 11933 } 11934 return result.substring(0, result.length - 2) + ")"; 11935 }; 11936 11937 function transformParams(params) { 11938 var paramsWoPars = trim(params.substring(1, params.length - 1)); 11939 var result = []; 11940 if(paramsWoPars !== "") { 11941 var paramList = paramsWoPars.split(","); 11942 for(var i=0; i < paramList.length; ++i) { 11943 var param = /\b([A-Za-z_$][\w$]*\b)\s*("[ABC][\d]*")?$/.exec(paramList[i]); 11944 result.push(new AstParam(param[1])); 11945 } 11946 } 11947 return new AstParams(result); 11948 } 11949 11950 function preExpressionTransform(expr) { 11951 var s = expr; 11952 // new type[] {...} --> {...} 11953 s = s.replace(/\bnew\s+([A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*)(?:\s*"C\d+")+\s*("A\d+")/g, function(all, type, init) { 11954 return init; 11955 }); 11956 // new Runnable() {...} --> "F???" 11957 s = s.replace(/\bnew\s+([A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*)(?:\s*"B\d+")\s*("A\d+")/g, function(all, type, init) { 11958 return addAtom(all, 'F'); 11959 }); 11960 // function(...) { } --> "H???" 11961 s = s.replace(functionsRegex, function(all) { 11962 return addAtom(all, 'H'); 11963 }); 11964 // new type[?] --> new ArrayList(?) 11965 s = s.replace(/\bnew\s+([A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*)\s*("C\d+"(?:\s*"C\d+")*)/g, function(all, type, index) { 11966 var args = index.replace(/"C(\d+)"/g, function(all, j) { return atoms[j]; }). 11967 replace(/\[\s*\]/g, "[0]").replace(/\s*\]\s*\[\s*/g, ", "); 11968 11969 var arrayInitializer = "(" + args.substring(1, args.length - 1) + ")"; 11970 return 'new ArrayList' + addAtom(arrayInitializer, 'B'); 11971 }); 11972 // .length() --> .length 11973 s = s.replace(/(\.\s*length)\s*"B\d+"/g, "$1"); 11974 // #000000 --> 0x000000 11975 s = s.replace(/#([0-9A-Fa-f]{6})\b/g, function(all, digits) { 11976 return "0xFF" + digits; 11977 }); 11978 // delete (type)???, (int)??? -> 0|??? 11979 s = s.replace(/"B(\d+)"(\s*(?:[\w$']|"B))/g, function(all, index, next) { 11980 var atom = atoms[index]; 11981 if(!/^\(\s*[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*\s*(?:"C\d+"\s*)*\)$/.test(atom)) { 11982 return all; 11983 } else if(/^\(\s*int\s*\)$/.test(atom)) { 11984 return "0|" + next; 11985 } else { 11986 var indexParts = atom.split(/"C(\d+)"/g); 11987 if(indexParts.length > 1) { 11988 // even items contains atom numbers, can check only first 11989 if(! /^\[\s*\]$/.test(atoms[indexParts[1]])) { 11990 return all; // fallback - not a cast 11991 } 11992 } 11993 return "" + next; 11994 } 11995 }); 11996 // super() -> $superCstr(), super. -> $super.; 11997 s = s.replace(/\bsuper(\s*"B\d+")/g, "$$superCstr$1").replace(/\bsuper(\s*\.)/g, "$$super$1"); 11998 // 3.0f -> 3.0 11999 s = s.replace(/\b(\.?\d+)[fF]/g, "$1"); 12000 // Weird (?) parsing errors with % 12001 s = s.replace(/([^\s])%([^=\s])/g, "$1 % $2"); 12002 // Since frameRate() and frameRate are different things, 12003 // we need to differentiate them somehow. So when we parse 12004 // the Processing.js source, replace frameRate so it isn't 12005 // confused with frameRate(), as well as keyPressed and mousePressed 12006 s = s.replace(/\b(frameRate|keyPressed|mousePressed)\b(?!\s*"B)/g, "__$1"); 12007 // "pixels" replacements: 12008 // pixels[i] = c => pixels.setPixel(i,c) | pixels[i] => pixels.getPixel(i) 12009 // pixels.length => pixels.getLength() 12010 // pixels = ar => pixels.set(ar) | pixels => pixels.toArray() 12011 s = s.replace(/\bpixels\s*(("C(\d+)")|\.length)?(\s*=(?!=)([^,\]\)\}\?\:]+))?/g, 12012 function(all, indexOrLength, index, atomIndex, equalsPart, rightSide) { 12013 if(index) { 12014 var atom = atoms[atomIndex]; 12015 if(equalsPart) { 12016 return "pixels.setPixel" + addAtom("(" +atom.substring(1, atom.length - 1) + 12017 "," + rightSide + ")", 'B'); 12018 } else { 12019 return "pixels.getPixel" + addAtom("(" + atom.substring(1, atom.length - 1) + 12020 ")", 'B'); 12021 } 12022 } else if(indexOrLength) { 12023 // length 12024 return "pixels.getLength" + addAtom("()", 'B'); 12025 } else { 12026 if(equalsPart) { 12027 return "pixels.set" + addAtom("(" + rightSide + ")", 'B'); 12028 } else { 12029 return "pixels.toArray" + addAtom("()", 'B'); 12030 } 12031 } 12032 }); 12033 // this() -> $constr() 12034 s = s.replace(/\bthis(\s*"B\d+")/g, "$$constr$1"); 12035 12036 return s; 12037 } 12038 12039 function AstInlineClass(baseInterfaceName, body) { 12040 this.baseInterfaceName = baseInterfaceName; 12041 this.body = body; 12042 body.owner = this; 12043 } 12044 AstInlineClass.prototype.toString = function() { 12045 return "new (function() {\n" + this.body + "})"; 12046 }; 12047 12048 function transformInlineClass(class_) { 12049 var m = new RegExp(/\bnew\s*(Runnable)\s*"B\d+"\s*"A(\d+)"/).exec(class_); 12050 if(m === null) { 12051 return "null"; 12052 } else { 12053 var oldClassId = currentClassId, newClassId = generateClassId(); 12054 currentClassId = newClassId; 12055 // only Runnable supported 12056 var inlineClass = new AstInlineClass("Runnable", transformClassBody(atoms[m[2]], m[1])); 12057 appendClass(inlineClass, newClassId, oldClassId); 12058 12059 currentClassId = oldClassId; 12060 return inlineClass; 12061 } 12062 } 12063 12064 function AstFunction(name, params, body) { 12065 this.name = name; 12066 this.params = params; 12067 this.body = body; 12068 } 12069 AstFunction.prototype.toString = function() { 12070 var oldContext = replaceContext; 12071 // saving "this." and parameters 12072 var names = appendToLookupTable({"this":null}, this.params.getNames()); 12073 replaceContext = function(name) { 12074 return names.hasOwnProperty(name) ? name : oldContext(name); 12075 }; 12076 var result = "function"; 12077 if(this.name) { 12078 result += " " + this.name; 12079 } 12080 result += this.params + " " + this.body; 12081 replaceContext = oldContext; 12082 return result; 12083 }; 12084 12085 function transformFunction(class_) { 12086 var m = new RegExp(/\b([A-Za-z_$][\w$]*)\s*"B(\d+)"\s*"A(\d+)"/).exec(class_); 12087 return new AstFunction( m[1] !== "function" ? m[1] : null, 12088 transformParams(atoms[m[2]]), transformStatementsBlock(atoms[m[3]])); 12089 } 12090 12091 function AstInlineObject(members) { 12092 this.members = members; 12093 } 12094 AstInlineObject.prototype.toString = function() { 12095 var oldContext = replaceContext; 12096 replaceContext = function(name) { 12097 return name === "this"? name : oldContext(name); // saving "this." 12098 }; 12099 var result = ""; 12100 for(var i=0,l=this.members.length;i<l;++i) { 12101 if(this.members[i].label) { 12102 result += this.members[i].label + ": "; 12103 } 12104 result += this.members[i].value.toString() + ", "; 12105 } 12106 replaceContext = oldContext; 12107 return result.substring(0, result.length - 2); 12108 }; 12109 12110 function transformInlineObject(obj) { 12111 var members = obj.split(','); 12112 for(var i=0; i < members.length; ++i) { 12113 var label = members[i].indexOf(':'); 12114 if(label < 0) { 12115 members[i] = { value: transformExpression(members[i]) }; 12116 } else { 12117 members[i] = { label: trim(members[i].substring(0, label)), 12118 value: transformExpression( trim(members[i].substring(label + 1)) ) }; 12119 } 12120 } 12121 return new AstInlineObject(members); 12122 } 12123 12124 function expandExpression(expr) { 12125 if(expr.charAt(0) === '(' || expr.charAt(0) === '[') { 12126 return expr.charAt(0) + expandExpression(expr.substring(1, expr.length - 1)) + expr.charAt(expr.length - 1); 12127 } else if(expr.charAt(0) === '{') { 12128 if(/^\{\s*(?:[A-Za-z_$][\w$]*|'\d+')\s*:/.test(expr)) { 12129 return "{" + addAtom(expr.substring(1, expr.length - 1), 'I') + "}"; 12130 } else { 12131 return "[" + expandExpression(expr.substring(1, expr.length - 1)) + "]"; 12132 } 12133 } else { 12134 var trimmed = trimSpaces(expr); 12135 var result = preExpressionTransform(trimmed.middle); 12136 result = result.replace(/"[ABC](\d+)"/g, function(all, index) { 12137 return expandExpression(atoms[index]); 12138 }); 12139 return trimmed.untrim(result); 12140 } 12141 } 12142 12143 function replaceContextInVars(expr) { 12144 return expr.replace(/(\.\s*)?(\b[A-Za-z_$][\w$]*\b)/g, 12145 function(all, memberAccessSign, identifier) { 12146 if(memberAccessSign) { 12147 return all; 12148 } else { 12149 return replaceContext(identifier); 12150 } 12151 }); 12152 } 12153 12154 function AstExpression(expr, transforms) { 12155 this.expr = expr; 12156 this.transforms = transforms; 12157 } 12158 AstExpression.prototype.toString = function() { 12159 var transforms = this.transforms; 12160 var expr = replaceContextInVars(this.expr); 12161 return expr.replace(/"!(\d+)"/g, function(all, index) { 12162 return transforms[index].toString(); 12163 }); 12164 }; 12165 12166 transformExpression = function(expr) { 12167 var transforms = []; 12168 var s = expandExpression(expr); 12169 s = s.replace(/"H(\d+)"/g, function(all, index) { 12170 transforms.push(transformFunction(atoms[index])); 12171 return '"!' + (transforms.length - 1) + '"'; 12172 }); 12173 s = s.replace(/"F(\d+)"/g, function(all, index) { 12174 transforms.push(transformInlineClass(atoms[index])); 12175 return '"!' + (transforms.length - 1) + '"'; 12176 }); 12177 s = s.replace(/"I(\d+)"/g, function(all, index) { 12178 transforms.push(transformInlineObject(atoms[index])); 12179 return '"!' + (transforms.length - 1) + '"'; 12180 }); 12181 12182 return new AstExpression(s, transforms); 12183 }; 12184 12185 function AstVarDefinition(name, value, isDefault) { 12186 this.name = name; 12187 this.value = value; 12188 this.isDefault = isDefault; 12189 } 12190 AstVarDefinition.prototype.toString = function() { 12191 return this.name + ' = ' + this.value; 12192 }; 12193 12194 function transformVarDefinition(def, defaultTypeValue) { 12195 var eqIndex = def.indexOf("="); 12196 var name, value, isDefault; 12197 if(eqIndex < 0) { 12198 name = def; 12199 value = defaultTypeValue; 12200 isDefault = true; 12201 } else { 12202 name = def.substring(0, eqIndex); 12203 value = transformExpression(def.substring(eqIndex + 1)); 12204 isDefault = false; 12205 } 12206 return new AstVarDefinition( trim(name.replace(/(\s*"C\d+")+/g, "")), 12207 value, isDefault); 12208 } 12209 12210 function getDefaultValueForType(type) { 12211 if(type === "int" || type === "float") { 12212 return "0"; 12213 } else if(type === "boolean") { 12214 return "false"; 12215 } else if(type === "color") { 12216 return "0x00000000"; 12217 } else { 12218 return "null"; 12219 } 12220 } 12221 12222 function AstVar(definitions, varType) { 12223 this.definitions = definitions; 12224 this.varType = varType; 12225 } 12226 AstVar.prototype.getNames = function() { 12227 var names = []; 12228 for(var i=0,l=this.definitions.length;i<l;++i) { 12229 names.push(this.definitions[i].name); 12230 } 12231 return names; 12232 }; 12233 AstVar.prototype.toString = function() { 12234 return "var " + this.definitions.join(","); 12235 }; 12236 function AstStatement(expression) { 12237 this.expression = expression; 12238 } 12239 AstStatement.prototype.toString = function() { 12240 return this.expression.toString(); 12241 }; 12242 12243 function transformStatement(statement) { 12244 if(fieldTest.test(statement)) { 12245 var attrAndType = attrAndTypeRegex.exec(statement); 12246 var definitions = statement.substring(attrAndType[0].length).split(","); 12247 var defaultTypeValue = getDefaultValueForType(attrAndType[2]); 12248 for(var i=0; i < definitions.length; ++i) { 12249 definitions[i] = transformVarDefinition(definitions[i], defaultTypeValue); 12250 } 12251 return new AstVar(definitions, attrAndType[2]); 12252 } else { 12253 return new AstStatement(transformExpression(statement)); 12254 } 12255 } 12256 12257 function AstForExpression(initStatement, condition, step) { 12258 this.initStatement = initStatement; 12259 this.condition = condition; 12260 this.step = step; 12261 } 12262 AstForExpression.prototype.toString = function() { 12263 return "(" + this.initStatement + "; " + this.condition + "; " + this.step + ")"; 12264 }; 12265 12266 function AstForInExpression(initStatement, container) { 12267 this.initStatement = initStatement; 12268 this.container = container; 12269 } 12270 AstForInExpression.prototype.toString = function() { 12271 var init = this.initStatement.toString(); 12272 if(init.indexOf("=") >= 0) { // can be without var declaration 12273 init = init.substring(0, init.indexOf("=")); 12274 } 12275 return "(" + init + " in " + this.container + ")"; 12276 }; 12277 12278 function transformForExpression(expr) { 12279 var content; 12280 if(/\bin\b/.test(expr)) { 12281 content = expr.substring(1, expr.length - 1).split(/\bin\b/g); 12282 return new AstForInExpression( transformStatement(trim(content[0])), 12283 transformExpression(content[1])); 12284 } else { 12285 content = expr.substring(1, expr.length - 1).split(";"); 12286 return new AstForExpression( transformStatement(trim(content[0])), 12287 transformExpression(content[1]), transformExpression(content[2])); 12288 } 12289 } 12290 12291 function AstInnerInterface(name) { 12292 this.name = name; 12293 } 12294 AstInnerInterface.prototype.toString = function() { 12295 return "this." + this.name + " = function " + this.name + "() { "+ 12296 "throw 'This is an interface'; };"; 12297 }; 12298 function AstInnerClass(name, body) { 12299 this.name = name; 12300 this.body = body; 12301 body.owner = this; 12302 } 12303 AstInnerClass.prototype.toString = function() { 12304 return "this." + this.name + " = function " + this.name + "() {\n" + 12305 this.body + "};"; 12306 }; 12307 12308 function transformInnerClass(class_) { 12309 var m = classesRegex.exec(class_); // 1 - attr, 2 - class|int, 3 - name, 4 - extends, 5 - implements, 6 - body 12310 classesRegex.lastIndex = 0; 12311 var body = atoms[getAtomIndex(m[6])]; 12312 if(m[2] === "interface") { 12313 return new AstInnerInterface(m[3]); 12314 } else { 12315 var oldClassId = currentClassId, newClassId = generateClassId(); 12316 currentClassId = newClassId; 12317 var innerClass = new AstInnerClass(m[3], transformClassBody(body, m[3], m[4], m[5])); 12318 appendClass(innerClass, newClassId, oldClassId); 12319 currentClassId = oldClassId; 12320 return innerClass; 12321 } 12322 } 12323 12324 function AstClassMethod(name, params, body) { 12325 this.name = name; 12326 this.params = params; 12327 this.body = body; 12328 } 12329 AstClassMethod.prototype.toString = function(){ 12330 var thisReplacement = replaceContext("this"); 12331 var paramNames = appendToLookupTable({}, this.params.getNames()); 12332 var oldContext = replaceContext; 12333 replaceContext = function(name) { 12334 return paramNames.hasOwnProperty(name) ? name : oldContext(name); 12335 }; 12336 var result = "processing.addMethod(" + thisReplacement + ", '" + this.name + "', function " + this.params + " " + 12337 this.body +");"; 12338 replaceContext = oldContext; 12339 return result; 12340 }; 12341 12342 function transformClassMethod(method) { 12343 var m = methodsRegex.exec(method); 12344 methodsRegex.lastIndex = 0; 12345 return new AstClassMethod(m[3], transformParams(atoms[getAtomIndex(m[4])]), 12346 transformStatementsBlock(atoms[getAtomIndex(m[6])]) ); 12347 } 12348 12349 function AstClassField(definitions, fieldType, isStatic) { 12350 this.definitions = definitions; 12351 this.fieldType = fieldType; 12352 this.isStatic = isStatic; 12353 } 12354 AstClassField.prototype.getNames = function() { 12355 var names = []; 12356 for(var i=0,l=this.definitions.length;i<l;++i) { 12357 names.push(this.definitions[i].name); 12358 } 12359 return names; 12360 }; 12361 AstClassField.prototype.toString = function() { 12362 var thisPrefix = replaceContext("this") + "."; 12363 if(this.isStatic) { 12364 var className = this.owner.name; 12365 var staticDeclarations = []; 12366 for(var i=0,l=this.definitions.length;i<l;++i) { 12367 var definition = this.definitions[i]; 12368 var name = definition.name, staticName = className + "." + name; 12369 var declaration = "if(" + staticName + " === void(0)) {\n" + 12370 " " + staticName + " = " + definition.value + "; }\n" + 12371 "processing.defineProperty(" + replaceContext("this") + ", " + 12372 "'" + name + "', { get: function(){return " + staticName + ";}, " + 12373 "set: function(val){" + staticName + " = val;} });\n"; 12374 staticDeclarations.push(declaration); 12375 } 12376 return staticDeclarations.join(""); 12377 } else { 12378 return thisPrefix + this.definitions.join("; " + thisPrefix); 12379 } 12380 }; 12381 12382 function transformClassField(statement) { 12383 var attrAndType = attrAndTypeRegex.exec(statement); 12384 var isStatic = attrAndType[1].indexOf("static") >= 0; 12385 var definitions = statement.substring(attrAndType[0].length).split(/,\s*/g); 12386 var defaultTypeValue = getDefaultValueForType(attrAndType[2]); 12387 for(var i=0; i < definitions.length; ++i) { 12388 definitions[i] = transformVarDefinition(definitions[i], defaultTypeValue); 12389 } 12390 return new AstClassField(definitions, attrAndType[2], isStatic); 12391 } 12392 12393 function AstConstructor(params, body) { 12394 this.params = params; 12395 this.body = body; 12396 } 12397 AstConstructor.prototype.toString = function() { 12398 var paramNames = appendToLookupTable({}, this.params.getNames()); 12399 var oldContext = replaceContext; 12400 replaceContext = function(name) { 12401 return paramNames.hasOwnProperty(name) ? name : oldContext(name); 12402 }; 12403 var prefix = "function $constr_" + this.params.params.length + this.params.toString(); 12404 var body = this.body.toString(); 12405 if(!/\$(superCstr|constr)\b/.test(body)) { 12406 body = "{\n$superCstr();\n" + body.substring(1); 12407 } 12408 replaceContext = oldContext; 12409 return prefix + body + "\n"; 12410 }; 12411 12412 function transformConstructor(cstr) { 12413 var m = new RegExp(/"B(\d+)"\s*"A(\d+)"/).exec(cstr); 12414 var params = transformParams(atoms[m[1]]); 12415 12416 return new AstConstructor(params, transformStatementsBlock(atoms[m[2]])); 12417 } 12418 12419 function AstClassBody(name, baseClassName, functions, methods, fields, cstrs, innerClasses, misc) { 12420 var i,l; 12421 this.name = name; 12422 this.baseClassName = baseClassName; 12423 this.functions = functions; 12424 this.methods = methods; 12425 this.fields = fields; 12426 this.cstrs = cstrs; 12427 this.innerClasses = innerClasses; 12428 this.misc = misc; 12429 for(i=0,l=fields.length; i<l; ++i) { 12430 fields[i].owner = this; 12431 } 12432 } 12433 AstClassBody.prototype.getMembers = function() { 12434 var members; 12435 if(this.owner.base) { 12436 members = this.owner.base.body.getMembers(); 12437 } else { 12438 members = { fields: [], methods: [], innerClasses: [] }; 12439 } 12440 var i, j, l, m; 12441 for(i=0,l=this.fields.length;i<l;++i) { 12442 members.fields = members.fields.concat(this.fields[i].getNames()); 12443 } 12444 for(i=0,l=this.methods.length;i<l;++i) { 12445 var method = this.methods[i]; 12446 members.methods.push(method.name); 12447 } 12448 for(i=0,l=this.innerClasses.length;i<l;++i) { 12449 var innerClass = this.innerClasses[i]; 12450 members.innerClasses.push(innerClass.name); 12451 } 12452 return members; 12453 }; 12454 AstClassBody.prototype.toString = function() { 12455 function getScopeLevel(p) { 12456 var i = 0; 12457 while(p) { 12458 ++i; 12459 p=p.scope; 12460 } 12461 return i; 12462 } 12463 12464 var scopeLevel = getScopeLevel(this.owner); 12465 12466 var selfId = "$this_" + scopeLevel; 12467 var result = "var " + selfId + " = this;\n"; 12468 12469 var members = this.getMembers(); 12470 var thisClassFields = appendToLookupTable({}, members.fields), 12471 thisClassMethods = appendToLookupTable({}, members.methods), 12472 thisClassInners = appendToLookupTable({}, members.innerClasses); 12473 12474 var oldContext = replaceContext; 12475 replaceContext = function(name) { 12476 if(name === "this") { 12477 return selfId; 12478 } else if(thisClassFields.hasOwnProperty(name) || thisClassInners.hasOwnProperty(name)) { 12479 return selfId + "." + name; 12480 } else if(thisClassMethods.hasOwnProperty(name)) { 12481 return "this." + name; 12482 } 12483 return oldContext(name); 12484 }; 12485 12486 if(this.baseClassName) { 12487 result += "var $super = {};\n"; 12488 result += "function $superCstr(){\n" + 12489 this.baseClassName + ".prototype.constructor.apply($super, arguments);\n" + 12490 "processing.extendClass(" + selfId + ", $super); }\n"; 12491 } else { 12492 result += "function $superCstr() { }\n"; 12493 } 12494 12495 result += this.functions.join('\n') + '\n'; 12496 result += this.innerClasses.join('\n'); 12497 12498 result += this.fields.join(";\n") + ";\n"; 12499 result += this.methods.join('\n') + '\n'; 12500 result += this.misc.tail; 12501 12502 result += this.cstrs.join('\n') + '\n'; 12503 12504 result += "function $constr() {\n"; 12505 var cstrsIfs = []; 12506 for(var i=0,l=this.cstrs.length;i<l;++i) { 12507 var paramsLength = this.cstrs[i].params.params.length; 12508 cstrsIfs.push("if(arguments.length === " + paramsLength + ") { " + 12509 "$constr_" + paramsLength + ".apply(" + selfId + ", arguments); }"); 12510 } 12511 if(cstrsIfs.length > 0) { 12512 result += cstrsIfs.join(" else ") + " else "; 12513 } 12514 // ??? add check if length is 0, otherwise fail 12515 result += "$superCstr(); }\n"; 12516 result += "$constr.apply(null, arguments);\n"; 12517 12518 replaceContext = oldContext; 12519 return result; 12520 }; 12521 12522 transformClassBody = function(body, name, baseName, impls) { 12523 var declarations = body.substring(1, body.length - 1); 12524 declarations = extractClassesAndMethods(declarations); 12525 declarations = extractConstructors(declarations, name); 12526 var methods = [], classes = [], cstrs = [], functions = []; 12527 declarations = declarations.replace(/"([DEGH])(\d+)"/g, function(all, type, index) { 12528 if(type === 'D') { methods.push(index); } 12529 else if(type === 'E') { classes.push(index); } 12530 else if(type === 'H') { functions.push(index); } 12531 else { cstrs.push(index); } 12532 return ""; 12533 }); 12534 var fields = declarations.split(';'); 12535 var baseClassName; 12536 var i; 12537 12538 if(baseName !== undef) { 12539 baseClassName = baseName.replace(/^\s*extends\s+([A-Za-z_$][\w$]*)\s*$/g, "$1"); 12540 } 12541 12542 for(i = 0; i < functions.length; ++i) { 12543 functions[i] = transformFunction(atoms[functions[i]]); 12544 } 12545 for(i = 0; i < methods.length; ++i) { 12546 methods[i] = transformClassMethod(atoms[methods[i]]); 12547 } 12548 for(i = 0; i < fields.length - 1; ++i) { 12549 var field = trimSpaces(fields[i]); 12550 fields[i] = transformClassField(field.middle); 12551 } 12552 var tail = fields.pop(); 12553 for(i = 0; i < cstrs.length; ++i) { 12554 cstrs[i] = transformConstructor(atoms[cstrs[i]]); 12555 } 12556 for(i = 0; i < classes.length; ++i) { 12557 classes[i] = transformInnerClass(atoms[classes[i]]); 12558 } 12559 12560 return new AstClassBody(name, baseClassName, functions, methods, fields, cstrs, 12561 classes, { tail: tail }); 12562 }; 12563 12564 function AstInterface(name) { 12565 this.name = name; 12566 } 12567 AstInterface.prototype.toString = function() { 12568 return "function " + this.name + "() { throw 'This is an interface'; }\n" + 12569 "processing." + this.name + " = " + this.name + ";"; 12570 }; 12571 function AstClass(name, body) { 12572 this.name = name; 12573 this.body = body; 12574 body.owner = this; 12575 } 12576 AstClass.prototype.toString = function() { 12577 var staticVars = ""; 12578 for (var i = 0, l = this.body.fields.length; i < l; i++) { 12579 if (this.body.fields[i].isStatic) { 12580 for (var x = 0, xl = this.body.fields[i].definitions.length; x < xl; x++) { 12581 staticVars += "var " + this.body.fields[i].definitions[x].name + " = " + this.body.name + "." + this.body.fields[i].definitions[x] + ";"; 12582 } 12583 } 12584 } 12585 return "function " + this.name + "() {\n" + this.body + "}\n" + 12586 staticVars + "\n" + 12587 "processing." + this.name + " = " + this.name + ";"; 12588 }; 12589 12590 12591 function transformGlobalClass(class_) { 12592 var m = classesRegex.exec(class_); // 1 - attr, 2 - class|int, 3 - name, 4 - extends, 5 - implements, 6 - body 12593 classesRegex.lastIndex = 0; 12594 var body = atoms[getAtomIndex(m[6])]; 12595 if(m[2] === "interface") { 12596 return new AstInterface(m[3]); 12597 } else { 12598 var oldClassId = currentClassId, newClassId = generateClassId(); 12599 currentClassId = newClassId; 12600 var globalClass = new AstClass(m[3], transformClassBody(body, m[3], m[4], m[5]) ); 12601 appendClass(globalClass, newClassId, oldClassId); 12602 12603 currentClassId = oldClassId; 12604 return globalClass; 12605 } 12606 } 12607 12608 function AstMethod(name, params, body) { 12609 this.name = name; 12610 this.params = params; 12611 this.body = body; 12612 } 12613 AstMethod.prototype.toString = function(){ 12614 var paramNames = appendToLookupTable({}, this.params.getNames()); 12615 var oldContext = replaceContext; 12616 replaceContext = function(name) { 12617 return paramNames.hasOwnProperty(name) ? name : oldContext(name); 12618 }; 12619 var result = "function " + this.name + this.params + " " + this.body + "\n" + 12620 "processing." + this.name + " = " + this.name + ";"; 12621 replaceContext = oldContext; 12622 return result; 12623 }; 12624 12625 function transformGlobalMethod(method) { 12626 var m = methodsRegex.exec(method); 12627 var result = 12628 methodsRegex.lastIndex = 0; 12629 return new AstMethod(m[3], transformParams(atoms[getAtomIndex(m[4])]), 12630 transformStatementsBlock(atoms[getAtomIndex(m[6])])); 12631 } 12632 12633 function preStatementsTransform(statements) { 12634 var s = statements; 12635 s = s.replace(/\b(catch\s*"B\d+"\s*"A\d+")(\s*catch\s*"B\d+"\s*"A\d+")+/g, "$1"); 12636 return s; 12637 } 12638 12639 function AstForStatement(argument, misc) { 12640 this.argument = argument; 12641 this.misc = misc; 12642 } 12643 AstForStatement.prototype.toString = function() { 12644 return this.misc.prefix + this.argument.toString(); 12645 }; 12646 function AstCatchStatement(argument, misc) { 12647 this.argument = argument; 12648 this.misc = misc; 12649 } 12650 AstCatchStatement.prototype.toString = function() { 12651 return this.misc.prefix + this.argument.toString(); 12652 }; 12653 function AstPrefixStatement(name, argument, misc) { 12654 this.name = name; 12655 this.argument = argument; 12656 this.misc = misc; 12657 } 12658 AstPrefixStatement.prototype.toString = function() { 12659 var result = this.misc.prefix; 12660 if(this.argument !== undef) { 12661 result += this.argument.toString(); 12662 } 12663 return result; 12664 }; 12665 function AstLabel(label) { 12666 this.label = label; 12667 } 12668 AstLabel.prototype.toString = function() { 12669 return this.label; 12670 }; 12671 12672 transformStatements = function(statements, transformMethod, transformClass) { 12673 var nextStatement = new RegExp(/\b(catch|for|if|switch|while|with)\s*"B(\d+)"|\b(do|else|finally|return|throw|try|break|continue)\b|("[ADEH](\d+)")|\b((?:case\s[^:]+|[A-Za-z_$][\w$]*\s*):)|(;)/g); 12674 var res = []; 12675 statements = preStatementsTransform(statements); 12676 var lastIndex = 0, m, space; 12677 while((m = nextStatement.exec(statements)) !== null) { 12678 if(m[1] !== undef) { // catch, for ... 12679 var i = statements.lastIndexOf('"B', nextStatement.lastIndex); 12680 var statementsPrefix = statements.substring(lastIndex, i); 12681 if(m[1] === "for") { 12682 res.push(new AstForStatement(transformForExpression(atoms[m[2]]), 12683 { prefix: statementsPrefix }) ); 12684 } else if(m[1] === "catch") { 12685 res.push(new AstCatchStatement(transformParams(atoms[m[2]]), 12686 { prefix: statementsPrefix }) ); 12687 } else { 12688 res.push(new AstPrefixStatement(m[1], transformExpression(atoms[m[2]]), 12689 { prefix: statementsPrefix }) ); 12690 } 12691 } else if(m[3] !== undef) { // do, else, ... 12692 res.push(new AstPrefixStatement(m[3], undef, 12693 { prefix: statements.substring(lastIndex, nextStatement.lastIndex) }) ); 12694 } else if(m[4] !== undef) { // block, class and methods 12695 space = statements.substring(lastIndex, nextStatement.lastIndex - m[4].length); 12696 if(trim(space).length !== 0) { continue; } // avoiding new type[] {} construct 12697 res.push(space); 12698 var kind = m[4].charAt(1), atomIndex = m[5]; 12699 if(kind === 'D') { 12700 res.push(transformMethod(atoms[atomIndex])); 12701 } else if(kind === 'E') { 12702 res.push(transformClass(atoms[atomIndex])); 12703 } else if(kind === 'H') { 12704 res.push(transformFunction(atoms[atomIndex])); 12705 } else { 12706 res.push(transformStatementsBlock(atoms[atomIndex])); 12707 } 12708 } else if(m[6] !== undef) { // label 12709 space = statements.substring(lastIndex, nextStatement.lastIndex - m[6].length); 12710 if(trim(space).length !== 0) { continue; } // avoiding ?: construct 12711 res.push(new AstLabel(statements.substring(lastIndex, nextStatement.lastIndex)) ); 12712 } else { // semicolon 12713 var statement = trimSpaces(statements.substring(lastIndex, nextStatement.lastIndex - 1)); 12714 res.push(statement.left); 12715 res.push(transformStatement(statement.middle)); 12716 res.push(statement.right + ";"); 12717 } 12718 lastIndex = nextStatement.lastIndex; 12719 } 12720 var statementsTail = trimSpaces(statements.substring(lastIndex)); 12721 res.push(statementsTail.left); 12722 if(statementsTail.middle !== "") { 12723 res.push(transformStatement(statementsTail.middle)); 12724 res.push(";" + statementsTail.right); 12725 } 12726 return res; 12727 }; 12728 12729 function getLocalNames(statements) { 12730 var localNames = []; 12731 for(var i=0,l=statements.length;i<l;++i) { 12732 var statement = statements[i]; 12733 if(statement instanceof AstVar) { 12734 localNames = localNames.concat(statement.getNames()); 12735 } else if(statement instanceof AstForStatement && 12736 statement.argument.initStatement instanceof AstVar) { 12737 localNames = localNames.concat(statement.argument.initStatement.getNames()); 12738 } else if(statement instanceof AstInnerInterface || statement instanceof AstInnerClass || 12739 statement instanceof AstInterface || statement instanceof AstClass || 12740 statement instanceof AstMethod || statement instanceof AstFunction) { 12741 localNames.push(statement.name); 12742 } 12743 } 12744 return appendToLookupTable({}, localNames); 12745 } 12746 12747 function AstStatementsBlock(statements) { 12748 this.statements = statements; 12749 } 12750 AstStatementsBlock.prototype.toString = function() { 12751 var localNames = getLocalNames(this.statements); 12752 var oldContext = replaceContext; 12753 12754 // replacing context only when necessary 12755 if(!isLookupTableEmpty(localNames)) { 12756 replaceContext = function(name) { 12757 return localNames.hasOwnProperty(name) ? name : oldContext(name); 12758 }; 12759 } 12760 12761 var result = "{\n" + this.statements.join('') + "\n}"; 12762 replaceContext = oldContext; 12763 return result; 12764 }; 12765 12766 transformStatementsBlock = function(block) { 12767 var content = trimSpaces(block.substring(1, block.length - 1)); 12768 return new AstStatementsBlock(transformStatements(content.middle)); 12769 }; 12770 12771 function AstRoot(statements) { 12772 this.statements = statements; 12773 } 12774 AstRoot.prototype.toString = function() { 12775 var localNames = getLocalNames(this.statements); 12776 replaceContext = function(name) { 12777 if(localNames.hasOwnProperty(name)) { 12778 return name; 12779 } else if(globalMembers.hasOwnProperty(name)) { 12780 return "processing." + name; 12781 } else if(PConstants.hasOwnProperty(name)) { 12782 return "$constants." + name; 12783 } 12784 return name; 12785 }; 12786 var result = "// this code was autogenerated from PJS\n" + 12787 "(function(processing, $constants) {\n" + 12788 this.statements.join('') + "\n})"; 12789 replaceContext = null; 12790 return result; 12791 }; 12792 12793 transformMain = function() { 12794 var statements = extractClassesAndMethods(atoms[0]); 12795 statements = statements.replace(/\bimport\s+[^;]+;/g, ""); 12796 return new AstRoot( transformStatements(statements, 12797 transformGlobalMethod, transformGlobalClass) ); 12798 }; 12799 12800 function generateMetadata(ast) { 12801 var globalScope = {}; 12802 var id, class_; 12803 for(id in declaredClasses) { 12804 if(declaredClasses.hasOwnProperty(id)) { 12805 class_ = declaredClasses[id]; 12806 var scopeId = class_.scopeId, name = class_.name; 12807 if(scopeId) { 12808 var scope = declaredClasses[scopeId]; 12809 class_.scope = scope; 12810 if(scope.inScope === undef) { 12811 scope.inScope = {}; 12812 } 12813 scope.inScope[name] = class_; 12814 } else { 12815 globalScope[name] = class_; 12816 } 12817 } 12818 } 12819 12820 function findInScopes(class_, name) { 12821 var parts = name.split('.'); 12822 var currentScope = class_.scope, found; 12823 while(currentScope) { 12824 if(currentScope.hasOwnProperty(parts[0])) { 12825 found = currentScope[parts[0]]; break; 12826 } 12827 currentScope = currentScope.scope; 12828 } 12829 if(found === undef) { 12830 found = globalScope[parts[0]]; 12831 } 12832 for(var i=1,l=parts.length;i<l && found;++i) { 12833 found = found.inScope[parts[i]]; 12834 } 12835 return found; 12836 } 12837 12838 for(id in declaredClasses) { 12839 if(declaredClasses.hasOwnProperty(id)) { 12840 class_ = declaredClasses[id]; 12841 var baseClassName = class_.body.baseClassName; 12842 if(baseClassName) { 12843 class_.base = findInScopes(class_, baseClassName); 12844 } 12845 } 12846 } 12847 } 12848 12849 var transformed = transformMain(); 12850 generateMetadata(transformed); 12851 12852 // remove empty extra lines with space 12853 var redendered = transformed.toString(); 12854 redendered = redendered.replace(/\s*\n(?:[\t ]*\n)+/g, "\n\n"); 12855 12856 return injectStrings(redendered, strings); 12857 }// Parser ends 12858 12859 function preprocessCode(aCode, sketch) { 12860 // Parse out @pjs directive, if any. 12861 var dm = new RegExp(/\/\*\s*@pjs\s+((?:[^\*]|\*+[^\*\/])*)\*\//g).exec(aCode); 12862 if (dm && dm.length === 2) { 12863 // masks contents of a JSON to be replaced later 12864 // to protect the contents from further parsing 12865 var jsonItems = [], 12866 directives = dm.splice(1, 2)[0].replace(/\{([\s\S]*?)\}/g, (function() { 12867 return function(all, item) { 12868 jsonItems.push(item); 12869 return "{" + (jsonItems.length-1) + "}"; 12870 }; 12871 }())).replace('\n', '').replace('\r', '').split(";"); 12872 12873 // We'll L/RTrim, and also remove any surrounding double quotes (e.g., just take string contents) 12874 var clean = function(s) { 12875 return s.replace(/^\s*["']?/, '').replace(/["']?\s*$/, ''); 12876 }; 12877 12878 for (var i = 0, dl = directives.length; i < dl; i++) { 12879 var pair = directives[i].split('='); 12880 if (pair && pair.length === 2) { 12881 var key = clean(pair[0]), 12882 value = clean(pair[1]), 12883 list = []; 12884 // A few directives require work beyond storying key/value pairings 12885 if (key === "preload") { 12886 list = value.split(','); 12887 // All pre-loaded images will get put in imageCache, keyed on filename 12888 for (var j = 0, jl = list.length; j < jl; j++) { 12889 var imageName = clean(list[j]); 12890 sketch.imageCache.add(imageName); 12891 } 12892 } else if (key === "transparent") { 12893 sketch.options.isTransparent = value === "true"; 12894 // fonts can be declared as a string containing a url, 12895 // or a JSON object, containing a font name, and a url 12896 } else if (key === "font") { 12897 list = value.split(","); 12898 for (var x = 0, xl = list.length; x < xl; x++) { 12899 var fontName = clean(list[x]), 12900 index = /^\{(\d*?)\}$/.exec(fontName); 12901 // if index is not null, send JSON, otherwise, send string 12902 sketch.fonts.add(index ? JSON.parse("{" + jsonItems[index[1]] + "}") : fontName); 12903 } 12904 } else if (key === "crisp") { 12905 sketch.options.crispLines = value === "true"; 12906 } else if (key === "pauseOnBlur") { 12907 sketch.options.pauseOnBlur = value === "true"; 12908 } else { 12909 sketch.options[key] = value; 12910 } 12911 } 12912 } 12913 } 12914 12915 // Check if 3D context is invoked -- this is not the best way to do this. 12916 var codeWoStrings = aCode.replace(/("(?:[^"\\\n]|\\.)*")|('(?:[^'\\\n]|\\.)*')|(([\[\(=|&!\^:?]\s*)(\/(?![*\/])(?:[^\/\\\n]|\\.)*\/[gim]*)\b)|(\/\/[^\n]*\n)|(\/\*(?:(?!\*\/)(?:.|\n))*\*\/)/g, ""); 12917 if (codeWoStrings.match(/\bsize\((?:.+),(?:.+),\s*(OPENGL|P3D)\s*\);/)) { 12918 sketch.use3DContext = true; 12919 } 12920 return aCode; 12921 } 12922 12923 // Parse/compiles Processing (Java-like) syntax to JavaScript syntax 12924 Processing.compile = function(pdeCode) { 12925 var sketch = new Processing.Sketch(); 12926 var code = preprocessCode(pdeCode, sketch); 12927 var compiledPde = parseProcessing(code); 12928 sketch.sourceCode = compiledPde; 12929 return sketch; 12930 }; 12931 12932 Error.prototype.printStackTrace = function() { 12933 return this.toString(); 12934 }; 12935 12936 Processing.version = "0.9.7"; 12937 12938 // Share lib space 12939 Processing.lib = {}; 12940 12941 // Store Processing instances 12942 Processing.instances = []; 12943 Processing.instanceIds = {}; 12944 12945 Processing.removeInstance = function(id) { 12946 Processing.instances.splice(Processing.instanceIds[id], 1); 12947 delete Processing.instanceIds[id]; 12948 }; 12949 12950 Processing.addInstance = function(processing) { 12951 if (processing.externals.canvas.id === undef || !processing.externals.canvas.id.length) { 12952 processing.externals.canvas.id = "__processing" + Processing.instances.length; 12953 } 12954 Processing.instanceIds[processing.externals.canvas.id] = Processing.instances.length; 12955 Processing.instances.push(processing); 12956 }; 12957 12958 Processing.getInstanceById = function(name) { 12959 return Processing.instances[Processing.instanceIds[name]]; 12960 }; 12961 12962 Processing.Sketch = function(attachFunction) { 12963 this.attachFunction = attachFunction; // can be optional 12964 this.use3DContext = false; 12965 this.options = { 12966 isTransparent: false, 12967 crispLines: false, 12968 pauseOnBlur: false 12969 }; 12970 this.imageCache = { 12971 pending: 0, 12972 images: {}, 12973 add: function(href) { 12974 var img = new Image(); 12975 img.onload = (function(owner) { 12976 return function() { 12977 owner.pending--; 12978 }; 12979 }(this)); 12980 this.pending++; 12981 this.images[href] = img; 12982 img.src = href; 12983 } 12984 }; 12985 this.fonts = { 12986 // template element used to compare font sizes 12987 template: (function() { 12988 var element = document.createElement('p'); 12989 element.style.fontFamily = "serif"; 12990 element.style.fontSize = "72px"; 12991 element.style.visibility = "hidden"; 12992 element.innerHTML = "abcmmmmmmmmmmlll"; 12993 document.getElementsByTagName("body")[0].appendChild(element); 12994 return element; 12995 }()), 12996 // number of attempts to load a font 12997 attempt: 0, 12998 // returns true is fonts are all loaded, 12999 // true if number of attempts hits the limit, 13000 // false otherwise 13001 pending: function() { 13002 var r = true; 13003 for (var i = 0; i < this.fontList.length; i++) { 13004 // compares size of text in pixels, if equal, custom font is not yet loaded 13005 if (this.fontList[i].offsetWidth === this.template.offsetWidth && this.fontList[i].offsetHeight === this.template.offsetHeight) { 13006 r = false; 13007 this.attempt++; 13008 } else { 13009 // removes loaded font from the array and dom, so we don't compare it again 13010 document.getElementsByTagName("body")[0].removeChild(this.fontList[i]); 13011 this.fontList.splice(i--, 1); 13012 this.attempt = 0; 13013 } 13014 } 13015 // give up loading after max attempts have been reached 13016 if (this.attempt >= 30) { 13017 r = true; 13018 // remove remaining elements from the dom and array 13019 for (var j = 0; j < this.fontList.length; j++) { 13020 document.getElementsByTagName("body")[0].removeChild(this.fontList[j]); 13021 this.fontList.splice(j--, 1); 13022 } 13023 } 13024 // Remove the template element from the dom once done comparing 13025 if (r) { 13026 document.getElementsByTagName("body")[0].removeChild(this.template); 13027 } 13028 return r; 13029 }, 13030 // fontList contains elements to compare font sizes against a template 13031 fontList: [], 13032 // string containing a css @font-face list of custom fonts 13033 fontFamily: "", 13034 // style element to hold the @font-face string 13035 style: document.createElement('style'), 13036 // adds a font to the font cache 13037 // creates an element using the font, to start loading the font, 13038 // and compare against a default font to see if the custom font is loaded 13039 add: function(fontSrc) { 13040 // fontSrc can be a string or a JSON object 13041 // string contains a url to a font 13042 // JSON object would contain a name and a url 13043 // acceptable fonts are .ttf, .otf, and a data uri 13044 var fontName = (typeof fontSrc === 'object' ? fontSrc.fontFace : fontSrc), 13045 fontUrl = (typeof fontSrc === 'object' ? fontSrc.url : fontSrc); 13046 // creating the @font-face style 13047 this.fontFamily += "@font-face{\n font-family: '" + fontName + "';\n src: url('" + fontUrl + "');\n}\n"; 13048 this.style.innerHTML = this.fontFamily; 13049 document.getElementsByTagName("head")[0].appendChild(this.style); 13050 // creating the element to load, and compare the new font 13051 var preLoader = document.createElement('p'); 13052 preLoader.style.fontFamily = "'" + fontName + "', serif"; 13053 preLoader.style.fontSize = "72px"; 13054 preLoader.style.visibility = "hidden"; 13055 preLoader.innerHTML = "abcmmmmmmmmmmlll"; 13056 document.getElementsByTagName("body")[0].appendChild(preLoader); 13057 this.fontList.push(preLoader); 13058 } 13059 }; 13060 this.sourceCode = undefined; 13061 this.attach = function(processing, constants) { 13062 // either attachFunction or sourceCode must be present on attach 13063 if(typeof this.attachFunction === "function") { 13064 this.attachFunction(processing, constants); 13065 } else if(this.sourceCode) { 13066 var func = eval(this.sourceCode); 13067 func(processing, constants); 13068 this.attachFunction = func; 13069 } else { 13070 throw "Unable to attach sketch to the processing instance"; 13071 } 13072 }; 13073 this.toString = function() { 13074 return this.sourceCode || "[attach: " + this.attachFunction + "]"; 13075 }; 13076 this.onblur = function() {}; 13077 this.onfocus = function() {}; 13078 }; 13079 13080 // Automatic Initialization Method 13081 var init = function() { 13082 var canvas = document.getElementsByTagName('canvas'); 13083 13084 for (var i = 0, l = canvas.length; i < l; i++) { 13085 // datasrc and data-src are deprecated. 13086 var processingSources = canvas[i].getAttribute('data-processing-sources'); 13087 if (processingSources === null) { 13088 // Temporary fallback for datasrc and data-src 13089 processingSources = canvas[i].getAttribute('data-src'); 13090 if (processingSources === null) { 13091 processingSources = canvas[i].getAttribute('datasrc'); 13092 } 13093 } 13094 if (processingSources) { 13095 // The problem: if the HTML canvas dimensions differ from the 13096 // dimensions specified in the size() call in the sketch, for 13097 // 3D sketches, browsers will either not render or render the 13098 // scene incorrectly. To fix this, we need to adjust the attributes 13099 // of the canvas width and height. 13100 // Get the source, we'll need to find what the user has used in size() 13101 var filenames = processingSources.split(' '); 13102 var code = ""; 13103 for (var j = 0, fl = filenames.length; j < fl; j++) { 13104 if (filenames[j]) { 13105 var block = ajax(filenames[j]); 13106 if (block !== false) { 13107 code += ";\n" + block; 13108 } 13109 } 13110 } 13111 Processing.addInstance(new Processing(canvas[i], code)); 13112 } 13113 } 13114 }; 13115 13116 document.addEventListener('DOMContentLoaded', function() { 13117 init(); 13118 }, false); 13119 13120 // pauseOnBlur handling 13121 window.addEventListener('blur', function() { 13122 for (var i = 0; i < Processing.instances.length; i++) { 13123 Processing.instances[i].externals.onblur(); 13124 } 13125 }, false); 13126 13127 window.addEventListener('focus', function() { 13128 for (var i = 0; i < Processing.instances.length; i++) { 13129 Processing.instances[i].externals.onfocus(); 13130 } 13131 }, false); 13132 13133}()); 13134