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