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]  +