front_idill/extern/fajran-tuiojs/examples/processingjs/processing.js
changeset 26 858e90c7cbaa
equal deleted inserted replaced
25:a7b0e40bcab0 26:858e90c7cbaa
       
     1 
       
     2 (function(window, document, Math, undef) {
       
     3 
       
     4   var nop = function(){};
       
     5 
       
     6   var debug = (function() {
       
     7     if ("console" in window) {
       
     8       return function(msg) {
       
     9         window.console.log('Processing.js: ' + msg);
       
    10       };
       
    11     }
       
    12     return nop();
       
    13   }());
       
    14 
       
    15   var ajax = function(url) {
       
    16     var xhr = new XMLHttpRequest();
       
    17     xhr.open("GET", url, false);
       
    18     if (xhr.overrideMimeType) {
       
    19       xhr.overrideMimeType("text/plain");
       
    20     }
       
    21     xhr.setRequestHeader("If-Modified-Since", "Fri, 01 Jan 1960 00:00:00 GMT");
       
    22     xhr.send(null);
       
    23     // failed request?
       
    24     if (xhr.status !== 200 && xhr.status !== 0) { throw ("XMLHttpRequest failed, status code " + xhr.status); }
       
    25     return xhr.responseText;
       
    26   };
       
    27 
       
    28   var isDOMPresent = ("document" in this) && !("fake" in this.document);
       
    29 
       
    30   // document.head polyfill for the benefit of Firefox 3.6
       
    31   document.head = document.head || document.getElementsByTagName('head')[0];
       
    32 
       
    33   // Typed Arrays: fallback to WebGL arrays or Native JS arrays if unavailable
       
    34   function setupTypedArray(name, fallback) {
       
    35     // Check if TypedArray exists, and use if so.
       
    36     if (name in window) {
       
    37       return window[name];
       
    38     }
       
    39 
       
    40     // Check if WebGLArray exists
       
    41     if (typeof window[fallback] === "function") {
       
    42       return window[fallback];
       
    43     }
       
    44 
       
    45     // Use Native JS array
       
    46     return function(obj) {
       
    47       if (obj instanceof Array) {
       
    48         return obj;
       
    49       }
       
    50       if (typeof obj === "number") {
       
    51         var arr = [];
       
    52         arr.length = obj;
       
    53         return arr;
       
    54       }
       
    55     };
       
    56   }
       
    57 
       
    58   var Float32Array = setupTypedArray("Float32Array", "WebGLFloatArray"),
       
    59       Int32Array   = setupTypedArray("Int32Array",   "WebGLIntArray"),
       
    60       Uint16Array  = setupTypedArray("Uint16Array",  "WebGLUnsignedShortArray"),
       
    61       Uint8Array   = setupTypedArray("Uint8Array",   "WebGLUnsignedByteArray");
       
    62 
       
    63   /* Browsers fixes end */
       
    64 
       
    65   /**
       
    66    * NOTE: in releases we replace symbolic PConstants.* names with their values.
       
    67    * Using PConstants.* in code below is fine.  See tools/rewrite-pconstants.js.
       
    68    */
       
    69   var PConstants = {
       
    70     X: 0,
       
    71     Y: 1,
       
    72     Z: 2,
       
    73 
       
    74     R: 3,
       
    75     G: 4,
       
    76     B: 5,
       
    77     A: 6,
       
    78 
       
    79     U: 7,
       
    80     V: 8,
       
    81 
       
    82     NX: 9,
       
    83     NY: 10,
       
    84     NZ: 11,
       
    85 
       
    86     EDGE: 12,
       
    87 
       
    88     // Stroke
       
    89     SR: 13,
       
    90     SG: 14,
       
    91     SB: 15,
       
    92     SA: 16,
       
    93 
       
    94     SW: 17,
       
    95 
       
    96     // Transformations (2D and 3D)
       
    97     TX: 18,
       
    98     TY: 19,
       
    99     TZ: 20,
       
   100 
       
   101     VX: 21,
       
   102     VY: 22,
       
   103     VZ: 23,
       
   104     VW: 24,
       
   105 
       
   106     // Material properties
       
   107     AR: 25,
       
   108     AG: 26,
       
   109     AB: 27,
       
   110 
       
   111     DR: 3,
       
   112     DG: 4,
       
   113     DB: 5,
       
   114     DA: 6,
       
   115 
       
   116     SPR: 28,
       
   117     SPG: 29,
       
   118     SPB: 30,
       
   119 
       
   120     SHINE: 31,
       
   121 
       
   122     ER: 32,
       
   123     EG: 33,
       
   124     EB: 34,
       
   125 
       
   126     BEEN_LIT: 35,
       
   127 
       
   128     VERTEX_FIELD_COUNT: 36,
       
   129 
       
   130     // Renderers
       
   131     P2D:    1,
       
   132     JAVA2D: 1,
       
   133     WEBGL:  2,
       
   134     P3D:    2,
       
   135     OPENGL: 2,
       
   136     PDF:    0,
       
   137     DXF:    0,
       
   138 
       
   139     // Platform IDs
       
   140     OTHER:   0,
       
   141     WINDOWS: 1,
       
   142     MAXOSX:  2,
       
   143     LINUX:   3,
       
   144 
       
   145     EPSILON: 0.0001,
       
   146 
       
   147     MAX_FLOAT:  3.4028235e+38,
       
   148     MIN_FLOAT: -3.4028235e+38,
       
   149     MAX_INT:    2147483647,
       
   150     MIN_INT:   -2147483648,
       
   151 
       
   152     PI:         Math.PI,
       
   153     TWO_PI:     2 * Math.PI,
       
   154     HALF_PI:    Math.PI / 2,
       
   155     THIRD_PI:   Math.PI / 3,
       
   156     QUARTER_PI: Math.PI / 4,
       
   157 
       
   158     DEG_TO_RAD: Math.PI / 180,
       
   159     RAD_TO_DEG: 180 / Math.PI,
       
   160 
       
   161     WHITESPACE: " \t\n\r\f\u00A0",
       
   162 
       
   163     // Color modes
       
   164     RGB:   1,
       
   165     ARGB:  2,
       
   166     HSB:   3,
       
   167     ALPHA: 4,
       
   168     CMYK:  5,
       
   169 
       
   170     // Image file types
       
   171     TIFF:  0,
       
   172     TARGA: 1,
       
   173     JPEG:  2,
       
   174     GIF:   3,
       
   175 
       
   176     // Filter/convert types
       
   177     BLUR:      11,
       
   178     GRAY:      12,
       
   179     INVERT:    13,
       
   180     OPAQUE:    14,
       
   181     POSTERIZE: 15,
       
   182     THRESHOLD: 16,
       
   183     ERODE:     17,
       
   184     DILATE:    18,
       
   185 
       
   186     // Blend modes
       
   187     REPLACE:    0,
       
   188     BLEND:      1 << 0,
       
   189     ADD:        1 << 1,
       
   190     SUBTRACT:   1 << 2,
       
   191     LIGHTEST:   1 << 3,
       
   192     DARKEST:    1 << 4,
       
   193     DIFFERENCE: 1 << 5,
       
   194     EXCLUSION:  1 << 6,
       
   195     MULTIPLY:   1 << 7,
       
   196     SCREEN:     1 << 8,
       
   197     OVERLAY:    1 << 9,
       
   198     HARD_LIGHT: 1 << 10,
       
   199     SOFT_LIGHT: 1 << 11,
       
   200     DODGE:      1 << 12,
       
   201     BURN:       1 << 13,
       
   202 
       
   203     // Color component bit masks
       
   204     ALPHA_MASK: 0xff000000,
       
   205     RED_MASK:   0x00ff0000,
       
   206     GREEN_MASK: 0x0000ff00,
       
   207     BLUE_MASK:  0x000000ff,
       
   208 
       
   209     // Projection matrices
       
   210     CUSTOM:       0,
       
   211     ORTHOGRAPHIC: 2,
       
   212     PERSPECTIVE:  3,
       
   213 
       
   214     // Shapes
       
   215     POINT:          2,
       
   216     POINTS:         2,
       
   217     LINE:           4,
       
   218     LINES:          4,
       
   219     TRIANGLE:       8,
       
   220     TRIANGLES:      9,
       
   221     TRIANGLE_STRIP: 10,
       
   222     TRIANGLE_FAN:   11,
       
   223     QUAD:           16,
       
   224     QUADS:          16,
       
   225     QUAD_STRIP:     17,
       
   226     POLYGON:        20,
       
   227     PATH:           21,
       
   228     RECT:           30,
       
   229     ELLIPSE:        31,
       
   230     ARC:            32,
       
   231     SPHERE:         40,
       
   232     BOX:            41,
       
   233 
       
   234     GROUP:          0,
       
   235     PRIMITIVE:      1,
       
   236     //PATH:         21, // shared with Shape PATH
       
   237     GEOMETRY:       3,
       
   238 
       
   239     // Shape Vertex
       
   240     VERTEX:        0,
       
   241     BEZIER_VERTEX: 1,
       
   242     CURVE_VERTEX:  2,
       
   243     BREAK:         3,
       
   244     CLOSESHAPE:    4,
       
   245 
       
   246     // Shape closing modes
       
   247     OPEN:  1,
       
   248     CLOSE: 2,
       
   249 
       
   250     // Shape drawing modes
       
   251     CORNER:          0, // Draw mode convention to use (x, y) to (width, height)
       
   252     CORNERS:         1, // Draw mode convention to use (x1, y1) to (x2, y2) coordinates
       
   253     RADIUS:          2, // Draw mode from the center, and using the radius
       
   254     CENTER_RADIUS:   2, // Deprecated! Use RADIUS instead
       
   255     CENTER:          3, // Draw from the center, using second pair of values as the diameter
       
   256     DIAMETER:        3, // Synonym for the CENTER constant. Draw from the center
       
   257     CENTER_DIAMETER: 3, // Deprecated! Use DIAMETER instead
       
   258 
       
   259     // Text vertical alignment modes
       
   260     BASELINE: 0,   // Default vertical alignment for text placement
       
   261     TOP:      101, // Align text to the top
       
   262     BOTTOM:   102, // Align text from the bottom, using the baseline
       
   263 
       
   264     // UV Texture coordinate modes
       
   265     NORMAL:     1,
       
   266     NORMALIZED: 1,
       
   267     IMAGE:      2,
       
   268 
       
   269     // Text placement modes
       
   270     MODEL: 4,
       
   271     SHAPE: 5,
       
   272 
       
   273     // Stroke modes
       
   274     SQUARE:  'butt',
       
   275     ROUND:   'round',
       
   276     PROJECT: 'square',
       
   277     MITER:   'miter',
       
   278     BEVEL:   'bevel',
       
   279 
       
   280     // Lighting modes
       
   281     AMBIENT:     0,
       
   282     DIRECTIONAL: 1,
       
   283     //POINT:     2, Shared with Shape constant
       
   284     SPOT:        3,
       
   285 
       
   286     // Key constants
       
   287 
       
   288     // Both key and keyCode will be equal to these values
       
   289     BACKSPACE: 8,
       
   290     TAB:       9,
       
   291     ENTER:     10,
       
   292     RETURN:    13,
       
   293     ESC:       27,
       
   294     DELETE:    127,
       
   295     CODED:     0xffff,
       
   296 
       
   297     // p.key will be CODED and p.keyCode will be this value
       
   298     SHIFT:     16,
       
   299     CONTROL:   17,
       
   300     ALT:       18,
       
   301     CAPSLK:    20,
       
   302     PGUP:      33,
       
   303     PGDN:      34,
       
   304     END:       35,
       
   305     HOME:      36,
       
   306     LEFT:      37,
       
   307     UP:        38,
       
   308     RIGHT:     39,
       
   309     DOWN:      40,
       
   310     F1:        112,
       
   311     F2:        113,
       
   312     F3:        114,
       
   313     F4:        115,
       
   314     F5:        116,
       
   315     F6:        117,
       
   316     F7:        118,
       
   317     F8:        119,
       
   318     F9:        120,
       
   319     F10:       121,
       
   320     F11:       122,
       
   321     F12:       123,
       
   322     NUMLK:     144,
       
   323     META:      157,
       
   324     INSERT:    155,
       
   325 
       
   326     // Cursor types
       
   327     ARROW:    'default',
       
   328     CROSS:    'crosshair',
       
   329     HAND:     'pointer',
       
   330     MOVE:     'move',
       
   331     TEXT:     'text',
       
   332     WAIT:     'wait',
       
   333     NOCURSOR: "url('data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=='), auto",
       
   334 
       
   335     // Hints
       
   336     DISABLE_OPENGL_2X_SMOOTH:     1,
       
   337     ENABLE_OPENGL_2X_SMOOTH:     -1,
       
   338     ENABLE_OPENGL_4X_SMOOTH:      2,
       
   339     ENABLE_NATIVE_FONTS:          3,
       
   340     DISABLE_DEPTH_TEST:           4,
       
   341     ENABLE_DEPTH_TEST:           -4,
       
   342     ENABLE_DEPTH_SORT:            5,
       
   343     DISABLE_DEPTH_SORT:          -5,
       
   344     DISABLE_OPENGL_ERROR_REPORT:  6,
       
   345     ENABLE_OPENGL_ERROR_REPORT:  -6,
       
   346     ENABLE_ACCURATE_TEXTURES:     7,
       
   347     DISABLE_ACCURATE_TEXTURES:   -7,
       
   348     HINT_COUNT:                  10,
       
   349 
       
   350     // PJS defined constants
       
   351     SINCOS_LENGTH:      720, // every half degree
       
   352     PRECISIONB:         15, // fixed point precision is limited to 15 bits!!
       
   353     PRECISIONF:         1 << 15,
       
   354     PREC_MAXVAL:        (1 << 15) - 1,
       
   355     PREC_ALPHA_SHIFT:   24 - 15,
       
   356     PREC_RED_SHIFT:     16 - 15,
       
   357     NORMAL_MODE_AUTO:   0,
       
   358     NORMAL_MODE_SHAPE:  1,
       
   359     NORMAL_MODE_VERTEX: 2,
       
   360     MAX_LIGHTS:         8
       
   361   };
       
   362 
       
   363   /**
       
   364    * Returns Java hashCode() result for the object. If the object has the "hashCode" function,
       
   365    * it preforms the call of this function. Otherwise it uses/creates the "$id" property,
       
   366    * which is used as the hashCode.
       
   367    *
       
   368    * @param {Object} obj          The object.
       
   369    * @returns {int}               The object's hash code.
       
   370    */
       
   371   function virtHashCode(obj) {
       
   372     if (typeof(obj) === "string") {
       
   373       var hash = 0;
       
   374       for (var i = 0; i < obj.length; ++i) {
       
   375         hash = (hash * 31 + obj.charCodeAt(i)) & 0xFFFFFFFF;
       
   376       }
       
   377       return hash;
       
   378     }
       
   379     if (typeof(obj) !== "object") {
       
   380       return obj & 0xFFFFFFFF;
       
   381     }
       
   382     if (obj.hashCode instanceof Function) {
       
   383       return obj.hashCode();
       
   384     }
       
   385     if (obj.$id === undef) {
       
   386         obj.$id = ((Math.floor(Math.random() * 0x10000) - 0x8000) << 16) | Math.floor(Math.random() * 0x10000);
       
   387     }
       
   388     return obj.$id;
       
   389   }
       
   390 
       
   391   /**
       
   392    * Returns Java equals() result for two objects. If the first object
       
   393    * has the "equals" function, it preforms the call of this function.
       
   394    * Otherwise the method uses the JavaScript === operator.
       
   395    *
       
   396    * @param {Object} obj          The first object.
       
   397    * @param {Object} other        The second object.
       
   398    *
       
   399    * @returns {boolean}           true if the objects are equal.
       
   400    */
       
   401   function virtEquals(obj, other) {
       
   402     if (obj === null || other === null) {
       
   403       return (obj === null) && (other === null);
       
   404     }
       
   405     if (typeof (obj) === "string") {
       
   406       return obj === other;
       
   407     }
       
   408     if (typeof(obj) !== "object") {
       
   409       return obj === other;
       
   410     }
       
   411     if (obj.equals instanceof Function) {
       
   412       return obj.equals(other);
       
   413     }
       
   414     return obj === other;
       
   415   }
       
   416 
       
   417   /**
       
   418   * A ObjectIterator is an iterator wrapper for objects. If passed object contains
       
   419   * the iterator method, the object instance will be replaced by the result returned by
       
   420   * this method call. If passed object is an array, the ObjectIterator instance iterates
       
   421   * through its items.
       
   422   *
       
   423   * @param {Object} obj          The object to be iterated.
       
   424   */
       
   425   var ObjectIterator = function(obj) {
       
   426     if (obj.iterator instanceof Function) {
       
   427       return obj.iterator();
       
   428     }
       
   429     if (obj instanceof Array) {
       
   430       // iterate through array items
       
   431       var index = -1;
       
   432       this.hasNext = function() {
       
   433         return ++index < obj.length;
       
   434       };
       
   435       this.next = function() {
       
   436         return obj[index];
       
   437       };
       
   438     } else {
       
   439       throw "Unable to iterate: " + obj;
       
   440     }
       
   441   };
       
   442 
       
   443   /**
       
   444    * An ArrayList stores a variable number of objects.
       
   445    *
       
   446    * @param {int} initialCapacity optional defines the initial capacity of the list, it's empty by default
       
   447    *
       
   448    * @returns {ArrayList} new ArrayList object
       
   449    */
       
   450   var ArrayList = (function() {
       
   451     function Iterator(array) {
       
   452       var index = 0;
       
   453       this.hasNext = function() {
       
   454         return index < array.length;
       
   455       };
       
   456 
       
   457       this.next = function() {
       
   458         return array[index++];
       
   459       };
       
   460 
       
   461       this.remove = function() {
       
   462         array.splice(index, 1);
       
   463       };
       
   464     }
       
   465 
       
   466     function ArrayList() {
       
   467       var array;
       
   468       if (arguments.length === 0) {
       
   469         array = [];
       
   470       } else if (arguments.length > 0 && typeof arguments[0] !== 'number') {
       
   471         array = arguments[0].toArray();
       
   472       } else {
       
   473         array = [];
       
   474         array.length = 0 | arguments[0];
       
   475       }
       
   476 
       
   477       /**
       
   478        * @member ArrayList
       
   479        * ArrayList.get() Returns the element at the specified position in this list.
       
   480        *
       
   481        * @param {int} i index of element to return
       
   482        *
       
   483        * @returns {Object} the element at the specified position in this list.
       
   484        */
       
   485       this.get = function(i) {
       
   486         return array[i];
       
   487       };
       
   488       /**
       
   489        * @member ArrayList
       
   490        * ArrayList.contains() Returns true if this list contains the specified element.
       
   491        *
       
   492        * @param {Object} item element whose presence in this List is to be tested.
       
   493        *
       
   494        * @returns {boolean} true if the specified element is present; false otherwise.
       
   495        */
       
   496       this.contains = function(item) {
       
   497         return this.indexOf(item)>-1;
       
   498       };
       
   499        /**
       
   500        * @member ArrayList
       
   501        * ArrayList.indexOf() Returns the position this element takes in the list, or -1 if the element is not found.
       
   502        *
       
   503        * @param {Object} item element whose position in this List is to be tested.
       
   504        *
       
   505        * @returns {int} the list position that the first match for this element holds in the list, or -1 if it is not in the list.
       
   506        */
       
   507       this.indexOf = function(item) {
       
   508         for (var i = 0, len = array.length; i < len; ++i) {
       
   509           if (virtEquals(item, array[i])) {
       
   510             return i;
       
   511           }
       
   512         }
       
   513         return -1;
       
   514       };
       
   515      /**
       
   516        * @member ArrayList
       
   517        * ArrayList.add() Adds the specified element to this list.
       
   518        *
       
   519        * @param {int}    index  optional index at which the specified element is to be inserted
       
   520        * @param {Object} object element to be added to the list
       
   521        */
       
   522       this.add = function() {
       
   523         if (arguments.length === 1) {
       
   524           array.push(arguments[0]); // for add(Object)
       
   525         } else if (arguments.length === 2) {
       
   526           var arg0 = arguments[0];
       
   527           if (typeof arg0 === 'number') {
       
   528             if (arg0 >= 0 && arg0 <= array.length) {
       
   529               array.splice(arg0, 0, arguments[1]); // for add(i, Object)
       
   530             } else {
       
   531               throw(arg0 + " is not a valid index");
       
   532             }
       
   533           } else {
       
   534             throw(typeof arg0 + " is not a number");
       
   535           }
       
   536         } else {
       
   537           throw("Please use the proper number of parameters.");
       
   538         }
       
   539       };
       
   540       /**
       
   541        * @member ArrayList
       
   542        * ArrayList.addAll(collection) appends all of the elements in the specified
       
   543        * Collection to the end of this list, in the order that they are returned by
       
   544        * the specified Collection's Iterator.
       
   545        *
       
   546        * When called as addAll(index, collection) the elements are inserted into
       
   547        * this list at the position indicated by index.
       
   548        *
       
   549        * @param {index} Optional; specifies the position the colletion should be inserted at
       
   550        * @param {collection} Any iterable object (ArrayList, HashMap.keySet(), etc.)
       
   551        * @throws out of bounds error for negative index, or index greater than list size.
       
   552        */
       
   553       this.addAll = function(arg1, arg2) {
       
   554         // addAll(int, Collection)
       
   555         var it;
       
   556         if (typeof arg1 === "number") {
       
   557           if (arg1 < 0 || arg1 > array.length) {
       
   558             throw("Index out of bounds for addAll: " + arg1 + " greater or equal than " + array.length);
       
   559           }
       
   560           it = new ObjectIterator(arg2);
       
   561           while (it.hasNext()) {
       
   562             array.splice(arg1++, 0, it.next());
       
   563           }
       
   564         }
       
   565         // addAll(Collection)
       
   566         else {
       
   567           it = new ObjectIterator(arg1);
       
   568           while (it.hasNext()) {
       
   569             array.push(it.next());
       
   570           }
       
   571         }
       
   572       };
       
   573       /**
       
   574        * @member ArrayList
       
   575        * ArrayList.set() Replaces the element at the specified position in this list with the specified element.
       
   576        *
       
   577        * @param {int}    index  index of element to replace
       
   578        * @param {Object} object element to be stored at the specified position
       
   579        */
       
   580       this.set = function() {
       
   581         if (arguments.length === 2) {
       
   582           var arg0 = arguments[0];
       
   583           if (typeof arg0 === 'number') {
       
   584             if (arg0 >= 0 && arg0 < array.length) {
       
   585               array.splice(arg0, 1, arguments[1]);
       
   586             } else {
       
   587               throw(arg0 + " is not a valid index.");
       
   588             }
       
   589           } else {
       
   590             throw(typeof arg0 + " is not a number");
       
   591           }
       
   592         } else {
       
   593           throw("Please use the proper number of parameters.");
       
   594         }
       
   595       };
       
   596 
       
   597       /**
       
   598        * @member ArrayList
       
   599        * ArrayList.size() Returns the number of elements in this list.
       
   600        *
       
   601        * @returns {int} the number of elements in this list
       
   602        */
       
   603       this.size = function() {
       
   604         return array.length;
       
   605       };
       
   606 
       
   607       /**
       
   608        * @member ArrayList
       
   609        * ArrayList.clear() Removes all of the elements from this list. The list will be empty after this call returns.
       
   610        */
       
   611       this.clear = function() {
       
   612         array.length = 0;
       
   613       };
       
   614 
       
   615       /**
       
   616        * @member ArrayList
       
   617        * ArrayList.remove() Removes an element either based on index, if the argument is a number, or
       
   618        * by equality check, if the argument is an object.
       
   619        *
       
   620        * @param {int|Object} item either the index of the element to be removed, or the element itself.
       
   621        *
       
   622        * @returns {Object|boolean} If removal is by index, the element that was removed, or null if nothing was removed. If removal is by object, true if removal occurred, otherwise false.
       
   623        */
       
   624       this.remove = function(item) {
       
   625         if (typeof item === 'number') {
       
   626           return array.splice(item, 1)[0];
       
   627         }
       
   628         item = this.indexOf(item);
       
   629         if (item > -1) {
       
   630           array.splice(item, 1);
       
   631           return true;
       
   632         }
       
   633         return false;
       
   634       };
       
   635 
       
   636       /**
       
   637        * @member ArrayList
       
   638        * ArrayList.isEmpty() Tests if this list has no elements.
       
   639        *
       
   640        * @returns {boolean} true if this list has no elements; false otherwise
       
   641        */
       
   642       this.isEmpty = function() {
       
   643          return !array.length;
       
   644       };
       
   645 
       
   646       /**
       
   647        * @member ArrayList
       
   648        * ArrayList.clone() Returns a shallow copy of this ArrayList instance. (The elements themselves are not copied.)
       
   649        *
       
   650        * @returns {ArrayList} a clone of this ArrayList instance
       
   651        */
       
   652       this.clone = function() {
       
   653         return new ArrayList(this);
       
   654       };
       
   655 
       
   656       /**
       
   657        * @member ArrayList
       
   658        * ArrayList.toArray() Returns an array containing all of the elements in this list in the correct order.
       
   659        *
       
   660        * @returns {Object[]} Returns an array containing all of the elements in this list in the correct order
       
   661        */
       
   662       this.toArray = function() {
       
   663         return array.slice(0);
       
   664       };
       
   665 
       
   666       this.iterator = function() {
       
   667         return new Iterator(array);
       
   668       };
       
   669     }
       
   670 
       
   671     return ArrayList;
       
   672   }());
       
   673 
       
   674   /**
       
   675   * A HashMap stores a collection of objects, each referenced by a key. This is similar to an Array, only
       
   676   * instead of accessing elements with a numeric index, a String  is used. (If you are familiar with
       
   677   * associative arrays from other languages, this is the same idea.)
       
   678   *
       
   679   * @param {int} initialCapacity          defines the initial capacity of the map, it's 16 by default
       
   680   * @param {float} loadFactor             the load factor for the map, the default is 0.75
       
   681   * @param {Map} m                        gives the new HashMap the same mappings as this Map
       
   682   */
       
   683   var HashMap = (function() {
       
   684     /**
       
   685     * @member HashMap
       
   686     * A HashMap stores a collection of objects, each referenced by a key. This is similar to an Array, only
       
   687     * instead of accessing elements with a numeric index, a String  is used. (If you are familiar with
       
   688     * associative arrays from other languages, this is the same idea.)
       
   689     *
       
   690     * @param {int} initialCapacity          defines the initial capacity of the map, it's 16 by default
       
   691     * @param {float} loadFactor             the load factor for the map, the default is 0.75
       
   692     * @param {Map} m                        gives the new HashMap the same mappings as this Map
       
   693     */
       
   694     function HashMap() {
       
   695       if (arguments.length === 1 && arguments[0] instanceof HashMap) {
       
   696         return arguments[0].clone();
       
   697       }
       
   698 
       
   699       var initialCapacity = arguments.length > 0 ? arguments[0] : 16;
       
   700       var loadFactor = arguments.length > 1 ? arguments[1] : 0.75;
       
   701       var buckets = [];
       
   702       buckets.length = initialCapacity;
       
   703       var count = 0;
       
   704       var hashMap = this;
       
   705 
       
   706       function getBucketIndex(key) {
       
   707         var index = virtHashCode(key) % buckets.length;
       
   708         return index < 0 ? buckets.length + index : index;
       
   709       }
       
   710       function ensureLoad() {
       
   711         if (count <= loadFactor * buckets.length) {
       
   712           return;
       
   713         }
       
   714         var allEntries = [];
       
   715         for (var i = 0; i < buckets.length; ++i) {
       
   716           if (buckets[i] !== undef) {
       
   717             allEntries = allEntries.concat(buckets[i]);
       
   718           }
       
   719         }
       
   720         var newBucketsLength = buckets.length * 2;
       
   721         buckets = [];
       
   722         buckets.length = newBucketsLength;
       
   723         for (var j = 0; j < allEntries.length; ++j) {
       
   724           var index = getBucketIndex(allEntries[j].key);
       
   725           var bucket = buckets[index];
       
   726           if (bucket === undef) {
       
   727             buckets[index] = bucket = [];
       
   728           }
       
   729           bucket.push(allEntries[j]);
       
   730         }
       
   731       }
       
   732 
       
   733       function Iterator(conversion, removeItem) {
       
   734         var bucketIndex = 0;
       
   735         var itemIndex = -1;
       
   736         var endOfBuckets = false;
       
   737 
       
   738         function findNext() {
       
   739           while (!endOfBuckets) {
       
   740             ++itemIndex;
       
   741             if (bucketIndex >= buckets.length) {
       
   742               endOfBuckets = true;
       
   743             } else if (buckets[bucketIndex] === undef || itemIndex >= buckets[bucketIndex].length) {
       
   744               itemIndex = -1;
       
   745               ++bucketIndex;
       
   746             } else {
       
   747               return;
       
   748             }
       
   749           }
       
   750         }
       
   751 
       
   752         /*
       
   753         * @member Iterator
       
   754         * Checks if the Iterator has more items
       
   755         */
       
   756         this.hasNext = function() {
       
   757           return !endOfBuckets;
       
   758         };
       
   759 
       
   760         /*
       
   761         * @member Iterator
       
   762         * Return the next Item
       
   763         */
       
   764         this.next = function() {
       
   765           var result = conversion(buckets[bucketIndex][itemIndex]);
       
   766           findNext();
       
   767           return result;
       
   768         };
       
   769 
       
   770         /*
       
   771         * @member Iterator
       
   772         * Remove the current item
       
   773         */
       
   774         this.remove = function() {
       
   775           removeItem(this.next());
       
   776           --itemIndex;
       
   777         };
       
   778 
       
   779         findNext();
       
   780       }
       
   781 
       
   782       function Set(conversion, isIn, removeItem) {
       
   783         this.clear = function() {
       
   784           hashMap.clear();
       
   785         };
       
   786 
       
   787         this.contains = function(o) {
       
   788           return isIn(o);
       
   789         };
       
   790 
       
   791         this.containsAll = function(o) {
       
   792           var it = o.iterator();
       
   793           while (it.hasNext()) {
       
   794             if (!this.contains(it.next())) {
       
   795               return false;
       
   796             }
       
   797           }
       
   798           return true;
       
   799         };
       
   800 
       
   801         this.isEmpty = function() {
       
   802           return hashMap.isEmpty();
       
   803         };
       
   804 
       
   805         this.iterator = function() {
       
   806           return new Iterator(conversion, removeItem);
       
   807         };
       
   808 
       
   809         this.remove = function(o) {
       
   810           if (this.contains(o)) {
       
   811             removeItem(o);
       
   812             return true;
       
   813           }
       
   814           return false;
       
   815         };
       
   816 
       
   817         this.removeAll = function(c) {
       
   818           var it = c.iterator();
       
   819           var changed = false;
       
   820           while (it.hasNext()) {
       
   821             var item = it.next();
       
   822             if (this.contains(item)) {
       
   823               removeItem(item);
       
   824               changed = true;
       
   825             }
       
   826           }
       
   827           return true;
       
   828         };
       
   829 
       
   830         this.retainAll = function(c) {
       
   831           var it = this.iterator();
       
   832           var toRemove = [];
       
   833           while (it.hasNext()) {
       
   834             var entry = it.next();
       
   835             if (!c.contains(entry)) {
       
   836               toRemove.push(entry);
       
   837             }
       
   838           }
       
   839           for (var i = 0; i < toRemove.length; ++i) {
       
   840             removeItem(toRemove[i]);
       
   841           }
       
   842           return toRemove.length > 0;
       
   843         };
       
   844 
       
   845         this.size = function() {
       
   846           return hashMap.size();
       
   847         };
       
   848 
       
   849         this.toArray = function() {
       
   850           var result = [];
       
   851           var it = this.iterator();
       
   852           while (it.hasNext()) {
       
   853             result.push(it.next());
       
   854           }
       
   855           return result;
       
   856         };
       
   857       }
       
   858 
       
   859       function Entry(pair) {
       
   860         this._isIn = function(map) {
       
   861           return map === hashMap && (pair.removed === undef);
       
   862         };
       
   863 
       
   864         this.equals = function(o) {
       
   865           return virtEquals(pair.key, o.getKey());
       
   866         };
       
   867 
       
   868         this.getKey = function() {
       
   869           return pair.key;
       
   870         };
       
   871 
       
   872         this.getValue = function() {
       
   873           return pair.value;
       
   874         };
       
   875 
       
   876         this.hashCode = function(o) {
       
   877           return virtHashCode(pair.key);
       
   878         };
       
   879 
       
   880         this.setValue = function(value) {
       
   881           var old = pair.value;
       
   882           pair.value = value;
       
   883           return old;
       
   884         };
       
   885       }
       
   886 
       
   887       this.clear = function() {
       
   888         count = 0;
       
   889         buckets = [];
       
   890         buckets.length = initialCapacity;
       
   891       };
       
   892 
       
   893       this.clone = function() {
       
   894         var map = new HashMap();
       
   895         map.putAll(this);
       
   896         return map;
       
   897       };
       
   898 
       
   899       this.containsKey = function(key) {
       
   900         var index = getBucketIndex(key);
       
   901         var bucket = buckets[index];
       
   902         if (bucket === undef) {
       
   903           return false;
       
   904         }
       
   905         for (var i = 0; i < bucket.length; ++i) {
       
   906           if (virtEquals(bucket[i].key, key)) {
       
   907             return true;
       
   908           }
       
   909         }
       
   910         return false;
       
   911       };
       
   912 
       
   913       this.containsValue = function(value) {
       
   914         for (var i = 0; i < buckets.length; ++i) {
       
   915           var bucket = buckets[i];
       
   916           if (bucket === undef) {
       
   917             continue;
       
   918           }
       
   919           for (var j = 0; j < bucket.length; ++j) {
       
   920             if (virtEquals(bucket[j].value, value)) {
       
   921               return true;
       
   922             }
       
   923           }
       
   924         }
       
   925         return false;
       
   926       };
       
   927 
       
   928       this.entrySet = function() {
       
   929         return new Set(
       
   930 
       
   931         function(pair) {
       
   932           return new Entry(pair);
       
   933         },
       
   934 
       
   935         function(pair) {
       
   936           return (pair instanceof Entry) && pair._isIn(hashMap);
       
   937         },
       
   938 
       
   939         function(pair) {
       
   940           return hashMap.remove(pair.getKey());
       
   941         });
       
   942       };
       
   943 
       
   944       this.get = function(key) {
       
   945         var index = getBucketIndex(key);
       
   946         var bucket = buckets[index];
       
   947         if (bucket === undef) {
       
   948           return null;
       
   949         }
       
   950         for (var i = 0; i < bucket.length; ++i) {
       
   951           if (virtEquals(bucket[i].key, key)) {
       
   952             return bucket[i].value;
       
   953           }
       
   954         }
       
   955         return null;
       
   956       };
       
   957 
       
   958       this.isEmpty = function() {
       
   959         return count === 0;
       
   960       };
       
   961 
       
   962       this.keySet = function() {
       
   963         return new Set(
       
   964           // get key from pair
       
   965           function(pair) {
       
   966             return pair.key;
       
   967           },
       
   968           // is-in test
       
   969           function(key) {
       
   970             return hashMap.containsKey(key);
       
   971           },
       
   972           // remove from hashmap by key
       
   973           function(key) {
       
   974             return hashMap.remove(key);
       
   975           }
       
   976         );
       
   977       };
       
   978 
       
   979       this.values = function() {
       
   980         return new Set(
       
   981           // get value from pair
       
   982           function(pair) {
       
   983             return pair.value;
       
   984           },
       
   985           // is-in test
       
   986           function(value) {
       
   987             return hashMap.containsValue(value);
       
   988           },
       
   989           // remove from hashmap by value
       
   990           function(value) {
       
   991             return hashMap.removeByValue(value);
       
   992           }
       
   993         );
       
   994       };
       
   995 
       
   996       this.put = function(key, value) {
       
   997         var index = getBucketIndex(key);
       
   998         var bucket = buckets[index];
       
   999         if (bucket === undef) {
       
  1000           ++count;
       
  1001           buckets[index] = [{
       
  1002             key: key,
       
  1003             value: value
       
  1004           }];
       
  1005           ensureLoad();
       
  1006           return null;
       
  1007         }
       
  1008         for (var i = 0; i < bucket.length; ++i) {
       
  1009           if (virtEquals(bucket[i].key, key)) {
       
  1010             var previous = bucket[i].value;
       
  1011             bucket[i].value = value;
       
  1012             return previous;
       
  1013           }
       
  1014         }
       
  1015         ++count;
       
  1016         bucket.push({
       
  1017           key: key,
       
  1018           value: value
       
  1019         });
       
  1020         ensureLoad();
       
  1021         return null;
       
  1022       };
       
  1023 
       
  1024       this.putAll = function(m) {
       
  1025         var it = m.entrySet().iterator();
       
  1026         while (it.hasNext()) {
       
  1027           var entry = it.next();
       
  1028           this.put(entry.getKey(), entry.getValue());
       
  1029         }
       
  1030       };
       
  1031 
       
  1032       this.remove = function(key) {
       
  1033         var index = getBucketIndex(key);
       
  1034         var bucket = buckets[index];
       
  1035         if (bucket === undef) {
       
  1036           return null;
       
  1037         }
       
  1038         for (var i = 0; i < bucket.length; ++i) {
       
  1039           if (virtEquals(bucket[i].key, key)) {
       
  1040             --count;
       
  1041             var previous = bucket[i].value;
       
  1042             bucket[i].removed = true;
       
  1043             if (bucket.length > 1) {
       
  1044               bucket.splice(i, 1);
       
  1045             } else {
       
  1046               buckets[index] = undef;
       
  1047             }
       
  1048             return previous;
       
  1049           }
       
  1050         }
       
  1051         return null;
       
  1052       };
       
  1053 
       
  1054       this.removeByValue = function(value) {
       
  1055         var bucket, i, ilen, pair;
       
  1056         for (bucket in buckets) {
       
  1057           if (buckets.hasOwnProperty(bucket)) {
       
  1058             for (i = 0, ilen = buckets[bucket].length; i < ilen; i++) {
       
  1059               pair = buckets[bucket][i];
       
  1060               // removal on values is based on identity, not equality
       
  1061               if (pair.value === value) {
       
  1062                 buckets[bucket].splice(i, 1);
       
  1063                 return true;
       
  1064               }
       
  1065             }
       
  1066           }
       
  1067         }
       
  1068         return false;
       
  1069       };
       
  1070 
       
  1071       this.size = function() {
       
  1072         return count;
       
  1073       };
       
  1074     }
       
  1075 
       
  1076     return HashMap;
       
  1077   }());
       
  1078 
       
  1079   var PVector = (function() {
       
  1080     function PVector(x, y, z) {
       
  1081       this.x = x || 0;
       
  1082       this.y = y || 0;
       
  1083       this.z = z || 0;
       
  1084     }
       
  1085 
       
  1086     PVector.dist = function(v1, v2) {
       
  1087       return v1.dist(v2);
       
  1088     };
       
  1089 
       
  1090     PVector.dot = function(v1, v2) {
       
  1091       return v1.dot(v2);
       
  1092     };
       
  1093 
       
  1094     PVector.cross = function(v1, v2) {
       
  1095       return v1.cross(v2);
       
  1096     };
       
  1097 
       
  1098     PVector.angleBetween = function(v1, v2) {
       
  1099       return Math.acos(v1.dot(v2) / (v1.mag() * v2.mag()));
       
  1100     };
       
  1101 
       
  1102     // Common vector operations for PVector
       
  1103     PVector.prototype = {
       
  1104       set: function(v, y, z) {
       
  1105         if (arguments.length === 1) {
       
  1106           this.set(v.x || v[0] || 0,
       
  1107                    v.y || v[1] || 0,
       
  1108                    v.z || v[2] || 0);
       
  1109         } else {
       
  1110           this.x = v;
       
  1111           this.y = y;
       
  1112           this.z = z;
       
  1113         }
       
  1114       },
       
  1115       get: function() {
       
  1116         return new PVector(this.x, this.y, this.z);
       
  1117       },
       
  1118       mag: function() {
       
  1119         var x = this.x,
       
  1120             y = this.y,
       
  1121             z = this.z;
       
  1122         return Math.sqrt(x * x + y * y + z * z);
       
  1123       },
       
  1124       add: function(v, y, z) {
       
  1125         if (arguments.length === 1) {
       
  1126           this.x += v.x;
       
  1127           this.y += v.y;
       
  1128           this.z += v.z;
       
  1129         } else {
       
  1130           this.x += v;
       
  1131           this.y += y;
       
  1132           this.z += z;
       
  1133         }
       
  1134       },
       
  1135       sub: function(v, y, z) {
       
  1136         if (arguments.length === 1) {
       
  1137           this.x -= v.x;
       
  1138           this.y -= v.y;
       
  1139           this.z -= v.z;
       
  1140         } else {
       
  1141           this.x -= v;
       
  1142           this.y -= y;
       
  1143           this.z -= z;
       
  1144         }
       
  1145       },
       
  1146       mult: function(v) {
       
  1147         if (typeof v === 'number') {
       
  1148           this.x *= v;
       
  1149           this.y *= v;
       
  1150           this.z *= v;
       
  1151         } else {
       
  1152           this.x *= v.x;
       
  1153           this.y *= v.y;
       
  1154           this.z *= v.z;
       
  1155         }
       
  1156       },
       
  1157       div: function(v) {
       
  1158         if (typeof v === 'number') {
       
  1159           this.x /= v;
       
  1160           this.y /= v;
       
  1161           this.z /= v;
       
  1162         } else {
       
  1163           this.x /= v.x;
       
  1164           this.y /= v.y;
       
  1165           this.z /= v.z;
       
  1166         }
       
  1167       },
       
  1168       dist: function(v) {
       
  1169         var dx = this.x - v.x,
       
  1170             dy = this.y - v.y,
       
  1171             dz = this.z - v.z;
       
  1172         return Math.sqrt(dx * dx + dy * dy + dz * dz);
       
  1173       },
       
  1174       dot: function(v, y, z) {
       
  1175         if (arguments.length === 1) {
       
  1176           return (this.x * v.x + this.y * v.y + this.z * v.z);
       
  1177         }
       
  1178         return (this.x * v + this.y * y + this.z * z);
       
  1179       },
       
  1180       cross: function(v) {
       
  1181         var x = this.x,
       
  1182             y = this.y,
       
  1183             z = this.z;
       
  1184         return new PVector(y * v.z - v.y * z,
       
  1185                            z * v.x - v.z * x,
       
  1186                            x * v.y - v.x * y);
       
  1187       },
       
  1188       normalize: function() {
       
  1189         var m = this.mag();
       
  1190         if (m > 0) {
       
  1191           this.div(m);
       
  1192         }
       
  1193       },
       
  1194       limit: function(high) {
       
  1195         if (this.mag() > high) {
       
  1196           this.normalize();
       
  1197           this.mult(high);
       
  1198         }
       
  1199       },
       
  1200       heading2D: function() {
       
  1201         return (-Math.atan2(-this.y, this.x));
       
  1202       },
       
  1203       toString: function() {
       
  1204         return "[" + this.x + ", " + this.y + ", " + this.z + "]";
       
  1205       },
       
  1206       array: function() {
       
  1207         return [this.x, this.y, this.z];
       
  1208       }
       
  1209     };
       
  1210 
       
  1211     function createPVectorMethod(method) {
       
  1212       return function(v1, v2) {
       
  1213         var v = v1.get();
       
  1214         v[method](v2);
       
  1215         return v;
       
  1216       };
       
  1217     }
       
  1218 
       
  1219     for (var method in PVector.prototype) {
       
  1220       if (PVector.prototype.hasOwnProperty(method) && !PVector.hasOwnProperty(method)) {
       
  1221         PVector[method] = createPVectorMethod(method);
       
  1222       }
       
  1223     }
       
  1224 
       
  1225     return PVector;
       
  1226   }());
       
  1227 
       
  1228   // Building defaultScope. Changing of the prototype protects
       
  1229   // internal Processing code from the changes in defaultScope
       
  1230   function DefaultScope() {}
       
  1231   DefaultScope.prototype = PConstants;
       
  1232 
       
  1233   var defaultScope = new DefaultScope();
       
  1234   defaultScope.ArrayList   = ArrayList;
       
  1235   defaultScope.HashMap     = HashMap;
       
  1236   defaultScope.PVector     = PVector;
       
  1237   defaultScope.ObjectIterator = ObjectIterator;
       
  1238   defaultScope.PConstants  = PConstants;
       
  1239   //defaultScope.PImage    = PImage;     // TODO
       
  1240   //defaultScope.PShape    = PShape;     // TODO
       
  1241   //defaultScope.PShapeSVG = PShapeSVG;  // TODO
       
  1242 
       
  1243   ////////////////////////////////////////////////////////////////////////////
       
  1244   // Class inheritance helper methods
       
  1245   ////////////////////////////////////////////////////////////////////////////
       
  1246 
       
  1247   defaultScope.defineProperty = function(obj, name, desc) {
       
  1248     if("defineProperty" in Object) {
       
  1249       Object.defineProperty(obj, name, desc);
       
  1250     } else {
       
  1251       if (desc.hasOwnProperty("get")) {
       
  1252         obj.__defineGetter__(name, desc.get);
       
  1253       }
       
  1254       if (desc.hasOwnProperty("set")) {
       
  1255         obj.__defineSetter__(name, desc.set);
       
  1256       }
       
  1257     }
       
  1258   };
       
  1259 
       
  1260   function extendClass(subClass, baseClass) {
       
  1261     function extendGetterSetter(propertyName) {
       
  1262       defaultScope.defineProperty(subClass, propertyName, {
       
  1263         get: function() {
       
  1264           return baseClass[propertyName];
       
  1265         },
       
  1266         set: function(v) {
       
  1267           baseClass[propertyName]=v;
       
  1268         },
       
  1269         enumerable: true
       
  1270       });
       
  1271     }
       
  1272 
       
  1273     var properties = [];
       
  1274     for (var propertyName in baseClass) {
       
  1275       if (typeof baseClass[propertyName] === 'function') {
       
  1276         // Overriding all non-overriden functions
       
  1277         if (!subClass.hasOwnProperty(propertyName)) {
       
  1278           subClass[propertyName] = baseClass[propertyName];
       
  1279         }
       
  1280       } else if(propertyName.charAt(0) !== "$" && !(propertyName in subClass)) {
       
  1281         // Delaying the properties extension due to the IE9 bug (see #918).
       
  1282         properties.push(propertyName);
       
  1283       }
       
  1284     }
       
  1285     while (properties.length > 0) {
       
  1286       extendGetterSetter(properties.shift());
       
  1287     }
       
  1288   }
       
  1289 
       
  1290   defaultScope.extendClassChain = function(base) {
       
  1291     var path = [base];
       
  1292     for (var self = base.$upcast; self; self = self.$upcast) {
       
  1293       extendClass(self, base);
       
  1294       path.push(self);
       
  1295       base = self;
       
  1296     }
       
  1297     while (path.length > 0) {
       
  1298       path.pop().$self=base;
       
  1299     }
       
  1300   };
       
  1301 
       
  1302   defaultScope.extendStaticMembers = function(derived, base) {
       
  1303     extendClass(derived, base);
       
  1304   };
       
  1305 
       
  1306   defaultScope.extendInterfaceMembers = function(derived, base) {
       
  1307     extendClass(derived, base);
       
  1308   };
       
  1309 
       
  1310   defaultScope.addMethod = function(object, name, fn, superAccessor) {
       
  1311     if (object[name]) {
       
  1312       var args = fn.length,
       
  1313         oldfn = object[name];
       
  1314 
       
  1315       object[name] = function() {
       
  1316         if (arguments.length === args) {
       
  1317           return fn.apply(this, arguments);
       
  1318         }
       
  1319         return oldfn.apply(this, arguments);
       
  1320       };
       
  1321     } else {
       
  1322       object[name] = fn;
       
  1323     }
       
  1324   };
       
  1325 
       
  1326   defaultScope.createJavaArray = function(type, bounds) {
       
  1327     var result = null;
       
  1328     if (typeof bounds[0] === 'number') {
       
  1329       var itemsCount = 0 | bounds[0];
       
  1330       if (bounds.length <= 1) {
       
  1331         result = [];
       
  1332         result.length = itemsCount;
       
  1333         for (var i = 0; i < itemsCount; ++i) {
       
  1334           result[i] = 0;
       
  1335         }
       
  1336       } else {
       
  1337         result = [];
       
  1338         var newBounds = bounds.slice(1);
       
  1339         for (var j = 0; j < itemsCount; ++j) {
       
  1340           result.push(defaultScope.createJavaArray(type, newBounds));
       
  1341         }
       
  1342       }
       
  1343     }
       
  1344     return result;
       
  1345   };
       
  1346 
       
  1347   var colors = {
       
  1348     aliceblue:            "#f0f8ff",
       
  1349     antiquewhite:         "#faebd7",
       
  1350     aqua:                 "#00ffff",
       
  1351     aquamarine:           "#7fffd4",
       
  1352     azure:                "#f0ffff",
       
  1353     beige:                "#f5f5dc",
       
  1354     bisque:               "#ffe4c4",
       
  1355     black:                "#000000",
       
  1356     blanchedalmond:       "#ffebcd",
       
  1357     blue:                 "#0000ff",
       
  1358     blueviolet:           "#8a2be2",
       
  1359     brown:                "#a52a2a",
       
  1360     burlywood:            "#deb887",
       
  1361     cadetblue:            "#5f9ea0",
       
  1362     chartreuse:           "#7fff00",
       
  1363     chocolate:            "#d2691e",
       
  1364     coral:                "#ff7f50",
       
  1365     cornflowerblue:       "#6495ed",
       
  1366     cornsilk:             "#fff8dc",
       
  1367     crimson:              "#dc143c",
       
  1368     cyan:                 "#00ffff",
       
  1369     darkblue:             "#00008b",
       
  1370     darkcyan:             "#008b8b",
       
  1371     darkgoldenrod:        "#b8860b",
       
  1372     darkgray:             "#a9a9a9",
       
  1373     darkgreen:            "#006400",
       
  1374     darkkhaki:            "#bdb76b",
       
  1375     darkmagenta:          "#8b008b",
       
  1376     darkolivegreen:       "#556b2f",
       
  1377     darkorange:           "#ff8c00",
       
  1378     darkorchid:           "#9932cc",
       
  1379     darkred:              "#8b0000",
       
  1380     darksalmon:           "#e9967a",
       
  1381     darkseagreen:         "#8fbc8f",
       
  1382     darkslateblue:        "#483d8b",
       
  1383     darkslategray:        "#2f4f4f",
       
  1384     darkturquoise:        "#00ced1",
       
  1385     darkviolet:           "#9400d3",
       
  1386     deeppink:             "#ff1493",
       
  1387     deepskyblue:          "#00bfff",
       
  1388     dimgray:              "#696969",
       
  1389     dodgerblue:           "#1e90ff",
       
  1390     firebrick:            "#b22222",
       
  1391     floralwhite:          "#fffaf0",
       
  1392     forestgreen:          "#228b22",
       
  1393     fuchsia:              "#ff00ff",
       
  1394     gainsboro:            "#dcdcdc",
       
  1395     ghostwhite:           "#f8f8ff",
       
  1396     gold:                 "#ffd700",
       
  1397     goldenrod:            "#daa520",
       
  1398     gray:                 "#808080",
       
  1399     green:                "#008000",
       
  1400     greenyellow:          "#adff2f",
       
  1401     honeydew:             "#f0fff0",
       
  1402     hotpink:              "#ff69b4",
       
  1403     indianred:            "#cd5c5c",
       
  1404     indigo:               "#4b0082",
       
  1405     ivory:                "#fffff0",
       
  1406     khaki:                "#f0e68c",
       
  1407     lavender:             "#e6e6fa",
       
  1408     lavenderblush:        "#fff0f5",
       
  1409     lawngreen:            "#7cfc00",
       
  1410     lemonchiffon:         "#fffacd",
       
  1411     lightblue:            "#add8e6",
       
  1412     lightcoral:           "#f08080",
       
  1413     lightcyan:            "#e0ffff",
       
  1414     lightgoldenrodyellow: "#fafad2",
       
  1415     lightgrey:            "#d3d3d3",
       
  1416     lightgreen:           "#90ee90",
       
  1417     lightpink:            "#ffb6c1",
       
  1418     lightsalmon:          "#ffa07a",
       
  1419     lightseagreen:        "#20b2aa",
       
  1420     lightskyblue:         "#87cefa",
       
  1421     lightslategray:       "#778899",
       
  1422     lightsteelblue:       "#b0c4de",
       
  1423     lightyellow:          "#ffffe0",
       
  1424     lime:                 "#00ff00",
       
  1425     limegreen:            "#32cd32",
       
  1426     linen:                "#faf0e6",
       
  1427     magenta:              "#ff00ff",
       
  1428     maroon:               "#800000",
       
  1429     mediumaquamarine:     "#66cdaa",
       
  1430     mediumblue:           "#0000cd",
       
  1431     mediumorchid:         "#ba55d3",
       
  1432     mediumpurple:         "#9370d8",
       
  1433     mediumseagreen:       "#3cb371",
       
  1434     mediumslateblue:      "#7b68ee",
       
  1435     mediumspringgreen:    "#00fa9a",
       
  1436     mediumturquoise:      "#48d1cc",
       
  1437     mediumvioletred:      "#c71585",
       
  1438     midnightblue:         "#191970",
       
  1439     mintcream:            "#f5fffa",
       
  1440     mistyrose:            "#ffe4e1",
       
  1441     moccasin:             "#ffe4b5",
       
  1442     navajowhite:          "#ffdead",
       
  1443     navy:                 "#000080",
       
  1444     oldlace:              "#fdf5e6",
       
  1445     olive:                "#808000",
       
  1446     olivedrab:            "#6b8e23",
       
  1447     orange:               "#ffa500",
       
  1448     orangered:            "#ff4500",
       
  1449     orchid:               "#da70d6",
       
  1450     palegoldenrod:        "#eee8aa",
       
  1451     palegreen:            "#98fb98",
       
  1452     paleturquoise:        "#afeeee",
       
  1453     palevioletred:        "#d87093",
       
  1454     papayawhip:           "#ffefd5",
       
  1455     peachpuff:            "#ffdab9",
       
  1456     peru:                 "#cd853f",
       
  1457     pink:                 "#ffc0cb",
       
  1458     plum:                 "#dda0dd",
       
  1459     powderblue:           "#b0e0e6",
       
  1460     purple:               "#800080",
       
  1461     red:                  "#ff0000",
       
  1462     rosybrown:            "#bc8f8f",
       
  1463     royalblue:            "#4169e1",
       
  1464     saddlebrown:          "#8b4513",
       
  1465     salmon:               "#fa8072",
       
  1466     sandybrown:           "#f4a460",
       
  1467     seagreen:             "#2e8b57",
       
  1468     seashell:             "#fff5ee",
       
  1469     sienna:               "#a0522d",
       
  1470     silver:               "#c0c0c0",
       
  1471     skyblue:              "#87ceeb",
       
  1472     slateblue:            "#6a5acd",
       
  1473     slategray:            "#708090",
       
  1474     snow:                 "#fffafa",
       
  1475     springgreen:          "#00ff7f",
       
  1476     steelblue:            "#4682b4",
       
  1477     tan:                  "#d2b48c",
       
  1478     teal:                 "#008080",
       
  1479     thistle:              "#d8bfd8",
       
  1480     tomato:               "#ff6347",
       
  1481     turquoise:            "#40e0d0",
       
  1482     violet:               "#ee82ee",
       
  1483     wheat:                "#f5deb3",
       
  1484     white:                "#ffffff",
       
  1485     whitesmoke:           "#f5f5f5",
       
  1486     yellow:               "#ffff00",
       
  1487     yellowgreen:          "#9acd32"
       
  1488   };
       
  1489 
       
  1490   // Unsupported Processing File and I/O operations.
       
  1491   (function(Processing) {
       
  1492     var unsupportedP5 = ("open() createOutput() createInput() BufferedReader selectFolder() " +
       
  1493                          "dataPath() createWriter() selectOutput() beginRecord() " +
       
  1494                          "saveStream() endRecord() selectInput() saveBytes() createReader() " +
       
  1495                          "beginRaw() endRaw() PrintWriter delay()").split(" "),
       
  1496         count = unsupportedP5.length,
       
  1497         prettyName,
       
  1498         p5Name;
       
  1499 
       
  1500     function createUnsupportedFunc(n) {
       
  1501       return function() {
       
  1502         throw "Processing.js does not support " + n + ".";
       
  1503       };
       
  1504     }
       
  1505 
       
  1506     while (count--) {
       
  1507       prettyName = unsupportedP5[count];
       
  1508       p5Name = prettyName.replace("()", "");
       
  1509 
       
  1510       Processing[p5Name] = createUnsupportedFunc(prettyName);
       
  1511     }
       
  1512   }(defaultScope));
       
  1513 
       
  1514   // screenWidth and screenHeight are shared by all instances.
       
  1515   // and return the width/height of the browser's viewport.
       
  1516   defaultScope.defineProperty(defaultScope, 'screenWidth',
       
  1517     { get: function() { return window.innerWidth; } });
       
  1518 
       
  1519   defaultScope.defineProperty(defaultScope, 'screenHeight',
       
  1520     { get: function() { return window.innerHeight; } });
       
  1521 
       
  1522   // Manage multiple Processing instances
       
  1523   var processingInstances = [];
       
  1524   var processingInstanceIds = {};
       
  1525 
       
  1526   var removeInstance = function(id) {
       
  1527     processingInstances.splice(processingInstanceIds[id], 1);
       
  1528     delete processingInstanceIds[id];
       
  1529   };
       
  1530 
       
  1531   var addInstance = function(processing) {
       
  1532     if (processing.externals.canvas.id === undef || !processing.externals.canvas.id.length) {
       
  1533       processing.externals.canvas.id = "__processing" + processingInstances.length;
       
  1534     }
       
  1535     processingInstanceIds[processing.externals.canvas.id] = processingInstances.length;
       
  1536     processingInstances.push(processing);
       
  1537   };
       
  1538 
       
  1539 
       
  1540   ////////////////////////////////////////////////////////////////////////////
       
  1541   // PFONT.JS START
       
  1542   ////////////////////////////////////////////////////////////////////////////
       
  1543 
       
  1544   /**
       
  1545    * [internal function] computeFontMetrics() calculates various metrics for text
       
  1546    * placement. Currently this function computes the ascent, descent and leading
       
  1547    * (from "lead", used for vertical space) values for the currently active font.
       
  1548    */
       
  1549   function computeFontMetrics(pfont) {
       
  1550     var emQuad = 250,
       
  1551         correctionFactor = pfont.size / emQuad,
       
  1552         canvas = document.createElement("canvas");
       
  1553     canvas.width = 2*emQuad;
       
  1554     canvas.height = 2*emQuad;
       
  1555     canvas.style.opacity = 0;
       
  1556     var cfmFont = pfont.getCSSDefinition(emQuad+"px", "normal"),
       
  1557         ctx = canvas.getContext("2d");
       
  1558     ctx.font = cfmFont;
       
  1559     pfont.context2d = ctx;
       
  1560 
       
  1561     // Size the canvas using a string with common max-ascent and max-descent letters.
       
  1562     // Changing the canvas dimensions resets the context, so we must reset the font.
       
  1563     var protrusions = "dbflkhyjqpg";
       
  1564     canvas.width = ctx.measureText(protrusions).width;
       
  1565     ctx.font = cfmFont;
       
  1566 
       
  1567     // for text lead values, we meaure a multiline text container.
       
  1568     var leadDiv = document.createElement("div");
       
  1569     leadDiv.style.position = "absolute";
       
  1570     leadDiv.style.opacity = 0;
       
  1571     leadDiv.style.fontFamily = '"' + pfont.name + '"';
       
  1572     leadDiv.style.fontSize = emQuad + "px";
       
  1573     leadDiv.innerHTML = protrusions + "<br/>" + protrusions;
       
  1574     document.body.appendChild(leadDiv);
       
  1575 
       
  1576     var w = canvas.width,
       
  1577         h = canvas.height,
       
  1578         baseline = h/2;
       
  1579 
       
  1580     // Set all canvas pixeldata values to 255, with all the content
       
  1581     // data being 0. This lets us scan for data[i] != 255.
       
  1582     ctx.fillStyle = "white";
       
  1583     ctx.fillRect(0, 0, w, h);
       
  1584     ctx.fillStyle = "black";
       
  1585     ctx.fillText(protrusions, 0, baseline);
       
  1586     var pixelData = ctx.getImageData(0, 0, w, h).data;
       
  1587 
       
  1588     // canvas pixel data is w*4 by h*4, because R, G, B and A are separate,
       
  1589     // consecutive values in the array, rather than stored as 32 bit ints.
       
  1590     var i = 0,
       
  1591         w4 = w * 4,
       
  1592         len = pixelData.length;
       
  1593 
       
  1594     // Finding the ascent uses a normal, forward scanline
       
  1595     while (++i < len && pixelData[i] === 255) {
       
  1596       nop();
       
  1597     }
       
  1598     var ascent = Math.round(i / w4);
       
  1599 
       
  1600     // Finding the descent uses a reverse scanline
       
  1601     i = len - 1;
       
  1602     while (--i > 0 && pixelData[i] === 255) {
       
  1603       nop();
       
  1604     }
       
  1605     var descent = Math.round(i / w4);
       
  1606 
       
  1607     // set font metrics
       
  1608     pfont.ascent = correctionFactor * (baseline - ascent);
       
  1609     pfont.descent = correctionFactor * (descent - baseline);
       
  1610 
       
  1611     // Then we try to get the real value from the browser
       
  1612     if (document.defaultView.getComputedStyle) {
       
  1613       var leadDivHeight = document.defaultView.getComputedStyle(leadDiv,null).getPropertyValue("height");
       
  1614       leadDivHeight = correctionFactor * leadDivHeight.replace("px","");
       
  1615       if (leadDivHeight >= pfont.size * 2) {
       
  1616         pfont.leading = Math.round(leadDivHeight/2);
       
  1617       }
       
  1618     }
       
  1619     document.body.removeChild(leadDiv);
       
  1620   }
       
  1621 
       
  1622   // Defines system (non-SVG) font.
       
  1623   function PFont(name, size) {
       
  1624     // according to the P5 API, new PFont() is legal (albeit completely useless)
       
  1625     if (name === undef) {
       
  1626       name = "";
       
  1627     }
       
  1628     this.name = name;
       
  1629     if (size === undef) {
       
  1630       size = 0;
       
  1631     }
       
  1632     this.size = size;
       
  1633     this.glyph = false;
       
  1634     this.ascent = 0;
       
  1635     this.descent = 0;
       
  1636     // For leading, the "safe" value uses the standard TEX ratio
       
  1637     this.leading = 1.2 * size;
       
  1638 
       
  1639     // Note that an italic, bold font must used "... Bold Italic"
       
  1640     // in P5. "... Italic Bold" is treated as normal/normal.
       
  1641     var illegalIndicator = name.indexOf(" Italic Bold");
       
  1642     if (illegalIndicator !== -1) {
       
  1643       name = name.substring(0, illegalIndicator);
       
  1644     }
       
  1645 
       
  1646     // determine font style
       
  1647     this.style = "normal";
       
  1648     var italicsIndicator = name.indexOf(" Italic");
       
  1649     if (italicsIndicator !== -1) {
       
  1650       name = name.substring(0, italicsIndicator);
       
  1651       this.style = "italic";
       
  1652     }
       
  1653 
       
  1654     // determine font weight
       
  1655     this.weight = "normal";
       
  1656     var boldIndicator = name.indexOf(" Bold");
       
  1657     if (boldIndicator !== -1) {
       
  1658       name = name.substring(0, boldIndicator);
       
  1659       this.weight = "bold";
       
  1660     }
       
  1661 
       
  1662     // determine font-family name
       
  1663     this.family = "sans-serif";
       
  1664     if (name !== undef) {
       
  1665       switch(name) {
       
  1666         case "sans-serif":
       
  1667         case "serif":
       
  1668         case "monospace":
       
  1669         case "fantasy":
       
  1670         case "cursive":
       
  1671           this.family = name;
       
  1672           break;
       
  1673         default:
       
  1674           this.family = '"' + name + '", sans-serif';
       
  1675           break;
       
  1676       }
       
  1677     }
       
  1678     // Calculate the ascent/descent/leading value based on
       
  1679     // how the browser renders this font.
       
  1680     this.context2d = null;
       
  1681     computeFontMetrics(this);
       
  1682     this.css = this.getCSSDefinition();
       
  1683     this.context2d.font = this.css;
       
  1684   }
       
  1685 
       
  1686   /**
       
  1687   * This function generates the CSS "font" string for this PFont
       
  1688   */
       
  1689   PFont.prototype.getCSSDefinition = function(fontSize, lineHeight) {
       
  1690     if(fontSize===undef) {
       
  1691       fontSize = this.size + "px";
       
  1692     }
       
  1693     if(lineHeight===undef) {
       
  1694       lineHeight = this.leading + "px";
       
  1695     }
       
  1696     // CSS "font" definition: font-style font-variant font-weight font-size/line-height font-family
       
  1697     var components = [this.style, "normal", this.weight, fontSize + "/" + lineHeight, this.family];
       
  1698     return components.join(" ");
       
  1699   };
       
  1700 
       
  1701   /**
       
  1702   * We cannot rely on there being a 2d context available,
       
  1703   * because we support OPENGL sketches, and canvas3d has
       
  1704   * no "measureText" function in the API.
       
  1705   */
       
  1706   PFont.prototype.measureTextWidth = function(string) {
       
  1707     return this.context2d.measureText(string).width;
       
  1708   };
       
  1709 
       
  1710   /**
       
  1711   * Global "loaded fonts" list, internal to PFont
       
  1712   */
       
  1713   PFont.PFontCache = {};
       
  1714 
       
  1715   /**
       
  1716   * This function acts as single access point for getting and caching
       
  1717   * fonts across all sketches handled by an instance of Processing.js
       
  1718   */
       
  1719   PFont.get = function(fontName, fontSize) {
       
  1720     var cache = PFont.PFontCache;
       
  1721     var idx = fontName+"/"+fontSize;
       
  1722     if (!cache[idx]) {
       
  1723       cache[idx] = new PFont(fontName, fontSize);
       
  1724     }
       
  1725     return cache[idx];
       
  1726   };
       
  1727 
       
  1728   /**
       
  1729   * Lists all standard fonts. Due to browser limitations, this list is
       
  1730   * not the system font list, like in P5, but the CSS "genre" list.
       
  1731   */
       
  1732   PFont.list = function() {
       
  1733     return ["sans-serif", "serif", "monospace", "fantasy", "cursive"];
       
  1734   };
       
  1735 
       
  1736   /**
       
  1737   * Loading external fonts through @font-face rules is handled by PFont,
       
  1738   * to ensure fonts loaded in this way are globally available.
       
  1739   */
       
  1740   PFont.preloading = {
       
  1741     // template element used to compare font sizes
       
  1742     template: {},
       
  1743     // indicates whether or not the reference tiny font has been loaded
       
  1744     initialized: false,
       
  1745     // load the reference tiny font via a css @font-face rule
       
  1746     initialize: function() {
       
  1747       var generateTinyFont = function() {
       
  1748         var encoded = "#E3KAI2wAgT1MvMg7Eo3VmNtYX7ABi3CxnbHlm" +
       
  1749                       "7Abw3kaGVhZ7ACs3OGhoZWE7A53CRobXR47AY3" +
       
  1750                       "AGbG9jYQ7G03Bm1heH7ABC3CBuYW1l7Ae3AgcG" +
       
  1751                       "9zd7AI3AE#B3AQ2kgTY18PPPUACwAg3ALSRoo3" +
       
  1752                       "#yld0xg32QAB77#E777773B#E3C#I#Q77773E#" +
       
  1753                       "Q7777777772CMAIw7AB77732B#M#Q3wAB#g3B#" +
       
  1754                       "E#E2BB//82BB////w#B7#gAEg3E77x2B32B#E#" +
       
  1755                       "Q#MTcBAQ32gAe#M#QQJ#E32M#QQJ#I#g32Q77#";
       
  1756         var expand = function(input) {
       
  1757                        return "AAAAAAAA".substr(~~input ? 7-input : 6);
       
  1758                      };
       
  1759         return encoded.replace(/[#237]/g, expand);
       
  1760       };
       
  1761       var fontface = document.createElement("style");
       
  1762       fontface.setAttribute("type","text/css");
       
  1763       fontface.innerHTML =  "@font-face {\n" +
       
  1764                             '  font-family: "PjsEmptyFont";' + "\n" +
       
  1765                             "  src: url('data:application/x-font-ttf;base64,"+generateTinyFont()+"')\n" +
       
  1766                             "       format('truetype');\n" +
       
  1767                             "}";
       
  1768       document.head.appendChild(fontface);
       
  1769 
       
  1770       // set up the template element
       
  1771       var element = document.createElement("span");
       
  1772       element.style.cssText = 'position: absolute; top: 0; left: 0; opacity: 0; font-family: "PjsEmptyFont", fantasy;';
       
  1773       element.innerHTML = "AAAAAAAA";
       
  1774       document.body.appendChild(element);
       
  1775       this.template = element;
       
  1776 
       
  1777       this.initialized = true;
       
  1778     },
       
  1779     // Shorthand function to get the computed width for an element.
       
  1780     getElementWidth: function(element) {
       
  1781       return document.defaultView.getComputedStyle(element,"").getPropertyValue("width");
       
  1782     },
       
  1783     // time taken so far in attempting to load a font
       
  1784     timeAttempted: 0,
       
  1785     // returns false if no fonts are pending load, or true otherwise.
       
  1786     pending: function(intervallength) {
       
  1787       if (!this.initialized) {
       
  1788         this.initialize();
       
  1789       }
       
  1790       var element,
       
  1791           computedWidthFont,
       
  1792           computedWidthRef = this.getElementWidth(this.template);
       
  1793       for (var i = 0; i < this.fontList.length; i++) {
       
  1794         // compares size of text in pixels. if equal, custom font is not yet loaded
       
  1795         element = this.fontList[i];
       
  1796         computedWidthFont = this.getElementWidth(element);
       
  1797         if (this.timeAttempted < 4000 && computedWidthFont === computedWidthRef) {
       
  1798           this.timeAttempted += intervallength;
       
  1799           return true;
       
  1800         } else {
       
  1801           document.body.removeChild(element);
       
  1802           this.fontList.splice(i--, 1);
       
  1803           this.timeAttempted = 0;
       
  1804         }
       
  1805       }
       
  1806       // if there are no more fonts to load, pending is false
       
  1807       if (this.fontList.length === 0) {
       
  1808         return false;
       
  1809       }
       
  1810       // We should have already returned before getting here.
       
  1811       // But, if we do get here, length!=0 so fonts are pending.
       
  1812       return true;
       
  1813     },
       
  1814     // fontList contains elements to compare font sizes against a template
       
  1815     fontList: [],
       
  1816     // addedList contains the fontnames of all the fonts loaded via @font-face
       
  1817     addedList: {},
       
  1818     // adds a font to the font cache
       
  1819     // creates an element using the font, to start loading the font,
       
  1820     // and compare against a default font to see if the custom font is loaded
       
  1821     add: function(fontSrc) {
       
  1822       if (!this.initialized) {
       
  1823        this.initialize();
       
  1824       }
       
  1825       // fontSrc can be a string or a javascript object
       
  1826       // acceptable fonts are .ttf, .otf, and data uri
       
  1827       var fontName = (typeof fontSrc === 'object' ? fontSrc.fontFace : fontSrc),
       
  1828           fontUrl = (typeof fontSrc === 'object' ? fontSrc.url : fontSrc);
       
  1829 
       
  1830       // check whether we already created the @font-face rule for this font
       
  1831       if (this.addedList[fontName]) {
       
  1832         return;
       
  1833       }
       
  1834 
       
  1835       // if we didn't, create the @font-face rule
       
  1836       var style = document.createElement("style");
       
  1837       style.setAttribute("type","text/css");
       
  1838       style.innerHTML = "@font-face{\n  font-family: '" + fontName + "';\n  src:  url('" + fontUrl + "');\n}\n";
       
  1839       document.head.appendChild(style);
       
  1840       this.addedList[fontName] = true;
       
  1841 
       
  1842       // also create the element to load and compare the new font
       
  1843       var element = document.createElement("span");
       
  1844       element.style.cssText = "position: absolute; top: 0; left: 0; opacity: 0;";
       
  1845       element.style.fontFamily = '"' + fontName + '", "PjsEmptyFont", fantasy';
       
  1846       element.innerHTML = "AAAAAAAA";
       
  1847       document.body.appendChild(element);
       
  1848       this.fontList.push(element);
       
  1849     }
       
  1850   };
       
  1851 
       
  1852 
       
  1853   // add to the default scope
       
  1854   defaultScope.PFont = PFont;
       
  1855 
       
  1856 
       
  1857   ////////////////////////////////////////////////////////////////////////////
       
  1858   // PFONT.JS END
       
  1859   ////////////////////////////////////////////////////////////////////////////
       
  1860 
       
  1861 
       
  1862   var Processing = this.Processing = function(aCanvas, aCode) {
       
  1863     // Previously we allowed calling Processing as a func instead of ctor, but no longer.
       
  1864     if (!(this instanceof Processing)) {
       
  1865       throw("called Processing constructor as if it were a function: missing 'new'.");
       
  1866     }
       
  1867 
       
  1868     var curElement,
       
  1869       pgraphicsMode = (aCanvas === undef && aCode === undef);
       
  1870 
       
  1871     if (pgraphicsMode) {
       
  1872       curElement = document.createElement("canvas");
       
  1873     } else {
       
  1874       // We'll take a canvas element or a string for a canvas element's id
       
  1875       curElement = typeof aCanvas === "string" ? document.getElementById(aCanvas) : aCanvas;
       
  1876     }
       
  1877 
       
  1878     if (!(curElement instanceof HTMLCanvasElement)) {
       
  1879       throw("called Processing constructor without passing canvas element reference or id.");
       
  1880     }
       
  1881 
       
  1882     function unimplemented(s) {
       
  1883       Processing.debug('Unimplemented - ' + s);
       
  1884     }
       
  1885 
       
  1886     // When something new is added to "p." it must also be added to the "names" array.
       
  1887     // The names array contains the names of everything that is inside "p."
       
  1888     var p = this;
       
  1889 
       
  1890     // PJS specific (non-p5) methods and properties to externalize
       
  1891     p.externals = {
       
  1892       canvas:  curElement,
       
  1893       context: undef,
       
  1894       sketch:  undef
       
  1895     };
       
  1896 
       
  1897     p.name            = 'Processing.js Instance'; // Set Processing defaults / environment variables
       
  1898     p.use3DContext    = false; // default '2d' canvas context
       
  1899 
       
  1900     /**
       
  1901      * Confirms if a Processing program is "focused", meaning that it is
       
  1902      * active and will accept input from mouse or keyboard. This variable
       
  1903      * is "true" if it is focused and "false" if not. This variable is
       
  1904      * often used when you want to warn people they need to click on the
       
  1905      * browser before it will work.
       
  1906     */
       
  1907     p.focused         = false;
       
  1908     p.breakShape      = false;
       
  1909 
       
  1910     // Glyph path storage for textFonts
       
  1911     p.glyphTable      = {};
       
  1912 
       
  1913     // Global vars for tracking mouse position
       
  1914     p.pmouseX         = 0;
       
  1915     p.pmouseY         = 0;
       
  1916     p.mouseX          = 0;
       
  1917     p.mouseY          = 0;
       
  1918     p.mouseButton     = 0;
       
  1919     p.mouseScroll     = 0;
       
  1920 
       
  1921     // Undefined event handlers to be replaced by user when needed
       
  1922     p.mouseClicked    = undef;
       
  1923     p.mouseDragged    = undef;
       
  1924     p.mouseMoved      = undef;
       
  1925     p.mousePressed    = undef;
       
  1926     p.mouseReleased   = undef;
       
  1927     p.mouseScrolled   = undef;
       
  1928     p.mouseOver       = undef;
       
  1929     p.mouseOut        = undef;
       
  1930     p.touchStart      = undef;
       
  1931     p.touchEnd        = undef;
       
  1932     p.touchMove       = undef;
       
  1933     p.touchCancel     = undef;
       
  1934     p.key             = undef;
       
  1935     p.keyCode         = undef;
       
  1936     p.keyPressed      = nop; // needed to remove function checks
       
  1937     p.keyReleased     = nop;
       
  1938     p.keyTyped        = nop;
       
  1939     p.draw            = undef;
       
  1940     p.setup           = undef;
       
  1941 
       
  1942     // Remapped vars
       
  1943     p.__mousePressed  = false;
       
  1944     p.__keyPressed    = false;
       
  1945     p.__frameRate     = 60;
       
  1946 
       
  1947     // The current animation frame
       
  1948     p.frameCount      = 0;
       
  1949 
       
  1950     // The height/width of the canvas
       
  1951     p.width           = 100;
       
  1952     p.height          = 100;
       
  1953 
       
  1954     // "Private" variables used to maintain state
       
  1955     var curContext,
       
  1956         curSketch,
       
  1957         drawing, // hold a Drawing2D or Drawing3D object
       
  1958         online = true,
       
  1959         doFill = true,
       
  1960         fillStyle = [1.0, 1.0, 1.0, 1.0],
       
  1961         currentFillColor = 0xFFFFFFFF,
       
  1962         isFillDirty = true,
       
  1963         doStroke = true,
       
  1964         strokeStyle = [0.0, 0.0, 0.0, 1.0],
       
  1965         currentStrokeColor = 0xFF000000,
       
  1966         isStrokeDirty = true,
       
  1967         lineWidth = 1,
       
  1968         loopStarted = false,
       
  1969         renderSmooth = false,
       
  1970         doLoop = true,
       
  1971         looping = 0,
       
  1972         curRectMode = PConstants.CORNER,
       
  1973         curEllipseMode = PConstants.CENTER,
       
  1974         normalX = 0,
       
  1975         normalY = 0,
       
  1976         normalZ = 0,
       
  1977         normalMode = PConstants.NORMAL_MODE_AUTO,
       
  1978         curFrameRate = 60,
       
  1979         curMsPerFrame = 1000/curFrameRate,
       
  1980         curCursor = PConstants.ARROW,
       
  1981         oldCursor = curElement.style.cursor,
       
  1982         curShape = PConstants.POLYGON,
       
  1983         curShapeCount = 0,
       
  1984         curvePoints = [],
       
  1985         curTightness = 0,
       
  1986         curveDet = 20,
       
  1987         curveInited = false,
       
  1988         backgroundObj = -3355444, // rgb(204, 204, 204) is the default gray background colour
       
  1989         bezDetail = 20,
       
  1990         colorModeA = 255,
       
  1991         colorModeX = 255,
       
  1992         colorModeY = 255,
       
  1993         colorModeZ = 255,
       
  1994         pathOpen = false,
       
  1995         mouseDragging = false,
       
  1996         pmouseXLastFrame = 0,
       
  1997         pmouseYLastFrame = 0,
       
  1998         curColorMode = PConstants.RGB,
       
  1999         curTint = null,
       
  2000         curTint3d = null,
       
  2001         getLoaded = false,
       
  2002         start = Date.now(),
       
  2003         timeSinceLastFPS = start,
       
  2004         framesSinceLastFPS = 0,
       
  2005         textcanvas,
       
  2006         curveBasisMatrix,
       
  2007         curveToBezierMatrix,
       
  2008         curveDrawMatrix,
       
  2009         bezierDrawMatrix,
       
  2010         bezierBasisInverse,
       
  2011         bezierBasisMatrix,
       
  2012         curContextCache = { attributes: {}, locations: {} },
       
  2013         // Shaders
       
  2014         programObject3D,
       
  2015         programObject2D,
       
  2016         programObjectUnlitShape,
       
  2017         boxBuffer,
       
  2018         boxNormBuffer,
       
  2019         boxOutlineBuffer,
       
  2020         rectBuffer,
       
  2021         rectNormBuffer,
       
  2022         sphereBuffer,
       
  2023         lineBuffer,
       
  2024         fillBuffer,
       
  2025         fillColorBuffer,
       
  2026         strokeColorBuffer,
       
  2027         pointBuffer,
       
  2028         shapeTexVBO,
       
  2029         canTex,   // texture for createGraphics
       
  2030         textTex,   // texture for 3d tex
       
  2031         curTexture = {width:0,height:0},
       
  2032         curTextureMode = PConstants.IMAGE,
       
  2033         usingTexture = false,
       
  2034         textBuffer,
       
  2035         textureBuffer,
       
  2036         indexBuffer,
       
  2037         // Text alignment
       
  2038         horizontalTextAlignment = PConstants.LEFT,
       
  2039         verticalTextAlignment = PConstants.BASELINE,
       
  2040         textMode = PConstants.MODEL,
       
  2041         // Font state
       
  2042         curFontName = "Arial",
       
  2043         curTextSize = 12,
       
  2044         curTextAscent = 9,
       
  2045         curTextDescent = 2,
       
  2046         curTextLeading = 14,
       
  2047         curTextFont = PFont.get(curFontName, curTextSize),
       
  2048         // Pixels cache
       
  2049         originalContext,
       
  2050         proxyContext = null,
       
  2051         isContextReplaced = false,
       
  2052         setPixelsCached,
       
  2053         maxPixelsCached = 1000,
       
  2054         pressedKeysMap = [],
       
  2055         lastPressedKeyCode = null,
       
  2056         codedKeys = [ PConstants.SHIFT, PConstants.CONTROL, PConstants.ALT, PConstants.CAPSLK, PConstants.PGUP, PConstants.PGDN,
       
  2057                       PConstants.END, PConstants.HOME, PConstants.LEFT, PConstants.UP, PConstants.RIGHT, PConstants.DOWN, PConstants.NUMLK,
       
  2058                       PConstants.INSERT, PConstants.F1, PConstants.F2, PConstants.F3, PConstants.F4, PConstants.F5, PConstants.F6, PConstants.F7,
       
  2059                       PConstants.F8, PConstants.F9, PConstants.F10, PConstants.F11, PConstants.F12, PConstants.META ];
       
  2060 
       
  2061     // Get padding and border style widths for mouse offsets
       
  2062     var stylePaddingLeft, stylePaddingTop, styleBorderLeft, styleBorderTop;
       
  2063 
       
  2064     if (document.defaultView && document.defaultView.getComputedStyle) {
       
  2065       stylePaddingLeft = parseInt(document.defaultView.getComputedStyle(curElement, null)['paddingLeft'], 10)      || 0;
       
  2066       stylePaddingTop  = parseInt(document.defaultView.getComputedStyle(curElement, null)['paddingTop'], 10)       || 0;
       
  2067       styleBorderLeft  = parseInt(document.defaultView.getComputedStyle(curElement, null)['borderLeftWidth'], 10)  || 0;
       
  2068       styleBorderTop   = parseInt(document.defaultView.getComputedStyle(curElement, null)['borderTopWidth'], 10)   || 0;
       
  2069     }
       
  2070 
       
  2071     // User can only have MAX_LIGHTS lights
       
  2072     var lightCount = 0;
       
  2073 
       
  2074     //sphere stuff
       
  2075     var sphereDetailV = 0,
       
  2076         sphereDetailU = 0,
       
  2077         sphereX = [],
       
  2078         sphereY = [],
       
  2079         sphereZ = [],
       
  2080         sinLUT = new Float32Array(PConstants.SINCOS_LENGTH),
       
  2081         cosLUT = new Float32Array(PConstants.SINCOS_LENGTH),
       
  2082         sphereVerts,
       
  2083         sphereNorms;
       
  2084 
       
  2085     // Camera defaults and settings
       
  2086     var cam,
       
  2087         cameraInv,
       
  2088         modelView,
       
  2089         modelViewInv,
       
  2090         userMatrixStack,
       
  2091         userReverseMatrixStack,
       
  2092         inverseCopy,
       
  2093         projection,
       
  2094         manipulatingCamera = false,
       
  2095         frustumMode = false,
       
  2096         cameraFOV = 60 * (Math.PI / 180),
       
  2097         cameraX = p.width / 2,
       
  2098         cameraY = p.height / 2,
       
  2099         cameraZ = cameraY / Math.tan(cameraFOV / 2),
       
  2100         cameraNear = cameraZ / 10,
       
  2101         cameraFar = cameraZ * 10,
       
  2102         cameraAspect = p.width / p.height;
       
  2103 
       
  2104     var vertArray = [],
       
  2105         curveVertArray = [],
       
  2106         curveVertCount = 0,
       
  2107         isCurve = false,
       
  2108         isBezier = false,
       
  2109         firstVert = true;
       
  2110 
       
  2111     //PShape stuff
       
  2112     var curShapeMode = PConstants.CORNER;
       
  2113 
       
  2114     // Stores states for pushStyle() and popStyle().
       
  2115     var styleArray = [];
       
  2116 
       
  2117     // Vertices are specified in a counter-clockwise order
       
  2118     // triangles are in this order: back, front, right, bottom, left, top
       
  2119     var boxVerts = new Float32Array([
       
  2120        0.5,  0.5, -0.5,  0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5,  0.5, -0.5,  0.5,  0.5, -0.5,
       
  2121        0.5,  0.5,  0.5, -0.5,  0.5,  0.5, -0.5, -0.5,  0.5, -0.5, -0.5,  0.5,  0.5, -0.5,  0.5,  0.5,  0.5,  0.5,
       
  2122        0.5,  0.5, -0.5,  0.5,  0.5,  0.5,  0.5, -0.5,  0.5,  0.5, -0.5,  0.5,  0.5, -0.5, -0.5,  0.5,  0.5, -0.5,
       
  2123        0.5, -0.5, -0.5,  0.5, -0.5,  0.5, -0.5, -0.5,  0.5, -0.5, -0.5,  0.5, -0.5, -0.5, -0.5,  0.5, -0.5, -0.5,
       
  2124       -0.5, -0.5, -0.5, -0.5, -0.5,  0.5, -0.5,  0.5,  0.5, -0.5,  0.5,  0.5, -0.5,  0.5, -0.5, -0.5, -0.5, -0.5,
       
  2125        0.5,  0.5,  0.5,  0.5,  0.5, -0.5, -0.5,  0.5, -0.5, -0.5,  0.5, -0.5, -0.5,  0.5,  0.5,  0.5,  0.5,  0.5]);
       
  2126 
       
  2127     var boxOutlineVerts = new Float32Array([
       
  2128        0.5,  0.5,  0.5,  0.5, -0.5,  0.5,  0.5,  0.5, -0.5,  0.5, -0.5, -0.5,
       
  2129       -0.5,  0.5, -0.5, -0.5, -0.5, -0.5, -0.5,  0.5,  0.5, -0.5, -0.5,  0.5,
       
  2130        0.5,  0.5,  0.5,  0.5,  0.5, -0.5,  0.5,  0.5, -0.5, -0.5,  0.5, -0.5,
       
  2131       -0.5,  0.5, -0.5, -0.5,  0.5,  0.5, -0.5,  0.5,  0.5,  0.5,  0.5,  0.5,
       
  2132        0.5, -0.5,  0.5,  0.5, -0.5, -0.5,  0.5, -0.5, -0.5, -0.5, -0.5, -0.5,
       
  2133       -0.5, -0.5, -0.5, -0.5, -0.5,  0.5, -0.5, -0.5,  0.5,  0.5, -0.5,  0.5]);
       
  2134 
       
  2135     var boxNorms = new Float32Array([
       
  2136        0,  0, -1,  0,  0, -1,  0,  0, -1,  0,  0, -1,  0,  0, -1,  0,  0, -1,
       
  2137        0,  0,  1,  0,  0,  1,  0,  0,  1,  0,  0,  1,  0,  0,  1,  0,  0,  1,
       
  2138        1,  0,  0,  1,  0,  0,  1,  0,  0,  1,  0,  0,  1,  0,  0,  1,  0,  0,
       
  2139        0, -1,  0,  0, -1,  0,  0, -1,  0,  0, -1,  0,  0, -1,  0,  0, -1,  0,
       
  2140       -1,  0,  0, -1,  0,  0, -1,  0,  0, -1,  0,  0, -1,  0,  0, -1,  0,  0,
       
  2141        0,  1,  0,  0,  1,  0,  0,  1,  0,  0,  1,  0,  0,  1,  0,  0,  1,  0]);
       
  2142 
       
  2143     // These verts are used for the fill and stroke using TRIANGLE_FAN and LINE_LOOP
       
  2144     var rectVerts = new Float32Array([0,0,0, 0,1,0, 1,1,0, 1,0,0]);
       
  2145 
       
  2146     var rectNorms = new Float32Array([0,0,1, 0,0,1, 0,0,1, 0,0,1]);
       
  2147 
       
  2148 
       
  2149     // Shader for points and lines in begin/endShape
       
  2150     var vShaderSrcUnlitShape =
       
  2151       "varying vec4 frontColor;" +
       
  2152 
       
  2153       "attribute vec3 aVertex;" +
       
  2154       "attribute vec4 aColor;" +
       
  2155 
       
  2156       "uniform mat4 uView;" +
       
  2157       "uniform mat4 uProjection;" +
       
  2158       "uniform float pointSize;" +
       
  2159 
       
  2160       "void main(void) {" +
       
  2161       "  frontColor = aColor;" +
       
  2162       "  gl_PointSize = pointSize;" +
       
  2163       "  gl_Position = uProjection * uView * vec4(aVertex, 1.0);" +
       
  2164       "}";
       
  2165 
       
  2166     var fShaderSrcUnlitShape =
       
  2167       "#ifdef GL_ES\n" +
       
  2168       "precision highp float;\n" +
       
  2169       "#endif\n" +
       
  2170 
       
  2171       "varying vec4 frontColor;" +
       
  2172 
       
  2173       "void main(void){" +
       
  2174       "  gl_FragColor = frontColor;" +
       
  2175       "}";
       
  2176 
       
  2177     // Shader for rect, text, box outlines, sphere outlines, point() and line()
       
  2178     var vertexShaderSource2D =
       
  2179       "varying vec4 frontColor;" +
       
  2180 
       
  2181       "attribute vec3 Vertex;" +
       
  2182       "attribute vec2 aTextureCoord;" +
       
  2183       "uniform vec4 color;" +
       
  2184 
       
  2185       "uniform mat4 model;" +
       
  2186       "uniform mat4 view;" +
       
  2187       "uniform mat4 projection;" +
       
  2188       "uniform float pointSize;" +
       
  2189       "varying vec2 vTextureCoord;"+
       
  2190 
       
  2191       "void main(void) {" +
       
  2192       "  gl_PointSize = pointSize;" +
       
  2193       "  frontColor = color;" +
       
  2194       "  gl_Position = projection * view * model * vec4(Vertex, 1.0);" +
       
  2195       "  vTextureCoord = aTextureCoord;" +
       
  2196       "}";
       
  2197 
       
  2198     var fragmentShaderSource2D =
       
  2199       "#ifdef GL_ES\n" +
       
  2200       "precision highp float;\n" +
       
  2201       "#endif\n" +
       
  2202 
       
  2203       "varying vec4 frontColor;" +
       
  2204       "varying vec2 vTextureCoord;"+
       
  2205 
       
  2206       "uniform sampler2D uSampler;"+
       
  2207       "uniform int picktype;"+
       
  2208 
       
  2209       "void main(void){" +
       
  2210       "  if(picktype == 0){"+
       
  2211       "    gl_FragColor = frontColor;" +
       
  2212       "  }" +
       
  2213       "  else if(picktype == 1){"+
       
  2214       "    float alpha = texture2D(uSampler, vTextureCoord).a;"+
       
  2215       "    gl_FragColor = vec4(frontColor.rgb*alpha, alpha);\n"+
       
  2216       "  }"+
       
  2217       "}";
       
  2218 
       
  2219     var webglMaxTempsWorkaround = /Windows/.test(navigator.userAgent);
       
  2220 
       
  2221     // Vertex shader for boxes and spheres
       
  2222     var vertexShaderSource3D =
       
  2223       "varying vec4 frontColor;" +
       
  2224 
       
  2225       "attribute vec3 Vertex;" +
       
  2226       "attribute vec3 Normal;" +
       
  2227       "attribute vec4 aColor;" +
       
  2228       "attribute vec2 aTexture;" +
       
  2229       "varying   vec2 vTexture;" +
       
  2230 
       
  2231       "uniform vec4 color;" +
       
  2232 
       
  2233       "uniform bool usingMat;" +
       
  2234       "uniform vec3 specular;" +
       
  2235       "uniform vec3 mat_emissive;" +
       
  2236       "uniform vec3 mat_ambient;" +
       
  2237       "uniform vec3 mat_specular;" +
       
  2238       "uniform float shininess;" +
       
  2239 
       
  2240       "uniform mat4 model;" +
       
  2241       "uniform mat4 view;" +
       
  2242       "uniform mat4 projection;" +
       
  2243       "uniform mat4 normalTransform;" +
       
  2244 
       
  2245       "uniform int lightCount;" +
       
  2246       "uniform vec3 falloff;" +
       
  2247 
       
  2248       // careful changing the order of these fields. Some cards
       
  2249       // have issues with memory alignment
       
  2250       "struct Light {" +
       
  2251       "  int type;" +
       
  2252       "  vec3 color;" +
       
  2253       "  vec3 position;" +
       
  2254       "  vec3 direction;" +
       
  2255       "  float angle;" +
       
  2256       "  vec3 halfVector;" +
       
  2257       "  float concentration;" +
       
  2258       "};" +
       
  2259 
       
  2260       // nVidia cards have issues with arrays of structures
       
  2261       // so instead we create 8 instances of Light
       
  2262       "uniform Light lights0;" +
       
  2263       "uniform Light lights1;" +
       
  2264       "uniform Light lights2;" +
       
  2265       "uniform Light lights3;" +
       
  2266       "uniform Light lights4;" +
       
  2267       "uniform Light lights5;" +
       
  2268       "uniform Light lights6;" +
       
  2269       "uniform Light lights7;" +
       
  2270 
       
  2271      // GLSL does not support switch
       
  2272       "Light getLight(int index){" +
       
  2273       "  if(index == 0) return lights0;" +
       
  2274       "  if(index == 1) return lights1;" +
       
  2275       "  if(index == 2) return lights2;" +
       
  2276       "  if(index == 3) return lights3;" +
       
  2277       "  if(index == 4) return lights4;" +
       
  2278       "  if(index == 5) return lights5;" +
       
  2279       "  if(index == 6) return lights6;" +
       
  2280       // Do not use a conditional for the last return statement
       
  2281       // because some video cards will fail and complain that
       
  2282       // "not all paths return"
       
  2283       "  return lights7;" +
       
  2284       "}" +
       
  2285 
       
  2286       "void AmbientLight( inout vec3 totalAmbient, in vec3 ecPos, in Light light ) {" +
       
  2287       // Get the vector from the light to the vertex
       
  2288       // Get the distance from the current vector to the light position
       
  2289       "  float d = length( light.position - ecPos );" +
       
  2290       "  float attenuation = 1.0 / ( falloff[0] + ( falloff[1] * d ) + ( falloff[2] * d * d ));" +
       
  2291       "  totalAmbient += light.color * attenuation;" +
       
  2292       "}" +
       
  2293 
       
  2294       "void DirectionalLight( inout vec3 col, inout vec3 spec, in vec3 vertNormal, in vec3 ecPos, in Light light ) {" +
       
  2295       "  float powerfactor = 0.0;" +
       
  2296       "  float nDotVP = max(0.0, dot( vertNormal, normalize(-light.position) ));" +
       
  2297       "  float nDotVH = max(0.0, dot( vertNormal, normalize(-light.position-normalize(ecPos) )));" +
       
  2298 
       
  2299       "  if( nDotVP != 0.0 ){" +
       
  2300       "    powerfactor = pow( nDotVH, shininess );" +
       
  2301       "  }" +
       
  2302 
       
  2303       "  col += light.color * nDotVP;" +
       
  2304       "  spec += specular * powerfactor;" +
       
  2305       "}" +
       
  2306 
       
  2307       "void PointLight( inout vec3 col, inout vec3 spec, in vec3 vertNormal, in vec3 ecPos, in Light light ) {" +
       
  2308       "  float powerfactor;" +
       
  2309 
       
  2310       // Get the vector from the light to the vertex
       
  2311       "   vec3 VP = light.position - ecPos;" +
       
  2312 
       
  2313       // Get the distance from the current vector to the light position
       
  2314       "  float d = length( VP ); " +
       
  2315 
       
  2316       // Normalize the light ray so it can be used in the dot product operation.
       
  2317       "  VP = normalize( VP );" +
       
  2318 
       
  2319       "  float attenuation = 1.0 / ( falloff[0] + ( falloff[1] * d ) + ( falloff[2] * d * d ));" +
       
  2320 
       
  2321       "  float nDotVP = max( 0.0, dot( vertNormal, VP ));" +
       
  2322       "  vec3 halfVector = normalize( VP - normalize(ecPos) );" +
       
  2323       "  float nDotHV = max( 0.0, dot( vertNormal, halfVector ));" +
       
  2324 
       
  2325       "  if( nDotVP == 0.0) {" +
       
  2326       "    powerfactor = 0.0;" +
       
  2327       "  }" +
       
  2328       "  else{" +
       
  2329       "    powerfactor = pow( nDotHV, shininess );" +
       
  2330       "  }" +
       
  2331 
       
  2332       "  spec += specular * powerfactor * attenuation;" +
       
  2333       "  col += light.color * nDotVP * attenuation;" +
       
  2334       "}" +
       
  2335 
       
  2336       /*
       
  2337       */
       
  2338       "void SpotLight( inout vec3 col, inout vec3 spec, in vec3 vertNormal, in vec3 ecPos, in Light light ) {" +
       
  2339       "  float spotAttenuation;" +
       
  2340       "  float powerfactor;" +
       
  2341 
       
  2342       // calculate the vector from the current vertex to the light.
       
  2343       "  vec3 VP = light.position - ecPos; " +
       
  2344       "  vec3 ldir = normalize( -light.direction );" +
       
  2345 
       
  2346       // get the distance from the spotlight and the vertex
       
  2347       "  float d = length( VP );" +
       
  2348       "  VP = normalize( VP );" +
       
  2349 
       
  2350       "  float attenuation = 1.0 / ( falloff[0] + ( falloff[1] * d ) + ( falloff[2] * d * d ) );" +
       
  2351 
       
  2352       // dot product of the vector from vertex to light and light direction.
       
  2353       "  float spotDot = dot( VP, ldir );" +
       
  2354 
       
  2355       // if the vertex falls inside the cone
       
  2356       (webglMaxTempsWorkaround ? // Windows reports max temps error if light.angle is used
       
  2357       "  spotAttenuation = 1.0; " :
       
  2358       "  if( spotDot > cos( light.angle ) ) {" +
       
  2359       "    spotAttenuation = pow( spotDot, light.concentration );" +
       
  2360       "  }" +
       
  2361       "  else{" +
       
  2362       "    spotAttenuation = 0.0;" +
       
  2363       "  }" +
       
  2364       "  attenuation *= spotAttenuation;" +
       
  2365       "") +
       
  2366 
       
  2367       "  float nDotVP = max( 0.0, dot( vertNormal, VP ));" +
       
  2368       "  vec3 halfVector = normalize( VP - normalize(ecPos) );" +
       
  2369       "  float nDotHV = max( 0.0, dot( vertNormal, halfVector ));" +
       
  2370 
       
  2371       "  if( nDotVP == 0.0 ) {" +
       
  2372       "    powerfactor = 0.0;" +
       
  2373       "  }" +
       
  2374       "  else {" +
       
  2375       "    powerfactor = pow( nDotHV, shininess );" +
       
  2376       "  }" +
       
  2377 
       
  2378       "  spec += specular * powerfactor * attenuation;" +
       
  2379       "  col += light.color * nDotVP * attenuation;" +
       
  2380       "}" +
       
  2381 
       
  2382       "void main(void) {" +
       
  2383       "  vec3 finalAmbient = vec3( 0.0, 0.0, 0.0 );" +
       
  2384       "  vec3 finalDiffuse = vec3( 0.0, 0.0, 0.0 );" +
       
  2385       "  vec3 finalSpecular = vec3( 0.0, 0.0, 0.0 );" +
       
  2386 
       
  2387       "  vec4 col = color;" +
       
  2388 
       
  2389       "  if(color[0] == -1.0){" +
       
  2390       "    col = aColor;" +
       
  2391       "  }" +
       
  2392 
       
  2393       // We use the sphere vertices as the normals when we create the sphere buffer.
       
  2394       // But this only works if the sphere vertices are unit length, so we
       
  2395       // have to normalize the normals here. Since this is only required for spheres
       
  2396       // we could consider placing this in a conditional later on.
       
  2397       "  vec3 norm = normalize(vec3( normalTransform * vec4( Normal, 0.0 ) ));" +
       
  2398 
       
  2399       "  vec4 ecPos4 = view * model * vec4(Vertex,1.0);" +
       
  2400       "  vec3 ecPos = (vec3(ecPos4))/ecPos4.w;" +
       
  2401 
       
  2402       // If there were no lights this draw call, just use the
       
  2403       // assigned fill color of the shape and the specular value
       
  2404       "  if( lightCount == 0 ) {" +
       
  2405       "    frontColor = col + vec4(mat_specular,1.0);" +
       
  2406       "  }" +
       
  2407       "  else {" +
       
  2408            // WebGL forces us to iterate over a constant value
       
  2409            // so we can't iterate using lightCount
       
  2410       "    for( int i = 0; i < 8; i++ ) {" +
       
  2411       "      Light l = getLight(i);" +
       
  2412 
       
  2413       // We can stop iterating if we know we have gone past
       
  2414       // the number of lights which are on
       
  2415       "      if( i >= lightCount ){" +
       
  2416       "        break;" +
       
  2417       "      }" +
       
  2418 
       
  2419       "      if( l.type == 0 ) {" +
       
  2420       "        AmbientLight( finalAmbient, ecPos, l );" +
       
  2421       "      }" +
       
  2422       "      else if( l.type == 1 ) {" +
       
  2423       "        DirectionalLight( finalDiffuse, finalSpecular, norm, ecPos, l );" +
       
  2424       "      }" +
       
  2425       "      else if( l.type == 2 ) {" +
       
  2426       "        PointLight( finalDiffuse, finalSpecular, norm, ecPos, l );" +
       
  2427       "      }" +
       
  2428       "      else {" +
       
  2429       "        SpotLight( finalDiffuse, finalSpecular, norm, ecPos, l );" +
       
  2430       "      }" +
       
  2431       "    }" +
       
  2432 
       
  2433       "   if( usingMat == false ) {" +
       
  2434       "     frontColor = vec4(" +
       
  2435       "       vec3(col) * finalAmbient +" +
       
  2436       "       vec3(col) * finalDiffuse +" +
       
  2437       "       vec3(col) * finalSpecular," +
       
  2438       "       col[3] );" +
       
  2439       "   }" +
       
  2440       "   else{" +
       
  2441       "     frontColor = vec4( " +
       
  2442       "       mat_emissive + " +
       
  2443       "       (vec3(col) * mat_ambient * finalAmbient) + " +
       
  2444       "       (vec3(col) * finalDiffuse) + " +
       
  2445       "       (mat_specular * finalSpecular), " +
       
  2446       "       col[3] );" +
       
  2447       "    }" +
       
  2448       "  }" +
       
  2449 
       
  2450       "  vTexture.xy = aTexture.xy;" +
       
  2451       "  gl_Position = projection * view * model * vec4( Vertex, 1.0 );" +
       
  2452       "}";
       
  2453 
       
  2454     var fragmentShaderSource3D =
       
  2455       "#ifdef GL_ES\n" +
       
  2456       "precision highp float;\n" +
       
  2457       "#endif\n" +
       
  2458 
       
  2459       "varying vec4 frontColor;" +
       
  2460 
       
  2461       "uniform sampler2D sampler;" +
       
  2462       "uniform bool usingTexture;" +
       
  2463       "varying vec2 vTexture;" +
       
  2464 
       
  2465       // In Processing, when a texture is used, the fill color is ignored
       
  2466       // vec4(1.0,1.0,1.0,0.5)
       
  2467       "void main(void){" +
       
  2468       "  if(usingTexture){" +
       
  2469       "    gl_FragColor = vec4(texture2D(sampler, vTexture.xy)) * frontColor;" +
       
  2470       "  }"+
       
  2471       "  else{" +
       
  2472       "    gl_FragColor = frontColor;" +
       
  2473       "  }" +
       
  2474       "}";
       
  2475 
       
  2476     ////////////////////////////////////////////////////////////////////////////
       
  2477     // 3D Functions
       
  2478     ////////////////////////////////////////////////////////////////////////////
       
  2479 
       
  2480     /*
       
  2481      * Sets a uniform variable in a program object to a particular
       
  2482      * value. Before calling this function, ensure the correct
       
  2483      * program object has been installed as part of the current
       
  2484      * rendering state by calling useProgram.
       
  2485      *
       
  2486      * On some systems, if the variable exists in the shader but isn't used,
       
  2487      * the compiler will optimize it out and this function will fail.
       
  2488      *
       
  2489      * @param {WebGLProgram} programObj program object returned from
       
  2490      * createProgramObject
       
  2491      * @param {String} varName the name of the variable in the shader
       
  2492      * @param {float | Array} varValue either a scalar value or an Array
       
  2493      *
       
  2494      * @returns none
       
  2495      *
       
  2496      * @see uniformi
       
  2497      * @see uniformMatrix
       
  2498     */
       
  2499     function uniformf(cacheId, programObj, varName, varValue) {
       
  2500       var varLocation = curContextCache.locations[cacheId];
       
  2501       if(varLocation === undef) {
       
  2502         varLocation = curContext.getUniformLocation(programObj, varName);
       
  2503         curContextCache.locations[cacheId] = varLocation;
       
  2504       }
       
  2505       // the variable won't be found if it was optimized out.
       
  2506       if (varLocation !== null) {
       
  2507         if (varValue.length === 4) {
       
  2508           curContext.uniform4fv(varLocation, varValue);
       
  2509         } else if (varValue.length === 3) {
       
  2510           curContext.uniform3fv(varLocation, varValue);
       
  2511         } else if (varValue.length === 2) {
       
  2512           curContext.uniform2fv(varLocation, varValue);
       
  2513         } else {
       
  2514           curContext.uniform1f(varLocation, varValue);
       
  2515         }
       
  2516       }
       
  2517     }
       
  2518 
       
  2519     /**
       
  2520      * Sets a uniform int or int array in a program object to a particular
       
  2521      * value. Before calling this function, ensure the correct
       
  2522      * program object has been installed as part of the current
       
  2523      * rendering state.
       
  2524      *
       
  2525      * On some systems, if the variable exists in the shader but isn't used,
       
  2526      * the compiler will optimize it out and this function will fail.
       
  2527      *
       
  2528      * @param {WebGLProgram} programObj program object returned from
       
  2529      * createProgramObject
       
  2530      * @param {String} varName the name of the variable in the shader
       
  2531      * @param {int | Array} varValue either a scalar value or an Array
       
  2532      *
       
  2533      * @returns none
       
  2534      *
       
  2535      * @see uniformf
       
  2536      * @see uniformMatrix
       
  2537     */
       
  2538     function uniformi(cacheId, programObj, varName, varValue) {
       
  2539       var varLocation = curContextCache.locations[cacheId];
       
  2540       if(varLocation === undef) {
       
  2541         varLocation = curContext.getUniformLocation(programObj, varName);
       
  2542         curContextCache.locations[cacheId] = varLocation;
       
  2543       }
       
  2544       // the variable won't be found if it was optimized out.
       
  2545       if (varLocation !== null) {
       
  2546         if (varValue.length === 4) {
       
  2547           curContext.uniform4iv(varLocation, varValue);
       
  2548         } else if (varValue.length === 3) {
       
  2549           curContext.uniform3iv(varLocation, varValue);
       
  2550         } else if (varValue.length === 2) {
       
  2551           curContext.uniform2iv(varLocation, varValue);
       
  2552         } else {
       
  2553           curContext.uniform1i(varLocation, varValue);
       
  2554         }
       
  2555       }
       
  2556     }
       
  2557 
       
  2558     /**
       
  2559      * Sets the value of a uniform matrix variable in a program
       
  2560      * object. Before calling this function, ensure the correct
       
  2561      * program object has been installed as part of the current
       
  2562      * rendering state.
       
  2563      *
       
  2564      * On some systems, if the variable exists in the shader but
       
  2565      * isn't used, the compiler will optimize it out and this
       
  2566      * function will fail.
       
  2567      *
       
  2568      * @param {WebGLProgram} programObj program object returned from
       
  2569      * createProgramObject
       
  2570      * @param {String} varName the name of the variable in the shader
       
  2571      * @param {boolean} transpose must be false
       
  2572      * @param {Array} matrix an array of 4, 9 or 16 values
       
  2573      *
       
  2574      * @returns none
       
  2575      *
       
  2576      * @see uniformi
       
  2577      * @see uniformf
       
  2578     */
       
  2579     function uniformMatrix(cacheId, programObj, varName, transpose, matrix) {
       
  2580       var varLocation = curContextCache.locations[cacheId];
       
  2581       if(varLocation === undef) {
       
  2582         varLocation = curContext.getUniformLocation(programObj, varName);
       
  2583         curContextCache.locations[cacheId] = varLocation;
       
  2584       }
       
  2585       // the variable won't be found if it was optimized out.
       
  2586       if (varLocation !== -1) {
       
  2587         if (matrix.length === 16) {
       
  2588           curContext.uniformMatrix4fv(varLocation, transpose, matrix);
       
  2589         } else if (matrix.length === 9) {
       
  2590           curContext.uniformMatrix3fv(varLocation, transpose, matrix);
       
  2591         } else {
       
  2592           curContext.uniformMatrix2fv(varLocation, transpose, matrix);
       
  2593         }
       
  2594       }
       
  2595     }
       
  2596 
       
  2597     /**
       
  2598      * Binds the VBO, sets the vertex attribute data for the program
       
  2599      * object and enables the attribute.
       
  2600      *
       
  2601      * On some systems, if the attribute exists in the shader but
       
  2602      * isn't used, the compiler will optimize it out and this
       
  2603      * function will fail.
       
  2604      *
       
  2605      * @param {WebGLProgram} programObj program object returned from
       
  2606      * createProgramObject
       
  2607      * @param {String} varName the name of the variable in the shader
       
  2608      * @param {int} size the number of components per vertex attribute
       
  2609      * @param {WebGLBuffer} VBO Vertex Buffer Object
       
  2610      *
       
  2611      * @returns none
       
  2612      *
       
  2613      * @see disableVertexAttribPointer
       
  2614     */
       
  2615     function vertexAttribPointer(cacheId, programObj, varName, size, VBO) {
       
  2616       var varLocation = curContextCache.attributes[cacheId];
       
  2617       if(varLocation === undef) {
       
  2618         varLocation = curContext.getAttribLocation(programObj, varName);
       
  2619         curContextCache.attributes[cacheId] = varLocation;
       
  2620       }
       
  2621       if (varLocation !== -1) {
       
  2622         curContext.bindBuffer(curContext.ARRAY_BUFFER, VBO);
       
  2623         curContext.vertexAttribPointer(varLocation, size, curContext.FLOAT, false, 0, 0);
       
  2624         curContext.enableVertexAttribArray(varLocation);
       
  2625       }
       
  2626     }
       
  2627 
       
  2628     /**
       
  2629      * Disables a program object attribute from being sent to WebGL.
       
  2630      *
       
  2631      * @param {WebGLProgram} programObj program object returned from
       
  2632      * createProgramObject
       
  2633      * @param {String} varName name of the attribute
       
  2634      *
       
  2635      * @returns none
       
  2636      *
       
  2637      * @see vertexAttribPointer
       
  2638     */
       
  2639     function disableVertexAttribPointer(cacheId, programObj, varName){
       
  2640       var varLocation = curContextCache.attributes[cacheId];
       
  2641       if(varLocation === undef) {
       
  2642         varLocation = curContext.getAttribLocation(programObj, varName);
       
  2643         curContextCache.attributes[cacheId] = varLocation;
       
  2644       }
       
  2645       if (varLocation !== -1) {
       
  2646         curContext.disableVertexAttribArray(varLocation);
       
  2647       }
       
  2648     }
       
  2649 
       
  2650     /**
       
  2651      * Creates a WebGL program object.
       
  2652      *
       
  2653      * @param {String} vetexShaderSource
       
  2654      * @param {String} fragmentShaderSource
       
  2655      *
       
  2656      * @returns {WebGLProgram} A program object
       
  2657     */
       
  2658     var createProgramObject = function(curContext, vetexShaderSource, fragmentShaderSource) {
       
  2659       var vertexShaderObject = curContext.createShader(curContext.VERTEX_SHADER);
       
  2660       curContext.shaderSource(vertexShaderObject, vetexShaderSource);
       
  2661       curContext.compileShader(vertexShaderObject);
       
  2662       if (!curContext.getShaderParameter(vertexShaderObject, curContext.COMPILE_STATUS)) {
       
  2663         throw curContext.getShaderInfoLog(vertexShaderObject);
       
  2664       }
       
  2665 
       
  2666       var fragmentShaderObject = curContext.createShader(curContext.FRAGMENT_SHADER);
       
  2667       curContext.shaderSource(fragmentShaderObject, fragmentShaderSource);
       
  2668       curContext.compileShader(fragmentShaderObject);
       
  2669       if (!curContext.getShaderParameter(fragmentShaderObject, curContext.COMPILE_STATUS)) {
       
  2670         throw curContext.getShaderInfoLog(fragmentShaderObject);
       
  2671       }
       
  2672 
       
  2673       var programObject = curContext.createProgram();
       
  2674       curContext.attachShader(programObject, vertexShaderObject);
       
  2675       curContext.attachShader(programObject, fragmentShaderObject);
       
  2676       curContext.linkProgram(programObject);
       
  2677       if (!curContext.getProgramParameter(programObject, curContext.LINK_STATUS)) {
       
  2678         throw "Error linking shaders.";
       
  2679       }
       
  2680 
       
  2681       return programObject;
       
  2682     };
       
  2683 
       
  2684     ////////////////////////////////////////////////////////////////////////////
       
  2685     // 2D/3D drawing handling
       
  2686     ////////////////////////////////////////////////////////////////////////////
       
  2687     var imageModeCorner = function(x, y, w, h, whAreSizes) {
       
  2688       return {
       
  2689         x: x,
       
  2690         y: y,
       
  2691         w: w,
       
  2692         h: h
       
  2693       };
       
  2694     };
       
  2695     var imageModeConvert = imageModeCorner;
       
  2696 
       
  2697     var imageModeCorners = function(x, y, w, h, whAreSizes) {
       
  2698       return {
       
  2699         x: x,
       
  2700         y: y,
       
  2701         w: whAreSizes ? w : w - x,
       
  2702         h: whAreSizes ? h : h - y
       
  2703       };
       
  2704     };
       
  2705 
       
  2706     var imageModeCenter = function(x, y, w, h, whAreSizes) {
       
  2707       return {
       
  2708         x: x - w / 2,
       
  2709         y: y - h / 2,
       
  2710         w: w,
       
  2711         h: h
       
  2712       };
       
  2713     };
       
  2714 
       
  2715     // Objects for shared, 2D and 3D contexts
       
  2716     var DrawingShared = function(){};
       
  2717     var Drawing2D = function(){};
       
  2718     var Drawing3D = function(){};
       
  2719     var DrawingPre = function(){};
       
  2720 
       
  2721     // Setup the prototype chain
       
  2722     Drawing2D.prototype = new DrawingShared();
       
  2723     Drawing2D.prototype.constructor = Drawing2D;
       
  2724     Drawing3D.prototype = new DrawingShared();
       
  2725     Drawing3D.prototype.constructor = Drawing3D;
       
  2726     DrawingPre.prototype = new DrawingShared();
       
  2727     DrawingPre.prototype.constructor = DrawingPre;
       
  2728 
       
  2729     // A no-op function for when the user calls 3D functions from a 2D sketch
       
  2730     // We can change this to a throw or console.error() later if we want
       
  2731     DrawingShared.prototype.a3DOnlyFunction = nop;
       
  2732 
       
  2733     ////////////////////////////////////////////////////////////////////////////
       
  2734     // Char handling
       
  2735     ////////////////////////////////////////////////////////////////////////////
       
  2736     var charMap = {};
       
  2737 
       
  2738     var Char = p.Character = function(chr) {
       
  2739       if (typeof chr === 'string' && chr.length === 1) {
       
  2740         this.code = chr.charCodeAt(0);
       
  2741       } else if (typeof chr === 'number') {
       
  2742         this.code = chr;
       
  2743       } else if (chr instanceof Char) {
       
  2744         this.code = chr;
       
  2745       } else {
       
  2746         this.code = NaN;
       
  2747       }
       
  2748 
       
  2749       return (charMap[this.code] === undef) ? charMap[this.code] = this : charMap[this.code];
       
  2750     };
       
  2751 
       
  2752     Char.prototype.toString = function() {
       
  2753       return String.fromCharCode(this.code);
       
  2754     };
       
  2755 
       
  2756     Char.prototype.valueOf = function() {
       
  2757       return this.code;
       
  2758     };
       
  2759 
       
  2760     /**
       
  2761      * Datatype for storing shapes. Processing can currently load and display SVG (Scalable Vector Graphics) shapes.
       
  2762      * Before a shape is used, it must be loaded with the <b>loadShape()</b> function. The <b>shape()</b> function is used to draw the shape to the display window.
       
  2763      * The <b>PShape</b> object contain a group of methods, linked below, that can operate on the shape data.
       
  2764      * <br><br>The <b>loadShape()</b> method supports SVG files created with Inkscape and Adobe Illustrator.
       
  2765      * It is not a full SVG implementation, but offers some straightforward support for handling vector data.
       
  2766      *
       
  2767      * @param {int} family the shape type, one of GROUP, PRIMITIVE, PATH, or GEOMETRY
       
  2768      *
       
  2769      * @see #shape()
       
  2770      * @see #loadShape()
       
  2771      * @see #shapeMode()
       
  2772      */
       
  2773     var PShape = p.PShape = function(family) {
       
  2774       this.family    = family || PConstants.GROUP;
       
  2775       this.visible   = true;
       
  2776       this.style     = true;
       
  2777       this.children  = [];
       
  2778       this.nameTable = [];
       
  2779       this.params    = [];
       
  2780       this.name      = "";
       
  2781       this.image     = null;  //type PImage
       
  2782       this.matrix    = null;
       
  2783       this.kind      = null;
       
  2784       this.close     = null;
       
  2785       this.width     = null;
       
  2786       this.height    = null;
       
  2787       this.parent    = null;
       
  2788     };
       
  2789     /**
       
  2790       * PShape methods
       
  2791       * missing: findChild(), apply(), contains(), findChild(), getPrimitive(), getParams(), getVertex() , getVertexCount(),
       
  2792       * getVertexCode() , getVertexCodes() , getVertexCodeCount(), getVertexX(), getVertexY(), getVertexZ()
       
  2793       */
       
  2794     PShape.prototype = {
       
  2795       /**
       
  2796        * @member PShape
       
  2797        * The isVisible() function returns a boolean value "true" if the image is set to be visible, "false" if not. This is modified with the <b>setVisible()</b> parameter.
       
  2798        * <br><br>The visibility of a shape is usually controlled by whatever program created the SVG file.
       
  2799        * For instance, this parameter is controlled by showing or hiding the shape in the layers palette in Adobe Illustrator.
       
  2800        *
       
  2801        * @return {boolean}  returns "true" if the image is set to be visible, "false" if not
       
  2802        */
       
  2803       isVisible: function(){
       
  2804         return this.visible;
       
  2805       },
       
  2806       /**
       
  2807        * @member PShape
       
  2808        * The setVisible() function sets the shape to be visible or invisible. This is determined by the value of the <b>visible</b> parameter.
       
  2809        * <br><br>The visibility of a shape is usually controlled by whatever program created the SVG file.
       
  2810        * For instance, this parameter is controlled by showing or hiding the shape in the layers palette in Adobe Illustrator.
       
  2811        *
       
  2812        * @param {boolean} visible "false" makes the shape invisible and "true" makes it visible
       
  2813        */
       
  2814       setVisible: function (visible){
       
  2815         this.visible = visible;
       
  2816       },
       
  2817       /**
       
  2818        * @member PShape
       
  2819        * The disableStyle() function disables the shape's style data and uses Processing's current styles. Styles include attributes such as colors, stroke weight, and stroke joints.
       
  2820        * Overrides this shape's style information and uses PGraphics styles and colors. Identical to ignoreStyles(true). Also disables styles for all child shapes.
       
  2821        */
       
  2822       disableStyle: function(){
       
  2823         this.style = false;
       
  2824         for(var i = 0, j=this.children.length; i<j; i++) {
       
  2825           this.children[i].disableStyle();
       
  2826         }
       
  2827       },
       
  2828       /**
       
  2829        * @member PShape
       
  2830        * The enableStyle() function enables the shape's style data and ignores Processing's current styles. Styles include attributes such as colors, stroke weight, and stroke joints.
       
  2831        */
       
  2832       enableStyle: function(){
       
  2833         this.style = true;
       
  2834         for(var i = 0, j=this.children.length; i<j; i++) {
       
  2835           this.children[i].enableStyle();
       
  2836         }
       
  2837       },
       
  2838       /**
       
  2839        * @member PShape
       
  2840        * The getFamily function returns the shape type
       
  2841        *
       
  2842        * @return {int} the shape type, one of GROUP, PRIMITIVE, PATH, or GEOMETRY
       
  2843        */
       
  2844       getFamily: function(){
       
  2845         return this.family;
       
  2846       },
       
  2847       /**
       
  2848        * @member PShape
       
  2849        * The getWidth() function gets the width of the drawing area (not necessarily the shape boundary).
       
  2850        */
       
  2851       getWidth: function(){
       
  2852         return this.width;
       
  2853       },
       
  2854       /**
       
  2855        * @member PShape
       
  2856        * The getHeight() function gets the height of the drawing area (not necessarily the shape boundary).
       
  2857        */
       
  2858       getHeight: function(){
       
  2859         return this.height;
       
  2860       },
       
  2861       /**
       
  2862        * @member PShape
       
  2863        * The setName() function sets the name of the shape
       
  2864        *
       
  2865        * @param {String} name the name of the shape
       
  2866        */
       
  2867       setName: function(name){
       
  2868         this.name = name;
       
  2869       },
       
  2870       /**
       
  2871        * @member PShape
       
  2872        * The getName() function returns the name of the shape
       
  2873        *
       
  2874        * @return {String} the name of the shape
       
  2875        */
       
  2876       getName: function(){
       
  2877         return this.name;
       
  2878       },
       
  2879       /**
       
  2880        * @member PShape
       
  2881        * Called by the following (the shape() command adds the g)
       
  2882        * PShape s = loadShapes("blah.svg");
       
  2883        * shape(s);
       
  2884        */
       
  2885       draw: function(){
       
  2886         if (this.visible) {
       
  2887           this.pre();
       
  2888           this.drawImpl();
       
  2889           this.post();
       
  2890         }
       
  2891       },
       
  2892       /**
       
  2893        * @member PShape
       
  2894        * the drawImpl() function draws the SVG document.
       
  2895        */
       
  2896       drawImpl: function(){
       
  2897         if (this.family === PConstants.GROUP) {
       
  2898           this.drawGroup();
       
  2899         } else if (this.family === PConstants.PRIMITIVE) {
       
  2900           this.drawPrimitive();
       
  2901         } else if (this.family === PConstants.GEOMETRY) {
       
  2902           this.drawGeometry();
       
  2903         } else if (this.family === PConstants.PATH) {
       
  2904           this.drawPath();
       
  2905         }
       
  2906       },
       
  2907       /**
       
  2908        * @member PShape
       
  2909        * The drawPath() function draws the <path> part of the SVG document.
       
  2910        */
       
  2911       drawPath: function(){
       
  2912         var i, j;
       
  2913         if (this.vertices.length === 0) { return; }
       
  2914         p.beginShape();
       
  2915         if (this.vertexCodes.length === 0) {  // each point is a simple vertex
       
  2916           if (this.vertices[0].length === 2) {  // drawing 2D vertices
       
  2917             for (i = 0, j = this.vertices.length; i < j; i++) {
       
  2918               p.vertex(this.vertices[i][0], this.vertices[i][1]);
       
  2919             }
       
  2920           } else {  // drawing 3D vertices
       
  2921             for (i = 0, j = this.vertices.length; i < j; i++) {
       
  2922               p.vertex(this.vertices[i][0],
       
  2923                        this.vertices[i][1],
       
  2924                        this.vertices[i][2]);
       
  2925             }
       
  2926           }
       
  2927         } else {  // coded set of vertices
       
  2928           var index = 0;
       
  2929           if (this.vertices[0].length === 2) {  // drawing a 2D path
       
  2930             for (i = 0, j = this.vertexCodes.length; i < j; i++) {
       
  2931               if (this.vertexCodes[i] === PConstants.VERTEX) {
       
  2932                 p.vertex(this.vertices[index][0], this.vertices[index][1]);
       
  2933                 if ( this.vertices[index]["moveTo"] === true) {
       
  2934                   vertArray[vertArray.length-1]["moveTo"] = true;
       
  2935                 } else if ( this.vertices[index]["moveTo"] === false) {
       
  2936                   vertArray[vertArray.length-1]["moveTo"] = false;
       
  2937                 }
       
  2938                 p.breakShape = false;
       
  2939                 index++;
       
  2940               } else if (this.vertexCodes[i] === PConstants.BEZIER_VERTEX) {
       
  2941                 p.bezierVertex(this.vertices[index+0][0],
       
  2942                                this.vertices[index+0][1],
       
  2943                                this.vertices[index+1][0],
       
  2944                                this.vertices[index+1][1],
       
  2945                                this.vertices[index+2][0],
       
  2946                                this.vertices[index+2][1]);
       
  2947                 index += 3;
       
  2948               } else if (this.vertexCodes[i] === PConstants.CURVE_VERTEX) {
       
  2949                 p.curveVertex(this.vertices[index][0],
       
  2950                               this.vertices[index][1]);
       
  2951                 index++;
       
  2952               } else if (this.vertexCodes[i] ===  PConstants.BREAK) {
       
  2953                 p.breakShape = true;
       
  2954               }
       
  2955             }
       
  2956           } else {  // drawing a 3D path
       
  2957             for (i = 0, j = this.vertexCodes.length; i < j; i++) {
       
  2958               if (this.vertexCodes[i] === PConstants.VERTEX) {
       
  2959                 p.vertex(this.vertices[index][0],
       
  2960                          this.vertices[index][1],
       
  2961                          this.vertices[index][2]);
       
  2962                 if (this.vertices[index]["moveTo"] === true) {
       
  2963                   vertArray[vertArray.length-1]["moveTo"] = true;
       
  2964                 } else if (this.vertices[index]["moveTo"] === false) {
       
  2965                   vertArray[vertArray.length-1]["moveTo"] = false;
       
  2966                 }
       
  2967                 p.breakShape = false;
       
  2968               } else if (this.vertexCodes[i] ===  PConstants.BEZIER_VERTEX) {
       
  2969                 p.bezierVertex(this.vertices[index+0][0],
       
  2970                                this.vertices[index+0][1],
       
  2971                                this.vertices[index+0][2],
       
  2972                                this.vertices[index+1][0],
       
  2973                                this.vertices[index+1][1],
       
  2974                                this.vertices[index+1][2],
       
  2975                                this.vertices[index+2][0],
       
  2976                                this.vertices[index+2][1],
       
  2977                                this.vertices[index+2][2]);
       
  2978                 index += 3;
       
  2979               } else if (this.vertexCodes[i] === PConstants.CURVE_VERTEX) {
       
  2980                 p.curveVertex(this.vertices[index][0],
       
  2981                               this.vertices[index][1],
       
  2982                               this.vertices[index][2]);
       
  2983                 index++;
       
  2984               } else if (this.vertexCodes[i] === PConstants.BREAK) {
       
  2985                 p.breakShape = true;
       
  2986               }
       
  2987             }
       
  2988           }
       
  2989         }
       
  2990         p.endShape(this.close ? PConstants.CLOSE : PConstants.OPEN);
       
  2991       },
       
  2992       /**
       
  2993        * @member PShape
       
  2994        * The drawGeometry() function draws the geometry part of the SVG document.
       
  2995        */
       
  2996       drawGeometry: function() {
       
  2997         var i, j;
       
  2998         p.beginShape(this.kind);
       
  2999         if (this.style) {
       
  3000           for (i = 0, j = this.vertices.length; i < j; i++) {
       
  3001             p.vertex(this.vertices[i]);
       
  3002           }
       
  3003         } else {
       
  3004           for (i = 0, j = this.vertices.length; i < j; i++) {
       
  3005             var vert = this.vertices[i];
       
  3006             if (vert[2] === 0) {
       
  3007               p.vertex(vert[0], vert[1]);
       
  3008             } else {
       
  3009               p.vertex(vert[0], vert[1], vert[2]);
       
  3010             }
       
  3011           }
       
  3012         }
       
  3013         p.endShape();
       
  3014       },
       
  3015       /**
       
  3016        * @member PShape
       
  3017        * The drawGroup() function draws the <g> part of the SVG document.
       
  3018        */
       
  3019       drawGroup: function() {
       
  3020         for (var i = 0, j = this.children.length; i < j; i++) {
       
  3021           this.children[i].draw();
       
  3022         }
       
  3023       },
       
  3024       /**
       
  3025        * @member PShape
       
  3026        * The drawPrimitive() function draws SVG document shape elements. These can be point, line, triangle, quad, rect, ellipse, arc, box, or sphere.
       
  3027        */
       
  3028       drawPrimitive: function() {
       
  3029         if (this.kind === PConstants.POINT) {
       
  3030           p.point(this.params[0], this.params[1]);
       
  3031         } else if (this.kind === PConstants.LINE) {
       
  3032           if (this.params.length === 4) {  // 2D
       
  3033             p.line(this.params[0], this.params[1],
       
  3034                    this.params[2], this.params[3]);
       
  3035           } else {  // 3D
       
  3036             p.line(this.params[0], this.params[1], this.params[2],
       
  3037                    this.params[3], this.params[4], this.params[5]);
       
  3038           }
       
  3039         } else if (this.kind === PConstants.TRIANGLE) {
       
  3040           p.triangle(this.params[0], this.params[1],
       
  3041                      this.params[2], this.params[3],
       
  3042                      this.params[4], this.params[5]);
       
  3043         } else if (this.kind === PConstants.QUAD) {
       
  3044           p.quad(this.params[0], this.params[1],
       
  3045                  this.params[2], this.params[3],
       
  3046                  this.params[4], this.params[5],
       
  3047                  this.params[6], this.params[7]);
       
  3048         } else if (this.kind === PConstants.RECT) {
       
  3049           if (this.image !== null) {
       
  3050             p.imageMode(PConstants.CORNER);
       
  3051             p.image(this.image,
       
  3052                     this.params[0],
       
  3053                     this.params[1],
       
  3054                     this.params[2],
       
  3055                     this.params[3]);
       
  3056           } else {
       
  3057             p.rectMode(PConstants.CORNER);
       
  3058             p.rect(this.params[0],
       
  3059                    this.params[1],
       
  3060                    this.params[2],
       
  3061                    this.params[3]);
       
  3062           }
       
  3063         } else if (this.kind === PConstants.ELLIPSE) {
       
  3064           p.ellipseMode(PConstants.CORNER);
       
  3065           p.ellipse(this.params[0],
       
  3066                     this.params[1],
       
  3067                     this.params[2],
       
  3068                     this.params[3]);
       
  3069         } else if (this.kind === PConstants.ARC) {
       
  3070           p.ellipseMode(PConstants.CORNER);
       
  3071           p.arc(this.params[0],
       
  3072                 this.params[1],
       
  3073                 this.params[2],
       
  3074                 this.params[3],
       
  3075                 this.params[4],
       
  3076                 this.params[5]);
       
  3077         } else if (this.kind === PConstants.BOX) {
       
  3078           if (this.params.length === 1) {
       
  3079             p.box(this.params[0]);
       
  3080           } else {
       
  3081             p.box(this.params[0], this.params[1], this.params[2]);
       
  3082           }
       
  3083         } else if (this.kind === PConstants.SPHERE) {
       
  3084           p.sphere(this.params[0]);
       
  3085         }
       
  3086       },
       
  3087       /**
       
  3088        * @member PShape
       
  3089        * The pre() function performs the preparations before the SVG is drawn. This includes doing transformations and storing previous styles.
       
  3090        */
       
  3091       pre: function() {
       
  3092         if (this.matrix) {
       
  3093           p.pushMatrix();
       
  3094           curContext.transform(this.matrix.elements[0],
       
  3095                                this.matrix.elements[3],
       
  3096                                this.matrix.elements[1],
       
  3097                                this.matrix.elements[4],
       
  3098                                this.matrix.elements[2],
       
  3099                                this.matrix.elements[5]);
       
  3100           //p.applyMatrix(this.matrix.elements[0],this.matrix.elements[0]);
       
  3101         }
       
  3102         if (this.style) {
       
  3103           p.pushStyle();
       
  3104           this.styles();
       
  3105         }
       
  3106       },
       
  3107       /**
       
  3108        * @member PShape
       
  3109        * The post() function performs the necessary actions after the SVG is drawn. This includes removing transformations and removing added styles.
       
  3110        */
       
  3111       post: function() {
       
  3112         if (this.matrix) {
       
  3113           p.popMatrix();
       
  3114         }
       
  3115         if (this.style) {
       
  3116           p.popStyle();
       
  3117         }
       
  3118       },
       
  3119       /**
       
  3120        * @member PShape
       
  3121        * The styles() function changes the Processing's current styles
       
  3122        */
       
  3123       styles: function() {
       
  3124         if (this.stroke) {
       
  3125           p.stroke(this.strokeColor);
       
  3126           p.strokeWeight(this.strokeWeight);
       
  3127           p.strokeCap(this.strokeCap);
       
  3128           p.strokeJoin(this.strokeJoin);
       
  3129         } else {
       
  3130           p.noStroke();
       
  3131         }
       
  3132 
       
  3133         if (this.fill) {
       
  3134           p.fill(this.fillColor);
       
  3135 
       
  3136         } else {
       
  3137           p.noFill();
       
  3138         }
       
  3139       },
       
  3140       /**
       
  3141        * @member PShape
       
  3142        * The getChild() function extracts a child shape from a parent shape. Specify the name of the shape with the <b>target</b> parameter or the
       
  3143        * layer position of the shape to get with the <b>index</b> parameter.
       
  3144        * The shape is returned as a <b>PShape</b> object, or <b>null</b> is returned if there is an error.
       
  3145        *
       
  3146        * @param {String} target   the name of the shape to get
       
  3147        * @param {int} index   the layer position of the shape to get
       
  3148        *
       
  3149        * @return {PShape} returns a child element of a shape as a PShape object or null if there is an error
       
  3150        */
       
  3151       getChild: function(child) {
       
  3152         var i, j;
       
  3153         if (typeof child === 'number') {
       
  3154           return this.children[child];
       
  3155         }
       
  3156         var found;
       
  3157         if(child === "" || this.name === child){
       
  3158           return this;
       
  3159         }
       
  3160         if(this.nameTable.length > 0) {
       
  3161           for(i = 0, j = this.nameTable.length; i < j || found; i++) {
       
  3162             if(this.nameTable[i].getName === child) {
       
  3163               found = this.nameTable[i];
       
  3164               break;
       
  3165             }
       
  3166           }
       
  3167           if (found) { return found; }
       
  3168         }
       
  3169         for(i = 0, j = this.children.length; i < j; i++) {
       
  3170           found = this.children[i].getChild(child);
       
  3171           if(found) { return found; }
       
  3172         }
       
  3173         return null;
       
  3174       },
       
  3175       /**
       
  3176        * @member PShape
       
  3177        * The getChildCount() returns the number of children
       
  3178        *
       
  3179        * @return {int} returns a count of children
       
  3180        */
       
  3181       getChildCount: function () {
       
  3182         return this.children.length;
       
  3183       },
       
  3184       /**
       
  3185        * @member PShape
       
  3186        * The addChild() adds a child to the PShape.
       
  3187        *
       
  3188        * @param {PShape} child the child to add
       
  3189        */
       
  3190       addChild: function( child ) {
       
  3191         this.children.push(child);
       
  3192         child.parent = this;
       
  3193         if (child.getName() !== null) {
       
  3194           this.addName(child.getName(), child);
       
  3195         }
       
  3196       },
       
  3197       /**
       
  3198        * @member PShape
       
  3199        * The addName() functions adds a shape to the name lookup table.
       
  3200        *
       
  3201        * @param {String} name   the name to be added
       
  3202        * @param {PShape} shape  the shape
       
  3203        */
       
  3204       addName: function(name,  shape) {
       
  3205         if (this.parent !== null) {
       
  3206           this.parent.addName( name, shape );
       
  3207         } else {
       
  3208           this.nameTable.push( [name, shape] );
       
  3209         }
       
  3210       },
       
  3211       /**
       
  3212        * @member PShape
       
  3213        * The translate() function specifies an amount to displace the shape. The <b>x</b> parameter specifies left/right translation, the <b>y</b> parameter specifies up/down translation, and the <b>z</b> parameter specifies translations toward/away from the screen.
       
  3214        * Subsequent calls to the method accumulates the effect. For example, calling <b>translate(50, 0)</b> and then <b>translate(20, 0)</b> is the same as <b>translate(70, 0)</b>.
       
  3215        * This transformation is applied directly to the shape, it's not refreshed each time <b>draw()</b> is run.
       
  3216        * <br><br>Using this method with the <b>z</b> parameter requires using the P3D or OPENGL parameter in combination with size.
       
  3217        *
       
  3218        * @param {int|float} x left/right translation
       
  3219        * @param {int|float} y up/down translation
       
  3220        * @param {int|float} z forward/back translation
       
  3221        *
       
  3222        * @see PMatrix2D#translate
       
  3223        * @see PMatrix3D#translate
       
  3224        */
       
  3225       translate: function() {
       
  3226         if(arguments.length === 2)
       
  3227         {
       
  3228           this.checkMatrix(2);
       
  3229           this.matrix.translate(arguments[0], arguments[1]);
       
  3230         } else {
       
  3231           this.checkMatrix(3);
       
  3232           this.matrix.translate(arguments[0], arguments[1], 0);
       
  3233         }
       
  3234       },
       
  3235       /**
       
  3236        * @member PShape
       
  3237        * The checkMatrix() function makes sure that the shape's matrix is 1) not null, and 2) has a matrix
       
  3238        * that can handle <em>at least</em> the specified number of dimensions.
       
  3239        *
       
  3240        * @param {int} dimensions the specified number of dimensions
       
  3241        */
       
  3242       checkMatrix: function(dimensions) {
       
  3243         if(this.matrix === null) {
       
  3244           if(dimensions === 2) {
       
  3245             this.matrix = new p.PMatrix2D();
       
  3246           } else {
       
  3247             this.matrix = new p.PMatrix3D();
       
  3248           }
       
  3249         }else if(dimensions === 3 && this.matrix instanceof p.PMatrix2D) {
       
  3250           this.matrix = new p.PMatrix3D();
       
  3251         }
       
  3252       },
       
  3253       /**
       
  3254        * @member PShape
       
  3255        * The rotateX() function rotates a shape around the x-axis the amount specified by the <b>angle</b> parameter. Angles should be specified in radians (values from 0 to TWO_PI) or converted to radians with the <b>radians()</b> method.
       
  3256        * <br><br>Shapes are always rotated around the upper-left corner of their bounding box. Positive numbers rotate objects in a clockwise direction.
       
  3257        * Subsequent calls to the method accumulates the effect. For example, calling <b>rotateX(HALF_PI)</b> and then <b>rotateX(HALF_PI)</b> is the same as <b>rotateX(PI)</b>.
       
  3258        * This transformation is applied directly to the shape, it's not refreshed each time <b>draw()</b> is run.
       
  3259        * <br><br>This method requires a 3D renderer. You need to pass P3D or OPENGL as a third parameter into the <b>size()</b> method as shown in the example above.
       
  3260        *
       
  3261        * @param {float}angle angle of rotation specified in radians
       
  3262        *
       
  3263        * @see PMatrix3D#rotateX
       
  3264        */
       
  3265       rotateX: function(angle) {
       
  3266         this.rotate(angle, 1, 0, 0);
       
  3267       },
       
  3268       /**
       
  3269        * @member PShape
       
  3270        * The rotateY() function rotates a shape around the y-axis the amount specified by the <b>angle</b> parameter. Angles should be specified in radians (values from 0 to TWO_PI) or converted to radians with the <b>radians()</b> method.
       
  3271        * <br><br>Shapes are always rotated around the upper-left corner of their bounding box. Positive numbers rotate objects in a clockwise direction.
       
  3272        * Subsequent calls to the method accumulates the effect. For example, calling <b>rotateY(HALF_PI)</b> and then <b>rotateY(HALF_PI)</b> is the same as <b>rotateY(PI)</b>.
       
  3273        * This transformation is applied directly to the shape, it's not refreshed each time <b>draw()</b> is run.
       
  3274        * <br><br>This method requires a 3D renderer. You need to pass P3D or OPENGL as a third parameter into the <b>size()</b> method as shown in the example above.
       
  3275        *
       
  3276        * @param {float}angle angle of rotation specified in radians
       
  3277        *
       
  3278        * @see PMatrix3D#rotateY
       
  3279        */
       
  3280       rotateY: function(angle) {
       
  3281         this.rotate(angle, 0, 1, 0);
       
  3282       },
       
  3283       /**
       
  3284        * @member PShape
       
  3285        * The rotateZ() function rotates a shape around the z-axis the amount specified by the <b>angle</b> parameter. Angles should be specified in radians (values from 0 to TWO_PI) or converted to radians with the <b>radians()</b> method.
       
  3286        * <br><br>Shapes are always rotated around the upper-left corner of their bounding box. Positive numbers rotate objects in a clockwise direction.
       
  3287        * Subsequent calls to the method accumulates the effect. For example, calling <b>rotateZ(HALF_PI)</b> and then <b>rotateZ(HALF_PI)</b> is the same as <b>rotateZ(PI)</b>.
       
  3288        * This transformation is applied directly to the shape, it's not refreshed each time <b>draw()</b> is run.
       
  3289        * <br><br>This method requires a 3D renderer. You need to pass P3D or OPENGL as a third parameter into the <b>size()</b> method as shown in the example above.
       
  3290        *
       
  3291        * @param {float}angle angle of rotation specified in radians
       
  3292        *
       
  3293        * @see PMatrix3D#rotateZ
       
  3294        */
       
  3295       rotateZ: function(angle) {
       
  3296         this.rotate(angle, 0, 0, 1);
       
  3297       },
       
  3298       /**
       
  3299        * @member PShape
       
  3300        * The rotate() function rotates a shape the amount specified by the <b>angle</b> parameter. Angles should be specified in radians (values from 0 to TWO_PI) or converted to radians with the <b>radians()</b> method.
       
  3301        * <br><br>Shapes are always rotated around the upper-left corner of their bounding box. Positive numbers rotate objects in a clockwise direction.
       
  3302        * Transformations apply to everything that happens after and subsequent calls to the method accumulates the effect.
       
  3303        * For example, calling <b>rotate(HALF_PI)</b> and then <b>rotate(HALF_PI)</b> is the same as <b>rotate(PI)</b>.
       
  3304        * This transformation is applied directly to the shape, it's not refreshed each time <b>draw()</b> is run.
       
  3305        * If optional parameters x,y,z are supplied, the rotate is about the point (x, y, z).
       
  3306        *
       
  3307        * @param {float}angle  angle of rotation specified in radians
       
  3308        * @param {float}x      x-coordinate of the point
       
  3309        * @param {float}y      y-coordinate of the point
       
  3310        * @param {float}z      z-coordinate of the point
       
  3311        * @see PMatrix2D#rotate
       
  3312        * @see PMatrix3D#rotate
       
  3313        */
       
  3314       rotate: function() {
       
  3315         if(arguments.length === 1){
       
  3316           this.checkMatrix(2);
       
  3317           this.matrix.rotate(arguments[0]);
       
  3318         } else {
       
  3319           this.checkMatrix(3);
       
  3320           this.matrix.rotate(arguments[0],
       
  3321                              arguments[1],
       
  3322                              arguments[2],
       
  3323                              arguments[3]);
       
  3324         }
       
  3325       },
       
  3326       /**
       
  3327        * @member PShape
       
  3328        * The scale() function increases or decreases the size of a shape by expanding and contracting vertices. Shapes always scale from the relative origin of their bounding box.
       
  3329        * Scale values are specified as decimal percentages. For example, the method call <b>scale(2.0)</b> increases the dimension of a shape by 200%.
       
  3330        * Subsequent calls to the method multiply the effect. For example, calling <b>scale(2.0)</b> and then <b>scale(1.5)</b> is the same as <b>scale(3.0)</b>.
       
  3331        * This transformation is applied directly to the shape, it's not refreshed each time <b>draw()</b> is run.
       
  3332        * <br><br>Using this fuction with the <b>z</b> parameter requires passing P3D or OPENGL into the size() parameter.
       
  3333        *
       
  3334        * @param {float}s      percentage to scale the object
       
  3335        * @param {float}x      percentage to scale the object in the x-axis
       
  3336        * @param {float}y      percentage to scale the object in the y-axis
       
  3337        * @param {float}z      percentage to scale the object in the z-axis
       
  3338        *
       
  3339        * @see PMatrix2D#scale
       
  3340        * @see PMatrix3D#scale
       
  3341        */
       
  3342       scale: function() {
       
  3343         if(arguments.length === 2) {
       
  3344           this.checkMatrix(2);
       
  3345           this.matrix.scale(arguments[0], arguments[1]);
       
  3346         } else if (arguments.length === 3) {
       
  3347           this.checkMatrix(2);
       
  3348           this.matrix.scale(arguments[0], arguments[1], arguments[2]);
       
  3349         } else {
       
  3350           this.checkMatrix(2);
       
  3351           this.matrix.scale(arguments[0]);
       
  3352         }
       
  3353       },
       
  3354       /**
       
  3355        * @member PShape
       
  3356        * The resetMatrix() function resets the matrix
       
  3357        *
       
  3358        * @see PMatrix2D#reset
       
  3359        * @see PMatrix3D#reset
       
  3360        */
       
  3361       resetMatrix: function() {
       
  3362         this.checkMatrix(2);
       
  3363         this.matrix.reset();
       
  3364       },
       
  3365       /**
       
  3366        * @member PShape
       
  3367        * The applyMatrix() function multiplies this matrix by another matrix of type PMatrix3D or PMatrix2D.
       
  3368        * Individual elements can also be provided
       
  3369        *
       
  3370        * @param {PMatrix3D|PMatrix2D} matrix   the matrix to multiply by
       
  3371        *
       
  3372        * @see PMatrix2D#apply
       
  3373        * @see PMatrix3D#apply
       
  3374        */
       
  3375       applyMatrix: function(matrix) {
       
  3376         if (arguments.length === 1) {
       
  3377           this.applyMatrix(matrix.elements[0],
       
  3378                            matrix.elements[1], 0,
       
  3379                            matrix.elements[2],
       
  3380                            matrix.elements[3],
       
  3381                            matrix.elements[4], 0,
       
  3382                            matrix.elements[5],
       
  3383                            0, 0, 1, 0,
       
  3384                            0, 0, 0, 1);
       
  3385         } else if (arguments.length === 6) {
       
  3386           this.checkMatrix(2);
       
  3387           this.matrix.apply(arguments[0], arguments[1], arguments[2], 0,
       
  3388                             arguments[3], arguments[4], arguments[5], 0,
       
  3389                             0,   0,   1,   0,
       
  3390                             0,   0,   0,   1);
       
  3391 
       
  3392         } else if (arguments.length === 16) {
       
  3393           this.checkMatrix(3);
       
  3394           this.matrix.apply(arguments[0],
       
  3395                             arguments[1],
       
  3396                             arguments[2],
       
  3397                             arguments[3],
       
  3398                             arguments[4],
       
  3399                             arguments[5],
       
  3400                             arguments[6],
       
  3401                             arguments[7],
       
  3402                             arguments[8],
       
  3403                             arguments[9],
       
  3404                             arguments[10],
       
  3405                             arguments[11],
       
  3406                             arguments[12],
       
  3407                             arguments[13],
       
  3408                             arguments[14],
       
  3409                             arguments[15]);
       
  3410         }
       
  3411       }
       
  3412     };
       
  3413 
       
  3414     /**
       
  3415      * SVG stands for Scalable Vector Graphics, a portable graphics format. It is
       
  3416      * a vector format so it allows for infinite resolution and relatively small
       
  3417      * file sizes. Most modern media software can view SVG files, including Adobe
       
  3418      * products, Firefox, etc. Illustrator and Inkscape can edit SVG files.
       
  3419      *
       
  3420      * @param {PApplet} parent     typically use "this"
       
  3421      * @param {String} filename    name of the SVG file to load
       
  3422      * @param {XMLElement} xml     an XMLElement element
       
  3423      * @param {PShapeSVG} parent   the parent PShapeSVG
       
  3424      *
       
  3425      * @see PShape
       
  3426      */
       
  3427     var PShapeSVG = p.PShapeSVG = function() {
       
  3428       p.PShape.call( this ); // PShape is the base class.
       
  3429       if (arguments.length === 1) { //xml element coming in
       
  3430         this.element  = arguments[0] ;//new p.XMLElement(null, arguments[0]);
       
  3431         // set values to their defaults according to the SVG spec
       
  3432         this.vertexCodes         = [];
       
  3433         this.vertices            = [];
       
  3434         this.opacity             = 1;
       
  3435 
       
  3436         this.stroke              = false;
       
  3437         this.strokeColor         = PConstants.ALPHA_MASK;
       
  3438         this.strokeWeight        = 1;
       
  3439         this.strokeCap           = PConstants.SQUARE;  // BUTT in svg spec
       
  3440         this.strokeJoin          = PConstants.MITER;
       
  3441         this.strokeGradient      = null;
       
  3442         this.strokeGradientPaint = null;
       
  3443         this.strokeName          = null;
       
  3444         this.strokeOpacity       = 1;
       
  3445 
       
  3446         this.fill                = true;
       
  3447         this.fillColor           = PConstants.ALPHA_MASK;
       
  3448         this.fillGradient        = null;
       
  3449         this.fillGradientPaint   = null;
       
  3450         this.fillName            = null;
       
  3451         this.fillOpacity         = 1;
       
  3452 
       
  3453         if (this.element.getName() !== "svg") {
       
  3454           throw("root is not <svg>, it's <" + this.element.getName() + ">");
       
  3455         }
       
  3456       }
       
  3457       else if (arguments.length === 2) {
       
  3458         if (typeof arguments[1] === 'string') {
       
  3459           if (arguments[1].indexOf(".svg") > -1) { //its a filename
       
  3460             this.element = new p.XMLElement(null, arguments[1]);
       
  3461             // set values to their defaults according to the SVG spec
       
  3462             this.vertexCodes         = [];
       
  3463             this.vertices            = [];
       
  3464             this.opacity             = 1;
       
  3465 
       
  3466             this.stroke              = false;
       
  3467             this.strokeColor         = PConstants.ALPHA_MASK;
       
  3468             this.strokeWeight        = 1;
       
  3469             this.strokeCap           = PConstants.SQUARE;  // BUTT in svg spec
       
  3470             this.strokeJoin          = PConstants.MITER;
       
  3471             this.strokeGradient      = "";
       
  3472             this.strokeGradientPaint = "";
       
  3473             this.strokeName          = "";
       
  3474             this.strokeOpacity       = 1;
       
  3475 
       
  3476             this.fill                = true;
       
  3477             this.fillColor           = PConstants.ALPHA_MASK;
       
  3478             this.fillGradient        = null;
       
  3479             this.fillGradientPaint   = null;
       
  3480             this.fillOpacity         = 1;
       
  3481 
       
  3482           }
       
  3483         } else { // XMLElement
       
  3484           if (arguments[0]) { // PShapeSVG
       
  3485             this.element             = arguments[1];
       
  3486             this.vertexCodes         = arguments[0].vertexCodes.slice();
       
  3487             this.vertices            = arguments[0].vertices.slice();
       
  3488 
       
  3489             this.stroke              = arguments[0].stroke;
       
  3490             this.strokeColor         = arguments[0].strokeColor;
       
  3491             this.strokeWeight        = arguments[0].strokeWeight;
       
  3492             this.strokeCap           = arguments[0].strokeCap;
       
  3493             this.strokeJoin          = arguments[0].strokeJoin;
       
  3494             this.strokeGradient      = arguments[0].strokeGradient;
       
  3495             this.strokeGradientPaint = arguments[0].strokeGradientPaint;
       
  3496             this.strokeName          = arguments[0].strokeName;
       
  3497 
       
  3498             this.fill                = arguments[0].fill;
       
  3499             this.fillColor           = arguments[0].fillColor;
       
  3500             this.fillGradient        = arguments[0].fillGradient;
       
  3501             this.fillGradientPaint   = arguments[0].fillGradientPaint;
       
  3502             this.fillName            = arguments[0].fillName;
       
  3503             this.strokeOpacity       = arguments[0].strokeOpacity;
       
  3504             this.fillOpacity         = arguments[0].fillOpacity;
       
  3505             this.opacity             = arguments[0].opacity;
       
  3506           }
       
  3507         }
       
  3508       }
       
  3509 
       
  3510       this.name      = this.element.getStringAttribute("id");
       
  3511       var displayStr = this.element.getStringAttribute("display", "inline");
       
  3512       this.visible   = displayStr !== "none";
       
  3513       var str = this.element.getAttribute("transform");
       
  3514       if (str) {
       
  3515         this.matrix = this.parseMatrix(str);
       
  3516       }
       
  3517       // not proper parsing of the viewBox, but will cover us for cases where
       
  3518       // the width and height of the object is not specified
       
  3519       var viewBoxStr = this.element.getStringAttribute("viewBox");
       
  3520       if ( viewBoxStr !== null ) {
       
  3521         var viewBox = viewBoxStr.split(" ");
       
  3522         this.width  = viewBox[2];
       
  3523         this.height = viewBox[3];
       
  3524       }
       
  3525 
       
  3526       // TODO if viewbox is not same as width/height, then use it to scale
       
  3527       // the original objects. for now, viewbox only used when width/height
       
  3528       // are empty values (which by the spec means w/h of "100%"
       
  3529       var unitWidth  = this.element.getStringAttribute("width");
       
  3530       var unitHeight = this.element.getStringAttribute("height");
       
  3531       if (unitWidth !== null) {
       
  3532         this.width  = this.parseUnitSize(unitWidth);
       
  3533         this.height = this.parseUnitSize(unitHeight);
       
  3534       } else {
       
  3535         if ((this.width === 0) || (this.height === 0)) {
       
  3536           // For the spec, the default is 100% and 100%. For purposes
       
  3537           // here, insert a dummy value because this is prolly just a
       
  3538           // font or something for which the w/h doesn't matter.
       
  3539           this.width  = 1;
       
  3540           this.height = 1;
       
  3541 
       
  3542           //show warning
       
  3543           throw("The width and/or height is not " +
       
  3544                 "readable in the <svg> tag of this file.");
       
  3545         }
       
  3546       }
       
  3547       this.parseColors(this.element);
       
  3548       this.parseChildren(this.element);
       
  3549 
       
  3550     };
       
  3551     /**
       
  3552      * PShapeSVG methods
       
  3553      * missing: getChild(), print(), parseStyleAttributes(), styles() - deals with strokeGradient and fillGradient
       
  3554      */
       
  3555     PShapeSVG.prototype = new PShape();
       
  3556     /**
       
  3557      * @member PShapeSVG
       
  3558      * The parseMatrix() function parses the specified SVG matrix into a PMatrix2D. Note that PMatrix2D
       
  3559      * is rotated relative to the SVG definition, so parameters are rearranged
       
  3560      * here. More about the transformation matrices in
       
  3561      * <a href="http://www.w3.org/TR/SVG/coords.html#TransformAttribute">this section</a>
       
  3562      * of the SVG documentation.
       
  3563      *
       
  3564      * @param {String} str text of the matrix param.
       
  3565      *
       
  3566      * @return {PMatrix2D} a PMatrix2D
       
  3567      */
       
  3568     PShapeSVG.prototype.parseMatrix = (function() {
       
  3569       function getCoords(s) {
       
  3570         var m = [];
       
  3571         s.replace(/\((.*?)\)/, (function() {
       
  3572           return function(all, params) {
       
  3573             // get the coordinates that can be separated by spaces or a comma
       
  3574             m = params.replace(/,+/g, " ").split(/\s+/);
       
  3575           };
       
  3576         }()));
       
  3577         return m;
       
  3578       }
       
  3579 
       
  3580       return function(str) {
       
  3581         this.checkMatrix(2);
       
  3582         var pieces = [];
       
  3583         str.replace(/\s*(\w+)\((.*?)\)/g, function(all) {
       
  3584           // get a list of transform definitions
       
  3585           pieces.push(p.trim(all));
       
  3586         });
       
  3587         if (pieces.length === 0) {
       
  3588           return null;
       
  3589         }
       
  3590 
       
  3591         for (var i = 0, j = pieces.length; i < j; i++) {
       
  3592           var m = getCoords(pieces[i]);
       
  3593 
       
  3594           if (pieces[i].indexOf("matrix") !== -1) {
       
  3595             this.matrix.set(m[0], m[2], m[4], m[1], m[3], m[5]);
       
  3596           } else if (pieces[i].indexOf("translate") !== -1) {
       
  3597             var tx = m[0];
       
  3598             var ty = (m.length === 2) ? m[1] : 0;
       
  3599             this.matrix.translate(tx,ty);
       
  3600           } else if (pieces[i].indexOf("scale") !== -1) {
       
  3601             var sx = m[0];
       
  3602             var sy = (m.length === 2) ? m[1] : m[0];
       
  3603             this.matrix.scale(sx,sy);
       
  3604           } else if (pieces[i].indexOf("rotate") !== -1) {
       
  3605             var angle = m[0];
       
  3606             if (m.length === 1) {
       
  3607               this.matrix.rotate(p.radians(angle));
       
  3608             } else if (m.length === 3) {
       
  3609               this.matrix.translate(m[1], m[2]);
       
  3610               this.matrix.rotate(p.radians(m[0]));
       
  3611               this.matrix.translate(-m[1], -m[2]);
       
  3612             }
       
  3613           } else if (pieces[i].indexOf("skewX") !== -1) {
       
  3614             this.matrix.skewX(parseFloat(m[0]));
       
  3615           } else if (pieces[i].indexOf("skewY") !== -1) {
       
  3616             this.matrix.skewY(m[0]);
       
  3617           }
       
  3618         }
       
  3619         return this.matrix;
       
  3620       };
       
  3621     }());
       
  3622 
       
  3623     /**
       
  3624      * @member PShapeSVG
       
  3625      * The parseChildren() function parses the specified XMLElement
       
  3626      *
       
  3627      * @param {XMLElement}element the XMLElement to parse
       
  3628      */
       
  3629     PShapeSVG.prototype.parseChildren = function(element) {
       
  3630       var newelement = element.getChildren();
       
  3631       var children   = new p.PShape();
       
  3632       for (var i = 0, j = newelement.length; i < j; i++) {
       
  3633         var kid = this.parseChild(newelement[i]);
       
  3634         if (kid) {
       
  3635           children.addChild(kid);
       
  3636         }
       
  3637       }
       
  3638       this.children.push(children);
       
  3639     };
       
  3640     /**
       
  3641      * @member PShapeSVG
       
  3642      * The getName() function returns the name
       
  3643      *
       
  3644      * @return {String} the name
       
  3645      */
       
  3646     PShapeSVG.prototype.getName = function() {
       
  3647       return this.name;
       
  3648     };
       
  3649     /**
       
  3650      * @member PShapeSVG
       
  3651      * The parseChild() function parses a child XML element.
       
  3652      *
       
  3653      * @param {XMLElement} elem the element to parse
       
  3654      *
       
  3655      * @return {PShape} the newly created PShape
       
  3656      */
       
  3657     PShapeSVG.prototype.parseChild = function( elem ) {
       
  3658       var name = elem.getName();
       
  3659       var shape;
       
  3660       if (name === "g") {
       
  3661         shape = new PShapeSVG(this, elem);
       
  3662       } else if (name === "defs") {
       
  3663         // generally this will contain gradient info, so may
       
  3664         // as well just throw it into a group element for parsing
       
  3665         shape = new PShapeSVG(this, elem);
       
  3666       } else if (name === "line") {
       
  3667         shape = new PShapeSVG(this, elem);
       
  3668         shape.parseLine();
       
  3669       } else if (name === "circle") {
       
  3670         shape = new PShapeSVG(this, elem);
       
  3671         shape.parseEllipse(true);
       
  3672       } else if (name === "ellipse") {
       
  3673         shape = new PShapeSVG(this, elem);
       
  3674         shape.parseEllipse(false);
       
  3675       } else if (name === "rect") {
       
  3676         shape = new PShapeSVG(this, elem);
       
  3677         shape.parseRect();
       
  3678       } else if (name === "polygon") {
       
  3679         shape = new PShapeSVG(this, elem);
       
  3680         shape.parsePoly(true);
       
  3681       } else if (name === "polyline") {
       
  3682         shape = new PShapeSVG(this, elem);
       
  3683         shape.parsePoly(false);
       
  3684       } else if (name === "path") {
       
  3685         shape = new PShapeSVG(this, elem);
       
  3686         shape.parsePath();
       
  3687       } else if (name === "radialGradient") {
       
  3688         //return new RadialGradient(this, elem);
       
  3689         unimplemented('PShapeSVG.prototype.parseChild, name = radialGradient');
       
  3690       } else if (name === "linearGradient") {
       
  3691         //return new LinearGradient(this, elem);
       
  3692         unimplemented('PShapeSVG.prototype.parseChild, name = linearGradient');
       
  3693       } else if (name === "text") {
       
  3694         unimplemented('PShapeSVG.prototype.parseChild, name = text');
       
  3695       } else if (name === "filter") {
       
  3696         unimplemented('PShapeSVG.prototype.parseChild, name = filter');
       
  3697       } else if (name === "mask") {
       
  3698         unimplemented('PShapeSVG.prototype.parseChild, name = mask');
       
  3699       } else {
       
  3700         // ignoring
       
  3701         nop();
       
  3702       }
       
  3703       return shape;
       
  3704     };
       
  3705     /**
       
  3706      * @member PShapeSVG
       
  3707      * The parsePath() function parses the <path> element of the svg file
       
  3708      * A path is defined by including a path element which contains a d="(path data)" attribute, where the d attribute contains
       
  3709      * the moveto, line, curve (both cubic and quadratic Beziers), arc and closepath instructions.
       
  3710      **/
       
  3711     PShapeSVG.prototype.parsePath = function() {
       
  3712       this.family = PConstants.PATH;
       
  3713       this.kind = 0;
       
  3714       var pathDataChars = [];
       
  3715       var c;
       
  3716       //change multiple spaces and commas to single space
       
  3717       var pathData = p.trim(this.element.getStringAttribute("d")
       
  3718                             .replace(/[\s,]+/g,' '));
       
  3719       if (pathData === null) {
       
  3720         return;
       
  3721       }
       
  3722       pathData = p.__toCharArray(pathData);
       
  3723       var cx     = 0,
       
  3724           cy     = 0,
       
  3725           ctrlX  = 0,
       
  3726           ctrlY  = 0,
       
  3727           ctrlX1 = 0,
       
  3728           ctrlX2 = 0,
       
  3729           ctrlY1 = 0,
       
  3730           ctrlY2 = 0,
       
  3731           endX   = 0,
       
  3732           endY   = 0,
       
  3733           ppx    = 0,
       
  3734           ppy    = 0,
       
  3735           px     = 0,
       
  3736           py     = 0,
       
  3737           i      = 0,
       
  3738           valOf  = 0;
       
  3739       var str = "";
       
  3740       var tmpArray =[];
       
  3741       var flag = false;
       
  3742       var lastInstruction;
       
  3743       var command;
       
  3744       var j, k;
       
  3745       while (i< pathData.length) {
       
  3746         valOf = pathData[i].valueOf();
       
  3747         if ((valOf >= 65 && valOf <= 90) || (valOf >= 97 && valOf <= 122)) {
       
  3748           // if it's a letter
       
  3749           // populate the tmpArray with coordinates
       
  3750           j = i;
       
  3751           i++;
       
  3752           if (i < pathData.length) { // don't go over boundary of array
       
  3753             tmpArray = [];
       
  3754             valOf = pathData[i].valueOf();
       
  3755             while (!((valOf >= 65 && valOf <= 90) ||
       
  3756                      (valOf >= 97 && valOf <= 100) ||
       
  3757                      (valOf >= 102 && valOf <= 122))
       
  3758                      && flag === false) { // if its NOT a letter
       
  3759               if (valOf === 32) { //if its a space and the str isn't empty
       
  3760                 // sometimes you get a space after the letter
       
  3761                 if (str !== "") {
       
  3762                   tmpArray.push(parseFloat(str));
       
  3763                   str = "";
       
  3764                 }
       
  3765                 i++;
       
  3766               } else if (valOf === 45) { //if it's a -
       
  3767                 // allow for 'e' notation in numbers, e.g. 2.10e-9
       
  3768                 if (pathData[i-1].valueOf() === 101) {
       
  3769                   str += pathData[i].toString();
       
  3770                   i++;
       
  3771                 } else {
       
  3772                   // sometimes no space separator after (ex: 104.535-16.322)
       
  3773                   if (str !== "") {
       
  3774                     tmpArray.push(parseFloat(str));
       
  3775                   }
       
  3776                   str = pathData[i].toString();
       
  3777                   i++;
       
  3778                 }
       
  3779               } else {
       
  3780                 str += pathData[i].toString();
       
  3781                 i++;
       
  3782               }
       
  3783               if (i === pathData.length) { // don't go over boundary of array
       
  3784                 flag = true;
       
  3785               } else {
       
  3786                 valOf = pathData[i].valueOf();
       
  3787               }
       
  3788             }
       
  3789           }
       
  3790           if (str !== "") {
       
  3791             tmpArray.push(parseFloat(str));
       
  3792             str = "";
       
  3793           }
       
  3794           command = pathData[j];
       
  3795           valOf = command.valueOf();
       
  3796           if (valOf === 77) {  // M - move to (absolute)
       
  3797             if (tmpArray.length >= 2 && tmpArray.length % 2 ===0) {
       
  3798               // need one+ pairs of co-ordinates
       
  3799               cx = tmpArray[0];
       
  3800               cy = tmpArray[1];
       
  3801               this.parsePathMoveto(cx, cy);
       
  3802               if (tmpArray.length > 2) {
       
  3803                 for (j = 2, k = tmpArray.length; j < k; j+=2) {
       
  3804                   // absolute line to
       
  3805                   cx = tmpArray[j];
       
  3806                   cy = tmpArray[j+1];
       
  3807                   this.parsePathLineto(cx,cy);
       
  3808                 }
       
  3809               }
       
  3810             }
       
  3811           } else if (valOf === 109) {  // m - move to (relative)
       
  3812             if (tmpArray.length >= 2 && tmpArray.length % 2 === 0) {
       
  3813               // need one+ pairs of co-ordinates
       
  3814               cx += tmpArray[0];
       
  3815               cy += tmpArray[1];
       
  3816               this.parsePathMoveto(cx,cy);
       
  3817               if (tmpArray.length > 2) {
       
  3818                 for (j = 2, k = tmpArray.length; j < k; j+=2) {
       
  3819                   // relative line to
       
  3820                   cx += tmpArray[j];
       
  3821                   cy += tmpArray[j + 1];
       
  3822                   this.parsePathLineto(cx,cy);
       
  3823                 }
       
  3824               }
       
  3825             }
       
  3826           } else if (valOf === 76) { // L - lineto (absolute)
       
  3827             if (tmpArray.length >= 2 && tmpArray.length % 2 === 0) {
       
  3828               // need one+ pairs of co-ordinates
       
  3829               for (j = 0, k = tmpArray.length; j < k; j+=2) {
       
  3830                 cx = tmpArray[j];
       
  3831                 cy = tmpArray[j + 1];
       
  3832                 this.parsePathLineto(cx,cy);
       
  3833               }
       
  3834             }
       
  3835           } else if (valOf === 108) { // l - lineto (relative)
       
  3836             if (tmpArray.length >= 2 && tmpArray.length % 2 === 0) {
       
  3837               // need one+ pairs of co-ordinates
       
  3838               for (j = 0, k = tmpArray.length; j < k; j+=2) {
       
  3839                 cx += tmpArray[j];
       
  3840                 cy += tmpArray[j+1];
       
  3841                 this.parsePathLineto(cx,cy);
       
  3842               }
       
  3843             }
       
  3844           } else if (valOf === 72) { // H - horizontal lineto (absolute)
       
  3845             for (j = 0, k = tmpArray.length; j < k; j++) {
       
  3846               // multiple x co-ordinates can be provided
       
  3847               cx = tmpArray[j];
       
  3848               this.parsePathLineto(cx, cy);
       
  3849             }
       
  3850           } else if (valOf === 104) { // h - horizontal lineto (relative)
       
  3851             for (j = 0, k = tmpArray.length; j < k; j++) {
       
  3852               // multiple x co-ordinates can be provided
       
  3853               cx += tmpArray[j];
       
  3854               this.parsePathLineto(cx, cy);
       
  3855             }
       
  3856           } else if (valOf === 86) { // V - vertical lineto (absolute)
       
  3857             for (j = 0, k = tmpArray.length; j < k; j++) {
       
  3858               // multiple y co-ordinates can be provided
       
  3859               cy = tmpArray[j];
       
  3860               this.parsePathLineto(cx, cy);
       
  3861             }
       
  3862           } else if (valOf === 118) { // v - vertical lineto (relative)
       
  3863             for (j = 0, k = tmpArray.length; j < k; j++) {
       
  3864               // multiple y co-ordinates can be provided
       
  3865               cy += tmpArray[j];
       
  3866               this.parsePathLineto(cx, cy);
       
  3867             }
       
  3868           } else if (valOf === 67) { // C - curve to (absolute)
       
  3869             if (tmpArray.length >= 6 && tmpArray.length % 6 === 0) {
       
  3870               // need one+ multiples of 6 co-ordinates
       
  3871               for (j = 0, k = tmpArray.length; j < k; j+=6) {
       
  3872                 ctrlX1 = tmpArray[j];
       
  3873                 ctrlY1 = tmpArray[j + 1];
       
  3874                 ctrlX2 = tmpArray[j + 2];
       
  3875                 ctrlY2 = tmpArray[j + 3];
       
  3876                 endX   = tmpArray[j + 4];
       
  3877                 endY   = tmpArray[j + 5];
       
  3878                 this.parsePathCurveto(ctrlX1,
       
  3879                                       ctrlY1,
       
  3880                                       ctrlX2,
       
  3881                                       ctrlY2,
       
  3882                                       endX,
       
  3883                                       endY);
       
  3884                 cx = endX;
       
  3885                 cy = endY;
       
  3886               }
       
  3887             }
       
  3888           } else if (valOf === 99) { // c - curve to (relative)
       
  3889             if (tmpArray.length >= 6 && tmpArray.length % 6 === 0) {
       
  3890               // need one+ multiples of 6 co-ordinates
       
  3891               for (j = 0, k = tmpArray.length; j < k; j+=6) {
       
  3892                 ctrlX1 = cx + tmpArray[j];
       
  3893                 ctrlY1 = cy + tmpArray[j + 1];
       
  3894                 ctrlX2 = cx + tmpArray[j + 2];
       
  3895                 ctrlY2 = cy + tmpArray[j + 3];
       
  3896                 endX   = cx + tmpArray[j + 4];
       
  3897                 endY   = cy + tmpArray[j + 5];
       
  3898                 this.parsePathCurveto(ctrlX1,
       
  3899                                       ctrlY1,
       
  3900                                       ctrlX2,
       
  3901                                       ctrlY2,
       
  3902                                       endX,
       
  3903                                       endY);
       
  3904                 cx = endX;
       
  3905                 cy = endY;
       
  3906               }
       
  3907             }
       
  3908           } else if (valOf === 83) { // S - curve to shorthand (absolute)
       
  3909             if (tmpArray.length >= 4 && tmpArray.length % 4 === 0) {
       
  3910               // need one+ multiples of 4 co-ordinates
       
  3911               for (j = 0, k = tmpArray.length; j < k; j+=4) {
       
  3912                 if (lastInstruction.toLowerCase() ===  "c" ||
       
  3913                     lastInstruction.toLowerCase() ===  "s") {
       
  3914                   ppx    = this.vertices[ this.vertices.length-2 ][0];
       
  3915                   ppy    = this.vertices[ this.vertices.length-2 ][1];
       
  3916                   px     = this.vertices[ this.vertices.length-1 ][0];
       
  3917                   py     = this.vertices[ this.vertices.length-1 ][1];
       
  3918                   ctrlX1 = px + (px - ppx);
       
  3919                   ctrlY1 = py + (py - ppy);
       
  3920                 } else {
       
  3921                   //If there is no previous curve,
       
  3922                   //the current point will be used as the first control point.
       
  3923                   ctrlX1 = this.vertices[this.vertices.length-1][0];
       
  3924                   ctrlY1 = this.vertices[this.vertices.length-1][1];
       
  3925                 }
       
  3926                 ctrlX2 = tmpArray[j];
       
  3927                 ctrlY2 = tmpArray[j + 1];
       
  3928                 endX   = tmpArray[j + 2];
       
  3929                 endY   = tmpArray[j + 3];
       
  3930                 this.parsePathCurveto(ctrlX1,
       
  3931                                       ctrlY1,
       
  3932                                       ctrlX2,
       
  3933                                       ctrlY2,
       
  3934                                       endX,
       
  3935                                       endY);
       
  3936                 cx = endX;
       
  3937                 cy = endY;
       
  3938               }
       
  3939             }
       
  3940           } else if (valOf === 115) { // s - curve to shorthand (relative)
       
  3941             if (tmpArray.length >= 4 && tmpArray.length % 4 === 0) {
       
  3942               // need one+ multiples of 4 co-ordinates
       
  3943               for (j = 0, k = tmpArray.length; j < k; j+=4) {
       
  3944                 if (lastInstruction.toLowerCase() ===  "c" ||
       
  3945                     lastInstruction.toLowerCase() ===  "s") {
       
  3946                   ppx    = this.vertices[this.vertices.length-2][0];
       
  3947                   ppy    = this.vertices[this.vertices.length-2][1];
       
  3948                   px     = this.vertices[this.vertices.length-1][0];
       
  3949                   py     = this.vertices[this.vertices.length-1][1];
       
  3950                   ctrlX1 = px + (px - ppx);
       
  3951                   ctrlY1 = py + (py - ppy);
       
  3952                 } else {
       
  3953                   //If there is no previous curve,
       
  3954                   //the current point will be used as the first control point.
       
  3955                   ctrlX1 = this.vertices[this.vertices.length-1][0];
       
  3956                   ctrlY1 = this.vertices[this.vertices.length-1][1];
       
  3957                 }
       
  3958                 ctrlX2 = cx + tmpArray[j];
       
  3959                 ctrlY2 = cy + tmpArray[j + 1];
       
  3960                 endX   = cx + tmpArray[j + 2];
       
  3961                 endY   = cy + tmpArray[j + 3];
       
  3962                 this.parsePathCurveto(ctrlX1,
       
  3963                                       ctrlY1,
       
  3964                                       ctrlX2,
       
  3965                                       ctrlY2,
       
  3966                                       endX,
       
  3967                                       endY);
       
  3968                 cx = endX;
       
  3969                 cy = endY;
       
  3970               }
       
  3971             }
       
  3972           } else if (valOf === 81) { // Q - quadratic curve to (absolute)
       
  3973             if (tmpArray.length >= 4 && tmpArray.length % 4 === 0) {
       
  3974               // need one+ multiples of 4 co-ordinates
       
  3975               for (j = 0, k = tmpArray.length; j < k; j+=4) {
       
  3976                 ctrlX = tmpArray[j];
       
  3977                 ctrlY = tmpArray[j + 1];
       
  3978                 endX  = tmpArray[j + 2];
       
  3979                 endY  = tmpArray[j + 3];
       
  3980                 this.parsePathQuadto(cx, cy, ctrlX, ctrlY, endX, endY);
       
  3981                 cx = endX;
       
  3982                 cy = endY;
       
  3983               }
       
  3984             }
       
  3985           } else if (valOf === 113) { // q - quadratic curve to (relative)
       
  3986             if (tmpArray.length >= 4 && tmpArray.length % 4 === 0) {
       
  3987               // need one+ multiples of 4 co-ordinates
       
  3988               for (j = 0, k = tmpArray.length; j < k; j+=4) {
       
  3989                 ctrlX = cx + tmpArray[j];
       
  3990                 ctrlY = cy + tmpArray[j + 1];
       
  3991                 endX  = cx + tmpArray[j + 2];
       
  3992                 endY  = cy + tmpArray[j + 3];
       
  3993                 this.parsePathQuadto(cx, cy, ctrlX, ctrlY, endX, endY);
       
  3994                 cx = endX;
       
  3995                 cy = endY;
       
  3996               }
       
  3997             }
       
  3998           } else if (valOf === 84) {
       
  3999             // T - quadratic curve to shorthand (absolute)
       
  4000             if (tmpArray.length >= 2 && tmpArray.length % 2 === 0) {
       
  4001               // need one+ pairs of co-ordinates
       
  4002               for (j = 0, k = tmpArray.length; j < k; j+=2) {
       
  4003                 if (lastInstruction.toLowerCase() ===  "q" ||
       
  4004                     lastInstruction.toLowerCase() ===  "t") {
       
  4005                   ppx   = this.vertices[this.vertices.length-2][0];
       
  4006                   ppy   = this.vertices[this.vertices.length-2][1];
       
  4007                   px    = this.vertices[this.vertices.length-1][0];
       
  4008                   py    = this.vertices[this.vertices.length-1][1];
       
  4009                   ctrlX = px + (px - ppx);
       
  4010                   ctrlY = py + (py - ppy);
       
  4011                 } else {
       
  4012                   // If there is no previous command or if the previous command
       
  4013                   // was not a Q, q, T or t, assume the control point is
       
  4014                   // coincident with the current point.
       
  4015                   ctrlX = cx;
       
  4016                   ctrlY = cy;
       
  4017                 }
       
  4018                 endX  = tmpArray[j];
       
  4019                 endY  = tmpArray[j + 1];
       
  4020                 this.parsePathQuadto(cx, cy, ctrlX, ctrlY, endX, endY);
       
  4021                 cx = endX;
       
  4022                 cy = endY;
       
  4023               }
       
  4024             }
       
  4025           } else if (valOf === 116) {
       
  4026             // t - quadratic curve to shorthand (relative)
       
  4027             if (tmpArray.length >= 2 && tmpArray.length % 2 === 0) {
       
  4028               // need one+ pairs of co-ordinates
       
  4029               for (j = 0, k = tmpArray.length; j < k; j+=2) {
       
  4030                 if (lastInstruction.toLowerCase() ===  "q" ||
       
  4031                     lastInstruction.toLowerCase() ===  "t") {
       
  4032                   ppx   = this.vertices[this.vertices.length-2][0];
       
  4033                   ppy   = this.vertices[this.vertices.length-2][1];
       
  4034                   px    = this.vertices[this.vertices.length-1][0];
       
  4035                   py    = this.vertices[this.vertices.length-1][1];
       
  4036                   ctrlX = px + (px - ppx);
       
  4037                   ctrlY = py + (py - ppy);
       
  4038                 } else {
       
  4039                   // If there is no previous command or if the previous command
       
  4040                   // was not a Q, q, T or t, assume the control point is
       
  4041                   // coincident with the current point.
       
  4042                   ctrlX = cx;
       
  4043                   ctrlY = cy;
       
  4044                 }
       
  4045                 endX  = cx + tmpArray[j];
       
  4046                 endY  = cy + tmpArray[j + 1];
       
  4047                 this.parsePathQuadto(cx, cy, ctrlX, ctrlY, endX, endY);
       
  4048                 cx = endX;
       
  4049                 cy = endY;
       
  4050               }
       
  4051             }
       
  4052           } else if (valOf === 90 || valOf === 122) { // Z or z (these do the same thing)
       
  4053             this.close = true;
       
  4054           }
       
  4055           lastInstruction = command.toString();
       
  4056         } else { i++;}
       
  4057       }
       
  4058     };
       
  4059     /**
       
  4060      * @member PShapeSVG
       
  4061      * PShapeSVG.parsePath() helper function
       
  4062      *
       
  4063      * @see PShapeSVG#parsePath
       
  4064      */
       
  4065     PShapeSVG.prototype.parsePathQuadto = function(x1, y1, cx, cy, x2, y2) {
       
  4066       if (this.vertices.length > 0) {
       
  4067         this.parsePathCode(PConstants.BEZIER_VERTEX);
       
  4068         // x1/y1 already covered by last moveto, lineto, or curveto
       
  4069         this.parsePathVertex(x1 + ((cx-x1)*2/3), y1 + ((cy-y1)*2/3));
       
  4070         this.parsePathVertex(x2 + ((cx-x2)*2/3), y2 + ((cy-y2)*2/3));
       
  4071         this.parsePathVertex(x2, y2);
       
  4072       } else {
       
  4073         throw ("Path must start with M/m");
       
  4074       }
       
  4075     };
       
  4076     /**
       
  4077      * @member PShapeSVG
       
  4078      * PShapeSVG.parsePath() helper function
       
  4079      *
       
  4080      * @see PShapeSVG#parsePath
       
  4081      */
       
  4082     PShapeSVG.prototype.parsePathCurveto = function(x1,  y1, x2, y2, x3, y3) {
       
  4083       if (this.vertices.length > 0) {
       
  4084         this.parsePathCode(PConstants.BEZIER_VERTEX );
       
  4085         this.parsePathVertex(x1, y1);
       
  4086         this.parsePathVertex(x2, y2);
       
  4087         this.parsePathVertex(x3, y3);
       
  4088       } else {
       
  4089         throw ("Path must start with M/m");
       
  4090       }
       
  4091     };
       
  4092     /**
       
  4093      * @member PShapeSVG
       
  4094      * PShapeSVG.parsePath() helper function
       
  4095      *
       
  4096      * @see PShapeSVG#parsePath
       
  4097      */
       
  4098     PShapeSVG.prototype.parsePathLineto = function(px, py) {
       
  4099       if (this.vertices.length > 0) {
       
  4100         this.parsePathCode(PConstants.VERTEX);
       
  4101         this.parsePathVertex(px, py);
       
  4102         // add property to distinguish between curContext.moveTo
       
  4103         // or curContext.lineTo
       
  4104         this.vertices[this.vertices.length-1]["moveTo"] = false;
       
  4105       } else {
       
  4106         throw ("Path must start with M/m");
       
  4107       }
       
  4108     };
       
  4109 
       
  4110     PShapeSVG.prototype.parsePathMoveto = function(px, py) {
       
  4111       if (this.vertices.length > 0) {
       
  4112         this.parsePathCode(PConstants.BREAK);
       
  4113       }
       
  4114       this.parsePathCode(PConstants.VERTEX);
       
  4115       this.parsePathVertex(px, py);
       
  4116       // add property to distinguish between curContext.moveTo
       
  4117       // or curContext.lineTo
       
  4118       this.vertices[this.vertices.length-1]["moveTo"] = true;
       
  4119     };
       
  4120     /**
       
  4121      * @member PShapeSVG
       
  4122      * PShapeSVG.parsePath() helper function
       
  4123      *
       
  4124      * @see PShapeSVG#parsePath
       
  4125      */
       
  4126     PShapeSVG.prototype.parsePathVertex = function(x,  y) {
       
  4127       var verts = [];
       
  4128       verts[0]  = x;
       
  4129       verts[1]  = y;
       
  4130       this.vertices.push(verts);
       
  4131     };
       
  4132     /**
       
  4133      * @member PShapeSVG
       
  4134      * PShapeSVG.parsePath() helper function
       
  4135      *
       
  4136      * @see PShapeSVG#parsePath
       
  4137      */
       
  4138     PShapeSVG.prototype.parsePathCode = function(what) {
       
  4139       this.vertexCodes.push(what);
       
  4140     };
       
  4141     /**
       
  4142      * @member PShapeSVG
       
  4143      * The parsePoly() function parses a polyline or polygon from an SVG file.
       
  4144      *
       
  4145      * @param {boolean}val true if shape is closed (polygon), false if not (polyline)
       
  4146      */
       
  4147     PShapeSVG.prototype.parsePoly = function(val) {
       
  4148       this.family    = PConstants.PATH;
       
  4149       this.close     = val;
       
  4150       var pointsAttr = p.trim(this.element.getStringAttribute("points")
       
  4151                               .replace(/[,\s]+/g,' '));
       
  4152       if (pointsAttr !== null) {
       
  4153         //split into array
       
  4154         var pointsBuffer = pointsAttr.split(" ");
       
  4155         if (pointsBuffer.length % 2 === 0) {
       
  4156           for (var i = 0, j = pointsBuffer.length; i < j; i++) {
       
  4157             var verts = [];
       
  4158             verts[0]  = pointsBuffer[i];
       
  4159             verts[1]  = pointsBuffer[++i];
       
  4160             this.vertices.push(verts);
       
  4161           }
       
  4162         } else {
       
  4163           throw("Error parsing polygon points: odd number of coordinates provided");
       
  4164         }
       
  4165       }
       
  4166     };
       
  4167     /**
       
  4168      * @member PShapeSVG
       
  4169      * The parseRect() function parses a rect from an SVG file.
       
  4170      */
       
  4171     PShapeSVG.prototype.parseRect = function() {
       
  4172       this.kind      = PConstants.RECT;
       
  4173       this.family    = PConstants.PRIMITIVE;
       
  4174       this.params    = [];
       
  4175       this.params[0] = this.element.getFloatAttribute("x");
       
  4176       this.params[1] = this.element.getFloatAttribute("y");
       
  4177       this.params[2] = this.element.getFloatAttribute("width");
       
  4178       this.params[3] = this.element.getFloatAttribute("height");
       
  4179       if (this.params[2] < 0 || this.params[3] < 0) {
       
  4180         throw("svg error: negative width or height found while parsing <rect>");
       
  4181       }
       
  4182     };
       
  4183     /**
       
  4184      * @member PShapeSVG
       
  4185      * The parseEllipse() function handles parsing ellipse and circle tags.
       
  4186      *
       
  4187      * @param {boolean}val true if this is a circle and not an ellipse
       
  4188      */
       
  4189     PShapeSVG.prototype.parseEllipse = function(val) {
       
  4190       this.kind   = PConstants.ELLIPSE;
       
  4191       this.family = PConstants.PRIMITIVE;
       
  4192       this.params = [];
       
  4193 
       
  4194       this.params[0] = this.element.getFloatAttribute("cx") | 0 ;
       
  4195       this.params[1] = this.element.getFloatAttribute("cy") | 0;
       
  4196 
       
  4197       var rx, ry;
       
  4198       if (val) {
       
  4199         rx = ry = this.element.getFloatAttribute("r");
       
  4200         if (rx < 0) {
       
  4201           throw("svg error: negative radius found while parsing <circle>");
       
  4202         }
       
  4203       } else {
       
  4204         rx = this.element.getFloatAttribute("rx");
       
  4205         ry = this.element.getFloatAttribute("ry");
       
  4206         if (rx < 0 || ry < 0) {
       
  4207           throw("svg error: negative x-axis radius or y-axis radius found while parsing <ellipse>");
       
  4208         }
       
  4209       }
       
  4210       this.params[0] -= rx;
       
  4211       this.params[1] -= ry;
       
  4212 
       
  4213       this.params[2] = rx*2;
       
  4214       this.params[3] = ry*2;
       
  4215     };
       
  4216     /**
       
  4217      * @member PShapeSVG
       
  4218      * The parseLine() function handles parsing line tags.
       
  4219      *
       
  4220      * @param {boolean}val true if this is a circle and not an ellipse
       
  4221      */
       
  4222     PShapeSVG.prototype.parseLine = function() {
       
  4223       this.kind = PConstants.LINE;
       
  4224       this.family = PConstants.PRIMITIVE;
       
  4225       this.params = [];
       
  4226       this.params[0] = this.element.getFloatAttribute("x1");
       
  4227       this.params[1] = this.element.getFloatAttribute("y1");
       
  4228       this.params[2] = this.element.getFloatAttribute("x2");
       
  4229       this.params[3] = this.element.getFloatAttribute("y2");
       
  4230     };
       
  4231     /**
       
  4232      * @member PShapeSVG
       
  4233      * The parseColors() function handles parsing the opacity, strijem stroke-width, stroke-linejoin,stroke-linecap, fill, and style attributes
       
  4234      *
       
  4235      * @param {XMLElement}element the element of which attributes to parse
       
  4236      */
       
  4237     PShapeSVG.prototype.parseColors = function(element) {
       
  4238       if (element.hasAttribute("opacity")) {
       
  4239         this.setOpacity(element.getAttribute("opacity"));
       
  4240       }
       
  4241       if (element.hasAttribute("stroke")) {
       
  4242         this.setStroke(element.getAttribute("stroke"));
       
  4243       }
       
  4244       if (element.hasAttribute("stroke-width")) {
       
  4245         // if NaN (i.e. if it's 'inherit') then default
       
  4246         // back to the inherit setting
       
  4247         this.setStrokeWeight(element.getAttribute("stroke-width"));
       
  4248       }
       
  4249       if (element.hasAttribute("stroke-linejoin") ) {
       
  4250         this.setStrokeJoin(element.getAttribute("stroke-linejoin"));
       
  4251       }
       
  4252       if (element.hasAttribute("stroke-linecap")) {
       
  4253         this.setStrokeCap(element.getStringAttribute("stroke-linecap"));
       
  4254       }
       
  4255       // fill defaults to black (though stroke defaults to "none")
       
  4256       // http://www.w3.org/TR/SVG/painting.html#FillProperties
       
  4257       if (element.hasAttribute("fill")) {
       
  4258         this.setFill(element.getStringAttribute("fill"));
       
  4259       }
       
  4260       if (element.hasAttribute("style")) {
       
  4261         var styleText   = element.getStringAttribute("style");
       
  4262         var styleTokens = styleText.toString().split( ";" );
       
  4263 
       
  4264         for (var i = 0, j = styleTokens.length; i < j; i++) {
       
  4265           var tokens = p.trim(styleTokens[i].split( ":" ));
       
  4266           if (tokens[0] === "fill") {
       
  4267               this.setFill(tokens[1]);
       
  4268           } else if (tokens[0] === "fill-opacity") {
       
  4269               this.setFillOpacity(tokens[1]);
       
  4270           } else if (tokens[0] === "stroke") {
       
  4271               this.setStroke(tokens[1]);
       
  4272           } else if (tokens[0] === "stroke-width") {
       
  4273               this.setStrokeWeight(tokens[1]);
       
  4274           } else if (tokens[0] === "stroke-linecap") {
       
  4275               this.setStrokeCap(tokens[1]);
       
  4276           } else if (tokens[0] === "stroke-linejoin") {
       
  4277               this.setStrokeJoin(tokens[1]);
       
  4278           } else if (tokens[0] === "stroke-opacity") {
       
  4279               this.setStrokeOpacity(tokens[1]);
       
  4280           } else if (tokens[0] === "opacity") {
       
  4281               this.setOpacity(tokens[1]);
       
  4282           } // Other attributes are not yet implemented
       
  4283         }
       
  4284       }
       
  4285     };
       
  4286     /**
       
  4287      * @member PShapeSVG
       
  4288      * PShapeSVG.parseColors() helper function
       
  4289      *
       
  4290      * @param {String} opacityText the value of fillOpacity
       
  4291      *
       
  4292      * @see PShapeSVG#parseColors
       
  4293      */
       
  4294     PShapeSVG.prototype.setFillOpacity = function(opacityText) {
       
  4295       this.fillOpacity = parseFloat(opacityText);
       
  4296       this.fillColor   = this.fillOpacity * 255  << 24 |
       
  4297                          this.fillColor & 0xFFFFFF;
       
  4298     };
       
  4299     /**
       
  4300      * @member PShapeSVG
       
  4301      * PShapeSVG.parseColors() helper function
       
  4302      *
       
  4303      * @param {String} fillText the value of fill
       
  4304      *
       
  4305      * @see PShapeSVG#parseColors
       
  4306      */
       
  4307     PShapeSVG.prototype.setFill = function (fillText) {
       
  4308       var opacityMask = this.fillColor & 0xFF000000;
       
  4309       if (fillText === "none") {
       
  4310         this.fill = false;
       
  4311       } else if (fillText.indexOf("#") === 0) {
       
  4312         this.fill      = true;
       
  4313         if (fillText.length === 4) {
       
  4314           // convert #00F to #0000FF
       
  4315           fillText = fillText.replace(/#(.)(.)(.)/,"#$1$1$2$2$3$3");
       
  4316         }
       
  4317         this.fillColor = opacityMask |
       
  4318                          (parseInt(fillText.substring(1), 16 )) &
       
  4319                          0xFFFFFF;
       
  4320       } else if (fillText.indexOf("rgb") === 0) {
       
  4321         this.fill      = true;
       
  4322         this.fillColor = opacityMask | this.parseRGB(fillText);
       
  4323       } else if (fillText.indexOf("url(#") === 0) {
       
  4324         this.fillName = fillText.substring(5, fillText.length - 1 );
       
  4325       } else if (colors[fillText]) {
       
  4326         this.fill      = true;
       
  4327         this.fillColor = opacityMask |
       
  4328                          (parseInt(colors[fillText].substring(1), 16)) &
       
  4329                          0xFFFFFF;
       
  4330       }
       
  4331     };
       
  4332     /**
       
  4333      * @member PShapeSVG
       
  4334      * PShapeSVG.parseColors() helper function
       
  4335      *
       
  4336      * @param {String} opacity the value of opacity
       
  4337      *
       
  4338      * @see PShapeSVG#parseColors
       
  4339      */
       
  4340     PShapeSVG.prototype.setOpacity = function(opacity) {
       
  4341       this.strokeColor = parseFloat(opacity) * 255 << 24 |
       
  4342                          this.strokeColor & 0xFFFFFF;
       
  4343       this.fillColor   = parseFloat(opacity) * 255 << 24 |
       
  4344                          this.fillColor & 0xFFFFFF;
       
  4345     };
       
  4346     /**
       
  4347      * @member PShapeSVG
       
  4348      * PShapeSVG.parseColors() helper function
       
  4349      *
       
  4350      * @param {String} strokeText the value to set stroke to
       
  4351      *
       
  4352      * @see PShapeSVG#parseColors
       
  4353      */
       
  4354     PShapeSVG.prototype.setStroke = function(strokeText) {
       
  4355       var opacityMask = this.strokeColor & 0xFF000000;
       
  4356       if (strokeText === "none") {
       
  4357         this.stroke = false;
       
  4358       } else if (strokeText.charAt( 0 ) === "#") {
       
  4359         this.stroke      = true;
       
  4360         if (strokeText.length === 4) {
       
  4361           // convert #00F to #0000FF
       
  4362           strokeText = strokeText.replace(/#(.)(.)(.)/,"#$1$1$2$2$3$3");
       
  4363         }
       
  4364         this.strokeColor = opacityMask |
       
  4365                            (parseInt( strokeText.substring( 1 ), 16 )) &
       
  4366                            0xFFFFFF;
       
  4367       } else if (strokeText.indexOf( "rgb" ) === 0 ) {
       
  4368         this.stroke = true;
       
  4369         this.strokeColor = opacityMask | this.parseRGB(strokeText);
       
  4370       } else if (strokeText.indexOf( "url(#" ) === 0) {
       
  4371         this.strokeName = strokeText.substring(5, strokeText.length - 1);
       
  4372       } else if (colors[strokeText]) {
       
  4373         this.stroke      = true;
       
  4374         this.strokeColor = opacityMask |
       
  4375                            (parseInt(colors[strokeText].substring(1), 16)) &
       
  4376                            0xFFFFFF;
       
  4377       }
       
  4378     };
       
  4379     /**
       
  4380      * @member PShapeSVG
       
  4381      * PShapeSVG.parseColors() helper function
       
  4382      *
       
  4383      * @param {String} weight the value to set strokeWeight to
       
  4384      *
       
  4385      * @see PShapeSVG#parseColors
       
  4386      */
       
  4387     PShapeSVG.prototype.setStrokeWeight = function(weight) {
       
  4388       this.strokeWeight = this.parseUnitSize(weight);
       
  4389     };
       
  4390     /**
       
  4391      * @member PShapeSVG
       
  4392      * PShapeSVG.parseColors() helper function
       
  4393      *
       
  4394      * @param {String} linejoin the value to set strokeJoin to
       
  4395      *
       
  4396      * @see PShapeSVG#parseColors
       
  4397      */
       
  4398     PShapeSVG.prototype.setStrokeJoin = function(linejoin) {
       
  4399       if (linejoin === "miter") {
       
  4400         this.strokeJoin = PConstants.MITER;
       
  4401 
       
  4402       } else if (linejoin === "round") {
       
  4403         this.strokeJoin = PConstants.ROUND;
       
  4404 
       
  4405       } else if (linejoin === "bevel") {
       
  4406         this.strokeJoin = PConstants.BEVEL;
       
  4407       }
       
  4408     };
       
  4409     /**
       
  4410      * @member PShapeSVG
       
  4411      * PShapeSVG.parseColors() helper function
       
  4412      *
       
  4413      * @param {String} linecap the value to set strokeCap to
       
  4414      *
       
  4415      * @see PShapeSVG#parseColors
       
  4416      */
       
  4417     PShapeSVG.prototype.setStrokeCap = function (linecap) {
       
  4418       if (linecap === "butt") {
       
  4419         this.strokeCap = PConstants.SQUARE;
       
  4420 
       
  4421       } else if (linecap === "round") {
       
  4422         this.strokeCap = PConstants.ROUND;
       
  4423 
       
  4424       } else if (linecap === "square") {
       
  4425         this.strokeCap = PConstants.PROJECT;
       
  4426       }
       
  4427     };
       
  4428     /**
       
  4429      * @member PShapeSVG
       
  4430      * PShapeSVG.parseColors() helper function
       
  4431      *
       
  4432      * @param {String} opacityText the value to set stroke opacity to
       
  4433      *
       
  4434      * @see PShapeSVG#parseColors
       
  4435      */
       
  4436     PShapeSVG.prototype.setStrokeOpacity =  function (opacityText) {
       
  4437       this.strokeOpacity = parseFloat(opacityText);
       
  4438       this.strokeColor   = this.strokeOpacity * 255 << 24 |
       
  4439                            this.strokeColor &
       
  4440                            0xFFFFFF;
       
  4441     };
       
  4442     /**
       
  4443      * @member PShapeSVG
       
  4444      * The parseRGB() function parses an rbg() color string and returns a color int
       
  4445      *
       
  4446      * @param {String} color the color to parse in rbg() format
       
  4447      *
       
  4448      * @return {int} the equivalent color int
       
  4449      */
       
  4450     PShapeSVG.prototype.parseRGB = function(color) {
       
  4451       var sub    = color.substring(color.indexOf('(') + 1, color.indexOf(')'));
       
  4452       var values = sub.split(", ");
       
  4453       return (values[0] << 16) | (values[1] << 8) | (values[2]);
       
  4454     };
       
  4455     /**
       
  4456      * @member PShapeSVG
       
  4457      * The parseUnitSize() function parse a size that may have a suffix for its units.
       
  4458      * Ignoring cases where this could also be a percentage.
       
  4459      * The <A HREF="http://www.w3.org/TR/SVG/coords.html#Units">units</A> spec:
       
  4460      * <UL>
       
  4461      * <LI>"1pt" equals "1.25px" (and therefore 1.25 user units)
       
  4462      * <LI>"1pc" equals "15px" (and therefore 15 user units)
       
  4463      * <LI>"1mm" would be "3.543307px" (3.543307 user units)
       
  4464      * <LI>"1cm" equals "35.43307px" (and therefore 35.43307 user units)
       
  4465      * <LI>"1in" equals "90px" (and therefore 90 user units)
       
  4466      * </UL>
       
  4467      */
       
  4468     PShapeSVG.prototype.parseUnitSize = function (text) {
       
  4469       var len = text.length - 2;
       
  4470       if (len < 0) { return text; }
       
  4471       if (text.indexOf("pt") === len) {
       
  4472         return parseFloat(text.substring(0, len)) * 1.25;
       
  4473       }
       
  4474       if (text.indexOf("pc") === len) {
       
  4475         return parseFloat( text.substring( 0, len)) * 15;
       
  4476       }
       
  4477       if (text.indexOf("mm") === len) {
       
  4478         return parseFloat( text.substring(0, len)) * 3.543307;
       
  4479       }
       
  4480       if (text.indexOf("cm") === len) {
       
  4481         return parseFloat(text.substring(0, len)) * 35.43307;
       
  4482       }
       
  4483       if (text.indexOf("in") === len) {
       
  4484         return parseFloat(text.substring(0, len)) * 90;
       
  4485       }
       
  4486       if (text.indexOf("px") === len) {
       
  4487         return parseFloat(text.substring(0, len));
       
  4488       }
       
  4489       return parseFloat(text);
       
  4490     };
       
  4491     /**
       
  4492      * The shape() function displays shapes to the screen.
       
  4493      * Processing currently works with SVG shapes only.
       
  4494      * The <b>shape</b> parameter specifies the shape to display and the <b>x</b>
       
  4495      * and <b>y</b> parameters define the location of the shape from its
       
  4496      * upper-left corner.
       
  4497      * The shape is displayed at its original size unless the <b>width</b>
       
  4498      * and <b>height</b> parameters specify a different size.
       
  4499      * The <b>shapeMode()</b> function changes the way the parameters work.
       
  4500      * A call to <b>shapeMode(CORNERS)</b>, for example, will change the width
       
  4501      * and height parameters to define the x and y values of the opposite corner
       
  4502      * of the shape.
       
  4503      * <br><br>
       
  4504      * Note complex shapes may draw awkwardly with P2D, P3D, and OPENGL. Those
       
  4505      * renderers do not yet support shapes that have holes or complicated breaks.
       
  4506      *
       
  4507      * @param {PShape} shape       the shape to display
       
  4508      * @param {int|float} x        x-coordinate of the shape
       
  4509      * @param {int|float} y        y-coordinate of the shape
       
  4510      * @param {int|float} width    width to display the shape
       
  4511      * @param {int|float} height   height to display the shape
       
  4512      *
       
  4513      * @see PShape
       
  4514      * @see loadShape()
       
  4515      * @see shapeMode()
       
  4516      */
       
  4517     p.shape = function(shape, x, y, width, height) {
       
  4518       if (arguments.length >= 1 && arguments[0] !== null) {
       
  4519         if (shape.isVisible()) {
       
  4520           p.pushMatrix();
       
  4521           if (curShapeMode === PConstants.CENTER) {
       
  4522             if (arguments.length === 5) {
       
  4523               p.translate(x - width/2, y - height/2);
       
  4524               p.scale(width / shape.getWidth(), height / shape.getHeight());
       
  4525             } else if (arguments.length === 3) {
       
  4526               p.translate(x - shape.getWidth()/2, - shape.getHeight()/2);
       
  4527             } else {
       
  4528               p.translate(-shape.getWidth()/2, -shape.getHeight()/2);
       
  4529             }
       
  4530           } else if (curShapeMode === PConstants.CORNER) {
       
  4531             if (arguments.length === 5) {
       
  4532               p.translate(x, y);
       
  4533               p.scale(width / shape.getWidth(), height / shape.getHeight());
       
  4534             } else if (arguments.length === 3) {
       
  4535               p.translate(x, y);
       
  4536             }
       
  4537           } else if (curShapeMode === PConstants.CORNERS) {
       
  4538             if (arguments.length === 5) {
       
  4539               width  -= x;
       
  4540               height -= y;
       
  4541               p.translate(x, y);
       
  4542               p.scale(width / shape.getWidth(), height / shape.getHeight());
       
  4543             } else if (arguments.length === 3) {
       
  4544               p.translate(x, y);
       
  4545             }
       
  4546           }
       
  4547           shape.draw();
       
  4548           if ((arguments.length === 1 && curShapeMode === PConstants.CENTER ) || arguments.length > 1) {
       
  4549             p.popMatrix();
       
  4550           }
       
  4551         }
       
  4552       }
       
  4553     };
       
  4554 
       
  4555     /**
       
  4556      * The shapeMode() function modifies the location from which shapes draw.
       
  4557      * The default mode is <b>shapeMode(CORNER)</b>, which specifies the
       
  4558      * location to be the upper left corner of the shape and uses the third
       
  4559      * and fourth parameters of <b>shape()</b> to specify the width and height.
       
  4560      * The syntax <b>shapeMode(CORNERS)</b> uses the first and second parameters
       
  4561      * of <b>shape()</b> to set the location of one corner and uses the third
       
  4562      * and fourth parameters to set the opposite corner.
       
  4563      * The syntax <b>shapeMode(CENTER)</b> draws the shape from its center point
       
  4564      * and uses the third and forth parameters of <b>shape()</b> to specify the
       
  4565      * width and height.
       
  4566      * The parameter must be written in "ALL CAPS" because Processing syntax
       
  4567      * is case sensitive.
       
  4568      *
       
  4569      * @param {int} mode One of CORNER, CORNERS, CENTER
       
  4570      *
       
  4571      * @see shape()
       
  4572      * @see rectMode()
       
  4573      */
       
  4574     p.shapeMode = function (mode) {
       
  4575       curShapeMode = mode;
       
  4576     };
       
  4577 
       
  4578     /**
       
  4579      * The loadShape() function loads vector shapes into a variable of type PShape. Currently, only SVG files may be loaded.
       
  4580      * In most cases, <b>loadShape()</b> should be used inside <b>setup()</b> because loading shapes inside <b>draw()</b> will reduce the speed of a sketch.
       
  4581      *
       
  4582      * @param {String} filename     an SVG file
       
  4583      *
       
  4584      * @return {PShape} a object of type PShape or null
       
  4585      * @see PShape
       
  4586      * @see PApplet#shape()
       
  4587      * @see PApplet#shapeMode()
       
  4588      */
       
  4589     p.loadShape = function (filename) {
       
  4590       if (arguments.length === 1) {
       
  4591         if (filename.indexOf(".svg") > -1) {
       
  4592           return new PShapeSVG(null, filename);
       
  4593         }
       
  4594       }
       
  4595       return null;
       
  4596     };
       
  4597 
       
  4598     /**
       
  4599      * XMLAttribute is an attribute of a XML element. This is an internal class
       
  4600      *
       
  4601      * @param {String} fname     the full name of the attribute
       
  4602      * @param {String} n         the short name of the attribute
       
  4603      * @param {String} namespace the namespace URI of the attribute
       
  4604      * @param {String} v         the value of the attribute
       
  4605      * @param {String }t         the type of the attribute
       
  4606      *
       
  4607      * @see XMLElement
       
  4608      */
       
  4609     var XMLAttribute = function(fname, n, nameSpace, v, t){
       
  4610       this.fullName = fname || "";
       
  4611       this.name = n || "";
       
  4612       this.namespace = nameSpace || "";
       
  4613       this.value = v;
       
  4614       this.type = t;
       
  4615     };
       
  4616     /**
       
  4617      * XMLAttribute methods
       
  4618      */
       
  4619     XMLAttribute.prototype = {
       
  4620       /**
       
  4621        * @member XMLAttribute
       
  4622        * The getName() function returns the short name of the attribute
       
  4623        *
       
  4624        * @return {String} the short name of the attribute
       
  4625        */
       
  4626       getName: function() {
       
  4627         return this.name;
       
  4628       },
       
  4629       /**
       
  4630        * @member XMLAttribute
       
  4631        * The getFullName() function returns the full name of the attribute
       
  4632        *
       
  4633        * @return {String} the full name of the attribute
       
  4634        */
       
  4635       getFullName: function() {
       
  4636         return this.fullName;
       
  4637       },
       
  4638       /**
       
  4639        * @member XMLAttribute
       
  4640        * The getNamespace() function returns the namespace of the attribute
       
  4641        *
       
  4642        * @return {String} the namespace of the attribute
       
  4643        */
       
  4644       getNamespace: function() {
       
  4645         return this.namespace;
       
  4646       },
       
  4647       /**
       
  4648        * @member XMLAttribute
       
  4649        * The getValue() function returns the value of the attribute
       
  4650        *
       
  4651        * @return {String} the value of the attribute
       
  4652        */
       
  4653       getValue: function() {
       
  4654         return this.value;
       
  4655       },
       
  4656       /**
       
  4657        * @member XMLAttribute
       
  4658        * The getValue() function returns the type of the attribute
       
  4659        *
       
  4660        * @return {String} the type of the attribute
       
  4661        */
       
  4662       getType: function() {
       
  4663         return this.type;
       
  4664       },
       
  4665       /**
       
  4666        * @member XMLAttribute
       
  4667        * The setValue() function sets the value of the attribute
       
  4668        *
       
  4669        * @param {String} newval the new value
       
  4670        */
       
  4671       setValue: function(newval) {
       
  4672         this.value = newval;
       
  4673       }
       
  4674     };
       
  4675 
       
  4676     /**
       
  4677      * XMLElement is a representation of an XML object. The object is able to parse XML code
       
  4678      *
       
  4679      * @param {PApplet} parent   typically use "this"
       
  4680      * @param {String} filename  name of the XML/SVG file to load
       
  4681      * @param {String} xml       the xml/svg string
       
  4682      * @param {String} fullname  the full name of the element
       
  4683      * @param {String} namespace the namespace  of the URI
       
  4684      * @param {String} systemID  the system ID of the XML data where the element starts
       
  4685      * @param {Integer }lineNr   the line in the XML data where the element starts
       
  4686      */
       
  4687     var XMLElement = p.XMLElement = function() {
       
  4688       this.attributes = [];
       
  4689       this.children   = [];
       
  4690       this.fullName   = null;
       
  4691       this.name       = null;
       
  4692       this.namespace  = "";
       
  4693       this.content = null;
       
  4694       this.parent    = null;
       
  4695       this.lineNr     = "";
       
  4696       this.systemID   = "";
       
  4697       this.type = "ELEMENT";
       
  4698 
       
  4699       if (arguments.length === 4) {
       
  4700         this.fullName   = arguments[0] || "";
       
  4701         if (arguments[1]) {
       
  4702           this.name = arguments[1];
       
  4703         } else {
       
  4704           var index = this.fullName.indexOf(':');
       
  4705           if (index >= 0) {
       
  4706             this.name = this.fullName.substring(index + 1);
       
  4707           } else {
       
  4708             this.name = this.fullName;
       
  4709           }
       
  4710         }
       
  4711         this.namespace = arguments[1];
       
  4712         this.lineNr    = arguments[3];
       
  4713         this.systemID  = arguments[2];
       
  4714       }
       
  4715       else if ((arguments.length === 2 && arguments[1].indexOf(".") > -1) ) {
       
  4716         // filename or svg xml element
       
  4717         this.parse(arguments[arguments.length -1]);
       
  4718       } else if (arguments.length === 1 && typeof arguments[0] === "string"){
       
  4719         this.parse(arguments[0]);
       
  4720       }
       
  4721     };
       
  4722     /**
       
  4723      * XMLElement methods
       
  4724      * missing: enumerateAttributeNames(), enumerateChildren(),
       
  4725      * NOTE: parse does not work when a url is passed in
       
  4726      */
       
  4727     XMLElement.prototype = {
       
  4728       /**
       
  4729        * @member XMLElement
       
  4730        * The parse() function retrieves the file via ajax() and uses DOMParser()
       
  4731        * parseFromString method to make an XML document
       
  4732        * @addon
       
  4733        *
       
  4734        * @param {String} filename name of the XML/SVG file to load
       
  4735        *
       
  4736        * @throws ExceptionType Error loading document
       
  4737        *
       
  4738        * @see XMLElement#parseChildrenRecursive
       
  4739        */
       
  4740       parse: function(textstring) {
       
  4741         var xmlDoc;
       
  4742         try {
       
  4743           var extension = textstring.substring(textstring.length-4);
       
  4744           if (extension === ".xml" || extension === ".svg") {
       
  4745             textstring = ajax(textstring);
       
  4746           }
       
  4747           xmlDoc = new DOMParser().parseFromString(textstring, "text/xml");
       
  4748           var elements = xmlDoc.documentElement;
       
  4749           if (elements) {
       
  4750             this.parseChildrenRecursive(null, elements);
       
  4751           } else {
       
  4752             throw ("Error loading document");
       
  4753           }
       
  4754           return this;
       
  4755         } catch(e) {
       
  4756           throw(e);
       
  4757         }
       
  4758       },
       
  4759       /**
       
  4760        * @member XMLElement
       
  4761        * Internal helper function for parse().
       
  4762        * Loops through the
       
  4763        * @addon
       
  4764        *
       
  4765        * @param {XMLElement} parent                      the parent node
       
  4766        * @param {XML document childNodes} elementpath    the remaining nodes that need parsing
       
  4767        *
       
  4768        * @return {XMLElement} the new element and its children elements
       
  4769        */
       
  4770       parseChildrenRecursive: function (parent , elementpath){
       
  4771         var xmlelement,
       
  4772           xmlattribute,
       
  4773           tmpattrib,
       
  4774           l, m,
       
  4775           child;
       
  4776         if (!parent) { // this element is the root element
       
  4777           this.fullName = elementpath.localName;
       
  4778           this.name     = elementpath.nodeName;
       
  4779           xmlelement    = this;
       
  4780         } else { // this element has a parent
       
  4781           xmlelement         = new XMLElement(elementpath.localName, elementpath.nodeName, "", "");
       
  4782           xmlelement.parent  = parent;
       
  4783         }
       
  4784 
       
  4785         // if this is a text node, return a PCData element, instead of an XML element.
       
  4786         if(elementpath.nodeType === 3 && elementpath.textContent !== "") {
       
  4787           return this.createPCDataElement(elementpath.textContent);
       
  4788         }
       
  4789 
       
  4790         // bind all attributes
       
  4791         for (l = 0, m = elementpath.attributes.length; l < m; l++) {
       
  4792           tmpattrib    = elementpath.attributes[l];
       
  4793           xmlattribute = new XMLAttribute(tmpattrib.getname,
       
  4794                                           tmpattrib.nodeName,
       
  4795                                           tmpattrib.namespaceURI,
       
  4796                                           tmpattrib.nodeValue,
       
  4797                                           tmpattrib.nodeType);
       
  4798           xmlelement.attributes.push(xmlattribute);
       
  4799         }
       
  4800 
       
  4801         // bind all children
       
  4802         for (l = 0, m = elementpath.childNodes.length; l < m; l++) {
       
  4803           var node = elementpath.childNodes[l];
       
  4804           if (node.nodeType === 1 || node.nodeType === 3) { // ELEMENT_NODE or TEXT_NODE
       
  4805             child = xmlelement.parseChildrenRecursive(xmlelement, node);
       
  4806             if (child !== null) {
       
  4807               xmlelement.children.push(child);
       
  4808             }
       
  4809           }
       
  4810         }
       
  4811 
       
  4812         return xmlelement;
       
  4813       },
       
  4814       /**
       
  4815        * @member XMLElement
       
  4816        * The createElement() function Creates an empty element
       
  4817        *
       
  4818        * @param {String} fullName   the full name of the element
       
  4819        * @param {String} namespace  the namespace URI
       
  4820        * @param {String} systemID   the system ID of the XML data where the element starts
       
  4821        * @param {int} lineNr    the line in the XML data where the element starts
       
  4822        */
       
  4823       createElement: function () {
       
  4824         if (arguments.length === 2) {
       
  4825           return new XMLElement(arguments[0], arguments[1], null, null);
       
  4826         }
       
  4827         return new XMLElement(arguments[0], arguments[1], arguments[2], arguments[3]);
       
  4828       },
       
  4829       /**
       
  4830        * @member XMLElement
       
  4831        * The createPCDataElement() function creates an element to be used for #PCDATA content.
       
  4832        * Because Processing discards whitespace TEXT nodes, this method will not build an element
       
  4833        * if the passed content is empty after trimming for whitespace.
       
  4834        *
       
  4835        * @return {XMLElement} new "test" XMLElement, or null if content consists only of whitespace
       
  4836        */
       
  4837       createPCDataElement: function (content) {
       
  4838         if(content.replace(/^\s+$/g,"") === "") {
       
  4839           return null;
       
  4840         }
       
  4841         var pcdata = new XMLElement();
       
  4842         pcdata.content = content;
       
  4843         pcdata.type = "TEXT";
       
  4844         return pcdata;
       
  4845       },
       
  4846       /**
       
  4847        * @member XMLElement
       
  4848        * The hasAttribute() function returns whether an attribute exists
       
  4849        *
       
  4850        * @param {String} name      name of the attribute
       
  4851        * @param {String} namespace the namespace URI of the attribute
       
  4852        *
       
  4853        * @return {boolean} true if the attribute exists
       
  4854        */
       
  4855       hasAttribute: function () {
       
  4856         if (arguments.length === 1) {
       
  4857           return this.getAttribute(arguments[0]) !== null;
       
  4858         }
       
  4859         if (arguments.length === 2) {
       
  4860           return this.getAttribute(arguments[0],arguments[1]) !== null;
       
  4861         }
       
  4862       },
       
  4863       /**
       
  4864        * @member XMLElement
       
  4865        * The equals() function checks to see if the XMLElement being passed in equals another XMLElement
       
  4866        *
       
  4867        * @param {XMLElement} rawElement the element to compare to
       
  4868        *
       
  4869        * @return {boolean} true if the element equals another element
       
  4870        */
       
  4871       equals: function(other) {
       
  4872         if (!(other instanceof XMLElement)) {
       
  4873           return false;
       
  4874         }
       
  4875         var i, j;
       
  4876         if (this.name !== other.getLocalName()) { return false; }
       
  4877         if (this.attributes.length !== other.getAttributeCount()) { return false; }
       
  4878         // attributes may be ordered differently
       
  4879         if (this.attributes.length !== other.attributes.length) { return false; }
       
  4880         var attr_name, attr_ns, attr_value, attr_type, attr_other;
       
  4881         for (i = 0, j = this.attributes.length; i < j; i++) {
       
  4882           attr_name = this.attributes[i].getName();
       
  4883           attr_ns = this.attributes[i].getNamespace();
       
  4884           attr_other = other.findAttribute(attr_name, attr_ns);
       
  4885           if (attr_other === null) { return false; }
       
  4886           if (this.attributes[i].getValue() !== attr_other.getValue()) { return false; }
       
  4887           if (this.attributes[i].getType() !== attr_other.getType()) { return false; }
       
  4888         }
       
  4889         // children must be ordered identically
       
  4890         if (this.children.length !== other.getChildCount()) { return false; }
       
  4891         if (this.children.length>0) {
       
  4892           var child1, child2;
       
  4893           for (i = 0, j = this.children.length; i < j; i++) {
       
  4894             child1 = this.getChild(i);
       
  4895             child2 = other.getChild(i);
       
  4896             if (!child1.equals(child2)) { return false; }
       
  4897           }
       
  4898           return true;
       
  4899         }
       
  4900         return (this.content === other.content);
       
  4901       },
       
  4902       /**
       
  4903        * @member XMLElement
       
  4904        * The getContent() function returns the content of an element. If there is no such content, null is returned
       
  4905        *
       
  4906        * @return {String} the (possibly null) content
       
  4907        */
       
  4908       getContent: function(){
       
  4909         if (this.type === "TEXT") {
       
  4910           return this.content;
       
  4911         }
       
  4912         var children = this.children;
       
  4913         if (children.length === 1 && children[0].type === "TEXT") {
       
  4914           return children[0].content;
       
  4915         }
       
  4916         return null;
       
  4917       },
       
  4918       /**
       
  4919        * @member XMLElement
       
  4920        * The getAttribute() function returns the value of an attribute
       
  4921        *
       
  4922        * @param {String} name         the non-null full name of the attribute
       
  4923        * @param {String} namespace    the namespace URI, which may be null
       
  4924        * @param {String} defaultValue the default value of the attribute
       
  4925        *
       
  4926        * @return {String} the value, or defaultValue if the attribute does not exist
       
  4927        */
       
  4928       getAttribute: function (){
       
  4929         var attribute;
       
  4930         if( arguments.length === 2 ){
       
  4931           attribute = this.findAttribute(arguments[0]);
       
  4932           if (attribute) {
       
  4933             return attribute.getValue();
       
  4934           }
       
  4935           return arguments[1];
       
  4936         } else if (arguments.length === 1) {
       
  4937           attribute = this.findAttribute(arguments[0]);
       
  4938           if (attribute) {
       
  4939             return attribute.getValue();
       
  4940           }
       
  4941           return null;
       
  4942         } else if (arguments.length === 3) {
       
  4943           attribute = this.findAttribute(arguments[0],arguments[1]);
       
  4944           if (attribute) {
       
  4945             return attribute.getValue();
       
  4946           }
       
  4947           return arguments[2];
       
  4948         }
       
  4949       },
       
  4950       /**
       
  4951        * @member XMLElement
       
  4952        * The getStringAttribute() function returns the string attribute of the element
       
  4953        * If the <b>defaultValue</b> parameter is used and the attribute doesn't exist, the <b>defaultValue</b> value is returned.
       
  4954        * When calling the function without the <b>defaultValue</b> parameter, if the attribute doesn't exist, the value 0 is returned.
       
  4955        *
       
  4956        * @param name         the name of the attribute
       
  4957        * @param defaultValue value returned if the attribute is not found
       
  4958        *
       
  4959        * @return {String} the value, or defaultValue if the attribute does not exist
       
  4960        */
       
  4961       getStringAttribute: function() {
       
  4962         if (arguments.length === 1) {
       
  4963           return this.getAttribute(arguments[0]);
       
  4964         }
       
  4965         if (arguments.length === 2){
       
  4966           return this.getAttribute(arguments[0], arguments[1]);
       
  4967         }
       
  4968         return this.getAttribute(arguments[0], arguments[1],arguments[2]);
       
  4969       },
       
  4970       /**
       
  4971        * Processing 1.5 XML API wrapper for the generic String
       
  4972        * attribute getter. This may only take one argument.
       
  4973        */
       
  4974       getString: function(attributeName) {
       
  4975         return this.getStringAttribute(attributeName);
       
  4976       },
       
  4977       /**
       
  4978        * @member XMLElement
       
  4979        * The getFloatAttribute() function returns the float attribute of the element.
       
  4980        * If the <b>defaultValue</b> parameter is used and the attribute doesn't exist, the <b>defaultValue</b> value is returned.
       
  4981        * When calling the function without the <b>defaultValue</b> parameter, if the attribute doesn't exist, the value 0 is returned.
       
  4982        *
       
  4983        * @param name         the name of the attribute
       
  4984        * @param defaultValue value returned if the attribute is not found
       
  4985        *
       
  4986        * @return {float} the value, or defaultValue if the attribute does not exist
       
  4987        */
       
  4988       getFloatAttribute: function() {
       
  4989         if (arguments.length === 1 ) {
       
  4990           return parseFloat(this.getAttribute(arguments[0], 0));
       
  4991         }
       
  4992         if (arguments.length === 2 ){
       
  4993           return this.getAttribute(arguments[0], arguments[1]);
       
  4994         }
       
  4995         return this.getAttribute(arguments[0], arguments[1],arguments[2]);
       
  4996       },
       
  4997       /**
       
  4998        * Processing 1.5 XML API wrapper for the generic float
       
  4999        * attribute getter. This may only take one argument.
       
  5000        */
       
  5001       getFloat: function(attributeName) {
       
  5002         return this.getFloatAttribute(attributeName);
       
  5003       },
       
  5004       /**
       
  5005        * @member XMLElement
       
  5006        * The getIntAttribute() function returns the integer attribute of the element.
       
  5007        * If the <b>defaultValue</b> parameter is used and the attribute doesn't exist, the <b>defaultValue</b> value is returned.
       
  5008        * When calling the function without the <b>defaultValue</b> parameter, if the attribute doesn't exist, the value 0 is returned.
       
  5009        *
       
  5010        * @param name         the name of the attribute
       
  5011        * @param defaultValue value returned if the attribute is not found
       
  5012        *
       
  5013        * @return {int} the value, or defaultValue if the attribute does not exist
       
  5014        */
       
  5015       getIntAttribute: function () {
       
  5016         if (arguments.length === 1) {
       
  5017           return this.getAttribute( arguments[0], 0 );
       
  5018         }
       
  5019         if (arguments.length === 2) {
       
  5020           return this.getAttribute(arguments[0], arguments[1]);
       
  5021         }
       
  5022         return this.getAttribute(arguments[0], arguments[1],arguments[2]);
       
  5023       },
       
  5024       /**
       
  5025        * Processing 1.5 XML API wrapper for the generic int
       
  5026        * attribute getter. This may only take one argument.
       
  5027        */
       
  5028       getInt: function(attributeName) {
       
  5029         return this.getIntAttribute(attributeName);
       
  5030       },
       
  5031       /**
       
  5032        * @member XMLElement
       
  5033        * The hasChildren() function returns whether the element has children.
       
  5034        *
       
  5035        * @return {boolean} true if the element has children.
       
  5036        */
       
  5037       hasChildren: function () {
       
  5038         return this.children.length > 0 ;
       
  5039       },
       
  5040       /**
       
  5041        * @member XMLElement
       
  5042        * The addChild() function adds a child element
       
  5043        *
       
  5044        * @param {XMLElement} child the non-null child to add.
       
  5045        */
       
  5046       addChild: function (child) {
       
  5047         if (child !== null) {
       
  5048           child.parent = this;
       
  5049           this.children.push(child);
       
  5050         }
       
  5051       },
       
  5052       /**
       
  5053        * @member XMLElement
       
  5054        * The insertChild() function inserts a child element at the index provided
       
  5055        *
       
  5056        * @param {XMLElement} child  the non-null child to add.
       
  5057        * @param {int} index     where to put the child.
       
  5058        */
       
  5059       insertChild: function (child, index) {
       
  5060         if (child) {
       
  5061           if ((child.getLocalName() === null) && (! this.hasChildren())) {
       
  5062             var lastChild = this.children[this.children.length -1];
       
  5063             if (lastChild.getLocalName() === null) {
       
  5064                 lastChild.setContent(lastChild.getContent() + child.getContent());
       
  5065                 return;
       
  5066             }
       
  5067           }
       
  5068           child.parent = this;
       
  5069           this.children.splice(index,0,child);
       
  5070         }
       
  5071       },
       
  5072       /**
       
  5073        * @member XMLElement
       
  5074        * The getChild() returns the child XMLElement as specified by the <b>index</b> parameter.
       
  5075        * The value of the <b>index</b> parameter must be less than the total number of children to avoid going out of the array storing the child elements.
       
  5076        * When the <b>path</b> parameter is specified, then it will return all children that match that path. The path is a series of elements and sub-elements, separated by slashes.
       
  5077        *
       
  5078        * @param {int} index     where to put the child.
       
  5079        * @param {String} path       path to a particular element
       
  5080        *
       
  5081        * @return {XMLElement} the element
       
  5082        */
       
  5083       getChild: function (){
       
  5084         if (typeof arguments[0]  === "number") {
       
  5085           return this.children[arguments[0]];
       
  5086         }
       
  5087         if (arguments[0].indexOf('/') !== -1) { // path was given
       
  5088           this.getChildRecursive(arguments[0].split("/"), 0);
       
  5089           return null;
       
  5090         }
       
  5091         var kid, kidName;
       
  5092         for (var i = 0, j = this.getChildCount(); i < j; i++) {
       
  5093           kid = this.getChild(i);
       
  5094           kidName = kid.getName();
       
  5095           if (kidName !== null && kidName === arguments[0]) {
       
  5096               return kid;
       
  5097           }
       
  5098         }
       
  5099         return null;
       
  5100       },
       
  5101       /**
       
  5102        * @member XMLElement
       
  5103        * The getChildren() returns all of the children as an XMLElement array.
       
  5104        * When the <b>path</b> parameter is specified, then it will return all children that match that path.
       
  5105        * The path is a series of elements and sub-elements, separated by slashes.
       
  5106        *
       
  5107        * @param {String} path       element name or path/to/element
       
  5108        *
       
  5109        * @return {XMLElement} array of child elements that match
       
  5110        *
       
  5111        * @see XMLElement#getChildCount()
       
  5112        * @see XMLElement#getChild()
       
  5113        */
       
  5114       getChildren: function(){
       
  5115         if (arguments.length === 1) {
       
  5116           if (typeof arguments[0]  === "number") {
       
  5117             return this.getChild( arguments[0]);
       
  5118           }
       
  5119           if (arguments[0].indexOf('/') !== -1) { // path was given
       
  5120             return this.getChildrenRecursive( arguments[0].split("/"), 0);
       
  5121           }
       
  5122           var matches = [];
       
  5123           var kid, kidName;
       
  5124           for (var i = 0, j = this.getChildCount(); i < j; i++) {
       
  5125             kid = this.getChild(i);
       
  5126             kidName = kid.getName();
       
  5127             if (kidName !== null && kidName === arguments[0]) {
       
  5128               matches.push(kid);
       
  5129             }
       
  5130           }
       
  5131           return matches;
       
  5132         }
       
  5133         return this.children;
       
  5134       },
       
  5135       /**
       
  5136        * @member XMLElement
       
  5137        * The getChildCount() returns the number of children for the element.
       
  5138        *
       
  5139        * @return {int} the count
       
  5140        *
       
  5141        * @see XMLElement#getChild()
       
  5142        * @see XMLElement#getChildren()
       
  5143        */
       
  5144       getChildCount: function(){
       
  5145         return this.children.length;
       
  5146       },
       
  5147       /**
       
  5148        * @member XMLElement
       
  5149        * Internal helper function for getChild().
       
  5150        *
       
  5151        * @param {String[]} items   result of splitting the query on slashes
       
  5152        * @param {int} offset   where in the items[] array we're currently looking
       
  5153        *
       
  5154        * @return {XMLElement} matching element or null if no match
       
  5155        */
       
  5156       getChildRecursive: function (items, offset) {
       
  5157         var kid, kidName;
       
  5158         for(var i = 0, j = this.getChildCount(); i < j; i++) {
       
  5159             kid = this.getChild(i);
       
  5160             kidName = kid.getName();
       
  5161             if (kidName !== null && kidName === items[offset]) {
       
  5162               if (offset === items.length-1) {
       
  5163                 return kid;
       
  5164               }
       
  5165               offset += 1;
       
  5166               return kid.getChildRecursive(items, offset);
       
  5167             }
       
  5168         }
       
  5169         return null;
       
  5170       },
       
  5171       /**
       
  5172        * @member XMLElement
       
  5173        * Internal helper function for getChildren().
       
  5174        *
       
  5175        * @param {String[]} items   result of splitting the query on slashes
       
  5176        * @param {int} offset   where in the items[] array we're currently looking
       
  5177        *
       
  5178        * @return {XMLElement[]} matching elements or empty array if no match
       
  5179        */
       
  5180       getChildrenRecursive: function (items, offset) {
       
  5181         if (offset === items.length-1) {
       
  5182           return this.getChildren(items[offset]);
       
  5183         }
       
  5184         var matches = this.getChildren(items[offset]);
       
  5185         var kidMatches = [];
       
  5186         for (var i = 0; i < matches.length; i++) {
       
  5187           kidMatches = kidMatches.concat(matches[i].getChildrenRecursive(items, offset+1));
       
  5188         }
       
  5189         return kidMatches;
       
  5190       },
       
  5191       /**
       
  5192        * @member XMLElement
       
  5193        * The isLeaf() function returns whether the element is a leaf element.
       
  5194        *
       
  5195        * @return {boolean} true if the element has no children.
       
  5196        */
       
  5197       isLeaf: function(){
       
  5198         return !this.hasChildren();
       
  5199       },
       
  5200       /**
       
  5201        * @member XMLElement
       
  5202        * The listChildren() function put the names of all children into an array. Same as looping through
       
  5203        * each child and calling getName() on each XMLElement.
       
  5204        *
       
  5205        * @return {String[]} a list of element names.
       
  5206        */
       
  5207       listChildren: function() {
       
  5208         var arr = [];
       
  5209         for (var i = 0, j = this.children.length; i < j; i++) {
       
  5210           arr.push( this.getChild(i).getName());
       
  5211         }
       
  5212         return arr;
       
  5213       },
       
  5214       /**
       
  5215        * @member XMLElement
       
  5216        * The removeAttribute() function removes an attribute
       
  5217        *
       
  5218        * @param {String} name        the non-null name of the attribute.
       
  5219        * @param {String} namespace   the namespace URI of the attribute, which may be null.
       
  5220        */
       
  5221       removeAttribute: function (name , namespace) {
       
  5222         this.namespace = namespace || "";
       
  5223         for (var i = 0, j = this.attributes.length; i < j; i++) {
       
  5224           if (this.attributes[i].getName() === name && this.attributes[i].getNamespace() === this.namespace) {
       
  5225             this.attributes.splice(i, 1);
       
  5226             break;
       
  5227           }
       
  5228         }
       
  5229       },
       
  5230       /**
       
  5231        * @member XMLElement
       
  5232        * The removeChild() removes a child element.
       
  5233        *
       
  5234        * @param {XMLElement} child      the the non-null child to be renoved
       
  5235        */
       
  5236       removeChild: function(child) {
       
  5237         if (child) {
       
  5238           for (var i = 0, j = this.children.length; i < j; i++) {
       
  5239             if (this.children[i].equals(child)) {
       
  5240               this.children.splice(i, 1);
       
  5241               break;
       
  5242             }
       
  5243           }
       
  5244         }
       
  5245       },
       
  5246       /**
       
  5247        * @member XMLElement
       
  5248        * The removeChildAtIndex() removes the child located at a certain index
       
  5249        *
       
  5250        * @param {int} index      the index of the child, where the first child has index 0
       
  5251        */
       
  5252       removeChildAtIndex: function(index) {
       
  5253         if (this.children.length > index) { //make sure its not outofbounds
       
  5254           this.children.splice(index, 1);
       
  5255         }
       
  5256       },
       
  5257       /**
       
  5258        * @member XMLElement
       
  5259        * The findAttribute() function searches an attribute
       
  5260        *
       
  5261        * @param {String} name        fullName the non-null full name of the attribute
       
  5262        * @param {String} namespace   the name space, which may be null
       
  5263        *
       
  5264        * @return {XMLAttribute} the attribute, or null if the attribute does not exist.
       
  5265        */
       
  5266       findAttribute: function (name, namespace) {
       
  5267         this.namespace = namespace || "";
       
  5268         for (var i = 0, j = this.attributes.length; i < j; i++) {
       
  5269           if (this.attributes[i].getName() === name && this.attributes[i].getNamespace() === this.namespace) {
       
  5270              return this.attributes[i];
       
  5271           }
       
  5272         }
       
  5273         return null;
       
  5274       },
       
  5275       /**
       
  5276        * @member XMLElement
       
  5277        * The setAttribute() function sets an attribute.
       
  5278        *
       
  5279        * @param {String} name        the non-null full name of the attribute
       
  5280        * @param {String} namespace   the non-null value of the attribute
       
  5281        */
       
  5282       setAttribute: function() {
       
  5283         var attr;
       
  5284         if (arguments.length === 3) {
       
  5285           var index = arguments[0].indexOf(':');
       
  5286           var name  = arguments[0].substring(index + 1);
       
  5287           attr      = this.findAttribute(name, arguments[1]);
       
  5288           if (attr) {
       
  5289             attr.setValue(arguments[2]);
       
  5290           } else {
       
  5291             attr = new XMLAttribute(arguments[0], name, arguments[1], arguments[2], "CDATA");
       
  5292             this.attributes.push(attr);
       
  5293           }
       
  5294         } else {
       
  5295           attr = this.findAttribute(arguments[0]);
       
  5296           if (attr) {
       
  5297             attr.setValue(arguments[1]);
       
  5298           } else {
       
  5299             attr = new XMLAttribute(arguments[0], arguments[0], null, arguments[1], "CDATA");
       
  5300             this.attributes.push(attr);
       
  5301           }
       
  5302         }
       
  5303       },
       
  5304       /**
       
  5305        * Processing 1.5 XML API wrapper for the generic String
       
  5306        * attribute setter. This must take two arguments.
       
  5307        */
       
  5308       setString: function(attribute, value) {
       
  5309         this.setAttribute(attribute, value);
       
  5310       },
       
  5311       /**
       
  5312        * Processing 1.5 XML API wrapper for the generic int
       
  5313        * attribute setter. This must take two arguments.
       
  5314        */
       
  5315       setInt: function(attribute, value) {
       
  5316         this.setAttribute(attribute, value);
       
  5317       },
       
  5318       /**
       
  5319        * Processing 1.5 XML API wrapper for the generic float
       
  5320        * attribute setter. This must take two arguments.
       
  5321        */
       
  5322       setFloat: function(attribute, value) {
       
  5323         this.setAttribute(attribute, value);
       
  5324       },
       
  5325       /**
       
  5326        * @member XMLElement
       
  5327        * The setContent() function sets the #PCDATA content. It is an error to call this method with a
       
  5328        * non-null value if there are child objects.
       
  5329        *
       
  5330        * @param {String} content     the (possibly null) content
       
  5331        */
       
  5332       setContent: function(content) {
       
  5333         if (this.children.length>0) {
       
  5334           Processing.debug("Tried to set content for XMLElement with children"); }
       
  5335         this.content = content;
       
  5336       },
       
  5337       /**
       
  5338        * @member XMLElement
       
  5339        * The setName() function sets the full name. This method also sets the short name and clears the
       
  5340        * namespace URI.
       
  5341        *
       
  5342        * @param {String} name        the non-null name
       
  5343        * @param {String} namespace   the namespace URI, which may be null.
       
  5344        */
       
  5345       setName: function() {
       
  5346         if (arguments.length === 1) {
       
  5347           this.name      = arguments[0];
       
  5348           this.fullName  = arguments[0];
       
  5349           this.namespace = null;
       
  5350         } else {
       
  5351           var index = arguments[0].indexOf(':');
       
  5352           if ((arguments[1] === null) || (index < 0)) {
       
  5353               this.name = arguments[0];
       
  5354           } else {
       
  5355               this.name = arguments[0].substring(index + 1);
       
  5356           }
       
  5357           this.fullName  = arguments[0];
       
  5358           this.namespace = arguments[1];
       
  5359         }
       
  5360       },
       
  5361       /**
       
  5362        * @member XMLElement
       
  5363        * The getName() function returns the full name (i.e. the name including an eventual namespace
       
  5364        * prefix) of the element.
       
  5365        *
       
  5366        * @return {String} the name, or null if the element only contains #PCDATA.
       
  5367        */
       
  5368       getName: function() {
       
  5369         return this.fullName;
       
  5370       },
       
  5371       /**
       
  5372        * @member XMLElement
       
  5373        * The getLocalName() function returns the local name (i.e. the name excluding an eventual namespace
       
  5374        * prefix) of the element.
       
  5375        *
       
  5376        * @return {String} the name, or null if the element only contains #PCDATA.
       
  5377        */
       
  5378       getLocalName: function() {
       
  5379         return this.name;
       
  5380       },
       
  5381       /**
       
  5382        * @member XMLElement
       
  5383        * The getAttributeCount() function returns the number of attributes for the node
       
  5384        * that this XMLElement represents.
       
  5385        *
       
  5386        * @return {int} the number of attributes in this XMLElement
       
  5387        */
       
  5388       getAttributeCount: function() {
       
  5389         return this.attributes.length;
       
  5390       },
       
  5391       /**
       
  5392        * @member XMLElement
       
  5393        * The toString() function returns the XML definition of an XMLElement.
       
  5394        *
       
  5395        * @return {String} the XML definition of this XMLElement
       
  5396        */
       
  5397       toString: function() {
       
  5398         // shortcut for text nodes
       
  5399         if(this.type==="TEXT") { return this.content; }
       
  5400 
       
  5401         // real XMLElements
       
  5402         var tagstring = (this.namespace !== "" && this.namespace !== this.name ? this.namespace + ":" : "") + this.name;
       
  5403         var xmlstring =  "<" + tagstring;
       
  5404         var a,c;
       
  5405 
       
  5406         // serialize the attributes to XML string
       
  5407         for (a = 0; a<this.attributes.length; a++) {
       
  5408           var attr = this.attributes[a];
       
  5409           xmlstring += " "  + attr.getName() + "=" + '"' + attr.getValue() + '"';
       
  5410         }
       
  5411 
       
  5412         // serialize all children to XML string
       
  5413         if (this.children.length === 0) {
       
  5414           if (this.content==="") {
       
  5415             xmlstring += "/>";
       
  5416           } else {
       
  5417             xmlstring += ">" + this.content + "</"+tagstring+">";
       
  5418           }
       
  5419         } else {
       
  5420           xmlstring += ">";
       
  5421           for (c = 0; c<this.children.length; c++) {
       
  5422             xmlstring += this.children[c].toString();
       
  5423           }
       
  5424           xmlstring += "</" + tagstring + ">";
       
  5425         }
       
  5426         return xmlstring;
       
  5427        }
       
  5428     };
       
  5429 
       
  5430     /**
       
  5431      * static Processing 1.5 XML API wrapper for the
       
  5432      * parse method. This may only take one argument.
       
  5433      */
       
  5434     XMLElement.parse = function(xmlstring) {
       
  5435       var element = new XMLElement();
       
  5436       element.parse(xmlstring);
       
  5437       return element;
       
  5438     };
       
  5439 
       
  5440     ////////////////////////////////////////////////////////////////////////////
       
  5441     // 2D Matrix
       
  5442     ////////////////////////////////////////////////////////////////////////////
       
  5443     /**
       
  5444      * Helper function for printMatrix(). Finds the largest scalar
       
  5445      * in the matrix, then number of digits left of the decimal.
       
  5446      * Call from PMatrix2D and PMatrix3D's print() function.
       
  5447      */
       
  5448     var printMatrixHelper = function(elements) {
       
  5449       var big = 0;
       
  5450       for (var i = 0; i < elements.length; i++) {
       
  5451         if (i !== 0) {
       
  5452           big = Math.max(big, Math.abs(elements[i]));
       
  5453         } else {
       
  5454           big = Math.abs(elements[i]);
       
  5455         }
       
  5456       }
       
  5457 
       
  5458       var digits = (big + "").indexOf(".");
       
  5459       if (digits === 0) {
       
  5460         digits = 1;
       
  5461       } else if (digits === -1) {
       
  5462         digits = (big + "").length;
       
  5463       }
       
  5464 
       
  5465       return digits;
       
  5466     };
       
  5467     /**
       
  5468      * PMatrix2D is a 3x2 affine matrix implementation. The constructor accepts another PMatrix2D or a list of six float elements.
       
  5469      * If no parameters are provided the matrix is set to the identity matrix.
       
  5470      *
       
  5471      * @param {PMatrix2D} matrix  the initial matrix to set to
       
  5472      * @param {float} m00         the first element of the matrix
       
  5473      * @param {float} m01         the second element of the matrix
       
  5474      * @param {float} m02         the third element of the matrix
       
  5475      * @param {float} m10         the fourth element of the matrix
       
  5476      * @param {float} m11         the fifth element of the matrix
       
  5477      * @param {float} m12         the sixth element of the matrix
       
  5478      */
       
  5479     var PMatrix2D = p.PMatrix2D = function() {
       
  5480       if (arguments.length === 0) {
       
  5481         this.reset();
       
  5482       } else if (arguments.length === 1 && arguments[0] instanceof PMatrix2D) {
       
  5483         this.set(arguments[0].array());
       
  5484       } else if (arguments.length === 6) {
       
  5485         this.set(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5]);
       
  5486       }
       
  5487     };
       
  5488     /**
       
  5489      * PMatrix2D methods
       
  5490      */
       
  5491     PMatrix2D.prototype = {
       
  5492       /**
       
  5493        * @member PMatrix2D
       
  5494        * The set() function sets the matrix elements. The function accepts either another PMatrix2D, an array of elements, or a list of six floats.
       
  5495        *
       
  5496        * @param {PMatrix2D} matrix    the matrix to set this matrix to
       
  5497        * @param {float[]} elements    an array of elements to set this matrix to
       
  5498        * @param {float} m00           the first element of the matrix
       
  5499        * @param {float} m01           the third element of the matrix
       
  5500        * @param {float} m10           the fourth element of the matrix
       
  5501        * @param {float} m11           the fith element of the matrix
       
  5502        * @param {float} m12           the sixth element of the matrix
       
  5503        */
       
  5504       set: function() {
       
  5505         if (arguments.length === 6) {
       
  5506           var a = arguments;
       
  5507           this.set([a[0], a[1], a[2],
       
  5508                     a[3], a[4], a[5]]);
       
  5509         } else if (arguments.length === 1 && arguments[0] instanceof PMatrix2D) {
       
  5510           this.elements = arguments[0].array();
       
  5511         } else if (arguments.length === 1 && arguments[0] instanceof Array) {
       
  5512           this.elements = arguments[0].slice();
       
  5513         }
       
  5514       },
       
  5515       /**
       
  5516        * @member PMatrix2D
       
  5517        * The get() function returns a copy of this PMatrix2D.
       
  5518        *
       
  5519        * @return {PMatrix2D} a copy of this PMatrix2D
       
  5520        */
       
  5521       get: function() {
       
  5522         var outgoing = new PMatrix2D();
       
  5523         outgoing.set(this.elements);
       
  5524         return outgoing;
       
  5525       },
       
  5526       /**
       
  5527        * @member PMatrix2D
       
  5528        * The reset() function sets this PMatrix2D to the identity matrix.
       
  5529        */
       
  5530       reset: function() {
       
  5531         this.set([1, 0, 0, 0, 1, 0]);
       
  5532       },
       
  5533       /**
       
  5534        * @member PMatrix2D
       
  5535        * The array() function returns a copy of the element values.
       
  5536        * @addon
       
  5537        *
       
  5538        * @return {float[]} returns a copy of the element values
       
  5539        */
       
  5540       array: function array() {
       
  5541         return this.elements.slice();
       
  5542       },
       
  5543       /**
       
  5544        * @member PMatrix2D
       
  5545        * The translate() function translates this matrix by moving the current coordinates to the location specified by tx and ty.
       
  5546        *
       
  5547        * @param {float} tx  the x-axis coordinate to move to
       
  5548        * @param {float} ty  the y-axis coordinate to move to
       
  5549        */
       
  5550       translate: function(tx, ty) {
       
  5551         this.elements[2] = tx * this.elements[0] + ty * this.elements[1] + this.elements[2];
       
  5552         this.elements[5] = tx * this.elements[3] + ty * this.elements[4] + this.elements[5];
       
  5553       },
       
  5554       /**
       
  5555        * @member PMatrix2D
       
  5556        * The invTranslate() function translates this matrix by moving the current coordinates to the negative location specified by tx and ty.
       
  5557        *
       
  5558        * @param {float} tx  the x-axis coordinate to move to
       
  5559        * @param {float} ty  the y-axis coordinate to move to
       
  5560        */
       
  5561       invTranslate: function(tx, ty) {
       
  5562         this.translate(-tx, -ty);
       
  5563       },
       
  5564        /**
       
  5565        * @member PMatrix2D
       
  5566        * The transpose() function is not used in processingjs.
       
  5567        */
       
  5568       transpose: function() {
       
  5569         // Does nothing in Processing.
       
  5570       },
       
  5571       /**
       
  5572        * @member PMatrix2D
       
  5573        * The mult() function multiplied this matrix.
       
  5574        * If two array elements are passed in the function will multiply a two element vector against this matrix.
       
  5575        * If target is null or not length four, a new float array will be returned.
       
  5576        * The values for vec and target can be the same (though that's less efficient).
       
  5577        * If two PVectors are passed in the function multiply the x and y coordinates of a PVector against this matrix.
       
  5578        *
       
  5579        * @param {PVector} source, target  the PVectors used to multiply this matrix
       
  5580        * @param {float[]} source, target  the arrays used to multiply this matrix
       
  5581        *
       
  5582        * @return {PVector|float[]} returns a PVector or an array representing the new matrix
       
  5583        */
       
  5584       mult: function(source, target) {
       
  5585         var x, y;
       
  5586         if (source instanceof PVector) {
       
  5587           x = source.x;
       
  5588           y = source.y;
       
  5589           if (!target) {
       
  5590             target = new PVector();
       
  5591           }
       
  5592         } else if (source instanceof Array) {
       
  5593           x = source[0];
       
  5594           y = source[1];
       
  5595           if (!target) {
       
  5596             target = [];
       
  5597           }
       
  5598         }
       
  5599         if (target instanceof Array) {
       
  5600           target[0] = this.elements[0] * x + this.elements[1] * y + this.elements[2];
       
  5601           target[1] = this.elements[3] * x + this.elements[4] * y + this.elements[5];
       
  5602         } else if (target instanceof PVector) {
       
  5603           target.x = this.elements[0] * x + this.elements[1] * y + this.elements[2];
       
  5604           target.y = this.elements[3] * x + this.elements[4] * y + this.elements[5];
       
  5605           target.z = 0;
       
  5606         }
       
  5607         return target;
       
  5608       },
       
  5609       /**
       
  5610        * @member PMatrix2D
       
  5611        * The multX() function calculates the x component of a vector from a transformation.
       
  5612        *
       
  5613        * @param {float} x the x component of the vector being transformed
       
  5614        * @param {float} y the y component of the vector being transformed
       
  5615        *
       
  5616        * @return {float} returnes the result of the calculation
       
  5617        */
       
  5618       multX: function(x, y) {
       
  5619         return (x * this.elements[0] + y * this.elements[1] + this.elements[2]);
       
  5620       },
       
  5621       /**
       
  5622        * @member PMatrix2D
       
  5623        * The multY() function calculates the y component of a vector from a transformation.
       
  5624        *
       
  5625        * @param {float} x the x component of the vector being transformed
       
  5626        * @param {float} y the y component of the vector being transformed
       
  5627        *
       
  5628        * @return {float} returnes the result of the calculation
       
  5629        */
       
  5630       multY: function(x, y) {
       
  5631         return (x * this.elements[3] + y * this.elements[4] + this.elements[5]);
       
  5632       },
       
  5633       /**
       
  5634        * @member PMatrix2D
       
  5635        * The skewX() function skews the matrix along the x-axis the amount specified by the angle parameter.
       
  5636        * Angles should be specified in radians (values from 0 to PI*2) or converted to radians with the <b>radians()</b> function.
       
  5637        *
       
  5638        * @param {float} angle  angle of skew specified in radians
       
  5639        */
       
  5640       skewX: function(angle) {
       
  5641         this.apply(1, 0, 1, angle, 0, 0);
       
  5642       },
       
  5643       /**
       
  5644        * @member PMatrix2D
       
  5645        * The skewY() function skews the matrix along the y-axis the amount specified by the angle parameter.
       
  5646        * Angles should be specified in radians (values from 0 to PI*2) or converted to radians with the <b>radians()</b> function.
       
  5647        *
       
  5648        * @param {float} angle  angle of skew specified in radians
       
  5649        */
       
  5650       skewY: function(angle) {
       
  5651         this.apply(1, 0, 1,  0, angle, 0);
       
  5652       },
       
  5653       /**
       
  5654        * @member PMatrix2D
       
  5655        * The determinant() function calvculates the determinant of this matrix.
       
  5656        *
       
  5657        * @return {float} the determinant of the matrix
       
  5658        */
       
  5659       determinant: function() {
       
  5660         return (this.elements[0] * this.elements[4] - this.elements[1] * this.elements[3]);
       
  5661       },
       
  5662       /**
       
  5663        * @member PMatrix2D
       
  5664        * The invert() function inverts this matrix
       
  5665        *
       
  5666        * @return {boolean} true if successful
       
  5667        */
       
  5668       invert: function() {
       
  5669         var d = this.determinant();
       
  5670         if (Math.abs( d ) > PConstants.MIN_INT) {
       
  5671           var old00 = this.elements[0];
       
  5672           var old01 = this.elements[1];
       
  5673           var old02 = this.elements[2];
       
  5674           var old10 = this.elements[3];
       
  5675           var old11 = this.elements[4];
       
  5676           var old12 = this.elements[5];
       
  5677           this.elements[0] =  old11 / d;
       
  5678           this.elements[3] = -old10 / d;
       
  5679           this.elements[1] = -old01 / d;
       
  5680           this.elements[4] =  old00 / d;
       
  5681           this.elements[2] = (old01 * old12 - old11 * old02) / d;
       
  5682           this.elements[5] = (old10 * old02 - old00 * old12) / d;
       
  5683           return true;
       
  5684         }
       
  5685         return false;
       
  5686       },
       
  5687       /**
       
  5688        * @member PMatrix2D
       
  5689        * The scale() function increases or decreases the size of a shape by expanding and contracting vertices. When only one parameter is specified scale will occur in all dimensions.
       
  5690        * This is equivalent to a two parameter call.
       
  5691        *
       
  5692        * @param {float} sx  the amount to scale on the x-axis
       
  5693        * @param {float} sy  the amount to scale on the y-axis
       
  5694        */
       
  5695       scale: function(sx, sy) {
       
  5696         if (sx && !sy) {
       
  5697           sy = sx;
       
  5698         }
       
  5699         if (sx && sy) {
       
  5700           this.elements[0] *= sx;
       
  5701           this.elements[1] *= sy;
       
  5702           this.elements[3] *= sx;
       
  5703           this.elements[4] *= sy;
       
  5704         }
       
  5705       },
       
  5706        /**
       
  5707         * @member PMatrix2D
       
  5708         * The invScale() function decreases or increases the size of a shape by contracting and expanding vertices. When only one parameter is specified scale will occur in all dimensions.
       
  5709         * This is equivalent to a two parameter call.
       
  5710         *
       
  5711         * @param {float} sx  the amount to scale on the x-axis
       
  5712         * @param {float} sy  the amount to scale on the y-axis
       
  5713         */
       
  5714       invScale: function(sx, sy) {
       
  5715         if (sx && !sy) {
       
  5716           sy = sx;
       
  5717         }
       
  5718         this.scale(1 / sx, 1 / sy);
       
  5719       },
       
  5720       /**
       
  5721        * @member PMatrix2D
       
  5722        * The apply() function multiplies the current matrix by the one specified through the parameters. Note that either a PMatrix2D or a list of floats can be passed in.
       
  5723        *
       
  5724        * @param {PMatrix2D} matrix    the matrix to apply this matrix to
       
  5725        * @param {float} m00           the first element of the matrix
       
  5726        * @param {float} m01           the third element of the matrix
       
  5727        * @param {float} m10           the fourth element of the matrix
       
  5728        * @param {float} m11           the fith element of the matrix
       
  5729        * @param {float} m12           the sixth element of the matrix
       
  5730        */
       
  5731       apply: function() {
       
  5732         var source;
       
  5733         if (arguments.length === 1 && arguments[0] instanceof PMatrix2D) {
       
  5734           source = arguments[0].array();
       
  5735         } else if (arguments.length === 6) {
       
  5736           source = Array.prototype.slice.call(arguments);
       
  5737         } else if (arguments.length === 1 && arguments[0] instanceof Array) {
       
  5738           source = arguments[0];
       
  5739         }
       
  5740 
       
  5741         var result = [0, 0, this.elements[2],
       
  5742                       0, 0, this.elements[5]];
       
  5743         var e = 0;
       
  5744         for (var row = 0; row < 2; row++) {
       
  5745           for (var col = 0; col < 3; col++, e++) {
       
  5746             result[e] += this.elements[row * 3 + 0] * source[col + 0] +
       
  5747                          this.elements[row * 3 + 1] * source[col + 3];
       
  5748           }
       
  5749         }
       
  5750         this.elements = result.slice();
       
  5751       },
       
  5752       /**
       
  5753        * @member PMatrix2D
       
  5754        * The preApply() function applies another matrix to the left of this one. Note that either a PMatrix2D or elements of a matrix can be passed in.
       
  5755        *
       
  5756        * @param {PMatrix2D} matrix    the matrix to apply this matrix to
       
  5757        * @param {float} m00           the first element of the matrix
       
  5758        * @param {float} m01           the third element of the matrix
       
  5759        * @param {float} m10           the fourth element of the matrix
       
  5760        * @param {float} m11           the fith element of the matrix
       
  5761        * @param {float} m12           the sixth element of the matrix
       
  5762        */
       
  5763       preApply: function() {
       
  5764         var source;
       
  5765         if (arguments.length === 1 && arguments[0] instanceof PMatrix2D) {
       
  5766           source = arguments[0].array();
       
  5767         } else if (arguments.length === 6) {
       
  5768           source = Array.prototype.slice.call(arguments);
       
  5769         } else if (arguments.length === 1 && arguments[0] instanceof Array) {
       
  5770           source = arguments[0];
       
  5771         }
       
  5772         var result = [0, 0, source[2],
       
  5773                       0, 0, source[5]];
       
  5774         result[2] = source[2] + this.elements[2] * source[0] + this.elements[5] * source[1];
       
  5775         result[5] = source[5] + this.elements[2] * source[3] + this.elements[5] * source[4];
       
  5776         result[0] = this.elements[0] * source[0] + this.elements[3] * source[1];
       
  5777         result[3] = this.elements[0] * source[3] + this.elements[3] * source[4];
       
  5778         result[1] = this.elements[1] * source[0] + this.elements[4] * source[1];
       
  5779         result[4] = this.elements[1] * source[3] + this.elements[4] * source[4];
       
  5780         this.elements = result.slice();
       
  5781       },
       
  5782       /**
       
  5783        * @member PMatrix2D
       
  5784        * The rotate() function rotates the matrix.
       
  5785        *
       
  5786        * @param {float} angle         the angle of rotation in radiants
       
  5787        */
       
  5788       rotate: function(angle) {
       
  5789         var c = Math.cos(angle);
       
  5790         var s = Math.sin(angle);
       
  5791         var temp1 = this.elements[0];
       
  5792         var temp2 = this.elements[1];
       
  5793         this.elements[0] =  c * temp1 + s * temp2;
       
  5794         this.elements[1] = -s * temp1 + c * temp2;
       
  5795         temp1 = this.elements[3];
       
  5796         temp2 = this.elements[4];
       
  5797         this.elements[3] =  c * temp1 + s * temp2;
       
  5798         this.elements[4] = -s * temp1 + c * temp2;
       
  5799       },
       
  5800       /**
       
  5801        * @member PMatrix2D
       
  5802        * The rotateZ() function rotates the matrix.
       
  5803        *
       
  5804        * @param {float} angle         the angle of rotation in radiants
       
  5805        */
       
  5806       rotateZ: function(angle) {
       
  5807         this.rotate(angle);
       
  5808       },
       
  5809       /**
       
  5810        * @member PMatrix2D
       
  5811        * The invRotateZ() function rotates the matrix in opposite direction.
       
  5812        *
       
  5813        * @param {float} angle         the angle of rotation in radiants
       
  5814        */
       
  5815       invRotateZ: function(angle) {
       
  5816         this.rotateZ(angle - Math.PI);
       
  5817       },
       
  5818       /**
       
  5819        * @member PMatrix2D
       
  5820        * The print() function prints out the elements of this matrix
       
  5821        */
       
  5822       print: function() {
       
  5823         var digits = printMatrixHelper(this.elements);
       
  5824         var output = "" + p.nfs(this.elements[0], digits, 4) + " " +
       
  5825                      p.nfs(this.elements[1], digits, 4) + " " +
       
  5826                      p.nfs(this.elements[2], digits, 4) + "\n" +
       
  5827                      p.nfs(this.elements[3], digits, 4) + " " +
       
  5828                      p.nfs(this.elements[4], digits, 4) + " " +
       
  5829                      p.nfs(this.elements[5], digits, 4) + "\n\n";
       
  5830         p.println(output);
       
  5831       }
       
  5832     };
       
  5833 
       
  5834     /**
       
  5835      * PMatrix3D is a 4x4  matrix implementation. The constructor accepts another PMatrix3D or a list of six or sixteen float elements.
       
  5836      * If no parameters are provided the matrix is set to the identity matrix.
       
  5837      */
       
  5838     var PMatrix3D = p.PMatrix3D = function() {
       
  5839       // When a matrix is created, it is set to an identity matrix
       
  5840       this.reset();
       
  5841     };
       
  5842     /**
       
  5843      * PMatrix3D methods
       
  5844      */
       
  5845     PMatrix3D.prototype = {
       
  5846       /**
       
  5847        * @member PMatrix2D
       
  5848        * The set() function sets the matrix elements. The function accepts either another PMatrix3D, an array of elements, or a list of six or sixteen floats.
       
  5849        *
       
  5850        * @param {PMatrix3D} matrix    the initial matrix to set to
       
  5851        * @param {float[]} elements    an array of elements to set this matrix to
       
  5852        * @param {float} m00           the first element of the matrix
       
  5853        * @param {float} m01           the second element of the matrix
       
  5854        * @param {float} m02           the third element of the matrix
       
  5855        * @param {float} m03           the fourth element of the matrix
       
  5856        * @param {float} m10           the fifth element of the matrix
       
  5857        * @param {float} m11           the sixth element of the matrix
       
  5858        * @param {float} m12           the seventh element of the matrix
       
  5859        * @param {float} m13           the eight element of the matrix
       
  5860        * @param {float} m20           the nineth element of the matrix
       
  5861        * @param {float} m21           the tenth element of the matrix
       
  5862        * @param {float} m22           the eleventh element of the matrix
       
  5863        * @param {float} m23           the twelveth element of the matrix
       
  5864        * @param {float} m30           the thirteenth element of the matrix
       
  5865        * @param {float} m31           the fourtheenth element of the matrix
       
  5866        * @param {float} m32           the fivetheenth element of the matrix
       
  5867        * @param {float} m33           the sixteenth element of the matrix
       
  5868        */
       
  5869       set: function() {
       
  5870         if (arguments.length === 16) {
       
  5871           this.elements = Array.prototype.slice.call(arguments);
       
  5872         } else if (arguments.length === 1 && arguments[0] instanceof PMatrix3D) {
       
  5873           this.elements = arguments[0].array();
       
  5874         } else if (arguments.length === 1 && arguments[0] instanceof Array) {
       
  5875           this.elements = arguments[0].slice();
       
  5876         }
       
  5877       },
       
  5878       /**
       
  5879        * @member PMatrix3D
       
  5880        * The get() function returns a copy of this PMatrix3D.
       
  5881        *
       
  5882        * @return {PMatrix3D} a copy of this PMatrix3D
       
  5883        */
       
  5884       get: function() {
       
  5885         var outgoing = new PMatrix3D();
       
  5886         outgoing.set(this.elements);
       
  5887         return outgoing;
       
  5888       },
       
  5889       /**
       
  5890        * @member PMatrix3D
       
  5891        * The reset() function sets this PMatrix3D to the identity matrix.
       
  5892        */
       
  5893       reset: function() {
       
  5894         this.elements = [1,0,0,0,
       
  5895                          0,1,0,0,
       
  5896                          0,0,1,0,
       
  5897                          0,0,0,1];
       
  5898       },
       
  5899       /**
       
  5900        * @member PMatrix3D
       
  5901        * The array() function returns a copy of the element values.
       
  5902        * @addon
       
  5903        *
       
  5904        * @return {float[]} returns a copy of the element values
       
  5905        */
       
  5906       array: function array() {
       
  5907         return this.elements.slice();
       
  5908       },
       
  5909       /**
       
  5910        * @member PMatrix3D
       
  5911        * The translate() function translates this matrix by moving the current coordinates to the location specified by tx, ty, and tz.
       
  5912        *
       
  5913        * @param {float} tx  the x-axis coordinate to move to
       
  5914        * @param {float} ty  the y-axis coordinate to move to
       
  5915        * @param {float} tz  the z-axis coordinate to move to
       
  5916        */
       
  5917       translate: function(tx, ty, tz) {
       
  5918         if (tz === undef) {
       
  5919           tz = 0;
       
  5920         }
       
  5921 
       
  5922         this.elements[3]  += tx * this.elements[0]  + ty * this.elements[1]  + tz * this.elements[2];
       
  5923         this.elements[7]  += tx * this.elements[4]  + ty * this.elements[5]  + tz * this.elements[6];
       
  5924         this.elements[11] += tx * this.elements[8]  + ty * this.elements[9]  + tz * this.elements[10];
       
  5925         this.elements[15] += tx * this.elements[12] + ty * this.elements[13] + tz * this.elements[14];
       
  5926       },
       
  5927       /**
       
  5928        * @member PMatrix3D
       
  5929        * The transpose() function transpose this matrix.
       
  5930        */
       
  5931       transpose: function() {
       
  5932         var temp = this.elements[4];
       
  5933         this.elements[4] = this.elements[1];
       
  5934         this.elements[1] = temp;
       
  5935 
       
  5936         temp = this.elements[8];
       
  5937         this.elements[8] = this.elements[2];
       
  5938         this.elements[2] = temp;
       
  5939 
       
  5940         temp = this.elements[6];
       
  5941         this.elements[6] = this.elements[9];
       
  5942         this.elements[9] = temp;
       
  5943 
       
  5944         temp = this.elements[3];
       
  5945         this.elements[3] = this.elements[12];
       
  5946         this.elements[12] = temp;
       
  5947 
       
  5948         temp = this.elements[7];
       
  5949         this.elements[7] = this.elements[13];
       
  5950         this.elements[13] = temp;
       
  5951 
       
  5952         temp = this.elements[11];
       
  5953         this.elements[11] = this.elements[14];
       
  5954         this.elements[14] = temp;
       
  5955       },
       
  5956       /**
       
  5957        * @member PMatrix3D
       
  5958        * The mult() function multiplied this matrix.
       
  5959        * If two array elements are passed in the function will multiply a two element vector against this matrix.
       
  5960        * If target is null or not length four, a new float array will be returned.
       
  5961        * The values for vec and target can be the same (though that's less efficient).
       
  5962        * If two PVectors are passed in the function multiply the x and y coordinates of a PVector against this matrix.
       
  5963        *
       
  5964        * @param {PVector} source, target  the PVectors used to multiply this matrix
       
  5965        * @param {float[]} source, target  the arrays used to multiply this matrix
       
  5966        *
       
  5967        * @return {PVector|float[]} returns a PVector or an array representing the new matrix
       
  5968        */
       
  5969       mult: function(source, target) {
       
  5970         var x, y, z, w;
       
  5971         if (source instanceof PVector) {
       
  5972           x = source.x;
       
  5973           y = source.y;
       
  5974           z = source.z;
       
  5975           w = 1;
       
  5976           if (!target) {
       
  5977             target = new PVector();
       
  5978           }
       
  5979         } else if (source instanceof Array) {
       
  5980           x = source[0];
       
  5981           y = source[1];
       
  5982           z = source[2];
       
  5983           w = source[3] || 1;
       
  5984 
       
  5985           if ( !target || (target.length !== 3 && target.length !== 4) ) {
       
  5986             target = [0, 0, 0];
       
  5987           }
       
  5988         }
       
  5989 
       
  5990         if (target instanceof Array) {
       
  5991           if (target.length === 3) {
       
  5992             target[0] = this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3];
       
  5993             target[1] = this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7];
       
  5994             target[2] = this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11];
       
  5995           } else if (target.length === 4) {
       
  5996             target[0] = this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3] * w;
       
  5997             target[1] = this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7] * w;
       
  5998             target[2] = this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11] * w;
       
  5999             target[3] = this.elements[12] * x + this.elements[13] * y + this.elements[14] * z + this.elements[15] * w;
       
  6000           }
       
  6001         }
       
  6002         if (target instanceof PVector) {
       
  6003           target.x = this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3];
       
  6004           target.y = this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7];
       
  6005           target.z = this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11];
       
  6006         }
       
  6007         return target;
       
  6008       },
       
  6009       /**
       
  6010        * @member PMatrix3D
       
  6011        * The preApply() function applies another matrix to the left of this one. Note that either a PMatrix3D or elements of a matrix can be passed in.
       
  6012        *
       
  6013        * @param {PMatrix3D} matrix    the matrix to apply this matrix to
       
  6014        * @param {float} m00           the first element of the matrix
       
  6015        * @param {float} m01           the second element of the matrix
       
  6016        * @param {float} m02           the third element of the matrix
       
  6017        * @param {float} m03           the fourth element of the matrix
       
  6018        * @param {float} m10           the fifth element of the matrix
       
  6019        * @param {float} m11           the sixth element of the matrix
       
  6020        * @param {float} m12           the seventh element of the matrix
       
  6021        * @param {float} m13           the eight element of the matrix
       
  6022        * @param {float} m20           the nineth element of the matrix
       
  6023        * @param {float} m21           the tenth element of the matrix
       
  6024        * @param {float} m22           the eleventh element of the matrix
       
  6025        * @param {float} m23           the twelveth element of the matrix
       
  6026        * @param {float} m30           the thirteenth element of the matrix
       
  6027        * @param {float} m31           the fourtheenth element of the matrix
       
  6028        * @param {float} m32           the fivetheenth element of the matrix
       
  6029        * @param {float} m33           the sixteenth element of the matrix
       
  6030        */
       
  6031       preApply: function() {
       
  6032         var source;
       
  6033         if (arguments.length === 1 && arguments[0] instanceof PMatrix3D) {
       
  6034           source = arguments[0].array();
       
  6035         } else if (arguments.length === 16) {
       
  6036           source = Array.prototype.slice.call(arguments);
       
  6037         } else if (arguments.length === 1 && arguments[0] instanceof Array) {
       
  6038           source = arguments[0];
       
  6039         }
       
  6040 
       
  6041         var result = [0, 0, 0, 0,
       
  6042                       0, 0, 0, 0,
       
  6043                       0, 0, 0, 0,
       
  6044                       0, 0, 0, 0];
       
  6045         var e = 0;
       
  6046         for (var row = 0; row < 4; row++) {
       
  6047           for (var col = 0; col < 4; col++, e++) {
       
  6048             result[e] += this.elements[col + 0] * source[row * 4 + 0] + this.elements[col + 4] *
       
  6049                          source[row * 4 + 1] + this.elements[col + 8] * source[row * 4 + 2] +
       
  6050                          this.elements[col + 12] * source[row * 4 + 3];
       
  6051           }
       
  6052         }
       
  6053         this.elements = result.slice();
       
  6054       },
       
  6055       /**
       
  6056        * @member PMatrix3D
       
  6057        * The apply() function multiplies the current matrix by the one specified through the parameters. Note that either a PMatrix3D or a list of floats can be passed in.
       
  6058        *
       
  6059        * @param {PMatrix3D} matrix    the matrix to apply this matrix to
       
  6060        * @param {float} m00           the first element of the matrix
       
  6061        * @param {float} m01           the second element of the matrix
       
  6062        * @param {float} m02           the third element of the matrix
       
  6063        * @param {float} m03           the fourth element of the matrix
       
  6064        * @param {float} m10           the fifth element of the matrix
       
  6065        * @param {float} m11           the sixth element of the matrix
       
  6066        * @param {float} m12           the seventh element of the matrix
       
  6067        * @param {float} m13           the eight element of the matrix
       
  6068        * @param {float} m20           the nineth element of the matrix
       
  6069        * @param {float} m21           the tenth element of the matrix
       
  6070        * @param {float} m22           the eleventh element of the matrix
       
  6071        * @param {float} m23           the twelveth element of the matrix
       
  6072        * @param {float} m30           the thirteenth element of the matrix
       
  6073        * @param {float} m31           the fourtheenth element of the matrix
       
  6074        * @param {float} m32           the fivetheenth element of the matrix
       
  6075        * @param {float} m33           the sixteenth element of the matrix
       
  6076        */
       
  6077       apply: function() {
       
  6078         var source;
       
  6079         if (arguments.length === 1 && arguments[0] instanceof PMatrix3D) {
       
  6080           source = arguments[0].array();
       
  6081         } else if (arguments.length === 16) {
       
  6082           source = Array.prototype.slice.call(arguments);
       
  6083         } else if (arguments.length === 1 && arguments[0] instanceof Array) {
       
  6084           source = arguments[0];
       
  6085         }
       
  6086 
       
  6087         var result = [0, 0, 0, 0,
       
  6088                       0, 0, 0, 0,
       
  6089                       0, 0, 0, 0,
       
  6090                       0, 0, 0, 0];
       
  6091         var e = 0;
       
  6092         for (var row = 0; row < 4; row++) {
       
  6093           for (var col = 0; col < 4; col++, e++) {
       
  6094             result[e] += this.elements[row * 4 + 0] * source[col + 0] + this.elements[row * 4 + 1] *
       
  6095                          source[col + 4] + this.elements[row * 4 + 2] * source[col + 8] +
       
  6096                          this.elements[row * 4 + 3] * source[col + 12];
       
  6097           }
       
  6098         }
       
  6099         this.elements = result.slice();
       
  6100       },
       
  6101       /**
       
  6102        * @member PMatrix3D
       
  6103        * The rotate() function rotates the matrix.
       
  6104        *
       
  6105        * @param {float} angle         the angle of rotation in radiants
       
  6106        */
       
  6107       rotate: function(angle, v0, v1, v2) {
       
  6108         if (!v1) {
       
  6109           this.rotateZ(angle);
       
  6110         } else {
       
  6111           // TODO should make sure this vector is normalized
       
  6112           var c = p.cos(angle);
       
  6113           var s = p.sin(angle);
       
  6114           var t = 1.0 - c;
       
  6115 
       
  6116           this.apply((t * v0 * v0) + c,
       
  6117                      (t * v0 * v1) - (s * v2),
       
  6118                      (t * v0 * v2) + (s * v1),
       
  6119                      0,
       
  6120                      (t * v0 * v1) + (s * v2),
       
  6121                      (t * v1 * v1) + c,
       
  6122                      (t * v1 * v2) - (s * v0),
       
  6123                      0,
       
  6124                      (t * v0 * v2) - (s * v1),
       
  6125                      (t * v1 * v2) + (s * v0),
       
  6126                      (t * v2 * v2) + c,
       
  6127                      0,
       
  6128                      0, 0, 0, 1);
       
  6129         }
       
  6130       },
       
  6131       /**
       
  6132        * @member PMatrix3D
       
  6133        * The invApply() function applies the inverted matrix to this matrix.
       
  6134        *
       
  6135        * @param {float} m00           the first element of the matrix
       
  6136        * @param {float} m01           the second element of the matrix
       
  6137        * @param {float} m02           the third element of the matrix
       
  6138        * @param {float} m03           the fourth element of the matrix
       
  6139        * @param {float} m10           the fifth element of the matrix
       
  6140        * @param {float} m11           the sixth element of the matrix
       
  6141        * @param {float} m12           the seventh element of the matrix
       
  6142        * @param {float} m13           the eight element of the matrix
       
  6143        * @param {float} m20           the nineth element of the matrix
       
  6144        * @param {float} m21           the tenth element of the matrix
       
  6145        * @param {float} m22           the eleventh element of the matrix
       
  6146        * @param {float} m23           the twelveth element of the matrix
       
  6147        * @param {float} m30           the thirteenth element of the matrix
       
  6148        * @param {float} m31           the fourtheenth element of the matrix
       
  6149        * @param {float} m32           the fivetheenth element of the matrix
       
  6150        * @param {float} m33           the sixteenth element of the matrix
       
  6151        *
       
  6152        * @return {boolean} returns true if the operation was successful.
       
  6153        */
       
  6154       invApply: function() {
       
  6155         if (inverseCopy === undef) {
       
  6156           inverseCopy = new PMatrix3D();
       
  6157         }
       
  6158         var a = arguments;
       
  6159         inverseCopy.set(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8],
       
  6160                         a[9], a[10], a[11], a[12], a[13], a[14], a[15]);
       
  6161 
       
  6162         if (!inverseCopy.invert()) {
       
  6163           return false;
       
  6164         }
       
  6165         this.preApply(inverseCopy);
       
  6166         return true;
       
  6167       },
       
  6168       /**
       
  6169        * @member PMatrix3D
       
  6170        * The rotateZ() function rotates the matrix.
       
  6171        *
       
  6172        * @param {float} angle         the angle of rotation in radiants
       
  6173        */
       
  6174       rotateX: function(angle) {
       
  6175         var c = p.cos(angle);
       
  6176         var s = p.sin(angle);
       
  6177         this.apply([1, 0, 0, 0, 0, c, -s, 0, 0, s, c, 0, 0, 0, 0, 1]);
       
  6178       },
       
  6179       /**
       
  6180        * @member PMatrix3D
       
  6181        * The rotateY() function rotates the matrix.
       
  6182        *
       
  6183        * @param {float} angle         the angle of rotation in radiants
       
  6184        */
       
  6185       rotateY: function(angle) {
       
  6186         var c = p.cos(angle);
       
  6187         var s = p.sin(angle);
       
  6188         this.apply([c, 0, s, 0, 0, 1, 0, 0, -s, 0, c, 0, 0, 0, 0, 1]);
       
  6189       },
       
  6190       /**
       
  6191        * @member PMatrix3D
       
  6192        * The rotateZ() function rotates the matrix.
       
  6193        *
       
  6194        * @param {float} angle         the angle of rotation in radiants
       
  6195        */
       
  6196       rotateZ: function(angle) {
       
  6197         var c = Math.cos(angle);
       
  6198         var s = Math.sin(angle);
       
  6199         this.apply([c, -s, 0, 0, s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
       
  6200       },
       
  6201       /**
       
  6202        * @member PMatrix3D
       
  6203        * The scale() function increases or decreases the size of a matrix by expanding and contracting vertices. When only one parameter is specified scale will occur in all dimensions.
       
  6204        * This is equivalent to a three parameter call.
       
  6205        *
       
  6206        * @param {float} sx  the amount to scale on the x-axis
       
  6207        * @param {float} sy  the amount to scale on the y-axis
       
  6208        * @param {float} sz  the amount to scale on the z-axis
       
  6209        */
       
  6210       scale: function(sx, sy, sz) {
       
  6211         if (sx && !sy && !sz) {
       
  6212           sy = sz = sx;
       
  6213         } else if (sx && sy && !sz) {
       
  6214           sz = 1;
       
  6215         }
       
  6216 
       
  6217         if (sx && sy && sz) {
       
  6218           this.elements[0]  *= sx;
       
  6219           this.elements[1]  *= sy;
       
  6220           this.elements[2]  *= sz;
       
  6221           this.elements[4]  *= sx;
       
  6222           this.elements[5]  *= sy;
       
  6223           this.elements[6]  *= sz;
       
  6224           this.elements[8]  *= sx;
       
  6225           this.elements[9]  *= sy;
       
  6226           this.elements[10] *= sz;
       
  6227           this.elements[12] *= sx;
       
  6228           this.elements[13] *= sy;
       
  6229           this.elements[14] *= sz;
       
  6230         }
       
  6231       },
       
  6232       /**
       
  6233        * @member PMatrix3D
       
  6234        * The skewX() function skews the matrix along the x-axis the amount specified by the angle parameter.
       
  6235        * Angles should be specified in radians (values from 0 to PI*2) or converted to radians with the <b>radians()</b> function.
       
  6236        *
       
  6237        * @param {float} angle  angle of skew specified in radians
       
  6238        */
       
  6239       skewX: function(angle) {
       
  6240         var t = Math.tan(angle);
       
  6241         this.apply(1, t, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
       
  6242       },
       
  6243       /**
       
  6244        * @member PMatrix3D
       
  6245        * The skewY() function skews the matrix along the y-axis the amount specified by the angle parameter.
       
  6246        * Angles should be specified in radians (values from 0 to PI*2) or converted to radians with the <b>radians()</b> function.
       
  6247        *
       
  6248        * @param {float} angle  angle of skew specified in radians
       
  6249        */
       
  6250       skewY: function(angle) {
       
  6251         var t = Math.tan(angle);
       
  6252         this.apply(1, 0, 0, 0, t, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
       
  6253       },
       
  6254       multX: function(x, y, z, w) {
       
  6255         if (!z) {
       
  6256           return this.elements[0] * x + this.elements[1] * y + this.elements[3];
       
  6257         }
       
  6258         if (!w) {
       
  6259           return this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3];
       
  6260         }
       
  6261         return this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3] * w;
       
  6262       },
       
  6263       multY: function(x, y, z, w) {
       
  6264         if (!z) {
       
  6265           return this.elements[4] * x + this.elements[5] * y + this.elements[7];
       
  6266         }
       
  6267         if (!w) {
       
  6268           return this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7];
       
  6269         }
       
  6270         return this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7] * w;
       
  6271       },
       
  6272       multZ: function(x, y, z, w) {
       
  6273         if (!w) {
       
  6274           return this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11];
       
  6275         }
       
  6276         return this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11] * w;
       
  6277       },
       
  6278       multW: function(x, y, z, w) {
       
  6279         if (!w) {
       
  6280           return this.elements[12] * x + this.elements[13] * y + this.elements[14] * z + this.elements[15];
       
  6281         }
       
  6282         return this.elements[12] * x + this.elements[13] * y + this.elements[14] * z + this.elements[15] * w;
       
  6283       },
       
  6284       /**
       
  6285        * @member PMatrix3D
       
  6286        * The invert() function inverts this matrix
       
  6287        *
       
  6288        * @return {boolean} true if successful
       
  6289        */
       
  6290       invert: function() {
       
  6291         var fA0 = this.elements[0] * this.elements[5] - this.elements[1] * this.elements[4];
       
  6292         var fA1 = this.elements[0] * this.elements[6] - this.elements[2] * this.elements[4];
       
  6293         var fA2 = this.elements[0] * this.elements[7] - this.elements[3] * this.elements[4];
       
  6294         var fA3 = this.elements[1] * this.elements[6] - this.elements[2] * this.elements[5];
       
  6295         var fA4 = this.elements[1] * this.elements[7] - this.elements[3] * this.elements[5];
       
  6296         var fA5 = this.elements[2] * this.elements[7] - this.elements[3] * this.elements[6];
       
  6297         var fB0 = this.elements[8] * this.elements[13] - this.elements[9] * this.elements[12];
       
  6298         var fB1 = this.elements[8] * this.elements[14] - this.elements[10] * this.elements[12];
       
  6299         var fB2 = this.elements[8] * this.elements[15] - this.elements[11] * this.elements[12];
       
  6300         var fB3 = this.elements[9] * this.elements[14] - this.elements[10] * this.elements[13];
       
  6301         var fB4 = this.elements[9] * this.elements[15] - this.elements[11] * this.elements[13];
       
  6302         var fB5 = this.elements[10] * this.elements[15] - this.elements[11] * this.elements[14];
       
  6303 
       
  6304         // Determinant
       
  6305         var fDet = fA0 * fB5 - fA1 * fB4 + fA2 * fB3 + fA3 * fB2 - fA4 * fB1 + fA5 * fB0;
       
  6306 
       
  6307         // Account for a very small value
       
  6308         // return false if not successful.
       
  6309         if (Math.abs(fDet) <= 1e-9) {
       
  6310           return false;
       
  6311         }
       
  6312 
       
  6313         var kInv = [];
       
  6314         kInv[0]  = +this.elements[5] * fB5 - this.elements[6] * fB4 + this.elements[7] * fB3;
       
  6315         kInv[4]  = -this.elements[4] * fB5 + this.elements[6] * fB2 - this.elements[7] * fB1;
       
  6316         kInv[8]  = +this.elements[4] * fB4 - this.elements[5] * fB2 + this.elements[7] * fB0;
       
  6317         kInv[12] = -this.elements[4] * fB3 + this.elements[5] * fB1 - this.elements[6] * fB0;
       
  6318         kInv[1]  = -this.elements[1] * fB5 + this.elements[2] * fB4 - this.elements[3] * fB3;
       
  6319         kInv[5]  = +this.elements[0] * fB5 - this.elements[2] * fB2 + this.elements[3] * fB1;
       
  6320         kInv[9]  = -this.elements[0] * fB4 + this.elements[1] * fB2 - this.elements[3] * fB0;
       
  6321         kInv[13] = +this.elements[0] * fB3 - this.elements[1] * fB1 + this.elements[2] * fB0;
       
  6322         kInv[2]  = +this.elements[13] * fA5 - this.elements[14] * fA4 + this.elements[15] * fA3;
       
  6323         kInv[6]  = -this.elements[12] * fA5 + this.elements[14] * fA2 - this.elements[15] * fA1;
       
  6324         kInv[10] = +this.elements[12] * fA4 - this.elements[13] * fA2 + this.elements[15] * fA0;
       
  6325         kInv[14] = -this.elements[12] * fA3 + this.elements[13] * fA1 - this.elements[14] * fA0;
       
  6326         kInv[3]  = -this.elements[9] * fA5 + this.elements[10] * fA4 - this.elements[11] * fA3;
       
  6327         kInv[7]  = +this.elements[8] * fA5 - this.elements[10] * fA2 + this.elements[11] * fA1;
       
  6328         kInv[11] = -this.elements[8] * fA4 + this.elements[9] * fA2 - this.elements[11] * fA0;
       
  6329         kInv[15] = +this.elements[8] * fA3 - this.elements[9] * fA1 + this.elements[10] * fA0;
       
  6330 
       
  6331         // Inverse using Determinant
       
  6332         var fInvDet = 1.0 / fDet;
       
  6333         kInv[0]  *= fInvDet;
       
  6334         kInv[1]  *= fInvDet;
       
  6335         kInv[2]  *= fInvDet;
       
  6336         kInv[3]  *= fInvDet;
       
  6337         kInv[4]  *= fInvDet;
       
  6338         kInv[5]  *= fInvDet;
       
  6339         kInv[6]  *= fInvDet;
       
  6340         kInv[7]  *= fInvDet;
       
  6341         kInv[8]  *= fInvDet;
       
  6342         kInv[9]  *= fInvDet;
       
  6343         kInv[10] *= fInvDet;
       
  6344         kInv[11] *= fInvDet;
       
  6345         kInv[12] *= fInvDet;
       
  6346         kInv[13] *= fInvDet;
       
  6347         kInv[14] *= fInvDet;
       
  6348         kInv[15] *= fInvDet;
       
  6349 
       
  6350         this.elements = kInv.slice();
       
  6351         return true;
       
  6352       },
       
  6353       toString: function() {
       
  6354         var str = "";
       
  6355         for (var i = 0; i < 15; i++) {
       
  6356           str += this.elements[i] + ", ";
       
  6357         }
       
  6358         str += this.elements[15];
       
  6359         return str;
       
  6360       },
       
  6361       /**
       
  6362        * @member PMatrix3D
       
  6363        * The print() function prints out the elements of this matrix
       
  6364        */
       
  6365       print: function() {
       
  6366         var digits = printMatrixHelper(this.elements);
       
  6367 
       
  6368         var output = "" + p.nfs(this.elements[0], digits, 4) + " " + p.nfs(this.elements[1], digits, 4) +
       
  6369                      " " + p.nfs(this.elements[2], digits, 4) + " " + p.nfs(this.elements[3], digits, 4) +
       
  6370                      "\n" + p.nfs(this.elements[4], digits, 4) + " " + p.nfs(this.elements[5], digits, 4) +
       
  6371                      " " + p.nfs(this.elements[6], digits, 4) + " " + p.nfs(this.elements[7], digits, 4) +
       
  6372                      "\n" + p.nfs(this.elements[8], digits, 4) + " " + p.nfs(this.elements[9], digits, 4) +
       
  6373                      " " + p.nfs(this.elements[10], digits, 4) + " " + p.nfs(this.elements[11], digits, 4) +
       
  6374                      "\n" + p.nfs(this.elements[12], digits, 4) + " " + p.nfs(this.elements[13], digits, 4) +
       
  6375                      " " + p.nfs(this.elements[14], digits, 4) + " " + p.nfs(this.elements[15], digits, 4) + "\n\n";
       
  6376         p.println(output);
       
  6377       },
       
  6378       invTranslate: function(tx, ty, tz) {
       
  6379         this.preApply(1, 0, 0, -tx, 0, 1, 0, -ty, 0, 0, 1, -tz, 0, 0, 0, 1);
       
  6380       },
       
  6381       invRotateX: function(angle) {
       
  6382         var c = Math.cos(-angle);
       
  6383         var s = Math.sin(-angle);
       
  6384         this.preApply([1, 0, 0, 0, 0, c, -s, 0, 0, s, c, 0, 0, 0, 0, 1]);
       
  6385       },
       
  6386       invRotateY: function(angle) {
       
  6387         var c = Math.cos(-angle);
       
  6388         var s = Math.sin(-angle);
       
  6389         this.preApply([c, 0, s, 0, 0, 1, 0, 0, -s, 0, c, 0, 0, 0, 0, 1]);
       
  6390       },
       
  6391       invRotateZ: function(angle) {
       
  6392         var c = Math.cos(-angle);
       
  6393         var s = Math.sin(-angle);
       
  6394         this.preApply([c, -s, 0, 0, s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
       
  6395       },
       
  6396       invScale: function(x, y, z) {
       
  6397         this.preApply([1 / x, 0, 0, 0, 0, 1 / y, 0, 0, 0, 0, 1 / z, 0, 0, 0, 0, 1]);
       
  6398       }
       
  6399     };
       
  6400 
       
  6401     /**
       
  6402      * @private
       
  6403      * The matrix stack stores the transformations and translations that occur within the space.
       
  6404      */
       
  6405     var PMatrixStack = p.PMatrixStack = function() {
       
  6406       this.matrixStack = [];
       
  6407     };
       
  6408 
       
  6409     /**
       
  6410      * @member PMatrixStack
       
  6411      * load pushes the matrix given in the function into the stack
       
  6412      *
       
  6413      * @param {Object | Array} matrix the matrix to be pushed into the stack
       
  6414      */
       
  6415     PMatrixStack.prototype.load = function() {
       
  6416       var tmpMatrix = drawing.$newPMatrix();
       
  6417 
       
  6418       if (arguments.length === 1) {
       
  6419         tmpMatrix.set(arguments[0]);
       
  6420       } else {
       
  6421         tmpMatrix.set(arguments);
       
  6422       }
       
  6423       this.matrixStack.push(tmpMatrix);
       
  6424     };
       
  6425 
       
  6426     Drawing2D.prototype.$newPMatrix = function() {
       
  6427       return new PMatrix2D();
       
  6428     };
       
  6429 
       
  6430     Drawing3D.prototype.$newPMatrix = function() {
       
  6431       return new PMatrix3D();
       
  6432     };
       
  6433 
       
  6434     /**
       
  6435      * @member PMatrixStack
       
  6436      * push adds a duplicate of the top of the stack onto the stack - uses the peek function
       
  6437      */
       
  6438     PMatrixStack.prototype.push = function() {
       
  6439       this.matrixStack.push(this.peek());
       
  6440     };
       
  6441 
       
  6442     /**
       
  6443      * @member PMatrixStack
       
  6444      * pop removes returns the matrix at the top of the stack
       
  6445      *
       
  6446      * @returns {Object} the matrix at the top of the stack
       
  6447      */
       
  6448     PMatrixStack.prototype.pop = function() {
       
  6449       return this.matrixStack.pop();
       
  6450     };
       
  6451 
       
  6452     /**
       
  6453      * @member PMatrixStack
       
  6454      * peek returns but doesn't remove the matrix at the top of the stack
       
  6455      *
       
  6456      * @returns {Object} the matrix at the top of the stack
       
  6457      */
       
  6458     PMatrixStack.prototype.peek = function() {
       
  6459       var tmpMatrix = drawing.$newPMatrix();
       
  6460 
       
  6461       tmpMatrix.set(this.matrixStack[this.matrixStack.length - 1]);
       
  6462       return tmpMatrix;
       
  6463     };
       
  6464 
       
  6465     /**
       
  6466      * @member PMatrixStack
       
  6467      * this function multiplies the matrix at the top of the stack with the matrix given as a parameter
       
  6468      *
       
  6469      * @param {Object | Array} matrix the matrix to be multiplied into the stack
       
  6470      */
       
  6471     PMatrixStack.prototype.mult = function(matrix) {
       
  6472       this.matrixStack[this.matrixStack.length - 1].apply(matrix);
       
  6473     };
       
  6474 
       
  6475     ////////////////////////////////////////////////////////////////////////////
       
  6476     // Array handling
       
  6477     ////////////////////////////////////////////////////////////////////////////
       
  6478 
       
  6479     /**
       
  6480     * The split() function breaks a string into pieces using a character or string
       
  6481     * as the divider. The delim  parameter specifies the character or characters that
       
  6482     * mark the boundaries between each piece. A String[] array is returned that contains
       
  6483     * each of the pieces.
       
  6484     * If the result is a set of numbers, you can convert the String[] array to to a float[]
       
  6485     * or int[] array using the datatype conversion functions int() and float() (see example above).
       
  6486     * The splitTokens() function works in a similar fashion, except that it splits using a range
       
  6487     * of characters instead of a specific character or sequence.
       
  6488     *
       
  6489     * @param {String} str       the String to be split
       
  6490     * @param {String} delim     the character or String used to separate the data
       
  6491     *
       
  6492     * @returns {string[]} The new string array
       
  6493     *
       
  6494     * @see splitTokens
       
  6495     * @see join
       
  6496     * @see trim
       
  6497     */
       
  6498     p.split = function(str, delim) {
       
  6499       return str.split(delim);
       
  6500     };
       
  6501 
       
  6502     /**
       
  6503     * The splitTokens() function splits a String at one or many character "tokens." The tokens
       
  6504     * parameter specifies the character or characters to be used as a boundary.
       
  6505     * If no tokens character is specified, any whitespace character is used to split.
       
  6506     * Whitespace characters include tab (\t), line feed (\n), carriage return (\r), form
       
  6507     * feed (\f), and space. To convert a String to an array of integers or floats, use the
       
  6508     * datatype conversion functions int() and float() to convert the array of Strings.
       
  6509     *
       
  6510     * @param {String} str       the String to be split
       
  6511     * @param {Char[]} tokens    list of individual characters that will be used as separators
       
  6512     *
       
  6513     * @returns {string[]} The new string array
       
  6514     *
       
  6515     * @see split
       
  6516     * @see join
       
  6517     * @see trim
       
  6518     */
       
  6519     p.splitTokens = function(str, tokens) {
       
  6520       if (arguments.length === 1) {
       
  6521         tokens = "\n\t\r\f ";
       
  6522       }
       
  6523 
       
  6524       tokens = "[" + tokens + "]";
       
  6525 
       
  6526       var ary = [];
       
  6527       var index = 0;
       
  6528       var pos = str.search(tokens);
       
  6529 
       
  6530       while (pos >= 0) {
       
  6531         if (pos === 0) {
       
  6532           str = str.substring(1);
       
  6533         } else {
       
  6534           ary[index] = str.substring(0, pos);
       
  6535           index++;
       
  6536           str = str.substring(pos);
       
  6537         }
       
  6538         pos = str.search(tokens);
       
  6539       }
       
  6540 
       
  6541       if (str.length > 0) {
       
  6542         ary[index] = str;
       
  6543       }
       
  6544 
       
  6545       if (ary.length === 0) {
       
  6546         ary = undef;
       
  6547       }
       
  6548 
       
  6549       return ary;
       
  6550     };
       
  6551 
       
  6552     /**
       
  6553     * Expands an array by one element and adds data to the new position. The datatype of
       
  6554     * the element parameter must be the same as the datatype of the array.
       
  6555     * When using an array of objects, the data returned from the function must be cast to
       
  6556     * the object array's data type. For example: SomeClass[] items = (SomeClass[])
       
  6557     * append(originalArray, element).
       
  6558     *
       
  6559     * @param {boolean[]|byte[]|char[]|int[]|float[]|String[]|array of objects} array boolean[],
       
  6560     * byte[], char[], int[], float[], or String[], or an array of objects
       
  6561     * @param {boolean[]|byte[]|char[]|int[]|float[]|String[]|array of objects} element new data for the array
       
  6562     *
       
  6563     * @returns Array (the same datatype as the input)
       
  6564     *
       
  6565     * @see shorten
       
  6566     * @see expand
       
  6567     */
       
  6568     p.append = function(array, element) {
       
  6569       array[array.length] = element;
       
  6570       return array;
       
  6571     };
       
  6572 
       
  6573     /**
       
  6574     * Concatenates two arrays. For example, concatenating the array { 1, 2, 3 } and the
       
  6575     * array { 4, 5, 6 } yields { 1, 2, 3, 4, 5, 6 }. Both parameters must be arrays of the
       
  6576     * same datatype.
       
  6577     * When using an array of objects, the data returned from the function must be cast to the
       
  6578     * object array's data type. For example: SomeClass[] items = (SomeClass[]) concat(array1, array2).
       
  6579     *
       
  6580     * @param {boolean[]|byte[]|char[]|int[]|float[]|String[]|array of objects} array1 boolean[],
       
  6581     * byte[], char[], int[], float[], String[], or an array of objects
       
  6582     * @param {boolean[]|byte[]|char[]|int[]|float[]|String[]|array of objects} array2 boolean[],
       
  6583     * byte[], char[], int[], float[], String[], or an array of objects
       
  6584     *
       
  6585     * @returns Array (the same datatype as the input)
       
  6586     *
       
  6587     * @see splice
       
  6588     */
       
  6589     p.concat = function(array1, array2) {
       
  6590       return array1.concat(array2);
       
  6591     };
       
  6592 
       
  6593     /**
       
  6594      * Sorts an array of numbers from smallest to largest and puts an array of
       
  6595      * words in alphabetical order. The original array is not modified, a
       
  6596      * re-ordered array is returned. The count parameter states the number of
       
  6597      * elements to sort. For example if there are 12 elements in an array and
       
  6598      * if count is the value 5, only the first five elements on the array will
       
  6599      * be sorted. Alphabetical ordering is case insensitive.
       
  6600      *
       
  6601      * @param {String[] | int[] | float[]}  array Array of elements to sort
       
  6602      * @param {int}                         numElem Number of elements to sort
       
  6603      *
       
  6604      * @returns {String[] | int[] | float[]} Array (same datatype as the input)
       
  6605      *
       
  6606      * @see reverse
       
  6607     */
       
  6608     p.sort = function(array, numElem) {
       
  6609       var ret = [];
       
  6610 
       
  6611       // depending on the type used (int, float) or string
       
  6612       // we'll need to use a different compare function
       
  6613       if (array.length > 0) {
       
  6614         // copy since we need to return another array
       
  6615         var elemsToCopy = numElem > 0 ? numElem : array.length;
       
  6616         for (var i = 0; i < elemsToCopy; i++) {
       
  6617           ret.push(array[i]);
       
  6618         }
       
  6619         if (typeof array[0] === "string") {
       
  6620           ret.sort();
       
  6621         }
       
  6622         // int or float
       
  6623         else {
       
  6624           ret.sort(function(a, b) {
       
  6625             return a - b;
       
  6626           });
       
  6627         }
       
  6628 
       
  6629         // copy on the rest of the elements that were not sorted in case the user
       
  6630         // only wanted a subset of an array to be sorted.
       
  6631         if (numElem > 0) {
       
  6632           for (var j = ret.length; j < array.length; j++) {
       
  6633             ret.push(array[j]);
       
  6634           }
       
  6635         }
       
  6636       }
       
  6637       return ret;
       
  6638     };
       
  6639 
       
  6640     /**
       
  6641     * Inserts a value or array of values into an existing array. The first two parameters must
       
  6642     * be of the same datatype. The array parameter defines the array which will be modified
       
  6643     * and the second parameter defines the data which will be inserted. When using an array
       
  6644     * of objects, the data returned from the function must be cast to the object array's data
       
  6645     * type. For example: SomeClass[] items = (SomeClass[]) splice(array1, array2, index).
       
  6646     *
       
  6647     * @param {boolean[]|byte[]|char[]|int[]|float[]|String[]|array of objects} array boolean[],
       
  6648     * byte[], char[], int[], float[], String[], or an array of objects
       
  6649     * @param {boolean|byte|char|int|float|String|boolean[]|byte[]|char[]|int[]|float[]|String[]|array of objects}
       
  6650     * value boolean, byte, char, int, float, String, boolean[], byte[], char[], int[],
       
  6651     * float[], String[], or other Object: value or an array of objects to be spliced in
       
  6652     * @param {int} index                position in the array from which to insert data
       
  6653     *
       
  6654     * @returns Array (the same datatype as the input)
       
  6655     *
       
  6656     * @see contract
       
  6657     * @see subset
       
  6658     */
       
  6659     p.splice = function(array, value, index) {
       
  6660 
       
  6661       // Trying to splice an empty array into "array" in P5 won't do
       
  6662       // anything, just return the original.
       
  6663       if(value.length === 0)
       
  6664       {
       
  6665         return array;
       
  6666       }
       
  6667 
       
  6668       // If the second argument was an array, we'll need to iterate over all
       
  6669       // the "value" elements and add one by one because
       
  6670       // array.splice(index, 0, value);
       
  6671       // would create a multi-dimensional array which isn't what we want.
       
  6672       if(value instanceof Array) {
       
  6673         for(var i = 0, j = index; i < value.length; j++,i++) {
       
  6674           array.splice(j, 0, value[i]);
       
  6675         }
       
  6676       } else {
       
  6677         array.splice(index, 0, value);
       
  6678       }
       
  6679 
       
  6680       return array;
       
  6681     };
       
  6682 
       
  6683     /**
       
  6684     * Extracts an array of elements from an existing array. The array parameter defines the
       
  6685     * array from which the elements will be copied and the offset and length parameters determine
       
  6686     * which elements to extract. If no length is given, elements will be extracted from the offset
       
  6687     * to the end of the array. When specifying the offset remember the first array element is 0.
       
  6688     * This function does not change the source array.
       
  6689     * When using an array of objects, the data returned from the function must be cast to the
       
  6690     * object array's data type.
       
  6691     *
       
  6692     * @param {boolean[]|byte[]|char[]|int[]|float[]|String[]|array of objects} array boolean[],
       
  6693     * byte[], char[], int[], float[], String[], or an array of objects
       
  6694     * @param {int} offset         position to begin
       
  6695     * @param {int} length         number of values to extract
       
  6696     *
       
  6697     * @returns Array (the same datatype as the input)
       
  6698     *
       
  6699     * @see splice
       
  6700     */
       
  6701     p.subset = function(array, offset, length) {
       
  6702       var end = (length !== undef) ? offset + length : array.length;
       
  6703       return array.slice(offset, end);
       
  6704     };
       
  6705 
       
  6706     /**
       
  6707     * Combines an array of Strings into one String, each separated by the character(s) used for
       
  6708     * the separator parameter. To join arrays of ints or floats, it's necessary to first convert
       
  6709     * them to strings using nf() or nfs().
       
  6710     *
       
  6711     * @param {Array} array              array of Strings
       
  6712     * @param {char|String} separator    char or String to be placed between each item
       
  6713     *
       
  6714     * @returns {String} The combined string
       
  6715     *
       
  6716     * @see split
       
  6717     * @see trim
       
  6718     * @see nf
       
  6719     * @see nfs
       
  6720     */
       
  6721     p.join = function(array, seperator) {
       
  6722       return array.join(seperator);
       
  6723     };
       
  6724 
       
  6725     /**
       
  6726     * Decreases an array by one element and returns the shortened array. When using an
       
  6727     * array of objects, the data returned from the function must be cast to the object array's
       
  6728     * data type. For example: SomeClass[] items = (SomeClass[]) shorten(originalArray).
       
  6729     *
       
  6730     * @param {boolean[]|byte[]|char[]|int[]|float[]|String[]|array of objects} array
       
  6731     * boolean[], byte[], char[], int[], float[], or String[], or an array of objects
       
  6732     *
       
  6733     * @returns Array (the same datatype as the input)
       
  6734     *
       
  6735     * @see append
       
  6736     * @see expand
       
  6737     */
       
  6738     p.shorten = function(ary) {
       
  6739       var newary = [];
       
  6740 
       
  6741       // copy array into new array
       
  6742       var len = ary.length;
       
  6743       for (var i = 0; i < len; i++) {
       
  6744         newary[i] = ary[i];
       
  6745       }
       
  6746       newary.pop();
       
  6747 
       
  6748       return newary;
       
  6749     };
       
  6750 
       
  6751     /**
       
  6752     * Increases the size of an array. By default, this function doubles the size of the array,
       
  6753     * but the optional newSize parameter provides precise control over the increase in size.
       
  6754     * When using an array of objects, the data returned from the function must be cast to the
       
  6755     * object array's data type. For example: SomeClass[] items = (SomeClass[]) expand(originalArray).
       
  6756     *
       
  6757     * @param {boolean[]|byte[]|char[]|int[]|float[]|String[]|array of objects} ary
       
  6758     * boolean[], byte[], char[], int[], float[], String[], or an array of objects
       
  6759     * @param {int} newSize              positive int: new size for the array
       
  6760     *
       
  6761     * @returns Array (the same datatype as the input)
       
  6762     *
       
  6763     * @see contract
       
  6764     */
       
  6765     p.expand = function(ary, targetSize) {
       
  6766       var temp = ary.slice(0),
       
  6767           newSize = targetSize || ary.length * 2;
       
  6768       temp.length = newSize;
       
  6769       return temp;
       
  6770     };
       
  6771 
       
  6772     /**
       
  6773     * Copies an array (or part of an array) to another array. The src array is copied to the
       
  6774     * dst array, beginning at the position specified by srcPos and into the position specified
       
  6775     * by dstPos. The number of elements to copy is determined by length. The simplified version
       
  6776     * with two arguments copies an entire array to another of the same size. It is equivalent
       
  6777     * to "arrayCopy(src, 0, dst, 0, src.length)". This function is far more efficient for copying
       
  6778     * array data than iterating through a for and copying each element.
       
  6779     *
       
  6780     * @param {Array} src an array of any data type: the source array
       
  6781     * @param {Array} dest an array of any data type (as long as it's the same as src): the destination array
       
  6782     * @param {int} srcPos     starting position in the source array
       
  6783     * @param {int} destPos    starting position in the destination array
       
  6784     * @param {int} length     number of array elements to be copied
       
  6785     *
       
  6786     * @returns none
       
  6787     */
       
  6788     p.arrayCopy = function() { // src, srcPos, dest, destPos, length) {
       
  6789       var src, srcPos = 0, dest, destPos = 0, length;
       
  6790 
       
  6791       if (arguments.length === 2) {
       
  6792         // recall itself and copy src to dest from start index 0 to 0 of src.length
       
  6793         src = arguments[0];
       
  6794         dest = arguments[1];
       
  6795         length = src.length;
       
  6796       } else if (arguments.length === 3) {
       
  6797         // recall itself and copy src to dest from start index 0 to 0 of length
       
  6798         src = arguments[0];
       
  6799         dest = arguments[1];
       
  6800         length = arguments[2];
       
  6801       } else if (arguments.length === 5) {
       
  6802         src = arguments[0];
       
  6803         srcPos = arguments[1];
       
  6804         dest = arguments[2];
       
  6805         destPos = arguments[3];
       
  6806         length = arguments[4];
       
  6807       }
       
  6808 
       
  6809       // copy src to dest from index srcPos to index destPos of length recursivly on objects
       
  6810       for (var i = srcPos, j = destPos; i < length + srcPos; i++, j++) {
       
  6811         if (dest[j] !== undef) {
       
  6812           dest[j] = src[i];
       
  6813         } else {
       
  6814           throw "array index out of bounds exception";
       
  6815         }
       
  6816       }
       
  6817     };
       
  6818 
       
  6819     /**
       
  6820     * Reverses the order of an array.
       
  6821     *
       
  6822     * @param {boolean[]|byte[]|char[]|int[]|float[]|String[]} array
       
  6823     * boolean[], byte[], char[], int[], float[], or String[]
       
  6824     *
       
  6825     * @returns Array (the same datatype as the input)
       
  6826     *
       
  6827     * @see sort
       
  6828     */
       
  6829     p.reverse = function(array) {
       
  6830       return array.reverse();
       
  6831     };
       
  6832 
       
  6833 
       
  6834     ////////////////////////////////////////////////////////////////////////////
       
  6835     // Color functions
       
  6836     ////////////////////////////////////////////////////////////////////////////
       
  6837 
       
  6838     // helper functions for internal blending modes
       
  6839     p.mix = function(a, b, f) {
       
  6840       return a + (((b - a) * f) >> 8);
       
  6841     };
       
  6842 
       
  6843     p.peg = function(n) {
       
  6844       return (n < 0) ? 0 : ((n > 255) ? 255 : n);
       
  6845     };
       
  6846 
       
  6847     // blending modes
       
  6848     /**
       
  6849     * These are internal blending modes used for BlendColor()
       
  6850     *
       
  6851     * @param {Color} c1       First Color to blend
       
  6852     * @param {Color} c2       Second Color to blend
       
  6853     *
       
  6854     * @returns {Color}        The blended Color
       
  6855     *
       
  6856     * @see BlendColor
       
  6857     * @see Blend
       
  6858     */
       
  6859     p.modes = (function() {
       
  6860       var ALPHA_MASK = PConstants.ALPHA_MASK,
       
  6861         RED_MASK = PConstants.RED_MASK,
       
  6862         GREEN_MASK = PConstants.GREEN_MASK,
       
  6863         BLUE_MASK = PConstants.BLUE_MASK,
       
  6864         min = Math.min,
       
  6865         max = Math.max;
       
  6866 
       
  6867       function applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb) {
       
  6868         var a = min(((c1 & 0xff000000) >>> 24) + f, 0xff) << 24;
       
  6869 
       
  6870         var r = (ar + (((cr - ar) * f) >> 8));
       
  6871         r = ((r < 0) ? 0 : ((r > 255) ? 255 : r)) << 16;
       
  6872 
       
  6873         var g = (ag + (((cg - ag) * f) >> 8));
       
  6874         g = ((g < 0) ? 0 : ((g > 255) ? 255 : g)) << 8;
       
  6875 
       
  6876         var b = ab + (((cb - ab) * f) >> 8);
       
  6877         b = (b < 0) ? 0 : ((b > 255) ? 255 : b);
       
  6878 
       
  6879         return (a | r | g | b);
       
  6880       }
       
  6881 
       
  6882       return {
       
  6883         replace: function(c1, c2) {
       
  6884           return c2;
       
  6885         },
       
  6886         blend: function(c1, c2) {
       
  6887           var f = (c2 & ALPHA_MASK) >>> 24,
       
  6888             ar = (c1 & RED_MASK),
       
  6889             ag = (c1 & GREEN_MASK),
       
  6890             ab = (c1 & BLUE_MASK),
       
  6891             br = (c2 & RED_MASK),
       
  6892             bg = (c2 & GREEN_MASK),
       
  6893             bb = (c2 & BLUE_MASK);
       
  6894 
       
  6895           return (min(((c1 & ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
       
  6896                   (ar + (((br - ar) * f) >> 8)) & RED_MASK |
       
  6897                   (ag + (((bg - ag) * f) >> 8)) & GREEN_MASK |
       
  6898                   (ab + (((bb - ab) * f) >> 8)) & BLUE_MASK);
       
  6899         },
       
  6900         add: function(c1, c2) {
       
  6901           var f = (c2 & ALPHA_MASK) >>> 24;
       
  6902           return (min(((c1 & ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
       
  6903                   min(((c1 & RED_MASK) + ((c2 & RED_MASK) >> 8) * f), RED_MASK) & RED_MASK |
       
  6904                   min(((c1 & GREEN_MASK) + ((c2 & GREEN_MASK) >> 8) * f), GREEN_MASK) & GREEN_MASK |
       
  6905                   min((c1 & BLUE_MASK) + (((c2 & BLUE_MASK) * f) >> 8), BLUE_MASK));
       
  6906         },
       
  6907         subtract: function(c1, c2) {
       
  6908           var f = (c2 & ALPHA_MASK) >>> 24;
       
  6909           return (min(((c1 & ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
       
  6910                   max(((c1 & RED_MASK) - ((c2 & RED_MASK) >> 8) * f), GREEN_MASK) & RED_MASK |
       
  6911                   max(((c1 & GREEN_MASK) - ((c2 & GREEN_MASK) >> 8) * f), BLUE_MASK) & GREEN_MASK |
       
  6912                   max((c1 & BLUE_MASK) - (((c2 & BLUE_MASK) * f) >> 8), 0));
       
  6913         },
       
  6914         lightest: function(c1, c2) {
       
  6915           var f = (c2 & ALPHA_MASK) >>> 24;
       
  6916           return (min(((c1 & ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
       
  6917                   max(c1 & RED_MASK, ((c2 & RED_MASK) >> 8) * f) & RED_MASK |
       
  6918                   max(c1 & GREEN_MASK, ((c2 & GREEN_MASK) >> 8) * f) & GREEN_MASK |
       
  6919                   max(c1 & BLUE_MASK, ((c2 & BLUE_MASK) * f) >> 8));
       
  6920         },
       
  6921         darkest: function(c1, c2) {
       
  6922           var f = (c2 & ALPHA_MASK) >>> 24,
       
  6923             ar = (c1 & RED_MASK),
       
  6924             ag = (c1 & GREEN_MASK),
       
  6925             ab = (c1 & BLUE_MASK),
       
  6926             br = min(c1 & RED_MASK, ((c2 & RED_MASK) >> 8) * f),
       
  6927             bg = min(c1 & GREEN_MASK, ((c2 & GREEN_MASK) >> 8) * f),
       
  6928             bb = min(c1 & BLUE_MASK, ((c2 & BLUE_MASK) * f) >> 8);
       
  6929 
       
  6930           return (min(((c1 & ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
       
  6931                   (ar + (((br - ar) * f) >> 8)) & RED_MASK |
       
  6932                   (ag + (((bg - ag) * f) >> 8)) & GREEN_MASK |
       
  6933                   (ab + (((bb - ab) * f) >> 8)) & BLUE_MASK);
       
  6934         },
       
  6935         difference: function(c1, c2) {
       
  6936           var f  = (c2 & ALPHA_MASK) >>> 24,
       
  6937             ar = (c1 & RED_MASK) >> 16,
       
  6938             ag = (c1 & GREEN_MASK) >> 8,
       
  6939             ab = (c1 & BLUE_MASK),
       
  6940             br = (c2 & RED_MASK) >> 16,
       
  6941             bg = (c2 & GREEN_MASK) >> 8,
       
  6942             bb = (c2 & BLUE_MASK),
       
  6943             cr = (ar > br) ? (ar - br) : (br - ar),
       
  6944             cg = (ag > bg) ? (ag - bg) : (bg - ag),
       
  6945             cb = (ab > bb) ? (ab - bb) : (bb - ab);
       
  6946 
       
  6947           return applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb);
       
  6948         },
       
  6949         exclusion: function(c1, c2) {
       
  6950           var f  = (c2 & ALPHA_MASK) >>> 24,
       
  6951             ar = (c1 & RED_MASK) >> 16,
       
  6952             ag = (c1 & GREEN_MASK) >> 8,
       
  6953             ab = (c1 & BLUE_MASK),
       
  6954             br = (c2 & RED_MASK) >> 16,
       
  6955             bg = (c2 & GREEN_MASK) >> 8,
       
  6956             bb = (c2 & BLUE_MASK),
       
  6957             cr = ar + br - ((ar * br) >> 7),
       
  6958             cg = ag + bg - ((ag * bg) >> 7),
       
  6959             cb = ab + bb - ((ab * bb) >> 7);
       
  6960 
       
  6961           return applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb);
       
  6962         },
       
  6963         multiply: function(c1, c2) {
       
  6964           var f  = (c2 & ALPHA_MASK) >>> 24,
       
  6965             ar = (c1 & RED_MASK) >> 16,
       
  6966             ag = (c1 & GREEN_MASK) >> 8,
       
  6967             ab = (c1 & BLUE_MASK),
       
  6968             br = (c2 & RED_MASK) >> 16,
       
  6969             bg = (c2 & GREEN_MASK) >> 8,
       
  6970             bb = (c2 & BLUE_MASK),
       
  6971             cr = (ar * br) >> 8,
       
  6972             cg = (ag * bg) >> 8,
       
  6973             cb = (ab * bb) >> 8;
       
  6974 
       
  6975           return applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb);
       
  6976         },
       
  6977         screen: function(c1, c2) {
       
  6978           var f  = (c2 & ALPHA_MASK) >>> 24,
       
  6979             ar = (c1 & RED_MASK) >> 16,
       
  6980             ag = (c1 & GREEN_MASK) >> 8,
       
  6981             ab = (c1 & BLUE_MASK),
       
  6982             br = (c2 & RED_MASK) >> 16,
       
  6983             bg = (c2 & GREEN_MASK) >> 8,
       
  6984             bb = (c2 & BLUE_MASK),
       
  6985             cr = 255 - (((255 - ar) * (255 - br)) >> 8),
       
  6986             cg = 255 - (((255 - ag) * (255 - bg)) >> 8),
       
  6987             cb = 255 - (((255 - ab) * (255 - bb)) >> 8);
       
  6988 
       
  6989           return applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb);
       
  6990         },
       
  6991         hard_light: function(c1, c2) {
       
  6992           var f  = (c2 & ALPHA_MASK) >>> 24,
       
  6993             ar = (c1 & RED_MASK) >> 16,
       
  6994             ag = (c1 & GREEN_MASK) >> 8,
       
  6995             ab = (c1 & BLUE_MASK),
       
  6996             br = (c2 & RED_MASK) >> 16,
       
  6997             bg = (c2 & GREEN_MASK) >> 8,
       
  6998             bb = (c2 & BLUE_MASK),
       
  6999             cr = (br < 128) ? ((ar * br) >> 7) : (255 - (((255 - ar) * (255 - br)) >> 7)),
       
  7000             cg = (bg < 128) ? ((ag * bg) >> 7) : (255 - (((255 - ag) * (255 - bg)) >> 7)),
       
  7001             cb = (bb < 128) ? ((ab * bb) >> 7) : (255 - (((255 - ab) * (255 - bb)) >> 7));
       
  7002 
       
  7003           return applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb);
       
  7004         },
       
  7005         soft_light: function(c1, c2) {
       
  7006           var f  = (c2 & ALPHA_MASK) >>> 24,
       
  7007             ar = (c1 & RED_MASK) >> 16,
       
  7008             ag = (c1 & GREEN_MASK) >> 8,
       
  7009             ab = (c1 & BLUE_MASK),
       
  7010             br = (c2 & RED_MASK) >> 16,
       
  7011             bg = (c2 & GREEN_MASK) >> 8,
       
  7012             bb = (c2 & BLUE_MASK),
       
  7013             cr = ((ar * br) >> 7) + ((ar * ar) >> 8) - ((ar * ar * br) >> 15),
       
  7014             cg = ((ag * bg) >> 7) + ((ag * ag) >> 8) - ((ag * ag * bg) >> 15),
       
  7015             cb = ((ab * bb) >> 7) + ((ab * ab) >> 8) - ((ab * ab * bb) >> 15);
       
  7016 
       
  7017           return applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb);
       
  7018         },
       
  7019         overlay: function(c1, c2) {
       
  7020           var f  = (c2 & ALPHA_MASK) >>> 24,
       
  7021             ar = (c1 & RED_MASK) >> 16,
       
  7022             ag = (c1 & GREEN_MASK) >> 8,
       
  7023             ab = (c1 & BLUE_MASK),
       
  7024             br = (c2 & RED_MASK) >> 16,
       
  7025             bg = (c2 & GREEN_MASK) >> 8,
       
  7026             bb = (c2 & BLUE_MASK),
       
  7027             cr = (ar < 128) ? ((ar * br) >> 7) : (255 - (((255 - ar) * (255 - br)) >> 7)),
       
  7028             cg = (ag < 128) ? ((ag * bg) >> 7) : (255 - (((255 - ag) * (255 - bg)) >> 7)),
       
  7029             cb = (ab < 128) ? ((ab * bb) >> 7) : (255 - (((255 - ab) * (255 - bb)) >> 7));
       
  7030 
       
  7031           return applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb);
       
  7032         },
       
  7033         dodge: function(c1, c2) {
       
  7034           var f  = (c2 & ALPHA_MASK) >>> 24,
       
  7035             ar = (c1 & RED_MASK) >> 16,
       
  7036             ag = (c1 & GREEN_MASK) >> 8,
       
  7037             ab = (c1 & BLUE_MASK),
       
  7038             br = (c2 & RED_MASK) >> 16,
       
  7039             bg = (c2 & GREEN_MASK) >> 8,
       
  7040             bb = (c2 & BLUE_MASK);
       
  7041 
       
  7042           var cr = 255;
       
  7043           if (br !== 255) {
       
  7044             cr = (ar << 8) / (255 - br);
       
  7045             cr = (cr < 0) ? 0 : ((cr > 255) ? 255 : cr);
       
  7046           }
       
  7047 
       
  7048           var cg = 255;
       
  7049           if (bg !== 255) {
       
  7050             cg = (ag << 8) / (255 - bg);
       
  7051             cg = (cg < 0) ? 0 : ((cg > 255) ? 255 : cg);
       
  7052           }
       
  7053 
       
  7054           var cb = 255;
       
  7055           if (bb !== 255) {
       
  7056             cb = (ab << 8) / (255 - bb);
       
  7057             cb = (cb < 0) ? 0 : ((cb > 255) ? 255 : cb);
       
  7058           }
       
  7059 
       
  7060           return applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb);
       
  7061         },
       
  7062         burn: function(c1, c2) {
       
  7063           var f  = (c2 & ALPHA_MASK) >>> 24,
       
  7064             ar = (c1 & RED_MASK) >> 16,
       
  7065             ag = (c1 & GREEN_MASK) >> 8,
       
  7066             ab = (c1 & BLUE_MASK),
       
  7067             br = (c2 & RED_MASK) >> 16,
       
  7068             bg = (c2 & GREEN_MASK) >> 8,
       
  7069             bb = (c2 & BLUE_MASK);
       
  7070 
       
  7071           var cr = 0;
       
  7072           if (br !== 0) {
       
  7073             cr = ((255 - ar) << 8) / br;
       
  7074             cr = 255 - ((cr < 0) ? 0 : ((cr > 255) ? 255 : cr));
       
  7075           }
       
  7076 
       
  7077           var cg = 0;
       
  7078           if (bg !== 0) {
       
  7079             cg = ((255 - ag) << 8) / bg;
       
  7080             cg = 255 - ((cg < 0) ? 0 : ((cg > 255) ? 255 : cg));
       
  7081           }
       
  7082 
       
  7083           var cb = 0;
       
  7084           if (bb !== 0) {
       
  7085             cb = ((255 - ab) << 8) / bb;
       
  7086             cb = 255 - ((cb < 0) ? 0 : ((cb > 255) ? 255 : cb));
       
  7087           }
       
  7088 
       
  7089           return applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb);
       
  7090         }
       
  7091       };
       
  7092     }());
       
  7093 
       
  7094     function color$4(aValue1, aValue2, aValue3, aValue4) {
       
  7095       var r, g, b, a;
       
  7096 
       
  7097       if (curColorMode === PConstants.HSB) {
       
  7098         var rgb = p.color.toRGB(aValue1, aValue2, aValue3);
       
  7099         r = rgb[0];
       
  7100         g = rgb[1];
       
  7101         b = rgb[2];
       
  7102       } else {
       
  7103         r = Math.round(255 * (aValue1 / colorModeX));
       
  7104         g = Math.round(255 * (aValue2 / colorModeY));
       
  7105         b = Math.round(255 * (aValue3 / colorModeZ));
       
  7106       }
       
  7107 
       
  7108       a = Math.round(255 * (aValue4 / colorModeA));
       
  7109 
       
  7110       // Limit values less than 0 and greater than 255
       
  7111       r = (r < 0) ? 0 : r;
       
  7112       g = (g < 0) ? 0 : g;
       
  7113       b = (b < 0) ? 0 : b;
       
  7114       a = (a < 0) ? 0 : a;
       
  7115       r = (r > 255) ? 255 : r;
       
  7116       g = (g > 255) ? 255 : g;
       
  7117       b = (b > 255) ? 255 : b;
       
  7118       a = (a > 255) ? 255 : a;
       
  7119 
       
  7120       // Create color int
       
  7121       return (a << 24) & PConstants.ALPHA_MASK | (r << 16) & PConstants.RED_MASK | (g << 8) & PConstants.GREEN_MASK | b & PConstants.BLUE_MASK;
       
  7122     }
       
  7123 
       
  7124     function color$2(aValue1, aValue2) {
       
  7125       var a;
       
  7126 
       
  7127       // Color int and alpha
       
  7128       if (aValue1 & PConstants.ALPHA_MASK) {
       
  7129         a = Math.round(255 * (aValue2 / colorModeA));
       
  7130         // Limit values less than 0 and greater than 255
       
  7131         a = (a > 255) ? 255 : a;
       
  7132         a = (a < 0) ? 0 : a;
       
  7133 
       
  7134         return aValue1 - (aValue1 & PConstants.ALPHA_MASK) + ((a << 24) & PConstants.ALPHA_MASK);
       
  7135       }
       
  7136       // Grayscale and alpha
       
  7137       if (curColorMode === PConstants.RGB) {
       
  7138         return color$4(aValue1, aValue1, aValue1, aValue2);
       
  7139       }
       
  7140       if (curColorMode === PConstants.HSB) {
       
  7141         return color$4(0, 0, (aValue1 / colorModeX) * colorModeZ, aValue2);
       
  7142       }
       
  7143     }
       
  7144 
       
  7145     function color$1(aValue1) {
       
  7146       // Grayscale
       
  7147       if (aValue1 <= colorModeX && aValue1 >= 0) {
       
  7148           if (curColorMode === PConstants.RGB) {
       
  7149             return color$4(aValue1, aValue1, aValue1, colorModeA);
       
  7150           }
       
  7151           if (curColorMode === PConstants.HSB) {
       
  7152             return color$4(0, 0, (aValue1 / colorModeX) * colorModeZ, colorModeA);
       
  7153           }
       
  7154       }
       
  7155       // Color int
       
  7156       if (aValue1) {
       
  7157         if (aValue1 > 2147483647) {
       
  7158           // Java Overflow
       
  7159           aValue1 -= 4294967296;
       
  7160         }
       
  7161         return aValue1;
       
  7162       }
       
  7163     }
       
  7164 
       
  7165     /**
       
  7166     * Creates colors for storing in variables of the color datatype. The parameters are
       
  7167     * interpreted as RGB or HSB values depending on the current colorMode(). The default
       
  7168     * mode is RGB values from 0 to 255 and therefore, the function call color(255, 204, 0)
       
  7169     * will return a bright yellow color. More about how colors are stored can be found in
       
  7170     * the reference for the color datatype.
       
  7171     *
       
  7172     * @param {int|float} aValue1        red or hue or grey values relative to the current color range.
       
  7173     * Also can be color value in hexadecimal notation (i.e. #FFCC00 or 0xFFFFCC00)
       
  7174     * @param {int|float} aValue2        green or saturation values relative to the current color range
       
  7175     * @param {int|float} aValue3        blue or brightness values relative to the current color range
       
  7176     * @param {int|float} aValue4        relative to current color range. Represents alpha
       
  7177     *
       
  7178     * @returns {color} the color
       
  7179     *
       
  7180     * @see colorMode
       
  7181     */
       
  7182     p.color = function(aValue1, aValue2, aValue3, aValue4) {
       
  7183 
       
  7184       // 4 arguments: (R, G, B, A) or (H, S, B, A)
       
  7185       if (aValue1 !== undef && aValue2 !== undef && aValue3 !== undef && aValue4 !== undef) {
       
  7186         return color$4(aValue1, aValue2, aValue3, aValue4);
       
  7187       }
       
  7188 
       
  7189       // 3 arguments: (R, G, B) or (H, S, B)
       
  7190       if (aValue1 !== undef && aValue2 !== undef && aValue3 !== undef) {
       
  7191         return color$4(aValue1, aValue2, aValue3, colorModeA);
       
  7192       }
       
  7193 
       
  7194       // 2 arguments: (Color, A) or (Grayscale, A)
       
  7195       if (aValue1 !== undef && aValue2 !== undef) {
       
  7196         return color$2(aValue1, aValue2);
       
  7197       }
       
  7198 
       
  7199       // 1 argument: (Grayscale) or (Color)
       
  7200       if (typeof aValue1 === "number") {
       
  7201         return color$1(aValue1);
       
  7202       }
       
  7203 
       
  7204       // Default
       
  7205       return color$4(colorModeX, colorModeY, colorModeZ, colorModeA);
       
  7206     };
       
  7207 
       
  7208     // Ease of use function to extract the colour bits into a string
       
  7209     p.color.toString = function(colorInt) {
       
  7210       return "rgba(" + ((colorInt & PConstants.RED_MASK) >>> 16) + "," + ((colorInt & PConstants.GREEN_MASK) >>> 8) +
       
  7211              "," + ((colorInt & PConstants.BLUE_MASK)) + "," + ((colorInt & PConstants.ALPHA_MASK) >>> 24) / 255 + ")";
       
  7212     };
       
  7213 
       
  7214     // Easy of use function to pack rgba values into a single bit-shifted color int.
       
  7215     p.color.toInt = function(r, g, b, a) {
       
  7216       return (a << 24) & PConstants.ALPHA_MASK | (r << 16) & PConstants.RED_MASK | (g << 8) & PConstants.GREEN_MASK | b & PConstants.BLUE_MASK;
       
  7217     };
       
  7218 
       
  7219     // Creates a simple array in [R, G, B, A] format, [255, 255, 255, 255]
       
  7220     p.color.toArray = function(colorInt) {
       
  7221       return [(colorInt & PConstants.RED_MASK) >>> 16, (colorInt & PConstants.GREEN_MASK) >>> 8,
       
  7222               colorInt & PConstants.BLUE_MASK, (colorInt & PConstants.ALPHA_MASK) >>> 24];
       
  7223     };
       
  7224 
       
  7225     // Creates a WebGL color array in [R, G, B, A] format. WebGL wants the color ranges between 0 and 1, [1, 1, 1, 1]
       
  7226     p.color.toGLArray = function(colorInt) {
       
  7227       return [((colorInt & PConstants.RED_MASK) >>> 16) / 255, ((colorInt & PConstants.GREEN_MASK) >>> 8) / 255,
       
  7228               (colorInt & PConstants.BLUE_MASK) / 255, ((colorInt & PConstants.ALPHA_MASK) >>> 24) / 255];
       
  7229     };
       
  7230 
       
  7231     // HSB conversion function from Mootools, MIT Licensed
       
  7232     p.color.toRGB = function(h, s, b) {
       
  7233       // Limit values greater than range
       
  7234       h = (h > colorModeX) ? colorModeX : h;
       
  7235       s = (s > colorModeY) ? colorModeY : s;
       
  7236       b = (b > colorModeZ) ? colorModeZ : b;
       
  7237 
       
  7238       h = (h / colorModeX) * 360;
       
  7239       s = (s / colorModeY) * 100;
       
  7240       b = (b / colorModeZ) * 100;
       
  7241 
       
  7242       var br = Math.round(b / 100 * 255);
       
  7243 
       
  7244       if (s === 0) { // Grayscale
       
  7245         return [br, br, br];
       
  7246       }
       
  7247       var hue = h % 360;
       
  7248       var f = hue % 60;
       
  7249       var p = Math.round((b * (100 - s)) / 10000 * 255);
       
  7250       var q = Math.round((b * (6000 - s * f)) / 600000 * 255);
       
  7251       var t = Math.round((b * (6000 - s * (60 - f))) / 600000 * 255);
       
  7252       switch (Math.floor(hue / 60)) {
       
  7253       case 0:
       
  7254         return [br, t, p];
       
  7255       case 1:
       
  7256         return [q, br, p];
       
  7257       case 2:
       
  7258         return [p, br, t];
       
  7259       case 3:
       
  7260         return [p, q, br];
       
  7261       case 4:
       
  7262         return [t, p, br];
       
  7263       case 5:
       
  7264         return [br, p, q];
       
  7265       }
       
  7266     };
       
  7267 
       
  7268     function colorToHSB(colorInt) {
       
  7269       var red, green, blue;
       
  7270 
       
  7271       red   = ((colorInt & PConstants.RED_MASK) >>> 16) / 255;
       
  7272       green = ((colorInt & PConstants.GREEN_MASK) >>> 8) / 255;
       
  7273       blue  = (colorInt & PConstants.BLUE_MASK) / 255;
       
  7274 
       
  7275       var max = p.max(p.max(red,green), blue),
       
  7276           min = p.min(p.min(red,green), blue),
       
  7277           hue, saturation;
       
  7278 
       
  7279       if (min === max) {
       
  7280         return [0, 0, max*colorModeZ];
       
  7281       }
       
  7282       saturation = (max - min) / max;
       
  7283 
       
  7284       if (red === max) {
       
  7285         hue = (green - blue) / (max - min);
       
  7286       } else if (green === max) {
       
  7287         hue = 2 + ((blue - red) / (max - min));
       
  7288       } else {
       
  7289         hue = 4 + ((red - green) / (max - min));
       
  7290       }
       
  7291 
       
  7292       hue /= 6;
       
  7293 
       
  7294       if (hue < 0) {
       
  7295         hue += 1;
       
  7296       } else if (hue > 1) {
       
  7297         hue -= 1;
       
  7298       }
       
  7299       return [hue*colorModeX, saturation*colorModeY, max*colorModeZ];
       
  7300     }
       
  7301 
       
  7302     /**
       
  7303     * Extracts the brightness value from a color.
       
  7304     *
       
  7305     * @param {color} colInt any value of the color datatype
       
  7306     *
       
  7307     * @returns {float} The brightness color value.
       
  7308     *
       
  7309     * @see red
       
  7310     * @see green
       
  7311     * @see blue
       
  7312     * @see hue
       
  7313     * @see saturation
       
  7314     */
       
  7315     p.brightness = function(colInt){
       
  7316       return colorToHSB(colInt)[2];
       
  7317     };
       
  7318 
       
  7319     /**
       
  7320     * Extracts the saturation value from a color.
       
  7321     *
       
  7322     * @param {color} colInt any value of the color datatype
       
  7323     *
       
  7324     * @returns {float} The saturation color value.
       
  7325     *
       
  7326     * @see red
       
  7327     * @see green
       
  7328     * @see blue
       
  7329     * @see hue
       
  7330     * @see brightness
       
  7331     */
       
  7332     p.saturation = function(colInt){
       
  7333       return colorToHSB(colInt)[1];
       
  7334     };
       
  7335 
       
  7336     /**
       
  7337     * Extracts the hue value from a color.
       
  7338     *
       
  7339     * @param {color} colInt any value of the color datatype
       
  7340     *
       
  7341     * @returns {float} The hue color value.
       
  7342     *
       
  7343     * @see red
       
  7344     * @see green
       
  7345     * @see blue
       
  7346     * @see saturation
       
  7347     * @see brightness
       
  7348     */
       
  7349     p.hue = function(colInt){
       
  7350       return colorToHSB(colInt)[0];
       
  7351     };
       
  7352 
       
  7353     /**
       
  7354     * Extracts the red value from a color, scaled to match current colorMode().
       
  7355     * This value is always returned as a float so be careful not to assign it to an int value.
       
  7356     *
       
  7357     * @param {color} aColor any value of the color datatype
       
  7358     *
       
  7359     * @returns {float} The red color value.
       
  7360     *
       
  7361     * @see green
       
  7362     * @see blue
       
  7363     * @see alpha
       
  7364     * @see >> right shift
       
  7365     * @see hue
       
  7366     * @see saturation
       
  7367     * @see brightness
       
  7368     */
       
  7369     p.red = function(aColor) {
       
  7370       return ((aColor & PConstants.RED_MASK) >>> 16) / 255 * colorModeX;
       
  7371     };
       
  7372 
       
  7373     /**
       
  7374     * Extracts the green value from a color, scaled to match current colorMode().
       
  7375     * This value is always returned as a float so be careful not to assign it to an int value.
       
  7376     *
       
  7377     * @param {color} aColor any value of the color datatype
       
  7378     *
       
  7379     * @returns {float} The green color value.
       
  7380     *
       
  7381     * @see red
       
  7382     * @see blue
       
  7383     * @see alpha
       
  7384     * @see >> right shift
       
  7385     * @see hue
       
  7386     * @see saturation
       
  7387     * @see brightness
       
  7388     */
       
  7389     p.green = function(aColor) {
       
  7390       return ((aColor & PConstants.GREEN_MASK) >>> 8) / 255 * colorModeY;
       
  7391     };
       
  7392 
       
  7393     /**
       
  7394     * Extracts the blue value from a color, scaled to match current colorMode().
       
  7395     * This value is always returned as a float so be careful not to assign it to an int value.
       
  7396     *
       
  7397     * @param {color} aColor any value of the color datatype
       
  7398     *
       
  7399     * @returns {float} The blue color value.
       
  7400     *
       
  7401     * @see red
       
  7402     * @see green
       
  7403     * @see alpha
       
  7404     * @see >> right shift
       
  7405     * @see hue
       
  7406     * @see saturation
       
  7407     * @see brightness
       
  7408     */
       
  7409     p.blue = function(aColor) {
       
  7410       return (aColor & PConstants.BLUE_MASK) / 255 * colorModeZ;
       
  7411     };
       
  7412 
       
  7413     /**
       
  7414     * Extracts the alpha value from a color, scaled to match current colorMode().
       
  7415     * This value is always returned as a float so be careful not to assign it to an int value.
       
  7416     *
       
  7417     * @param {color} aColor any value of the color datatype
       
  7418     *
       
  7419     * @returns {float} The alpha color value.
       
  7420     *
       
  7421     * @see red
       
  7422     * @see green
       
  7423     * @see blue
       
  7424     * @see >> right shift
       
  7425     * @see hue
       
  7426     * @see saturation
       
  7427     * @see brightness
       
  7428     */
       
  7429     p.alpha = function(aColor) {
       
  7430       return ((aColor & PConstants.ALPHA_MASK) >>> 24) / 255 * colorModeA;
       
  7431     };
       
  7432 
       
  7433     /**
       
  7434     * Calculates a color or colors between two colors at a specific increment.
       
  7435     * The amt parameter is the amount to interpolate between the two values where 0.0
       
  7436     * equal to the first point, 0.1 is very near the first point, 0.5 is half-way in between, etc.
       
  7437     *
       
  7438     * @param {color} c1     interpolate from this color
       
  7439     * @param {color} c2     interpolate to this color
       
  7440     * @param {float} amt    between 0.0 and 1.0
       
  7441     *
       
  7442     * @returns {float} The blended color.
       
  7443     *
       
  7444     * @see blendColor
       
  7445     * @see color
       
  7446     */
       
  7447     p.lerpColor = function(c1, c2, amt) {
       
  7448       var r, g, b, a, r1, g1, b1, a1, r2, g2, b2, a2;
       
  7449       var hsb1, hsb2, rgb, h, s;
       
  7450       var colorBits1 = p.color(c1);
       
  7451       var colorBits2 = p.color(c2);
       
  7452 
       
  7453       if (curColorMode === PConstants.HSB) {
       
  7454         // Special processing for HSB mode.
       
  7455         // Get HSB and Alpha values for Color 1 and 2
       
  7456         hsb1 = colorToHSB(colorBits1);
       
  7457         a1 = ((colorBits1 & PConstants.ALPHA_MASK) >>> 24) / colorModeA;
       
  7458         hsb2 = colorToHSB(colorBits2);
       
  7459         a2 = ((colorBits2 & PConstants.ALPHA_MASK) >>> 24) / colorModeA;
       
  7460 
       
  7461         // Return lerp value for each channel, for HSB components
       
  7462         h = p.lerp(hsb1[0], hsb2[0], amt);
       
  7463         s = p.lerp(hsb1[1], hsb2[1], amt);
       
  7464         b = p.lerp(hsb1[2], hsb2[2], amt);
       
  7465         rgb = p.color.toRGB(h, s, b);
       
  7466         // ... and for Alpha-range
       
  7467         a = p.lerp(a1, a2, amt) * colorModeA;
       
  7468 
       
  7469         return (a << 24) & PConstants.ALPHA_MASK |
       
  7470                (rgb[0] << 16) & PConstants.RED_MASK |
       
  7471                (rgb[1] << 8) & PConstants.GREEN_MASK |
       
  7472                rgb[2] & PConstants.BLUE_MASK;
       
  7473       }
       
  7474 
       
  7475       // Get RGBA values for Color 1 to floats
       
  7476       r1 = (colorBits1 & PConstants.RED_MASK) >>> 16;
       
  7477       g1 = (colorBits1 & PConstants.GREEN_MASK) >>> 8;
       
  7478       b1 = (colorBits1 & PConstants.BLUE_MASK);
       
  7479       a1 = ((colorBits1 & PConstants.ALPHA_MASK) >>> 24) / colorModeA;
       
  7480 
       
  7481       // Get RGBA values for Color 2 to floats
       
  7482       r2 = (colorBits2 & PConstants.RED_MASK) >>> 16;
       
  7483       g2 = (colorBits2 & PConstants.GREEN_MASK) >>> 8;
       
  7484       b2 = (colorBits2 & PConstants.BLUE_MASK);
       
  7485       a2 = ((colorBits2 & PConstants.ALPHA_MASK) >>> 24) / colorModeA;
       
  7486 
       
  7487       // Return lerp value for each channel, INT for color, Float for Alpha-range
       
  7488       r = p.lerp(r1, r2, amt) | 0;
       
  7489       g = p.lerp(g1, g2, amt) | 0;
       
  7490       b = p.lerp(b1, b2, amt) | 0;
       
  7491       a = p.lerp(a1, a2, amt) * colorModeA;
       
  7492 
       
  7493       return (a << 24) & PConstants.ALPHA_MASK |
       
  7494              (r << 16) & PConstants.RED_MASK |
       
  7495              (g << 8) & PConstants.GREEN_MASK |
       
  7496              b & PConstants.BLUE_MASK;
       
  7497     };
       
  7498 
       
  7499     /**
       
  7500     * Changes the way Processing interprets color data. By default, fill(), stroke(), and background()
       
  7501     * colors are set by values between 0 and 255 using the RGB color model. It is possible to change the
       
  7502     * numerical range used for specifying colors and to switch color systems. For example, calling colorMode(RGB, 1.0)
       
  7503     * will specify that values are specified between 0 and 1. The limits for defining colors are altered by setting the
       
  7504     * parameters range1, range2, range3, and range 4.
       
  7505     *
       
  7506     * @param {MODE} mode Either RGB or HSB, corresponding to Red/Green/Blue and Hue/Saturation/Brightness
       
  7507     * @param {int|float} range              range for all color elements
       
  7508     * @param {int|float} range1             range for the red or hue depending on the current color mode
       
  7509     * @param {int|float} range2             range for the green or saturation depending on the current color mode
       
  7510     * @param {int|float} range3             range for the blue or brightness depending on the current color mode
       
  7511     * @param {int|float} range4             range for the alpha
       
  7512     *
       
  7513     * @returns none
       
  7514     *
       
  7515     * @see background
       
  7516     * @see fill
       
  7517     * @see stroke
       
  7518     */
       
  7519     p.colorMode = function() { // mode, range1, range2, range3, range4
       
  7520       curColorMode = arguments[0];
       
  7521       if (arguments.length > 1) {
       
  7522         colorModeX   = arguments[1];
       
  7523         colorModeY   = arguments[2] || arguments[1];
       
  7524         colorModeZ   = arguments[3] || arguments[1];
       
  7525         colorModeA   = arguments[4] || arguments[1];
       
  7526       }
       
  7527     };
       
  7528 
       
  7529     /**
       
  7530     * Blends two color values together based on the blending mode given as the MODE parameter.
       
  7531     * The possible modes are described in the reference for the blend() function.
       
  7532     *
       
  7533     * @param {color} c1 color: the first color to blend
       
  7534     * @param {color} c2 color: the second color to blend
       
  7535     * @param {MODE} MODE Either BLEND, ADD, SUBTRACT, DARKEST, LIGHTEST, DIFFERENCE, EXCLUSION, MULTIPLY,
       
  7536     * SCREEN, OVERLAY, HARD_LIGHT, SOFT_LIGHT, DODGE, or BURN
       
  7537     *
       
  7538     * @returns {float} The blended color.
       
  7539     *
       
  7540     * @see blend
       
  7541     * @see color
       
  7542     */
       
  7543     p.blendColor = function(c1, c2, mode) {
       
  7544       if (mode === PConstants.REPLACE) {
       
  7545         return p.modes.replace(c1, c2);
       
  7546       } else if (mode === PConstants.BLEND) {
       
  7547         return p.modes.blend(c1, c2);
       
  7548       } else if (mode === PConstants.ADD) {
       
  7549         return p.modes.add(c1, c2);
       
  7550       } else if (mode === PConstants.SUBTRACT) {
       
  7551         return p.modes.subtract(c1, c2);
       
  7552       } else if (mode === PConstants.LIGHTEST) {
       
  7553         return p.modes.lightest(c1, c2);
       
  7554       } else if (mode === PConstants.DARKEST) {
       
  7555         return p.modes.darkest(c1, c2);
       
  7556       } else if (mode === PConstants.DIFFERENCE) {
       
  7557         return p.modes.difference(c1, c2);
       
  7558       } else if (mode === PConstants.EXCLUSION) {
       
  7559         return p.modes.exclusion(c1, c2);
       
  7560       } else if (mode === PConstants.MULTIPLY) {
       
  7561         return p.modes.multiply(c1, c2);
       
  7562       } else if (mode === PConstants.SCREEN) {
       
  7563         return p.modes.screen(c1, c2);
       
  7564       } else if (mode === PConstants.HARD_LIGHT) {
       
  7565         return p.modes.hard_light(c1, c2);
       
  7566       } else if (mode === PConstants.SOFT_LIGHT) {
       
  7567         return p.modes.soft_light(c1, c2);
       
  7568       } else if (mode === PConstants.OVERLAY) {
       
  7569         return p.modes.overlay(c1, c2);
       
  7570       } else if (mode === PConstants.DODGE) {
       
  7571         return p.modes.dodge(c1, c2);
       
  7572       } else if (mode === PConstants.BURN) {
       
  7573         return p.modes.burn(c1, c2);
       
  7574       }
       
  7575     };
       
  7576 
       
  7577     ////////////////////////////////////////////////////////////////////////////
       
  7578     // Canvas-Matrix manipulation
       
  7579     ////////////////////////////////////////////////////////////////////////////
       
  7580 
       
  7581     function saveContext() {
       
  7582       curContext.save();
       
  7583     }
       
  7584 
       
  7585     function restoreContext() {
       
  7586       curContext.restore();
       
  7587       isStrokeDirty = true;
       
  7588       isFillDirty = true;
       
  7589     }
       
  7590 
       
  7591     /**
       
  7592     * Prints the current matrix to the text window.
       
  7593     *
       
  7594     * @returns none
       
  7595     *
       
  7596     * @see pushMatrix
       
  7597     * @see popMatrix
       
  7598     * @see resetMatrix
       
  7599     * @see applyMatrix
       
  7600     */
       
  7601     p.printMatrix = function() {
       
  7602       modelView.print();
       
  7603     };
       
  7604 
       
  7605     /**
       
  7606     * Specifies an amount to displace objects within the display window. The x parameter specifies left/right translation,
       
  7607     * the y parameter specifies up/down translation, and the z parameter specifies translations toward/away from the screen.
       
  7608     * Using this function with the z  parameter requires using the P3D or OPENGL parameter in combination with size as shown
       
  7609     * in the above example. Transformations apply to everything that happens after and subsequent calls to the function
       
  7610     * accumulates the effect. For example, calling translate(50, 0) and then translate(20, 0) is the same as translate(70, 0).
       
  7611     * If translate() is called within draw(), the transformation is reset when the loop begins again.
       
  7612     * This function can be further controlled by the pushMatrix() and popMatrix().
       
  7613     *
       
  7614     * @param {int|float} x        left/right translation
       
  7615     * @param {int|float} y        up/down translation
       
  7616     * @param {int|float} z        forward/back translation
       
  7617     *
       
  7618     * @returns none
       
  7619     *
       
  7620     * @see pushMatrix
       
  7621     * @see popMatrix
       
  7622     * @see scale
       
  7623     * @see rotate
       
  7624     * @see rotateX
       
  7625     * @see rotateY
       
  7626     * @see rotateZ
       
  7627     */
       
  7628     Drawing2D.prototype.translate = function(x, y) {
       
  7629       modelView.translate(x, y);
       
  7630       modelViewInv.invTranslate(x, y);
       
  7631       curContext.translate(x, y);
       
  7632     };
       
  7633 
       
  7634     Drawing3D.prototype.translate = function(x, y, z) {
       
  7635       modelView.translate(x, y, z);
       
  7636       modelViewInv.invTranslate(x, y, z);
       
  7637     };
       
  7638 
       
  7639     /**
       
  7640     * Increases or decreases the size of a shape by expanding and contracting vertices. Objects always scale from their
       
  7641     * relative origin to the coordinate system. Scale values are specified as decimal percentages. For example, the
       
  7642     * function call scale(2.0) increases the dimension of a shape by 200%. Transformations apply to everything that
       
  7643     * happens after and subsequent calls to the function multiply the effect. For example, calling scale(2.0) and
       
  7644     * then scale(1.5) is the same as scale(3.0). If scale() is called within draw(), the transformation is reset when
       
  7645     * the loop begins again. Using this fuction with the z  parameter requires passing P3D or OPENGL into the size()
       
  7646     * parameter as shown in the example above. This function can be further controlled by pushMatrix() and popMatrix().
       
  7647     *
       
  7648     * @param {int|float} size     percentage to scale the object
       
  7649     * @param {int|float} x        percentage to scale the object in the x-axis
       
  7650     * @param {int|float} y        percentage to scale the object in the y-axis
       
  7651     * @param {int|float} z        percentage to scale the object in the z-axis
       
  7652     *
       
  7653     * @returns none
       
  7654     *
       
  7655     * @see pushMatrix
       
  7656     * @see popMatrix
       
  7657     * @see translate
       
  7658     * @see rotate
       
  7659     * @see rotateX
       
  7660     * @see rotateY
       
  7661     * @see rotateZ
       
  7662     */
       
  7663     Drawing2D.prototype.scale = function(x, y) {
       
  7664       modelView.scale(x, y);
       
  7665       modelViewInv.invScale(x, y);
       
  7666       curContext.scale(x, y || x);
       
  7667     };
       
  7668 
       
  7669     Drawing3D.prototype.scale = function(x, y, z) {
       
  7670       modelView.scale(x, y, z);
       
  7671       modelViewInv.invScale(x, y, z);
       
  7672     };
       
  7673 
       
  7674     /**
       
  7675     * Pushes the current transformation matrix onto the matrix stack. Understanding pushMatrix() and popMatrix()
       
  7676     * requires understanding the concept of a matrix stack. The pushMatrix() function saves the current coordinate
       
  7677     * system to the stack and popMatrix() restores the prior coordinate system. pushMatrix() and popMatrix() are
       
  7678     * used in conjuction with the other transformation methods and may be embedded to control the scope of
       
  7679     * the transformations.
       
  7680     *
       
  7681     * @returns none
       
  7682     *
       
  7683     * @see popMatrix
       
  7684     * @see translate
       
  7685     * @see rotate
       
  7686     * @see rotateX
       
  7687     * @see rotateY
       
  7688     * @see rotateZ
       
  7689     */
       
  7690     Drawing2D.prototype.pushMatrix = function() {
       
  7691       userMatrixStack.load(modelView);
       
  7692       userReverseMatrixStack.load(modelViewInv);
       
  7693       saveContext();
       
  7694     };
       
  7695 
       
  7696     Drawing3D.prototype.pushMatrix = function() {
       
  7697       userMatrixStack.load(modelView);
       
  7698       userReverseMatrixStack.load(modelViewInv);
       
  7699     };
       
  7700 
       
  7701     /**
       
  7702     * Pops the current transformation matrix off the matrix stack. Understanding pushing and popping requires
       
  7703     * understanding the concept of a matrix stack. The pushMatrix() function saves the current coordinate system to
       
  7704     * the stack and popMatrix() restores the prior coordinate system. pushMatrix() and popMatrix() are used in
       
  7705     * conjuction with the other transformation methods and may be embedded to control the scope of the transformations.
       
  7706     *
       
  7707     * @returns none
       
  7708     *
       
  7709     * @see popMatrix
       
  7710     * @see pushMatrix
       
  7711     */
       
  7712     Drawing2D.prototype.popMatrix = function() {
       
  7713       modelView.set(userMatrixStack.pop());
       
  7714       modelViewInv.set(userReverseMatrixStack.pop());
       
  7715       restoreContext();
       
  7716     };
       
  7717 
       
  7718     Drawing3D.prototype.popMatrix = function() {
       
  7719       modelView.set(userMatrixStack.pop());
       
  7720       modelViewInv.set(userReverseMatrixStack.pop());
       
  7721     };
       
  7722 
       
  7723     /**
       
  7724     * Replaces the current matrix with the identity matrix. The equivalent function in OpenGL is glLoadIdentity().
       
  7725     *
       
  7726     * @returns none
       
  7727     *
       
  7728     * @see popMatrix
       
  7729     * @see pushMatrix
       
  7730     * @see applyMatrix
       
  7731     * @see printMatrix
       
  7732     */
       
  7733     Drawing2D.prototype.resetMatrix = function() {
       
  7734       modelView.reset();
       
  7735       modelViewInv.reset();
       
  7736       curContext.setTransform(1,0,0,1,0,0);
       
  7737     };
       
  7738 
       
  7739     Drawing3D.prototype.resetMatrix = function() {
       
  7740       modelView.reset();
       
  7741       modelViewInv.reset();
       
  7742     };
       
  7743 
       
  7744     /**
       
  7745     * Multiplies the current matrix by the one specified through the parameters. This is very slow because it will
       
  7746     * try to calculate the inverse of the transform, so avoid it whenever possible. The equivalent function
       
  7747     * in OpenGL is glMultMatrix().
       
  7748     *
       
  7749     * @param {int|float} n00-n15      numbers which define the 4x4 matrix to be multiplied
       
  7750     *
       
  7751     * @returns none
       
  7752     *
       
  7753     * @see popMatrix
       
  7754     * @see pushMatrix
       
  7755     * @see resetMatrix
       
  7756     * @see printMatrix
       
  7757     */
       
  7758     DrawingShared.prototype.applyMatrix = function() {
       
  7759       var a = arguments;
       
  7760       modelView.apply(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15]);
       
  7761       modelViewInv.invApply(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15]);
       
  7762     };
       
  7763 
       
  7764     Drawing2D.prototype.applyMatrix = function() {
       
  7765       var a = arguments;
       
  7766       for (var cnt = a.length; cnt < 16; cnt++) {
       
  7767         a[cnt] = 0;
       
  7768       }
       
  7769       a[10] = a[15] = 1;
       
  7770       DrawingShared.prototype.applyMatrix.apply(this, a);
       
  7771     };
       
  7772 
       
  7773     /**
       
  7774     * Rotates a shape around the x-axis the amount specified by the angle parameter. Angles should be
       
  7775     * specified in radians (values from 0 to PI*2) or converted to radians with the radians()  function.
       
  7776     * Objects are always rotated around their relative position to the origin and positive numbers
       
  7777     * rotate objects in a counterclockwise direction. Transformations apply to everything that happens
       
  7778     * after and subsequent calls to the function accumulates the effect. For example, calling rotateX(PI/2)
       
  7779     * and then rotateX(PI/2) is the same as rotateX(PI). If rotateX() is called within the draw(), the
       
  7780     * transformation is reset when the loop begins again. This function requires passing P3D or OPENGL
       
  7781     * into the size() parameter as shown in the example above.
       
  7782     *
       
  7783     * @param {int|float} angleInRadians     angle of rotation specified in radians
       
  7784     *
       
  7785     * @returns none
       
  7786     *
       
  7787     * @see rotateY
       
  7788     * @see rotateZ
       
  7789     * @see rotate
       
  7790     * @see translate
       
  7791     * @see scale
       
  7792     * @see popMatrix
       
  7793     * @see pushMatrix
       
  7794     */
       
  7795     p.rotateX = function(angleInRadians) {
       
  7796       modelView.rotateX(angleInRadians);
       
  7797       modelViewInv.invRotateX(angleInRadians);
       
  7798     };
       
  7799 
       
  7800     /**
       
  7801     * Rotates a shape around the z-axis the amount specified by the angle parameter. Angles should be
       
  7802     * specified in radians (values from 0 to PI*2) or converted to radians with the radians()  function.
       
  7803     * Objects are always rotated around their relative position to the origin and positive numbers
       
  7804     * rotate objects in a counterclockwise direction. Transformations apply to everything that happens
       
  7805     * after and subsequent calls to the function accumulates the effect. For example, calling rotateZ(PI/2)
       
  7806     * and then rotateZ(PI/2) is the same as rotateZ(PI). If rotateZ() is called within the draw(), the
       
  7807     * transformation is reset when the loop begins again. This function requires passing P3D or OPENGL
       
  7808     * into the size() parameter as shown in the example above.
       
  7809     *
       
  7810     * @param {int|float} angleInRadians     angle of rotation specified in radians
       
  7811     *
       
  7812     * @returns none
       
  7813     *
       
  7814     * @see rotateX
       
  7815     * @see rotateY
       
  7816     * @see rotate
       
  7817     * @see translate
       
  7818     * @see scale
       
  7819     * @see popMatrix
       
  7820     * @see pushMatrix
       
  7821     */
       
  7822     Drawing2D.prototype.rotateZ = function() {
       
  7823       throw "rotateZ() is not supported in 2D mode. Use rotate(float) instead.";
       
  7824     };
       
  7825 
       
  7826     Drawing3D.prototype.rotateZ = function(angleInRadians) {
       
  7827       modelView.rotateZ(angleInRadians);
       
  7828       modelViewInv.invRotateZ(angleInRadians);
       
  7829     };
       
  7830 
       
  7831     /**
       
  7832     * Rotates a shape around the y-axis the amount specified by the angle parameter. Angles should be
       
  7833     * specified in radians (values from 0 to PI*2) or converted to radians with the radians()  function.
       
  7834     * Objects are always rotated around their relative position to the origin and positive numbers
       
  7835     * rotate objects in a counterclockwise direction. Transformations apply to everything that happens
       
  7836     * after and subsequent calls to the function accumulates the effect. For example, calling rotateY(PI/2)
       
  7837     * and then rotateY(PI/2) is the same as rotateY(PI). If rotateY() is called within the draw(), the
       
  7838     * transformation is reset when the loop begins again. This function requires passing P3D or OPENGL
       
  7839     * into the size() parameter as shown in the example above.
       
  7840     *
       
  7841     * @param {int|float} angleInRadians     angle of rotation specified in radians
       
  7842     *
       
  7843     * @returns none
       
  7844     *
       
  7845     * @see rotateX
       
  7846     * @see rotateZ
       
  7847     * @see rotate
       
  7848     * @see translate
       
  7849     * @see scale
       
  7850     * @see popMatrix
       
  7851     * @see pushMatrix
       
  7852     */
       
  7853     p.rotateY = function(angleInRadians) {
       
  7854       modelView.rotateY(angleInRadians);
       
  7855       modelViewInv.invRotateY(angleInRadians);
       
  7856     };
       
  7857 
       
  7858     /**
       
  7859     * Rotates a shape the amount specified by the angle parameter. Angles should be specified in radians
       
  7860     * (values from 0 to TWO_PI) or converted to radians with the radians() function. Objects are always
       
  7861     * rotated around their relative position to the origin and positive numbers rotate objects in a
       
  7862     * clockwise direction. Transformations apply to everything that happens after and subsequent calls
       
  7863     * to the function accumulates the effect. For example, calling rotate(HALF_PI) and then rotate(HALF_PI)
       
  7864     * is the same as rotate(PI). All tranformations are reset when draw() begins again. Technically,
       
  7865     * rotate() multiplies the current transformation matrix by a rotation matrix. This function can be
       
  7866     * further controlled by the pushMatrix() and popMatrix().
       
  7867     *
       
  7868     * @param {int|float} angleInRadians     angle of rotation specified in radians
       
  7869     *
       
  7870     * @returns none
       
  7871     *
       
  7872     * @see rotateX
       
  7873     * @see rotateY
       
  7874     * @see rotateZ
       
  7875     * @see rotate
       
  7876     * @see translate
       
  7877     * @see scale
       
  7878     * @see popMatrix
       
  7879     * @see pushMatrix
       
  7880     */
       
  7881     Drawing2D.prototype.rotate = function(angleInRadians) {
       
  7882       modelView.rotateZ(angleInRadians);
       
  7883       modelViewInv.invRotateZ(angleInRadians);
       
  7884       curContext.rotate(angleInRadians);
       
  7885     };
       
  7886 
       
  7887     Drawing3D.prototype.rotate = function(angleInRadians) {
       
  7888       p.rotateZ(angleInRadians);
       
  7889     };
       
  7890 
       
  7891     /**
       
  7892     * The pushStyle() function saves the current style settings and popStyle()  restores the prior settings.
       
  7893     * Note that these functions are always used together. They allow you to change the style settings and later
       
  7894     * return to what you had. When a new style is started with pushStyle(), it builds on the current style information.
       
  7895     * The pushStyle() and popStyle() functions can be embedded to provide more control (see the second example
       
  7896     * above for a demonstration.)
       
  7897     * The style information controlled by the following functions are included in the style: fill(), stroke(), tint(),
       
  7898     * strokeWeight(), strokeCap(), strokeJoin(), imageMode(), rectMode(), ellipseMode(), shapeMode(), colorMode(),
       
  7899     * textAlign(), textFont(), textMode(), textSize(), textLeading(), emissive(), specular(), shininess(), ambient()
       
  7900     *
       
  7901     * @returns none
       
  7902     *
       
  7903     * @see popStyle
       
  7904     */
       
  7905     p.pushStyle = function() {
       
  7906       // Save the canvas state.
       
  7907       saveContext();
       
  7908 
       
  7909       p.pushMatrix();
       
  7910 
       
  7911       var newState = {
       
  7912         'doFill': doFill,
       
  7913         'currentFillColor': currentFillColor,
       
  7914         'doStroke': doStroke,
       
  7915         'currentStrokeColor': currentStrokeColor,
       
  7916         'curTint': curTint,
       
  7917         'curRectMode': curRectMode,
       
  7918         'curColorMode': curColorMode,
       
  7919         'colorModeX': colorModeX,
       
  7920         'colorModeZ': colorModeZ,
       
  7921         'colorModeY': colorModeY,
       
  7922         'colorModeA': colorModeA,
       
  7923         'curTextFont': curTextFont,
       
  7924         'horizontalTextAlignment': horizontalTextAlignment,
       
  7925         'verticalTextAlignment': verticalTextAlignment,
       
  7926         'textMode': textMode,
       
  7927         'curFontName': curFontName,
       
  7928         'curTextSize': curTextSize,
       
  7929         'curTextAscent': curTextAscent,
       
  7930         'curTextDescent': curTextDescent,
       
  7931         'curTextLeading': curTextLeading
       
  7932       };
       
  7933 
       
  7934       styleArray.push(newState);
       
  7935     };
       
  7936 
       
  7937     /**
       
  7938     * The pushStyle() function saves the current style settings and popStyle()  restores the prior settings; these
       
  7939     * functions are always used together. They allow you to change the style settings and later return to what you had.
       
  7940     * When a new style is started with pushStyle(), it builds on the current style information. The pushStyle() and
       
  7941     * popStyle() functions can be embedded to provide more control (see the second example above for a demonstration.)
       
  7942     *
       
  7943     * @returns none
       
  7944     *
       
  7945     * @see pushStyle
       
  7946     */
       
  7947     p.popStyle = function() {
       
  7948       var oldState = styleArray.pop();
       
  7949 
       
  7950       if (oldState) {
       
  7951         restoreContext();
       
  7952 
       
  7953         p.popMatrix();
       
  7954 
       
  7955         doFill = oldState.doFill;
       
  7956         currentFillColor = oldState.currentFillColor;
       
  7957         doStroke = oldState.doStroke;
       
  7958         currentStrokeColor = oldState.currentStrokeColor;
       
  7959         curTint = oldState.curTint;
       
  7960         curRectMode = oldState.curRectmode;
       
  7961         curColorMode = oldState.curColorMode;
       
  7962         colorModeX = oldState.colorModeX;
       
  7963         colorModeZ = oldState.colorModeZ;
       
  7964         colorModeY = oldState.colorModeY;
       
  7965         colorModeA = oldState.colorModeA;
       
  7966         curTextFont = oldState.curTextFont;
       
  7967         curFontName = oldState.curFontName;
       
  7968         curTextSize = oldState.curTextSize;
       
  7969         horizontalTextAlignment = oldState.horizontalTextAlignment;
       
  7970         verticalTextAlignment = oldState.verticalTextAlignment;
       
  7971         textMode = oldState.textMode;
       
  7972         curTextAscent = oldState.curTextAscent;
       
  7973         curTextDescent = oldState.curTextDescent;
       
  7974         curTextLeading = oldState.curTextLeading;
       
  7975       } else {
       
  7976         throw "Too many popStyle() without enough pushStyle()";
       
  7977       }
       
  7978     };
       
  7979 
       
  7980     ////////////////////////////////////////////////////////////////////////////
       
  7981     // Time based functions
       
  7982     ////////////////////////////////////////////////////////////////////////////
       
  7983 
       
  7984     /**
       
  7985     * Processing communicates with the clock on your computer.
       
  7986     * The year() function returns the current year as an integer (2003, 2004, 2005, etc).
       
  7987     *
       
  7988     * @returns {float} The current year.
       
  7989     *
       
  7990     * @see millis
       
  7991     * @see second
       
  7992     * @see minute
       
  7993     * @see hour
       
  7994     * @see day
       
  7995     * @see month
       
  7996     */
       
  7997     p.year = function() {
       
  7998       return new Date().getFullYear();
       
  7999     };
       
  8000     /**
       
  8001     * Processing communicates with the clock on your computer.
       
  8002     * The month() function returns the current month as a value from 1 - 12.
       
  8003     *
       
  8004     * @returns {float} The current month.
       
  8005     *
       
  8006     * @see millis
       
  8007     * @see second
       
  8008     * @see minute
       
  8009     * @see hour
       
  8010     * @see day
       
  8011     * @see year
       
  8012     */
       
  8013     p.month = function() {
       
  8014       return new Date().getMonth() + 1;
       
  8015     };
       
  8016     /**
       
  8017     * Processing communicates with the clock on your computer.
       
  8018     * The day() function returns the current day as a value from 1 - 31.
       
  8019     *
       
  8020     * @returns {float} The current day.
       
  8021     *
       
  8022     * @see millis
       
  8023     * @see second
       
  8024     * @see minute
       
  8025     * @see hour
       
  8026     * @see month
       
  8027     * @see year
       
  8028     */
       
  8029     p.day = function() {
       
  8030       return new Date().getDate();
       
  8031     };
       
  8032     /**
       
  8033     * Processing communicates with the clock on your computer.
       
  8034     * The hour() function returns the current hour as a value from 0 - 23.
       
  8035     *
       
  8036     * @returns {float} The current hour.
       
  8037     *
       
  8038     * @see millis
       
  8039     * @see second
       
  8040     * @see minute
       
  8041     * @see month
       
  8042     * @see day
       
  8043     * @see year
       
  8044     */
       
  8045     p.hour = function() {
       
  8046       return new Date().getHours();
       
  8047     };
       
  8048     /**
       
  8049     * Processing communicates with the clock on your computer.
       
  8050     * The minute() function returns the current minute as a value from 0 - 59.
       
  8051     *
       
  8052     * @returns {float} The current minute.
       
  8053     *
       
  8054     * @see millis
       
  8055     * @see second
       
  8056     * @see month
       
  8057     * @see hour
       
  8058     * @see day
       
  8059     * @see year
       
  8060     */
       
  8061     p.minute = function() {
       
  8062       return new Date().getMinutes();
       
  8063     };
       
  8064     /**
       
  8065     * Processing communicates with the clock on your computer.
       
  8066     * The second() function returns the current second as a value from 0 - 59.
       
  8067     *
       
  8068     * @returns {float} The current minute.
       
  8069     *
       
  8070     * @see millis
       
  8071     * @see month
       
  8072     * @see minute
       
  8073     * @see hour
       
  8074     * @see day
       
  8075     * @see year
       
  8076     */
       
  8077     p.second = function() {
       
  8078       return new Date().getSeconds();
       
  8079     };
       
  8080     /**
       
  8081     * Returns the number of milliseconds (thousandths of a second) since starting a sketch.
       
  8082     * This information is often used for timing animation sequences.
       
  8083     *
       
  8084     * @returns {long} The number of milliseconds since starting the sketch.
       
  8085     *
       
  8086     * @see month
       
  8087     * @see second
       
  8088     * @see minute
       
  8089     * @see hour
       
  8090     * @see day
       
  8091     * @see year
       
  8092     */
       
  8093     p.millis = function() {
       
  8094       return Date.now() - start;
       
  8095     };
       
  8096 
       
  8097     /**
       
  8098     * Executes the code within draw() one time. This functions allows the program to update
       
  8099     * the display window only when necessary, for example when an event registered by
       
  8100     * mousePressed() or keyPressed() occurs.
       
  8101     * In structuring a program, it only makes sense to call redraw() within events such as
       
  8102     * mousePressed(). This is because redraw() does not run draw() immediately (it only sets
       
  8103     * a flag that indicates an update is needed).
       
  8104     * Calling redraw() within draw() has no effect because draw() is continuously called anyway.
       
  8105     *
       
  8106     * @returns none
       
  8107     *
       
  8108     * @see noLoop
       
  8109     * @see loop
       
  8110     */
       
  8111     function redrawHelper() {
       
  8112       var sec = (Date.now() - timeSinceLastFPS) / 1000;
       
  8113       framesSinceLastFPS++;
       
  8114       var fps = framesSinceLastFPS / sec;
       
  8115 
       
  8116       // recalculate FPS every half second for better accuracy.
       
  8117       if (sec > 0.5) {
       
  8118         timeSinceLastFPS = Date.now();
       
  8119         framesSinceLastFPS = 0;
       
  8120         p.__frameRate = fps;
       
  8121       }
       
  8122 
       
  8123       p.frameCount++;
       
  8124     }
       
  8125 
       
  8126     Drawing2D.prototype.redraw = function() {
       
  8127       redrawHelper();
       
  8128 
       
  8129       curContext.lineWidth = lineWidth;
       
  8130       var pmouseXLastEvent = p.pmouseX,
       
  8131           pmouseYLastEvent = p.pmouseY;
       
  8132       p.pmouseX = pmouseXLastFrame;
       
  8133       p.pmouseY = pmouseYLastFrame;
       
  8134 
       
  8135       saveContext();
       
  8136       p.draw();
       
  8137       restoreContext();
       
  8138 
       
  8139       pmouseXLastFrame = p.mouseX;
       
  8140       pmouseYLastFrame = p.mouseY;
       
  8141       p.pmouseX = pmouseXLastEvent;
       
  8142       p.pmouseY = pmouseYLastEvent;
       
  8143     };
       
  8144 
       
  8145     Drawing3D.prototype.redraw = function() {
       
  8146       redrawHelper();
       
  8147 
       
  8148       var pmouseXLastEvent = p.pmouseX,
       
  8149           pmouseYLastEvent = p.pmouseY;
       
  8150       p.pmouseX = pmouseXLastFrame;
       
  8151       p.pmouseY = pmouseYLastFrame;
       
  8152       // even if the color buffer isn't cleared with background(),
       
  8153       // the depth buffer needs to be cleared regardless.
       
  8154       curContext.clear(curContext.DEPTH_BUFFER_BIT);
       
  8155       curContextCache = { attributes: {}, locations: {} };
       
  8156       // Delete all the lighting states and the materials the
       
  8157       // user set in the last draw() call.
       
  8158       p.noLights();
       
  8159       p.lightFalloff(1, 0, 0);
       
  8160       p.shininess(1);
       
  8161       p.ambient(255, 255, 255);
       
  8162       p.specular(0, 0, 0);
       
  8163       p.emissive(0, 0, 0);
       
  8164       p.camera();
       
  8165       p.draw();
       
  8166 
       
  8167       pmouseXLastFrame = p.mouseX;
       
  8168       pmouseYLastFrame = p.mouseY;
       
  8169       p.pmouseX = pmouseXLastEvent;
       
  8170       p.pmouseY = pmouseYLastEvent;
       
  8171     };
       
  8172 
       
  8173     /**
       
  8174     * Stops Processing from continuously executing the code within draw(). If loop() is
       
  8175     * called, the code in draw() begin to run continuously again. If using noLoop() in
       
  8176     * setup(), it should be the last line inside the block.
       
  8177     * When noLoop() is used, it's not possible to manipulate or access the screen inside event
       
  8178     * handling functions such as mousePressed() or keyPressed(). Instead, use those functions
       
  8179     * to call redraw() or loop(), which will run draw(), which can update the screen properly.
       
  8180     * This means that when noLoop() has been called, no drawing can happen, and functions like
       
  8181     * saveFrame() or loadPixels() may not be used.
       
  8182     * Note that if the sketch is resized, redraw() will be called to update the sketch, even
       
  8183     * after noLoop() has been specified. Otherwise, the sketch would enter an odd state until
       
  8184     * loop() was called.
       
  8185     *
       
  8186     * @returns none
       
  8187     *
       
  8188     * @see redraw
       
  8189     * @see draw
       
  8190     * @see loop
       
  8191     */
       
  8192     p.noLoop = function() {
       
  8193       doLoop = false;
       
  8194       loopStarted = false;
       
  8195       clearInterval(looping);
       
  8196       curSketch.onPause();
       
  8197     };
       
  8198 
       
  8199     /**
       
  8200     * Causes Processing to continuously execute the code within draw(). If noLoop() is called,
       
  8201     * the code in draw() stops executing.
       
  8202     *
       
  8203     * @returns none
       
  8204     *
       
  8205     * @see noLoop
       
  8206     */
       
  8207     p.loop = function() {
       
  8208       if (loopStarted) {
       
  8209         return;
       
  8210       }
       
  8211 
       
  8212       timeSinceLastFPS = Date.now();
       
  8213       framesSinceLastFPS = 0;
       
  8214 
       
  8215       looping = window.setInterval(function() {
       
  8216         try {
       
  8217           curSketch.onFrameStart();
       
  8218           p.redraw();
       
  8219           curSketch.onFrameEnd();
       
  8220         } catch(e_loop) {
       
  8221           window.clearInterval(looping);
       
  8222           throw e_loop;
       
  8223         }
       
  8224       }, curMsPerFrame);
       
  8225       doLoop = true;
       
  8226       loopStarted = true;
       
  8227       curSketch.onLoop();
       
  8228     };
       
  8229 
       
  8230     /**
       
  8231     * Specifies the number of frames to be displayed every second. If the processor is not
       
  8232     * fast enough to maintain the specified rate, it will not be achieved. For example, the
       
  8233     * function call frameRate(30) will attempt to refresh 30 times a second. It is recommended
       
  8234     * to set the frame rate within setup(). The default rate is 60 frames per second.
       
  8235     *
       
  8236     * @param {int} aRate        number of frames per second.
       
  8237     *
       
  8238     * @returns none
       
  8239     *
       
  8240     * @see delay
       
  8241     */
       
  8242     p.frameRate = function(aRate) {
       
  8243       curFrameRate = aRate;
       
  8244       curMsPerFrame = 1000 / curFrameRate;
       
  8245 
       
  8246       // clear and reset interval
       
  8247       if (doLoop) {
       
  8248         p.noLoop();
       
  8249         p.loop();
       
  8250       }
       
  8251     };
       
  8252 
       
  8253     ////////////////////////////////////////////////////////////////////////////
       
  8254     // JavaScript event binding and releasing
       
  8255     ////////////////////////////////////////////////////////////////////////////
       
  8256 
       
  8257     var eventHandlers = [];
       
  8258 
       
  8259     function attachEventHandler(elem, type, fn) {
       
  8260       if (elem.addEventListener) {
       
  8261         elem.addEventListener(type, fn, false);
       
  8262       } else {
       
  8263         elem.attachEvent("on" + type, fn);
       
  8264       }
       
  8265       eventHandlers.push({elem: elem, type: type, fn: fn});
       
  8266     }
       
  8267 
       
  8268     function detachEventHandler(eventHandler) {
       
  8269       var elem = eventHandler.elem,
       
  8270           type = eventHandler.type,
       
  8271           fn   = eventHandler.fn;
       
  8272       if (elem.removeEventListener) {
       
  8273         elem.removeEventListener(type, fn, false);
       
  8274       } else if (elem.detachEvent) {
       
  8275         elem.detachEvent("on" + type, fn);
       
  8276       }
       
  8277     }
       
  8278 
       
  8279     /**
       
  8280     * Quits/stops/exits the program. Programs without a draw() function exit automatically
       
  8281     * after the last line has run, but programs with draw() run continuously until the
       
  8282     * program is manually stopped or exit() is run.
       
  8283     * Rather than terminating immediately, exit() will cause the sketch to exit after draw()
       
  8284     * has completed (or after setup() completes if called during the setup() method).
       
  8285     *
       
  8286     * @returns none
       
  8287     */
       
  8288     p.exit = function() {
       
  8289       window.clearInterval(looping);
       
  8290 
       
  8291       removeInstance(p.externals.canvas.id);
       
  8292 
       
  8293       // Step through the libraries to detach them
       
  8294       for (var lib in Processing.lib) {
       
  8295         if (Processing.lib.hasOwnProperty(lib)) {
       
  8296           if (Processing.lib[lib].hasOwnProperty("detach")) {
       
  8297             Processing.lib[lib].detach(p);
       
  8298           }
       
  8299         }
       
  8300       }
       
  8301 
       
  8302       var i = eventHandlers.length;
       
  8303       while (i--) {
       
  8304         detachEventHandler(eventHandlers[i]);
       
  8305       }
       
  8306       curSketch.onExit();
       
  8307     };
       
  8308 
       
  8309     ////////////////////////////////////////////////////////////////////////////
       
  8310     // MISC functions
       
  8311     ////////////////////////////////////////////////////////////////////////////
       
  8312 
       
  8313     /**
       
  8314     * Sets the cursor to a predefined symbol, an image, or turns it on if already hidden.
       
  8315     * If you are trying to set an image as the cursor, it is recommended to make the size
       
  8316     * 16x16 or 32x32 pixels. It is not possible to load an image as the cursor if you are
       
  8317     * exporting your program for the Web. The values for parameters x and y must be less
       
  8318     * than the dimensions of the image.
       
  8319     *
       
  8320     * @param {MODE} MODE either ARROW, CROSS, HAND, MOVE, TEXT, WAIT
       
  8321     * @param {PImage} image       any variable of type PImage
       
  8322     * @param {int}    x           the horizonal active spot of the cursor
       
  8323     * @param {int}    y           the vertical active spot of the cursor
       
  8324     *
       
  8325     * @returns none
       
  8326     *
       
  8327     * @see noCursor
       
  8328     */
       
  8329     p.cursor = function() {
       
  8330       if (arguments.length > 1 || (arguments.length === 1 && arguments[0] instanceof p.PImage)) {
       
  8331         var image = arguments[0],
       
  8332           x, y;
       
  8333         if (arguments.length >= 3) {
       
  8334           x = arguments[1];
       
  8335           y = arguments[2];
       
  8336           if (x < 0 || y < 0 || y >= image.height || x >= image.width) {
       
  8337             throw "x and y must be non-negative and less than the dimensions of the image";
       
  8338           }
       
  8339         } else {
       
  8340           x = image.width >>> 1;
       
  8341           y = image.height >>> 1;
       
  8342         }
       
  8343 
       
  8344         // see https://developer.mozilla.org/en/Using_URL_values_for_the_cursor_property
       
  8345         var imageDataURL = image.toDataURL();
       
  8346         var style = "url(\"" + imageDataURL + "\") " + x + " " + y + ", default";
       
  8347         curCursor = curElement.style.cursor = style;
       
  8348       } else if (arguments.length === 1) {
       
  8349         var mode = arguments[0];
       
  8350         curCursor = curElement.style.cursor = mode;
       
  8351       } else {
       
  8352         curCursor = curElement.style.cursor = oldCursor;
       
  8353       }
       
  8354     };
       
  8355 
       
  8356     /**
       
  8357     * Hides the cursor from view.
       
  8358     *
       
  8359     * @returns none
       
  8360     *
       
  8361     * @see cursor
       
  8362     */
       
  8363     p.noCursor = function() {
       
  8364       curCursor = curElement.style.cursor = PConstants.NOCURSOR;
       
  8365     };
       
  8366 
       
  8367     /**
       
  8368     * Links to a webpage either in the same window or in a new window. The complete URL
       
  8369     * must be specified.
       
  8370     *
       
  8371     * @param {String} href      complete url as a String in quotes
       
  8372     * @param {String} target    name of the window to load the URL as a string in quotes
       
  8373     *
       
  8374     * @returns none
       
  8375     */
       
  8376     p.link = function(href, target) {
       
  8377       if (target !== undef) {
       
  8378         window.open(href, target);
       
  8379       } else {
       
  8380         window.location = href;
       
  8381       }
       
  8382     };
       
  8383 
       
  8384     // PGraphics methods
       
  8385     // These functions exist only for compatibility with P5
       
  8386     p.beginDraw = nop;
       
  8387     p.endDraw = nop;
       
  8388 
       
  8389     /**
       
  8390      * This function takes content from a canvas and turns it into an ImageData object to be used with a PImage
       
  8391      *
       
  8392      * @returns {ImageData}        ImageData object to attach to a PImage (1D array of pixel data)
       
  8393      *
       
  8394      * @see PImage
       
  8395      */
       
  8396     Drawing2D.prototype.toImageData = function(x, y, w, h) {
       
  8397       x = x !== undef ? x : 0;
       
  8398       y = y !== undef ? y : 0;
       
  8399       w = w !== undef ? w : p.width;
       
  8400       h = h !== undef ? h : p.height;
       
  8401       return curContext.getImageData(x, y, w, h);
       
  8402     };
       
  8403 
       
  8404     Drawing3D.prototype.toImageData = function(x, y, w, h) {
       
  8405       x = x !== undef ? x : 0;
       
  8406       y = y !== undef ? y : 0;
       
  8407       w = w !== undef ? w : p.width;
       
  8408       h = h !== undef ? h : p.height;
       
  8409       var c = document.createElement("canvas"),
       
  8410           ctx = c.getContext("2d"),
       
  8411           obj = ctx.createImageData(w, h),
       
  8412           uBuff = new Uint8Array(w * h * 4);
       
  8413       curContext.readPixels(x, y, w, h, curContext.RGBA, curContext.UNSIGNED_BYTE, uBuff);
       
  8414       for (var i=0, ul=uBuff.length, obj_data=obj.data; i < ul; i++) {
       
  8415         obj_data[i] = uBuff[(h - 1 - Math.floor(i / 4 / w)) * w * 4 + (i % (w * 4))];
       
  8416       }
       
  8417       return obj;
       
  8418     };
       
  8419 
       
  8420     /**
       
  8421     * Displays message in the browser's status area. This is the text area in the lower
       
  8422     * left corner of the browser. The status() function will only work when the
       
  8423     * Processing program is running in a web browser.
       
  8424     *
       
  8425     * @param {String} text      any valid String
       
  8426     *
       
  8427     * @returns none
       
  8428     */
       
  8429     p.status = function(text) {
       
  8430       window.status = text;
       
  8431     };
       
  8432 
       
  8433     ////////////////////////////////////////////////////////////////////////////
       
  8434     // Binary Functions
       
  8435     ////////////////////////////////////////////////////////////////////////////
       
  8436 
       
  8437     /**
       
  8438     * Converts a byte, char, int, or color to a String containing the equivalent binary
       
  8439     * notation. For example color(0, 102, 153, 255) will convert to the String
       
  8440     * "11111111000000000110011010011001". This function can help make your geeky debugging
       
  8441     * sessions much happier.
       
  8442     *
       
  8443     * @param {byte|char|int|color} num          byte, char, int, color: value to convert
       
  8444     * @param {int} numBits                      number of digits to return
       
  8445     *
       
  8446     * @returns {String}
       
  8447     *
       
  8448     * @see unhex
       
  8449     * @see hex
       
  8450     * @see unbinary
       
  8451     */
       
  8452     p.binary = function(num, numBits) {
       
  8453       var bit;
       
  8454       if (numBits > 0) {
       
  8455         bit = numBits;
       
  8456       } else if(num instanceof Char) {
       
  8457         bit = 16;
       
  8458         num |= 0; // making it int
       
  8459       } else {
       
  8460         // autodetect, skipping zeros
       
  8461         bit = 32;
       
  8462         while (bit > 1 && !((num >>> (bit - 1)) & 1)) {
       
  8463           bit--;
       
  8464         }
       
  8465       }
       
  8466       var result = "";
       
  8467       while (bit > 0) {
       
  8468         result += ((num >>> (--bit)) & 1) ? "1" : "0";
       
  8469       }
       
  8470       return result;
       
  8471     };
       
  8472 
       
  8473     /**
       
  8474     * Converts a String representation of a binary number to its equivalent integer value.
       
  8475     * For example, unbinary("00001000") will return 8.
       
  8476     *
       
  8477     * @param {String} binaryString String
       
  8478     *
       
  8479     * @returns {Int}
       
  8480     *
       
  8481     * @see hex
       
  8482     * @see binary
       
  8483     * @see unbinary
       
  8484     */
       
  8485     p.unbinary = function(binaryString) {
       
  8486       var i = binaryString.length - 1, mask = 1, result = 0;
       
  8487       while (i >= 0) {
       
  8488         var ch = binaryString[i--];
       
  8489         if (ch !== '0' && ch !== '1') {
       
  8490           throw "the value passed into unbinary was not an 8 bit binary number";
       
  8491         }
       
  8492         if (ch === '1') {
       
  8493           result += mask;
       
  8494         }
       
  8495         mask <<= 1;
       
  8496       }
       
  8497       return result;
       
  8498     };
       
  8499 
       
  8500     /**
       
  8501     * Number-to-String formatting function. Prepends "plus" or "minus" depending
       
  8502     * on whether the value is positive or negative, respectively, after padding
       
  8503     * the value with zeroes on the left and right, the number of zeroes used dictated
       
  8504     * by the values 'leftDigits' and 'rightDigits'. 'value' cannot be an array.
       
  8505     *
       
  8506     * @param {int|float} value                 the number to format
       
  8507     * @param {String} plus                     the prefix for positive numbers
       
  8508     * @param {String} minus                    the prefix for negative numbers
       
  8509     * @param {int} left                        number of digits to the left of the decimal point
       
  8510     * @param {int} right                       number of digits to the right of the decimal point
       
  8511     * @param {String} group                    string delimited for groups, such as the comma in "1,000"
       
  8512     *
       
  8513     * @returns {String or String[]}
       
  8514     *
       
  8515     * @see nfCore
       
  8516     */
       
  8517     function nfCoreScalar(value, plus, minus, leftDigits, rightDigits, group) {
       
  8518       var sign = (value < 0) ? minus : plus;
       
  8519       var autoDetectDecimals = rightDigits === 0;
       
  8520       var rightDigitsOfDefault = (rightDigits === undef || rightDigits < 0) ? 0 : rightDigits;
       
  8521 
       
  8522       var absValue = Math.abs(value);
       
  8523       if (autoDetectDecimals) {
       
  8524         rightDigitsOfDefault = 1;
       
  8525         absValue *= 10;
       
  8526         while (Math.abs(Math.round(absValue) - absValue) > 1e-6 && rightDigitsOfDefault < 7) {
       
  8527           ++rightDigitsOfDefault;
       
  8528           absValue *= 10;
       
  8529         }
       
  8530       } else if (rightDigitsOfDefault !== 0) {
       
  8531         absValue *= Math.pow(10, rightDigitsOfDefault);
       
  8532       }
       
  8533 
       
  8534       // Using Java's default rounding policy HALF_EVEN. This policy is based
       
  8535       // on the idea that 0.5 values round to the nearest even number, and
       
  8536       // everything else is rounded normally.
       
  8537       var number, doubled = absValue * 2;
       
  8538       if (Math.floor(absValue) === absValue) {
       
  8539         number = absValue;
       
  8540       } else if (Math.floor(doubled) === doubled) {
       
  8541         var floored = Math.floor(absValue);
       
  8542         number = floored + (floored % 2);
       
  8543       } else {
       
  8544         number = Math.round(absValue);
       
  8545       }
       
  8546 
       
  8547       var buffer = "";
       
  8548       var totalDigits = leftDigits + rightDigitsOfDefault;
       
  8549       while (totalDigits > 0 || number > 0) {
       
  8550         totalDigits--;
       
  8551         buffer = "" + (number % 10) + buffer;
       
  8552         number = Math.floor(number / 10);
       
  8553       }
       
  8554       if (group !== undef) {
       
  8555         var i = buffer.length - 3 - rightDigitsOfDefault;
       
  8556         while(i > 0) {
       
  8557           buffer = buffer.substring(0,i) + group + buffer.substring(i);
       
  8558           i-=3;
       
  8559         }
       
  8560       }
       
  8561       if (rightDigitsOfDefault > 0) {
       
  8562         return sign + buffer.substring(0, buffer.length - rightDigitsOfDefault) +
       
  8563                "." + buffer.substring(buffer.length - rightDigitsOfDefault, buffer.length);
       
  8564       }
       
  8565       return sign + buffer;
       
  8566     }
       
  8567 
       
  8568     /**
       
  8569     * Number-to-String formatting function. Prepends "plus" or "minus" depending
       
  8570     * on whether the value is positive or negative, respectively, after padding
       
  8571     * the value with zeroes on the left and right, the number of zeroes used dictated
       
  8572     * by the values 'leftDigits' and 'rightDigits'. 'value' can be an array;
       
  8573     * if the input is an array, each value in it is formatted separately, and
       
  8574     * an array with formatted values is returned.
       
  8575     *
       
  8576     * @param {int|int[]|float|float[]} value   the number(s) to format
       
  8577     * @param {String} plus                     the prefix for positive numbers
       
  8578     * @param {String} minus                    the prefix for negative numbers
       
  8579     * @param {int} left                        number of digits to the left of the decimal point
       
  8580     * @param {int} right                       number of digits to the right of the decimal point
       
  8581     * @param {String} group                    string delimited for groups, such as the comma in "1,000"
       
  8582     *
       
  8583     * @returns {String or String[]}
       
  8584     *
       
  8585     * @see nfCoreScalar
       
  8586     */
       
  8587     function nfCore(value, plus, minus, leftDigits, rightDigits, group) {
       
  8588       if (value instanceof Array) {
       
  8589         var arr = [];
       
  8590         for (var i = 0, len = value.length; i < len; i++) {
       
  8591           arr.push(nfCoreScalar(value[i], plus, minus, leftDigits, rightDigits, group));
       
  8592         }
       
  8593         return arr;
       
  8594       }
       
  8595       return nfCoreScalar(value, plus, minus, leftDigits, rightDigits, group);
       
  8596     }
       
  8597 
       
  8598     /**
       
  8599     * Utility function for formatting numbers into strings. There are two versions, one for
       
  8600     * formatting floats and one for formatting ints. The values for the digits, left, and
       
  8601     * right parameters should always be positive integers.
       
  8602     * As shown in the above example, nf() is used to add zeros to the left and/or right
       
  8603     * of a number. This is typically for aligning a list of numbers. To remove digits from
       
  8604     * a floating-point number, use the int(), ceil(), floor(), or round() functions.
       
  8605     *
       
  8606     * @param {int|int[]|float|float[]} value   the number(s) to format
       
  8607     * @param {int} left                        number of digits to the left of the decimal point
       
  8608     * @param {int} right                       number of digits to the right of the decimal point
       
  8609     *
       
  8610     * @returns {String or String[]}
       
  8611     *
       
  8612     * @see nfs
       
  8613     * @see nfp
       
  8614     * @see nfc
       
  8615     */
       
  8616     p.nf = function(value, leftDigits, rightDigits) { return nfCore(value, "", "-", leftDigits, rightDigits); };
       
  8617 
       
  8618     /**
       
  8619     * Utility function for formatting numbers into strings. Similar to nf()  but leaves a blank space in front
       
  8620     * of positive numbers so they align with negative numbers in spite of the minus symbol. There are two
       
  8621     * versions, one for formatting floats and one for formatting ints. The values for the digits, left,
       
  8622     * and right parameters should always be positive integers.
       
  8623     *
       
  8624     * @param {int|int[]|float|float[]} value   the number(s) to format
       
  8625     * @param {int} left                        number of digits to the left of the decimal point
       
  8626     * @param {int} right                       number of digits to the right of the decimal point
       
  8627     *
       
  8628     * @returns {String or String[]}
       
  8629     *
       
  8630     * @see nf
       
  8631     * @see nfp
       
  8632     * @see nfc
       
  8633     */
       
  8634     p.nfs = function(value, leftDigits, rightDigits) { return nfCore(value, " ", "-", leftDigits, rightDigits); };
       
  8635 
       
  8636     /**
       
  8637     * Utility function for formatting numbers into strings. Similar to nf()  but puts a "+" in front of
       
  8638     * positive numbers and a "-" in front of negative numbers. There are two versions, one for formatting
       
  8639     * floats and one for formatting ints. The values for the digits, left, and right parameters should
       
  8640     * always be positive integers.
       
  8641     *
       
  8642     * @param {int|int[]|float|float[]} value   the number(s) to format
       
  8643     * @param {int} left                        number of digits to the left of the decimal point
       
  8644     * @param {int} right                       number of digits to the right of the decimal point
       
  8645     *
       
  8646     * @returns {String or String[]}
       
  8647     *
       
  8648     * @see nfs
       
  8649     * @see nf
       
  8650     * @see nfc
       
  8651     */
       
  8652     p.nfp = function(value, leftDigits, rightDigits) { return nfCore(value, "+", "-", leftDigits, rightDigits); };
       
  8653 
       
  8654     /**
       
  8655     * Utility function for formatting numbers into strings and placing appropriate commas to mark
       
  8656     * units of 1000. There are two versions, one for formatting ints and one for formatting an array
       
  8657     * of ints. The value for the digits parameter should always be a positive integer.
       
  8658     *
       
  8659     * @param {int|int[]|float|float[]} value   the number(s) to format
       
  8660     * @param {int} left                        number of digits to the left of the decimal point
       
  8661     * @param {int} right                       number of digits to the right of the decimal point
       
  8662     *
       
  8663     * @returns {String or String[]}
       
  8664     *
       
  8665     * @see nf
       
  8666     * @see nfs
       
  8667     * @see nfp
       
  8668     */
       
  8669     p.nfc = function(value, leftDigits, rightDigits) { return nfCore(value, "", "-", leftDigits, rightDigits, ","); };
       
  8670 
       
  8671     var decimalToHex = function(d, padding) {
       
  8672       //if there is no padding value added, default padding to 8 else go into while statement.
       
  8673       padding = (padding === undef || padding === null) ? padding = 8 : padding;
       
  8674       if (d < 0) {
       
  8675         d = 0xFFFFFFFF + d + 1;
       
  8676       }
       
  8677       var hex = Number(d).toString(16).toUpperCase();
       
  8678       while (hex.length < padding) {
       
  8679         hex = "0" + hex;
       
  8680       }
       
  8681       if (hex.length >= padding) {
       
  8682         hex = hex.substring(hex.length - padding, hex.length);
       
  8683       }
       
  8684       return hex;
       
  8685     };
       
  8686 
       
  8687     // note: since we cannot keep track of byte, int types by default the returned string is 8 chars long
       
  8688     // if no 2nd argument is passed.  closest compromise we can use to match java implementation Feb 5 2010
       
  8689     // also the char parser has issues with chars that are not digits or letters IE: !@#$%^&*
       
  8690     /**
       
  8691     * Converts a byte, char, int, or color to a String containing the equivalent hexadecimal notation.
       
  8692     * For example color(0, 102, 153, 255) will convert to the String "FF006699". This function can help
       
  8693     * make your geeky debugging sessions much happier.
       
  8694     *
       
  8695     * @param {byte|char|int|Color} value   the value to turn into a hex string
       
  8696     * @param {int} digits                 the number of digits to return
       
  8697     *
       
  8698     * @returns {String}
       
  8699     *
       
  8700     * @see unhex
       
  8701     * @see binary
       
  8702     * @see unbinary
       
  8703     */
       
  8704     p.hex = function(value, len) {
       
  8705       if (arguments.length === 1) {
       
  8706         if (value instanceof Char) {
       
  8707           len = 4;
       
  8708         } else { // int or byte, indistinguishable at the moment, default to 8
       
  8709           len = 8;
       
  8710         }
       
  8711       }
       
  8712       return decimalToHex(value, len);
       
  8713     };
       
  8714 
       
  8715     function unhexScalar(hex) {
       
  8716       var value = parseInt("0x" + hex, 16);
       
  8717 
       
  8718       // correct for int overflow java expectation
       
  8719       if (value > 2147483647) {
       
  8720         value -= 4294967296;
       
  8721       }
       
  8722       return value;
       
  8723     }
       
  8724 
       
  8725     /**
       
  8726     * Converts a String representation of a hexadecimal number to its equivalent integer value.
       
  8727     *
       
  8728     * @param {String} hex   the hex string to convert to an int
       
  8729     *
       
  8730     * @returns {int}
       
  8731     *
       
  8732     * @see hex
       
  8733     * @see binary
       
  8734     * @see unbinary
       
  8735     */
       
  8736     p.unhex = function(hex) {
       
  8737       if (hex instanceof Array) {
       
  8738         var arr = [];
       
  8739         for (var i = 0; i < hex.length; i++) {
       
  8740           arr.push(unhexScalar(hex[i]));
       
  8741         }
       
  8742         return arr;
       
  8743       }
       
  8744       return unhexScalar(hex);
       
  8745     };
       
  8746 
       
  8747     // Load a file or URL into strings
       
  8748     /**
       
  8749     * Reads the contents of a file or url and creates a String array of its individual lines.
       
  8750     * The filename parameter can also be a URL to a file found online.  If the file is not available or an error occurs,
       
  8751     * null will be returned and an error message will be printed to the console. The error message does not halt
       
  8752     * the program.
       
  8753     *
       
  8754     * @param {String} filename    name of the file or url to load
       
  8755     *
       
  8756     * @returns {String[]}
       
  8757     *
       
  8758     * @see loadBytes
       
  8759     * @see saveStrings
       
  8760     * @see saveBytes
       
  8761     */
       
  8762     p.loadStrings = function(filename) {
       
  8763       if (localStorage[filename]) {
       
  8764         return localStorage[filename].split("\n");
       
  8765       }
       
  8766 
       
  8767       var filecontent = ajax(filename);
       
  8768       if(typeof filecontent !== "string" || filecontent === "") {
       
  8769         return [];
       
  8770       }
       
  8771 
       
  8772       // deal with the fact that Windows uses \r\n, Unix uses \n,
       
  8773       // Mac uses \r, and we actually expect \n
       
  8774       filecontent = filecontent.replace(/(\r\n?)/g,"\n").replace(/\n$/,"");
       
  8775 
       
  8776       return filecontent.split("\n");
       
  8777     };
       
  8778 
       
  8779     // Writes an array of strings to a file, one line per string
       
  8780     /**
       
  8781     * Writes an array of strings to a file, one line per string. This file is saved to the localStorage.
       
  8782     *
       
  8783     * @param {String} filename    name of the file to save to localStorage
       
  8784     * @param {String[]} strings   string array to be written
       
  8785     *
       
  8786     * @see loadBytes
       
  8787     * @see loadStrings
       
  8788     * @see saveBytes
       
  8789     */
       
  8790     p.saveStrings = function(filename, strings) {
       
  8791       localStorage[filename] = strings.join('\n');
       
  8792     };
       
  8793 
       
  8794     /**
       
  8795     * Reads the contents of a file or url and places it in a byte array. If a file is specified, it must be located in the localStorage.
       
  8796     * The filename parameter can also be a URL to a file found online.
       
  8797     *
       
  8798     * @param {String} filename   name of a file in the localStorage or a URL.
       
  8799     *
       
  8800     * @returns {byte[]}
       
  8801     *
       
  8802     * @see loadStrings
       
  8803     * @see saveStrings
       
  8804     * @see saveBytes
       
  8805     */
       
  8806     p.loadBytes = function(url) {
       
  8807       var string = ajax(url);
       
  8808       var ret = [];
       
  8809 
       
  8810       for (var i = 0; i < string.length; i++) {
       
  8811         ret.push(string.charCodeAt(i));
       
  8812       }
       
  8813 
       
  8814       return ret;
       
  8815     };
       
  8816 
       
  8817     /**
       
  8818      * Removes the first argument from the arguments set -- shifts.
       
  8819      *
       
  8820      * @param {Arguments} args  The Arguments object.
       
  8821      *
       
  8822      * @return {Object[]}       Returns an array of arguments except first one.
       
  8823      *
       
  8824      * @see #match
       
  8825      */
       
  8826     function removeFirstArgument(args) {
       
  8827       return Array.prototype.slice.call(args, 1);
       
  8828     }
       
  8829 
       
  8830     ////////////////////////////////////////////////////////////////////////////
       
  8831     // String Functions
       
  8832     ////////////////////////////////////////////////////////////////////////////
       
  8833     /**
       
  8834      * The matchAll() function is identical to match(), except that it returns an array of all matches in
       
  8835      * the specified String, rather than just the first.
       
  8836      *
       
  8837      * @param {String} aString  the String to search inside
       
  8838      * @param {String} aRegExp  the regexp to be used for matching
       
  8839      *
       
  8840      * @return {String[]} returns an array of matches
       
  8841      *
       
  8842      * @see #match
       
  8843      */
       
  8844     p.matchAll = function(aString, aRegExp) {
       
  8845       var results = [],
       
  8846           latest;
       
  8847       var regexp = new RegExp(aRegExp, "g");
       
  8848       while ((latest = regexp.exec(aString)) !== null) {
       
  8849         results.push(latest);
       
  8850         if (latest[0].length === 0) {
       
  8851           ++regexp.lastIndex;
       
  8852         }
       
  8853       }
       
  8854       return results.length > 0 ? results : null;
       
  8855     };
       
  8856     /**
       
  8857      * The contains(string) function returns true if the string passed in the parameter
       
  8858      * is a substring of this string. It returns false if the string passed
       
  8859      * in the parameter is not a substring of this string.
       
  8860      *
       
  8861      * @param {String} The string to look for in the current string
       
  8862      *
       
  8863      * @return {boolean} returns true if this string contains
       
  8864      * the string passed as parameter. returns false, otherwise.
       
  8865      *
       
  8866      */
       
  8867     p.__contains = function (subject, subStr) {
       
  8868       if (typeof subject !== "string") {
       
  8869         return subject.contains.apply(subject, removeFirstArgument(arguments));
       
  8870       }
       
  8871       //Parameter is not null AND
       
  8872       //The type of the parameter is the same as this object (string)
       
  8873       //The javascript function that finds a substring returns 0 or higher
       
  8874       return (
       
  8875         (subject !== null) &&
       
  8876         (subStr !== null) &&
       
  8877         (typeof subStr === "string") &&
       
  8878         (subject.indexOf(subStr) > -1)
       
  8879       );
       
  8880     };
       
  8881     /**
       
  8882      * The __replaceAll() function searches all matches between a substring (or regular expression) and a string,
       
  8883      * and replaces the matched substring with a new substring
       
  8884      *
       
  8885      * @param {String} subject    a substring
       
  8886      * @param {String} regex      a substring or a regular expression
       
  8887      * @param {String} replace    the string to replace the found value
       
  8888      *
       
  8889      * @return {String} returns result
       
  8890      *
       
  8891      * @see #match
       
  8892      */
       
  8893     p.__replaceAll = function(subject, regex, replacement) {
       
  8894       if (typeof subject !== "string") {
       
  8895         return subject.replaceAll.apply(subject, removeFirstArgument(arguments));
       
  8896       }
       
  8897 
       
  8898       return subject.replace(new RegExp(regex, "g"), replacement);
       
  8899     };
       
  8900     /**
       
  8901      * The __replaceFirst() function searches first matche between a substring (or regular expression) and a string,
       
  8902      * and replaces the matched substring with a new substring
       
  8903      *
       
  8904      * @param {String} subject    a substring
       
  8905      * @param {String} regex      a substring or a regular expression
       
  8906      * @param {String} replace    the string to replace the found value
       
  8907      *
       
  8908      * @return {String} returns result
       
  8909      *
       
  8910      * @see #match
       
  8911      */
       
  8912     p.__replaceFirst = function(subject, regex, replacement) {
       
  8913       if (typeof subject !== "string") {
       
  8914         return subject.replaceFirst.apply(subject, removeFirstArgument(arguments));
       
  8915       }
       
  8916 
       
  8917       return subject.replace(new RegExp(regex, ""), replacement);
       
  8918     };
       
  8919     /**
       
  8920      * The __replace() function searches all matches between a substring and a string,
       
  8921      * and replaces the matched substring with a new substring
       
  8922      *
       
  8923      * @param {String} subject         a substring
       
  8924      * @param {String} what         a substring to find
       
  8925      * @param {String} replacement    the string to replace the found value
       
  8926      *
       
  8927      * @return {String} returns result
       
  8928      */
       
  8929     p.__replace = function(subject, what, replacement) {
       
  8930       if (typeof subject !== "string") {
       
  8931         return subject.replace.apply(subject, removeFirstArgument(arguments));
       
  8932       }
       
  8933       if (what instanceof RegExp) {
       
  8934         return subject.replace(what, replacement);
       
  8935       }
       
  8936 
       
  8937       if (typeof what !== "string") {
       
  8938         what = what.toString();
       
  8939       }
       
  8940       if (what === "") {
       
  8941         return subject;
       
  8942       }
       
  8943 
       
  8944       var i = subject.indexOf(what);
       
  8945       if (i < 0) {
       
  8946         return subject;
       
  8947       }
       
  8948 
       
  8949       var j = 0, result = "";
       
  8950       do {
       
  8951         result += subject.substring(j, i) + replacement;
       
  8952         j = i + what.length;
       
  8953       } while ( (i = subject.indexOf(what, j)) >= 0);
       
  8954       return result + subject.substring(j);
       
  8955     };
       
  8956     /**
       
  8957      * The __equals() function compares two strings (or objects) to see if they are the same.
       
  8958      * This method is necessary because it's not possible to compare strings using the equality operator (==).
       
  8959      * Returns true if the strings are the same and false if they are not.
       
  8960      *
       
  8961      * @param {String} subject  a string used for comparison
       
  8962      * @param {String} other  a string used for comparison with
       
  8963      *
       
  8964      * @return {boolean} true is the strings are the same false otherwise
       
  8965      */
       
  8966     p.__equals = function(subject, other) {
       
  8967       if (subject.equals instanceof Function) {
       
  8968         return subject.equals.apply(subject, removeFirstArgument(arguments));
       
  8969       }
       
  8970 
       
  8971       // TODO use virtEquals for HashMap here
       
  8972       return subject.valueOf() === other.valueOf();
       
  8973     };
       
  8974     /**
       
  8975      * The __equalsIgnoreCase() function compares two strings to see if they are the same.
       
  8976      * Returns true if the strings are the same, either when forced to all lower case or
       
  8977      * all upper case.
       
  8978      *
       
  8979      * @param {String} subject  a string used for comparison
       
  8980      * @param {String} other  a string used for comparison with
       
  8981      *
       
  8982      * @return {boolean} true is the strings are the same, ignoring case. false otherwise
       
  8983      */
       
  8984     p.__equalsIgnoreCase = function(subject, other) {
       
  8985       if (typeof subject !== "string") {
       
  8986         return subject.equalsIgnoreCase.apply(subject, removeFirstArgument(arguments));
       
  8987       }
       
  8988 
       
  8989       return subject.toLowerCase() === other.toLowerCase();
       
  8990     };
       
  8991     /**
       
  8992      * The __toCharArray() function splits the string into a char array.
       
  8993      *
       
  8994      * @param {String} subject The string
       
  8995      *
       
  8996      * @return {Char[]} a char array
       
  8997      */
       
  8998     p.__toCharArray = function(subject) {
       
  8999       if (typeof subject !== "string") {
       
  9000         return subject.toCharArray.apply(subject, removeFirstArgument(arguments));
       
  9001       }
       
  9002 
       
  9003       var chars = [];
       
  9004       for (var i = 0, len = subject.length; i < len; ++i) {
       
  9005         chars[i] = new Char(subject.charAt(i));
       
  9006       }
       
  9007       return chars;
       
  9008     };
       
  9009     /**
       
  9010      * The __split() function splits a string using the regex delimiter
       
  9011      * specified. If limit is specified, the resultant array will have number
       
  9012      * of elements equal to or less than the limit.
       
  9013      *
       
  9014      * @param {String} subject string to be split
       
  9015      * @param {String} regexp  regex string used to split the subject
       
  9016      * @param {int}    limit   max number of tokens to be returned
       
  9017      *
       
  9018      * @return {String[]} an array of tokens from the split string
       
  9019      */
       
  9020     p.__split = function(subject, regex, limit) {
       
  9021       if (typeof subject !== "string") {
       
  9022         return subject.split.apply(subject, removeFirstArgument(arguments));
       
  9023       }
       
  9024 
       
  9025       var pattern = new RegExp(regex);
       
  9026 
       
  9027       // If limit is not specified, use JavaScript's built-in String.split.
       
  9028       if ((limit === undef) || (limit < 1)) {
       
  9029         return subject.split(pattern);
       
  9030       }
       
  9031 
       
  9032       // If limit is specified, JavaScript's built-in String.split has a
       
  9033       // different behaviour than Java's. A Java-compatible implementation is
       
  9034       // provided here.
       
  9035       var result = [], currSubject = subject, pos;
       
  9036       while (((pos = currSubject.search(pattern)) !== -1)
       
  9037           && (result.length < (limit - 1))) {
       
  9038         var match = pattern.exec(currSubject).toString();
       
  9039         result.push(currSubject.substring(0, pos));
       
  9040         currSubject = currSubject.substring(pos + match.length);
       
  9041       }
       
  9042       if ((pos !== -1) || (currSubject !== "")) {
       
  9043         result.push(currSubject);
       
  9044       }
       
  9045       return result;
       
  9046     };
       
  9047     /**
       
  9048      * The codePointAt() function returns the unicode value of the character at a given index of a string.
       
  9049      *
       
  9050      * @param  {int} idx         the index of the character
       
  9051      *
       
  9052      * @return {String} code     the String containing the unicode value of the character
       
  9053      */
       
  9054     p.__codePointAt = function(subject, idx) {
       
  9055       var code = subject.charCodeAt(idx),
       
  9056           hi,
       
  9057           low;
       
  9058       if (0xD800 <= code && code <= 0xDBFF) {
       
  9059         hi = code;
       
  9060         low = subject.charCodeAt(idx + 1);
       
  9061         return ((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000;
       
  9062       }
       
  9063       return code;
       
  9064     };
       
  9065     /**
       
  9066      * The match() function matches a string with a regular expression, and returns the match as an
       
  9067      * array. The first index is the matching expression, and array elements
       
  9068      * [1] and higher represent each of the groups (sequences found in parens).
       
  9069      *
       
  9070      * @param {String} str      the String to be searched
       
  9071      * @param {String} regexp   the regexp to be used for matching
       
  9072      *
       
  9073      * @return {String[]} an array of matching strings
       
  9074      */
       
  9075     p.match = function(str, regexp) {
       
  9076       return str.match(regexp);
       
  9077     };
       
  9078     /**
       
  9079      * The startsWith() function tests if a string starts with the specified prefix.  If the prefix
       
  9080      * is the empty String or equal to the subject String, startsWith() will also return true.
       
  9081      *
       
  9082      * @param {String} prefix   the String used to compare against the start of the subject String.
       
  9083      * @param {int}    toffset  (optional) an offset into the subject String where searching should begin.
       
  9084      *
       
  9085      * @return {boolean} true if the subject String starts with the prefix.
       
  9086      */
       
  9087     p.__startsWith = function(subject, prefix, toffset) {
       
  9088       if (typeof subject !== "string") {
       
  9089         return subject.startsWith.apply(subject, removeFirstArgument(arguments));
       
  9090       }
       
  9091 
       
  9092       toffset = toffset || 0;
       
  9093       if (toffset < 0 || toffset > subject.length) {
       
  9094         return false;
       
  9095       }
       
  9096       return (prefix === '' || prefix === subject) ? true : (subject.indexOf(prefix) === toffset);
       
  9097     };
       
  9098     /**
       
  9099      * The endsWith() function tests if a string ends with the specified suffix.  If the suffix
       
  9100      * is the empty String, endsWith() will also return true.
       
  9101      *
       
  9102      * @param {String} suffix   the String used to compare against the end of the subject String.
       
  9103      *
       
  9104      * @return {boolean} true if the subject String starts with the prefix.
       
  9105      */
       
  9106     p.__endsWith = function(subject, suffix) {
       
  9107       if (typeof subject !== "string") {
       
  9108         return subject.endsWith.apply(subject, removeFirstArgument(arguments));
       
  9109       }
       
  9110 
       
  9111       var suffixLen = suffix ? suffix.length : 0;
       
  9112       return (suffix === '' || suffix === subject) ? true :
       
  9113         (subject.indexOf(suffix) === subject.length - suffixLen);
       
  9114     };
       
  9115 
       
  9116     ////////////////////////////////////////////////////////////////////////////
       
  9117     // Other java specific functions
       
  9118     ////////////////////////////////////////////////////////////////////////////
       
  9119 
       
  9120     /**
       
  9121      * The returns hash code of the.
       
  9122      *
       
  9123      * @param {Object} subject The string
       
  9124      *
       
  9125      * @return {int} a hash code
       
  9126      */
       
  9127     p.__hashCode = function(subject) {
       
  9128       if (subject.hashCode instanceof Function) {
       
  9129         return subject.hashCode.apply(subject, removeFirstArgument(arguments));
       
  9130       }
       
  9131       return virtHashCode(subject);
       
  9132     };
       
  9133     /**
       
  9134      * The __printStackTrace() prints stack trace to the console.
       
  9135      *
       
  9136      * @param {Exception} subject The error
       
  9137      */
       
  9138     p.__printStackTrace = function(subject) {
       
  9139       p.println("Exception: " + subject.toString() );
       
  9140     };
       
  9141 
       
  9142     var logBuffer = [];
       
  9143 
       
  9144     /**
       
  9145      * The println() function writes to the console area of the Processing environment.
       
  9146      * Each call to this function creates a new line of output. Individual elements can be separated with quotes ("") and joined with the string concatenation operator (+).
       
  9147      *
       
  9148      * @param {String} message the string to write to the console
       
  9149      *
       
  9150      * @see #join
       
  9151      * @see #print
       
  9152      */
       
  9153     p.println = function(message) {
       
  9154       var bufferLen = logBuffer.length;
       
  9155       if (bufferLen) {
       
  9156         Processing.logger.log(logBuffer.join(""));
       
  9157         logBuffer.length = 0; // clear log buffer
       
  9158       }
       
  9159 
       
  9160       if (arguments.length === 0 && bufferLen === 0) {
       
  9161         Processing.logger.log("");
       
  9162       } else if (arguments.length !== 0) {
       
  9163         Processing.logger.log(message);
       
  9164       }
       
  9165     };
       
  9166     /**
       
  9167      * The print() function writes to the console area of the Processing environment.
       
  9168      *
       
  9169      * @param {String} message the string to write to the console
       
  9170      *
       
  9171      * @see #join
       
  9172      */
       
  9173     p.print = function(message) {
       
  9174       logBuffer.push(message);
       
  9175     };
       
  9176 
       
  9177     // Alphanumeric chars arguments automatically converted to numbers when
       
  9178     // passed in, and will come out as numbers.
       
  9179     p.str = function(val) {
       
  9180       if (val instanceof Array) {
       
  9181         var arr = [];
       
  9182         for (var i = 0; i < val.length; i++) {
       
  9183           arr.push(val[i].toString() + "");
       
  9184         }
       
  9185         return arr;
       
  9186       }
       
  9187       return (val.toString() + "");
       
  9188     };
       
  9189     /**
       
  9190      * Remove whitespace characters from the beginning and ending
       
  9191      * of a String or a String array. Works like String.trim() but includes the
       
  9192      * unicode nbsp character as well. If an array is passed in the function will return a new array not effecting the array passed in.
       
  9193      *
       
  9194      * @param {String} str    the string to trim
       
  9195      * @param {String[]} str  the string array to trim
       
  9196      *
       
  9197      * @return {String|String[]} retrurns a string or an array will removed whitespaces
       
  9198      */
       
  9199     p.trim = function(str) {
       
  9200       if (str instanceof Array) {
       
  9201         var arr = [];
       
  9202         for (var i = 0; i < str.length; i++) {
       
  9203           arr.push(str[i].replace(/^\s*/, '').replace(/\s*$/, '').replace(/\r*$/, ''));
       
  9204         }
       
  9205         return arr;
       
  9206       }
       
  9207       return str.replace(/^\s*/, '').replace(/\s*$/, '').replace(/\r*$/, '');
       
  9208     };
       
  9209 
       
  9210     // Conversion
       
  9211     function booleanScalar(val) {
       
  9212       if (typeof val === 'number') {
       
  9213         return val !== 0;
       
  9214       }
       
  9215       if (typeof val === 'boolean') {
       
  9216         return val;
       
  9217       }
       
  9218       if (typeof val === 'string') {
       
  9219         return val.toLowerCase() === 'true';
       
  9220       }
       
  9221       if (val instanceof Char) {
       
  9222         // 1, T or t
       
  9223         return val.code === 49 || val.code === 84 || val.code === 116;
       
  9224       }
       
  9225     }
       
  9226 
       
  9227     /**
       
  9228      * Converts the passed parameter to the function to its boolean value.
       
  9229      * It will return an array of booleans if an array is passed in.
       
  9230      *
       
  9231      * @param {int, byte, string} val          the parameter to be converted to boolean
       
  9232      * @param {int[], byte[], string[]} val    the array to be converted to boolean[]
       
  9233      *
       
  9234      * @return {boolean|boolean[]} returns a boolean or an array of booleans
       
  9235      */
       
  9236     p.parseBoolean = function (val) {
       
  9237       if (val instanceof Array) {
       
  9238         var ret = [];
       
  9239         for (var i = 0; i < val.length; i++) {
       
  9240           ret.push(booleanScalar(val[i]));
       
  9241         }
       
  9242         return ret;
       
  9243       }
       
  9244       return booleanScalar(val);
       
  9245     };
       
  9246 
       
  9247     /**
       
  9248      * Converts the passed parameter to the function to its byte value.
       
  9249      * A byte is a number between -128 and 127.
       
  9250      * It will return an array of bytes if an array is passed in.
       
  9251      *
       
  9252      * @param {int, char} what        the parameter to be conveted to byte
       
  9253      * @param {int[], char[]} what    the array to be converted to byte[]
       
  9254      *
       
  9255      * @return {byte|byte[]} returns a byte or an array of bytes
       
  9256      */
       
  9257     p.parseByte = function(what) {
       
  9258       if (what instanceof Array) {
       
  9259         var bytes = [];
       
  9260         for (var i = 0; i < what.length; i++) {
       
  9261           bytes.push((0 - (what[i] & 0x80)) | (what[i] & 0x7F));
       
  9262         }
       
  9263         return bytes;
       
  9264       }
       
  9265       return (0 - (what & 0x80)) | (what & 0x7F);
       
  9266     };
       
  9267 
       
  9268     /**
       
  9269      * Converts the passed parameter to the function to its char value.
       
  9270      * It will return an array of chars if an array is passed in.
       
  9271      *
       
  9272      * @param {int, byte} key        the parameter to be conveted to char
       
  9273      * @param {int[], byte[]} key    the array to be converted to char[]
       
  9274      *
       
  9275      * @return {char|char[]} returns a char or an array of chars
       
  9276      */
       
  9277     p.parseChar = function(key) {
       
  9278       if (typeof key === "number") {
       
  9279         return new Char(String.fromCharCode(key & 0xFFFF));
       
  9280       }
       
  9281       if (key instanceof Array) {
       
  9282         var ret = [];
       
  9283         for (var i = 0; i < key.length; i++) {
       
  9284           ret.push(new Char(String.fromCharCode(key[i] & 0xFFFF)));
       
  9285         }
       
  9286         return ret;
       
  9287       }
       
  9288       throw "char() may receive only one argument of type int, byte, int[], or byte[].";
       
  9289     };
       
  9290 
       
  9291     // Processing doc claims good argument types are: int, char, byte, boolean,
       
  9292     // String, int[], char[], byte[], boolean[], String[].
       
  9293     // floats should not work. However, floats with only zeroes right of the
       
  9294     // decimal will work because JS converts those to int.
       
  9295     function floatScalar(val) {
       
  9296       if (typeof val === 'number') {
       
  9297         return val;
       
  9298       }
       
  9299       if (typeof val === 'boolean') {
       
  9300         return val ? 1 : 0;
       
  9301       }
       
  9302       if (typeof val === 'string') {
       
  9303         return parseFloat(val);
       
  9304       }
       
  9305       if (val instanceof Char) {
       
  9306         return val.code;
       
  9307       }
       
  9308     }
       
  9309 
       
  9310     /**
       
  9311      * Converts the passed parameter to the function to its float value.
       
  9312      * It will return an array of floats if an array is passed in.
       
  9313      *
       
  9314      * @param {int, char, boolean, string} val            the parameter to be conveted to float
       
  9315      * @param {int[], char[], boolean[], string[]} val    the array to be converted to float[]
       
  9316      *
       
  9317      * @return {float|float[]} returns a float or an array of floats
       
  9318      */
       
  9319     p.parseFloat = function(val) {
       
  9320       if (val instanceof Array) {
       
  9321         var ret = [];
       
  9322         for (var i = 0; i < val.length; i++) {
       
  9323           ret.push(floatScalar(val[i]));
       
  9324         }
       
  9325         return ret;
       
  9326       }
       
  9327       return floatScalar(val);
       
  9328     };
       
  9329 
       
  9330     function intScalar(val, radix) {
       
  9331       if (typeof val === 'number') {
       
  9332         return val & 0xFFFFFFFF;
       
  9333       }
       
  9334       if (typeof val === 'boolean') {
       
  9335         return val ? 1 : 0;
       
  9336       }
       
  9337       if (typeof val === 'string') {
       
  9338         var number = parseInt(val, radix || 10); // Default to decimal radix.
       
  9339         return number & 0xFFFFFFFF;
       
  9340       }
       
  9341       if (val instanceof Char) {
       
  9342         return val.code;
       
  9343       }
       
  9344     }
       
  9345 
       
  9346     /**
       
  9347      * Converts the passed parameter to the function to its int value.
       
  9348      * It will return an array of ints if an array is passed in.
       
  9349      *
       
  9350      * @param {string, char, boolean, float} val            the parameter to be conveted to int
       
  9351      * @param {string[], char[], boolean[], float[]} val    the array to be converted to int[]
       
  9352      * @param {int} radix                                   optional the radix of the number (for js compatibility)
       
  9353      *
       
  9354      * @return {int|int[]} returns a int or an array of ints
       
  9355      */
       
  9356     p.parseInt = function(val, radix) {
       
  9357       if (val instanceof Array) {
       
  9358         var ret = [];
       
  9359         for (var i = 0; i < val.length; i++) {
       
  9360           if (typeof val[i] === 'string' && !/^\s*[+\-]?\d+\s*$/.test(val[i])) {
       
  9361             ret.push(0);
       
  9362           } else {
       
  9363             ret.push(intScalar(val[i], radix));
       
  9364           }
       
  9365         }
       
  9366         return ret;
       
  9367       }
       
  9368       return intScalar(val, radix);
       
  9369     };
       
  9370 
       
  9371     p.__int_cast = function(val) {
       
  9372       return 0|val;
       
  9373     };
       
  9374 
       
  9375     p.__instanceof = function(obj, type) {
       
  9376       if (typeof type !== "function") {
       
  9377         throw "Function is expected as type argument for instanceof operator";
       
  9378       }
       
  9379 
       
  9380       if (typeof obj === "string") {
       
  9381         // special case for strings
       
  9382         return type === Object || type === String;
       
  9383       }
       
  9384 
       
  9385       if (obj instanceof type) {
       
  9386         // fast check if obj is already of type instance
       
  9387         return true;
       
  9388       }
       
  9389 
       
  9390       if (typeof obj !== "object" || obj === null) {
       
  9391         return false; // not an object or null
       
  9392       }
       
  9393 
       
  9394       var objType = obj.constructor;
       
  9395       if (type.$isInterface) {
       
  9396         // expecting the interface
       
  9397         // queueing interfaces from type and its base classes
       
  9398         var interfaces = [];
       
  9399         while (objType) {
       
  9400           if (objType.$interfaces) {
       
  9401             interfaces = interfaces.concat(objType.$interfaces);
       
  9402           }
       
  9403           objType = objType.$base;
       
  9404         }
       
  9405         while (interfaces.length > 0) {
       
  9406           var i = interfaces.shift();
       
  9407           if (i === type) {
       
  9408             return true;
       
  9409           }
       
  9410           // wide search in base interfaces
       
  9411           if (i.$interfaces) {
       
  9412             interfaces = interfaces.concat(i.$interfaces);
       
  9413           }
       
  9414         }
       
  9415         return false;
       
  9416       }
       
  9417 
       
  9418       while (objType.hasOwnProperty("$base")) {
       
  9419         objType = objType.$base;
       
  9420         if (objType === type) {
       
  9421           return true; // object was found
       
  9422         }
       
  9423       }
       
  9424 
       
  9425       return false;
       
  9426     };
       
  9427 
       
  9428     ////////////////////////////////////////////////////////////////////////////
       
  9429     // Math functions
       
  9430     ////////////////////////////////////////////////////////////////////////////
       
  9431 
       
  9432     // Calculation
       
  9433     /**
       
  9434     * Calculates the absolute value (magnitude) of a number. The absolute value of a number is always positive.
       
  9435     *
       
  9436     * @param {int|float} value   int or float
       
  9437     *
       
  9438     * @returns {int|float}
       
  9439     */
       
  9440     p.abs = Math.abs;
       
  9441 
       
  9442     /**
       
  9443     * Calculates the closest int value that is greater than or equal to the value of the parameter.
       
  9444     * For example, ceil(9.03) returns the value 10.
       
  9445     *
       
  9446     * @param {float} value   float
       
  9447     *
       
  9448     * @returns {int}
       
  9449     *
       
  9450     * @see floor
       
  9451     * @see round
       
  9452     */
       
  9453     p.ceil = Math.ceil;
       
  9454 
       
  9455     /**
       
  9456     * Constrains a value to not exceed a maximum and minimum value.
       
  9457     *
       
  9458     * @param {int|float} value   the value to constrain
       
  9459     * @param {int|float} value   minimum limit
       
  9460     * @param {int|float} value   maximum limit
       
  9461     *
       
  9462     * @returns {int|float}
       
  9463     *
       
  9464     * @see max
       
  9465     * @see min
       
  9466     */
       
  9467     p.constrain = function(aNumber, aMin, aMax) {
       
  9468       return aNumber > aMax ? aMax : aNumber < aMin ? aMin : aNumber;
       
  9469     };
       
  9470 
       
  9471     /**
       
  9472     * Calculates the distance between two points.
       
  9473     *
       
  9474     * @param {int|float} x1     int or float: x-coordinate of the first point
       
  9475     * @param {int|float} y1     int or float: y-coordinate of the first point
       
  9476     * @param {int|float} z1     int or float: z-coordinate of the first point
       
  9477     * @param {int|float} x2     int or float: x-coordinate of the second point
       
  9478     * @param {int|float} y2     int or float: y-coordinate of the second point
       
  9479     * @param {int|float} z2     int or float: z-coordinate of the second point
       
  9480     *
       
  9481     * @returns {float}
       
  9482     */
       
  9483     p.dist = function() {
       
  9484       var dx, dy, dz;
       
  9485       if (arguments.length === 4) {
       
  9486         dx = arguments[0] - arguments[2];
       
  9487         dy = arguments[1] - arguments[3];
       
  9488         return Math.sqrt(dx * dx + dy * dy);
       
  9489       }
       
  9490       if (arguments.length === 6) {
       
  9491         dx = arguments[0] - arguments[3];
       
  9492         dy = arguments[1] - arguments[4];
       
  9493         dz = arguments[2] - arguments[5];
       
  9494         return Math.sqrt(dx * dx + dy * dy + dz * dz);
       
  9495       }
       
  9496     };
       
  9497 
       
  9498     /**
       
  9499     * Returns Euler's number e (2.71828...) raised to the power of the value parameter.
       
  9500     *
       
  9501     * @param {int|float} value   int or float: the exponent to raise e to
       
  9502     *
       
  9503     * @returns {float}
       
  9504     */
       
  9505     p.exp = Math.exp;
       
  9506 
       
  9507     /**
       
  9508     * Calculates the closest int value that is less than or equal to the value of the parameter.
       
  9509     *
       
  9510     * @param {int|float} value        the value to floor
       
  9511     *
       
  9512     * @returns {int|float}
       
  9513     *
       
  9514     * @see ceil
       
  9515     * @see round
       
  9516     */
       
  9517     p.floor = Math.floor;
       
  9518 
       
  9519     /**
       
  9520     * Calculates a number between two numbers at a specific increment. The amt  parameter is the
       
  9521     * amount to interpolate between the two values where 0.0 equal to the first point, 0.1 is very
       
  9522     * near the first point, 0.5 is half-way in between, etc. The lerp function is convenient for
       
  9523     * creating motion along a straight path and for drawing dotted lines.
       
  9524     *
       
  9525     * @param {int|float} value1       float or int: first value
       
  9526     * @param {int|float} value2       float or int: second value
       
  9527     * @param {int|float} amt          float: between 0.0 and 1.0
       
  9528     *
       
  9529     * @returns {float}
       
  9530     *
       
  9531     * @see curvePoint
       
  9532     * @see bezierPoint
       
  9533     */
       
  9534     p.lerp = function(value1, value2, amt) {
       
  9535       return ((value2 - value1) * amt) + value1;
       
  9536     };
       
  9537 
       
  9538     /**
       
  9539     * Calculates the natural logarithm (the base-e logarithm) of a number. This function
       
  9540     * expects the values greater than 0.0.
       
  9541     *
       
  9542     * @param {int|float} value        int or float: number must be greater then 0.0
       
  9543     *
       
  9544     * @returns {float}
       
  9545     */
       
  9546     p.log = Math.log;
       
  9547 
       
  9548     /**
       
  9549     * Calculates the magnitude (or length) of a vector. A vector is a direction in space commonly
       
  9550     * used in computer graphics and linear algebra. Because it has no "start" position, the magnitude
       
  9551     * of a vector can be thought of as the distance from coordinate (0,0) to its (x,y) value.
       
  9552     * Therefore, mag() is a shortcut for writing "dist(0, 0, x, y)".
       
  9553     *
       
  9554     * @param {int|float} a       float or int: first value
       
  9555     * @param {int|float} b       float or int: second value
       
  9556     * @param {int|float} c       float or int: third value
       
  9557     *
       
  9558     * @returns {float}
       
  9559     *
       
  9560     * @see dist
       
  9561     */
       
  9562     p.mag = function(a, b, c) {
       
  9563       if (c) {
       
  9564         return Math.sqrt(a * a + b * b + c * c);
       
  9565       }
       
  9566 
       
  9567       return Math.sqrt(a * a + b * b);
       
  9568     };
       
  9569 
       
  9570     /**
       
  9571     * Re-maps a number from one range to another. In the example above, the number '25' is converted from
       
  9572     * a value in the range 0..100 into a value that ranges from the left edge (0) to the right edge (width) of the screen.
       
  9573     * Numbers outside the range are not clamped to 0 and 1, because out-of-range values are often intentional and useful.
       
  9574     *
       
  9575     * @param {float} value        The incoming value to be converted
       
  9576     * @param {float} istart       Lower bound of the value's current range
       
  9577     * @param {float} istop        Upper bound of the value's current range
       
  9578     * @param {float} ostart       Lower bound of the value's target range
       
  9579     * @param {float} ostop        Upper bound of the value's target range
       
  9580     *
       
  9581     * @returns {float}
       
  9582     *
       
  9583     * @see norm
       
  9584     * @see lerp
       
  9585     */
       
  9586     p.map = function(value, istart, istop, ostart, ostop) {
       
  9587       return ostart + (ostop - ostart) * ((value - istart) / (istop - istart));
       
  9588     };
       
  9589 
       
  9590     /**
       
  9591     * Determines the largest value in a sequence of numbers.
       
  9592     *
       
  9593     * @param {int|float} value1         int or float
       
  9594     * @param {int|float} value2         int or float
       
  9595     * @param {int|float} value3         int or float
       
  9596     * @param {int|float} array          int or float array
       
  9597     *
       
  9598     * @returns {int|float}
       
  9599     *
       
  9600     * @see min
       
  9601     */
       
  9602     p.max = function() {
       
  9603       if (arguments.length === 2) {
       
  9604         return arguments[0] < arguments[1] ? arguments[1] : arguments[0];
       
  9605       }
       
  9606       var numbers = arguments.length === 1 ? arguments[0] : arguments; // if single argument, array is used
       
  9607       if (! ("length" in numbers && numbers.length > 0)) {
       
  9608         throw "Non-empty array is expected";
       
  9609       }
       
  9610       var max = numbers[0],
       
  9611         count = numbers.length;
       
  9612       for (var i = 1; i < count; ++i) {
       
  9613         if (max < numbers[i]) {
       
  9614           max = numbers[i];
       
  9615         }
       
  9616       }
       
  9617       return max;
       
  9618     };
       
  9619 
       
  9620     /**
       
  9621     * Determines the smallest value in a sequence of numbers.
       
  9622     *
       
  9623     * @param {int|float} value1         int or float
       
  9624     * @param {int|float} value2         int or float
       
  9625     * @param {int|float} value3         int or float
       
  9626     * @param {int|float} array          int or float array
       
  9627     *
       
  9628     * @returns {int|float}
       
  9629     *
       
  9630     * @see max
       
  9631     */
       
  9632     p.min = function() {
       
  9633       if (arguments.length === 2) {
       
  9634         return arguments[0] < arguments[1] ? arguments[0] : arguments[1];
       
  9635       }
       
  9636       var numbers = arguments.length === 1 ? arguments[0] : arguments; // if single argument, array is used
       
  9637       if (! ("length" in numbers && numbers.length > 0)) {
       
  9638         throw "Non-empty array is expected";
       
  9639       }
       
  9640       var min = numbers[0],
       
  9641         count = numbers.length;
       
  9642       for (var i = 1; i < count; ++i) {
       
  9643         if (min > numbers[i]) {
       
  9644           min = numbers[i];
       
  9645         }
       
  9646       }
       
  9647       return min;
       
  9648     };
       
  9649 
       
  9650     /**
       
  9651     * Normalizes a number from another range into a value between 0 and 1.
       
  9652     * Identical to map(value, low, high, 0, 1);
       
  9653     * Numbers outside the range are not clamped to 0 and 1, because out-of-range
       
  9654     * values are often intentional and useful.
       
  9655     *
       
  9656     * @param {float} aNumber    The incoming value to be converted
       
  9657     * @param {float} low        Lower bound of the value's current range
       
  9658     * @param {float} high       Upper bound of the value's current range
       
  9659     *
       
  9660     * @returns {float}
       
  9661     *
       
  9662     * @see map
       
  9663     * @see lerp
       
  9664     */
       
  9665     p.norm = function(aNumber, low, high) {
       
  9666       return (aNumber - low) / (high - low);
       
  9667     };
       
  9668 
       
  9669     /**
       
  9670     * Facilitates exponential expressions. The pow() function is an efficient way of
       
  9671     * multiplying numbers by themselves (or their reciprocal) in large quantities.
       
  9672     * For example, pow(3, 5) is equivalent to the expression 3*3*3*3*3 and pow(3, -5)
       
  9673     * is equivalent to 1 / 3*3*3*3*3.
       
  9674     *
       
  9675     * @param {int|float} num        base of the exponential expression
       
  9676     * @param {int|float} exponent   power of which to raise the base
       
  9677     *
       
  9678     * @returns {float}
       
  9679     *
       
  9680     * @see sqrt
       
  9681     */
       
  9682     p.pow = Math.pow;
       
  9683 
       
  9684     /**
       
  9685     * Calculates the integer closest to the value parameter. For example, round(9.2) returns the value 9.
       
  9686     *
       
  9687     * @param {float} value        number to round
       
  9688     *
       
  9689     * @returns {int}
       
  9690     *
       
  9691     * @see floor
       
  9692     * @see ceil
       
  9693     */
       
  9694     p.round = Math.round;
       
  9695 
       
  9696     /**
       
  9697     * Squares a number (multiplies a number by itself). The result is always a positive number,
       
  9698     * as multiplying two negative numbers always yields a positive result. For example, -1 * -1 = 1.
       
  9699     *
       
  9700     * @param {float} value        int or float
       
  9701     *
       
  9702     * @returns {float}
       
  9703     *
       
  9704     * @see sqrt
       
  9705     */
       
  9706     p.sq = function(aNumber) {
       
  9707       return aNumber * aNumber;
       
  9708     };
       
  9709 
       
  9710     /**
       
  9711     * Calculates the square root of a number. The square root of a number is always positive,
       
  9712     * even though there may be a valid negative root. The square root s of number a is such
       
  9713     * that s*s = a. It is the opposite of squaring.
       
  9714     *
       
  9715     * @param {float} value        int or float, non negative
       
  9716     *
       
  9717     * @returns {float}
       
  9718     *
       
  9719     * @see pow
       
  9720     * @see sq
       
  9721     */
       
  9722     p.sqrt = Math.sqrt;
       
  9723 
       
  9724     // Trigonometry
       
  9725     /**
       
  9726     * The inverse of cos(), returns the arc cosine of a value. This function expects the
       
  9727     * values in the range of -1 to 1 and values are returned in the range 0 to PI (3.1415927).
       
  9728     *
       
  9729     * @param {float} value        the value whose arc cosine is to be returned
       
  9730     *
       
  9731     * @returns {float}
       
  9732     *
       
  9733     * @see cos
       
  9734     * @see asin
       
  9735     * @see atan
       
  9736     */
       
  9737     p.acos = Math.acos;
       
  9738 
       
  9739     /**
       
  9740     * The inverse of sin(), returns the arc sine of a value. This function expects the values
       
  9741     * in the range of -1 to 1 and values are returned in the range -PI/2 to PI/2.
       
  9742     *
       
  9743     * @param {float} value        the value whose arc sine is to be returned
       
  9744     *
       
  9745     * @returns {float}
       
  9746     *
       
  9747     * @see sin
       
  9748     * @see acos
       
  9749     * @see atan
       
  9750     */
       
  9751     p.asin = Math.asin;
       
  9752 
       
  9753     /**
       
  9754     * The inverse of tan(), returns the arc tangent of a value. This function expects the values
       
  9755     * in the range of -Infinity to Infinity (exclusive) and values are returned in the range -PI/2 to PI/2 .
       
  9756     *
       
  9757     * @param {float} value        -Infinity to Infinity (exclusive)
       
  9758     *
       
  9759     * @returns {float}
       
  9760     *
       
  9761     * @see tan
       
  9762     * @see asin
       
  9763     * @see acos
       
  9764     */
       
  9765     p.atan = Math.atan;
       
  9766 
       
  9767     /**
       
  9768     * Calculates the angle (in radians) from a specified point to the coordinate origin as measured from
       
  9769     * the positive x-axis. Values are returned as a float in the range from PI to -PI. The atan2() function
       
  9770     * is most often used for orienting geometry to the position of the cursor. Note: The y-coordinate of the
       
  9771     * point is the first parameter and the x-coordinate is the second due the the structure of calculating the tangent.
       
  9772     *
       
  9773     * @param {float} y        y-coordinate of the point
       
  9774     * @param {float} x        x-coordinate of the point
       
  9775     *
       
  9776     * @returns {float}
       
  9777     *
       
  9778     * @see tan
       
  9779     */
       
  9780     p.atan2 = Math.atan2;
       
  9781 
       
  9782     /**
       
  9783     * Calculates the cosine of an angle. This function expects the values of the angle parameter to be provided
       
  9784     * in radians (values from 0 to PI*2). Values are returned in the range -1 to 1.
       
  9785     *
       
  9786     * @param {float} value        an angle in radians
       
  9787     *
       
  9788     * @returns {float}
       
  9789     *
       
  9790     * @see tan
       
  9791     * @see sin
       
  9792     */
       
  9793     p.cos = Math.cos;
       
  9794 
       
  9795     /**
       
  9796     * Converts a radian measurement to its corresponding value in degrees. Radians and degrees are two ways of
       
  9797     * measuring the same thing. There are 360 degrees in a circle and 2*PI radians in a circle. For example,
       
  9798     * 90 degrees = PI/2 = 1.5707964. All trigonometric methods in Processing require their parameters to be specified in radians.
       
  9799     *
       
  9800     * @param {int|float} value        an angle in radians
       
  9801     *
       
  9802     * @returns {float}
       
  9803     *
       
  9804     * @see radians
       
  9805     */
       
  9806     p.degrees = function(aAngle) {
       
  9807       return (aAngle * 180) / Math.PI;
       
  9808     };
       
  9809 
       
  9810     /**
       
  9811     * Converts a degree measurement to its corresponding value in radians. Radians and degrees are two ways of
       
  9812     * measuring the same thing. There are 360 degrees in a circle and 2*PI radians in a circle. For example,
       
  9813     * 90 degrees = PI/2 = 1.5707964. All trigonometric methods in Processing require their parameters to be specified in radians.
       
  9814     *
       
  9815     * @param {int|float} value        an angle in radians
       
  9816     *
       
  9817     * @returns {float}
       
  9818     *
       
  9819     * @see degrees
       
  9820     */
       
  9821     p.radians = function(aAngle) {
       
  9822       return (aAngle / 180) * Math.PI;
       
  9823     };
       
  9824 
       
  9825     /**
       
  9826     * Calculates the sine of an angle. This function expects the values of the angle parameter to be provided in
       
  9827     * radians (values from 0 to 6.28). Values are returned in the range -1 to 1.
       
  9828     *
       
  9829     * @param {float} value        an angle in radians
       
  9830     *
       
  9831     * @returns {float}
       
  9832     *
       
  9833     * @see cos
       
  9834     * @see radians
       
  9835     */
       
  9836     p.sin = Math.sin;
       
  9837 
       
  9838     /**
       
  9839     * Calculates the ratio of the sine and cosine of an angle. This function expects the values of the angle
       
  9840     * parameter to be provided in radians (values from 0 to PI*2). Values are returned in the range infinity to -infinity.
       
  9841     *
       
  9842     * @param {float} value        an angle in radians
       
  9843     *
       
  9844     * @returns {float}
       
  9845     *
       
  9846     * @see cos
       
  9847     * @see sin
       
  9848     * @see radians
       
  9849     */
       
  9850     p.tan = Math.tan;
       
  9851 
       
  9852     var currentRandom = Math.random;
       
  9853 
       
  9854     /**
       
  9855     * Generates random numbers. Each time the random() function is called, it returns an unexpected value within
       
  9856     * the specified range. If one parameter is passed to the function it will return a float between zero and the
       
  9857     * value of the high parameter. The function call random(5) returns values between 0 and 5 (starting at zero,
       
  9858     * up to but not including 5). If two parameters are passed, it will return a float with a value between the
       
  9859     * parameters. The function call random(-5, 10.2) returns values starting at -5 up to (but not including) 10.2.
       
  9860     * To convert a floating-point random number to an integer, use the int() function.
       
  9861     *
       
  9862     * @param {int|float} value1         if one parameter is used, the top end to random from, if two params the low end
       
  9863     * @param {int|float} value2         the top end of the random range
       
  9864     *
       
  9865     * @returns {float}
       
  9866     *
       
  9867     * @see randomSeed
       
  9868     * @see noise
       
  9869     */
       
  9870     p.random = function() {
       
  9871       if(arguments.length === 0) {
       
  9872         return currentRandom();
       
  9873       }
       
  9874       if(arguments.length === 1) {
       
  9875         return currentRandom() * arguments[0];
       
  9876       }
       
  9877       var aMin = arguments[0], aMax = arguments[1];
       
  9878       return currentRandom() * (aMax - aMin) + aMin;
       
  9879     };
       
  9880 
       
  9881     // Pseudo-random generator
       
  9882     function Marsaglia(i1, i2) {
       
  9883       // from http://www.math.uni-bielefeld.de/~sillke/ALGORITHMS/random/marsaglia-c
       
  9884       var z=i1 || 362436069, w= i2 || 521288629;
       
  9885       var nextInt = function() {
       
  9886         z=(36969*(z&65535)+(z>>>16)) & 0xFFFFFFFF;
       
  9887         w=(18000*(w&65535)+(w>>>16)) & 0xFFFFFFFF;
       
  9888         return (((z&0xFFFF)<<16) | (w&0xFFFF)) & 0xFFFFFFFF;
       
  9889       };
       
  9890 
       
  9891       this.nextDouble = function() {
       
  9892         var i = nextInt() / 4294967296;
       
  9893         return i < 0 ? 1 + i : i;
       
  9894       };
       
  9895       this.nextInt = nextInt;
       
  9896     }
       
  9897     Marsaglia.createRandomized = function() {
       
  9898       var now = new Date();
       
  9899       return new Marsaglia((now / 60000) & 0xFFFFFFFF, now & 0xFFFFFFFF);
       
  9900     };
       
  9901 
       
  9902     /**
       
  9903     * Sets the seed value for random(). By default, random() produces different results each time the
       
  9904     * program is run. Set the value parameter to a constant to return the same pseudo-random numbers
       
  9905     * each time the software is run.
       
  9906     *
       
  9907     * @param {int|float} seed         int
       
  9908     *
       
  9909     * @see random
       
  9910     * @see noise
       
  9911     * @see noiseSeed
       
  9912     */
       
  9913     p.randomSeed = function(seed) {
       
  9914       currentRandom = (new Marsaglia(seed)).nextDouble;
       
  9915     };
       
  9916 
       
  9917     // Random
       
  9918     // We have two random()'s in the code... what does this do ? and which one is current ?
       
  9919     p.Random = function(seed) {
       
  9920       var haveNextNextGaussian = false, nextNextGaussian, random;
       
  9921 
       
  9922       this.nextGaussian = function() {
       
  9923         if (haveNextNextGaussian) {
       
  9924           haveNextNextGaussian = false;
       
  9925           return nextNextGaussian;
       
  9926         }
       
  9927         var v1, v2, s;
       
  9928         do {
       
  9929           v1 = 2 * random() - 1; // between -1.0 and 1.0
       
  9930           v2 = 2 * random() - 1; // between -1.0 and 1.0
       
  9931           s = v1 * v1 + v2 * v2;
       
  9932         }
       
  9933         while (s >= 1 || s === 0);
       
  9934 
       
  9935         var multiplier = Math.sqrt(-2 * Math.log(s) / s);
       
  9936         nextNextGaussian = v2 * multiplier;
       
  9937         haveNextNextGaussian = true;
       
  9938 
       
  9939         return v1 * multiplier;
       
  9940       };
       
  9941 
       
  9942       // by default use standard random, otherwise seeded
       
  9943       random = (seed === undef) ? Math.random : (new Marsaglia(seed)).nextDouble;
       
  9944     };
       
  9945 
       
  9946     // Noise functions and helpers
       
  9947     function PerlinNoise(seed) {
       
  9948       var rnd = seed !== undef ? new Marsaglia(seed) : Marsaglia.createRandomized();
       
  9949       var i, j;
       
  9950       // http://www.noisemachine.com/talk1/17b.html
       
  9951       // http://mrl.nyu.edu/~perlin/noise/
       
  9952       // generate permutation
       
  9953       var perm = new Uint8Array(512);
       
  9954       for(i=0;i<256;++i) { perm[i] = i; }
       
  9955       for(i=0;i<256;++i) { var t = perm[j = rnd.nextInt() & 0xFF]; perm[j] = perm[i]; perm[i] = t; }
       
  9956       // copy to avoid taking mod in perm[0];
       
  9957       for(i=0;i<256;++i) { perm[i + 256] = perm[i]; }
       
  9958 
       
  9959       function grad3d(i,x,y,z) {
       
  9960         var h = i & 15; // convert into 12 gradient directions
       
  9961         var u = h<8 ? x : y,
       
  9962             v = h<4 ? y : h===12||h===14 ? x : z;
       
  9963         return ((h&1) === 0 ? u : -u) + ((h&2) === 0 ? v : -v);
       
  9964       }
       
  9965 
       
  9966       function grad2d(i,x,y) {
       
  9967         var v = (i & 1) === 0 ? x : y;
       
  9968         return (i&2) === 0 ? -v : v;
       
  9969       }
       
  9970 
       
  9971       function grad1d(i,x) {
       
  9972         return (i&1) === 0 ? -x : x;
       
  9973       }
       
  9974 
       
  9975       function lerp(t,a,b) { return a + t * (b - a); }
       
  9976 
       
  9977       this.noise3d = function(x, y, z) {
       
  9978         var X = Math.floor(x)&255, Y = Math.floor(y)&255, Z = Math.floor(z)&255;
       
  9979         x -= Math.floor(x); y -= Math.floor(y); z -= Math.floor(z);
       
  9980         var fx = (3-2*x)*x*x, fy = (3-2*y)*y*y, fz = (3-2*z)*z*z;
       
  9981         var p0 = perm[X]+Y, p00 = perm[p0] + Z, p01 = perm[p0 + 1] + Z,
       
  9982             p1 = perm[X + 1] + Y, p10 = perm[p1] + Z, p11 = perm[p1 + 1] + Z;
       
  9983         return lerp(fz,
       
  9984           lerp(fy, lerp(fx, grad3d(perm[p00], x, y, z), grad3d(perm[p10], x-1, y, z)),
       
  9985                    lerp(fx, grad3d(perm[p01], x, y-1, z), grad3d(perm[p11], x-1, y-1,z))),
       
  9986           lerp(fy, lerp(fx, grad3d(perm[p00 + 1], x, y, z-1), grad3d(perm[p10 + 1], x-1, y, z-1)),
       
  9987                    lerp(fx, grad3d(perm[p01 + 1], x, y-1, z-1), grad3d(perm[p11 + 1], x-1, y-1,z-1))));
       
  9988       };
       
  9989 
       
  9990       this.noise2d = function(x, y) {
       
  9991         var X = Math.floor(x)&255, Y = Math.floor(y)&255;
       
  9992         x -= Math.floor(x); y -= Math.floor(y);
       
  9993         var fx = (3-2*x)*x*x, fy = (3-2*y)*y*y;
       
  9994         var p0 = perm[X]+Y, p1 = perm[X + 1] + Y;
       
  9995         return lerp(fy,
       
  9996           lerp(fx, grad2d(perm[p0], x, y), grad2d(perm[p1], x-1, y)),
       
  9997           lerp(fx, grad2d(perm[p0 + 1], x, y-1), grad2d(perm[p1 + 1], x-1, y-1)));
       
  9998       };
       
  9999 
       
 10000       this.noise1d = function(x) {
       
 10001         var X = Math.floor(x)&255;
       
 10002         x -= Math.floor(x);
       
 10003         var fx = (3-2*x)*x*x;
       
 10004         return lerp(fx, grad1d(perm[X], x), grad1d(perm[X+1], x-1));
       
 10005       };
       
 10006     }
       
 10007 
       
 10008     // processing defaults
       
 10009     var noiseProfile = { generator: undef, octaves: 4, fallout: 0.5, seed: undef};
       
 10010 
       
 10011     /**
       
 10012     * Returns the Perlin noise value at specified coordinates. Perlin noise is a random sequence
       
 10013     * generator producing a more natural ordered, harmonic succession of numbers compared to the
       
 10014     * standard random() function. It was invented by Ken Perlin in the 1980s and been used since
       
 10015     * in graphical applications to produce procedural textures, natural motion, shapes, terrains etc.
       
 10016     * The main difference to the random() function is that Perlin noise is defined in an infinite
       
 10017     * n-dimensional space where each pair of coordinates corresponds to a fixed semi-random value
       
 10018     * (fixed only for the lifespan of the program). The resulting value will always be between 0.0
       
 10019     * and 1.0. Processing can compute 1D, 2D and 3D noise, depending on the number of coordinates
       
 10020     * given. The noise value can be animated by moving through the noise space as demonstrated in
       
 10021     * the example above. The 2nd and 3rd dimension can also be interpreted as time.
       
 10022     * The actual noise is structured similar to an audio signal, in respect to the function's use
       
 10023     * of frequencies. Similar to the concept of harmonics in physics, perlin noise is computed over
       
 10024     * several octaves which are added together for the final result.
       
 10025     * Another way to adjust the character of the resulting sequence is the scale of the input
       
 10026     * coordinates. As the function works within an infinite space the value of the coordinates
       
 10027     * doesn't matter as such, only the distance between successive coordinates does (eg. when using
       
 10028     * noise() within a loop). As a general rule the smaller the difference between coordinates, the
       
 10029     * smoother the resulting noise sequence will be. Steps of 0.005-0.03 work best for most applications,
       
 10030     * but this will differ depending on use.
       
 10031     *
       
 10032     * @param {float} x          x coordinate in noise space
       
 10033     * @param {float} y          y coordinate in noise space
       
 10034     * @param {float} z          z coordinate in noise space
       
 10035     *
       
 10036     * @returns {float}
       
 10037     *
       
 10038     * @see random
       
 10039     * @see noiseDetail
       
 10040     */
       
 10041     p.noise = function(x, y, z) {
       
 10042       if(noiseProfile.generator === undef) {
       
 10043         // caching
       
 10044         noiseProfile.generator = new PerlinNoise(noiseProfile.seed);
       
 10045       }
       
 10046       var generator = noiseProfile.generator;
       
 10047       var effect = 1, k = 1, sum = 0;
       
 10048       for(var i=0; i<noiseProfile.octaves; ++i) {
       
 10049         effect *= noiseProfile.fallout;
       
 10050         switch (arguments.length) {
       
 10051         case 1:
       
 10052           sum += effect * (1 + generator.noise1d(k*x))/2; break;
       
 10053         case 2:
       
 10054           sum += effect * (1 + generator.noise2d(k*x, k*y))/2; break;
       
 10055         case 3:
       
 10056           sum += effect * (1 + generator.noise3d(k*x, k*y, k*z))/2; break;
       
 10057         }
       
 10058         k *= 2;
       
 10059       }
       
 10060       return sum;
       
 10061     };
       
 10062 
       
 10063     /**
       
 10064     * Adjusts the character and level of detail produced by the Perlin noise function.
       
 10065     * Similar to harmonics in physics, noise is computed over several octaves. Lower octaves
       
 10066     * contribute more to the output signal and as such define the overal intensity of the noise,
       
 10067     * whereas higher octaves create finer grained details in the noise sequence. By default,
       
 10068     * noise is computed over 4 octaves with each octave contributing exactly half than its
       
 10069     * predecessor, starting at 50% strength for the 1st octave. This falloff amount can be
       
 10070     * changed by adding an additional function parameter. Eg. a falloff factor of 0.75 means
       
 10071     * each octave will now have 75% impact (25% less) of the previous lower octave. Any value
       
 10072     * between 0.0 and 1.0 is valid, however note that values greater than 0.5 might result in
       
 10073     * greater than 1.0 values returned by noise(). By changing these parameters, the signal
       
 10074     * created by the noise() function can be adapted to fit very specific needs and characteristics.
       
 10075     *
       
 10076     * @param {int} octaves          number of octaves to be used by the noise() function
       
 10077     * @param {float} falloff        falloff factor for each octave
       
 10078     *
       
 10079     * @see noise
       
 10080     */
       
 10081     p.noiseDetail = function(octaves, fallout) {
       
 10082       noiseProfile.octaves = octaves;
       
 10083       if(fallout !== undef) {
       
 10084         noiseProfile.fallout = fallout;
       
 10085       }
       
 10086     };
       
 10087 
       
 10088     /**
       
 10089     * Sets the seed value for noise(). By default, noise() produces different results each
       
 10090     * time the program is run. Set the value parameter to a constant to return the same
       
 10091     * pseudo-random numbers each time the software is run.
       
 10092     *
       
 10093     * @param {int} seed         int
       
 10094     *
       
 10095     * @returns {float}
       
 10096     *
       
 10097     * @see random
       
 10098     * @see radomSeed
       
 10099     * @see noise
       
 10100     * @see noiseDetail
       
 10101     */
       
 10102     p.noiseSeed = function(seed) {
       
 10103       noiseProfile.seed = seed;
       
 10104       noiseProfile.generator = undef;
       
 10105     };
       
 10106 
       
 10107     /**
       
 10108     * Defines the dimension of the display window in units of pixels. The size() function must
       
 10109     * be the first line in setup(). If size() is not called, the default size of the window is
       
 10110     * 100x100 pixels. The system variables width and height are set by the parameters passed to
       
 10111     * the size() function.
       
 10112     *
       
 10113     * @param {int} aWidth     width of the display window in units of pixels
       
 10114     * @param {int} aHeight    height of the display window in units of pixels
       
 10115     * @param {MODE} aMode     Either P2D, P3D, JAVA2D, or OPENGL
       
 10116     *
       
 10117     * @see createGraphics
       
 10118     * @see screen
       
 10119     */
       
 10120     DrawingShared.prototype.size = function(aWidth, aHeight, aMode) {
       
 10121       if (doStroke) {
       
 10122         p.stroke(0);
       
 10123       }
       
 10124 
       
 10125       if (doFill) {
       
 10126         p.fill(255);
       
 10127       }
       
 10128 
       
 10129       // The default 2d context has already been created in the p.init() stage if
       
 10130       // a 3d context was not specified. This is so that a 2d context will be
       
 10131       // available if size() was not called.
       
 10132       var savedProperties = {
       
 10133         fillStyle: curContext.fillStyle,
       
 10134         strokeStyle: curContext.strokeStyle,
       
 10135         lineCap: curContext.lineCap,
       
 10136         lineJoin: curContext.lineJoin
       
 10137       };
       
 10138       // remove the style width and height properties to ensure that the canvas gets set to
       
 10139       // aWidth and aHeight coming in
       
 10140       if (curElement.style.length > 0 ) {
       
 10141         curElement.style.removeProperty("width");
       
 10142         curElement.style.removeProperty("height");
       
 10143       }
       
 10144 
       
 10145       curElement.width = p.width = aWidth || 100;
       
 10146       curElement.height = p.height = aHeight || 100;
       
 10147 
       
 10148       for (var prop in savedProperties) {
       
 10149         if (savedProperties.hasOwnProperty(prop)) {
       
 10150           curContext[prop] = savedProperties[prop];
       
 10151         }
       
 10152       }
       
 10153 
       
 10154       // make sure to set the default font the first time round.
       
 10155       p.textFont(curTextFont);
       
 10156 
       
 10157       // Set the background to whatever it was called last as if background() was called before size()
       
 10158       // If background() hasn't been called before, set background() to a light gray
       
 10159       p.background();
       
 10160 
       
 10161       // set 5% for pixels to cache (or 1000)
       
 10162       maxPixelsCached = Math.max(1000, aWidth * aHeight * 0.05);
       
 10163 
       
 10164       // Externalize the context
       
 10165       p.externals.context = curContext;
       
 10166 
       
 10167       for (var i = 0; i < PConstants.SINCOS_LENGTH; i++) {
       
 10168         sinLUT[i] = p.sin(i * (PConstants.PI / 180) * 0.5);
       
 10169         cosLUT[i] = p.cos(i * (PConstants.PI / 180) * 0.5);
       
 10170       }
       
 10171     };
       
 10172 
       
 10173     Drawing2D.prototype.size = function(aWidth, aHeight, aMode) {
       
 10174       if (curContext === undef) {
       
 10175         // size() was called without p.init() default context, i.e. p.createGraphics()
       
 10176         curContext = curElement.getContext("2d");
       
 10177         userMatrixStack = new PMatrixStack();
       
 10178         userReverseMatrixStack = new PMatrixStack();
       
 10179         modelView = new PMatrix2D();
       
 10180         modelViewInv = new PMatrix2D();
       
 10181       }
       
 10182 
       
 10183       DrawingShared.prototype.size.apply(this, arguments);
       
 10184     };
       
 10185 
       
 10186     Drawing3D.prototype.size = (function() {
       
 10187       var size3DCalled = false;
       
 10188 
       
 10189       return function size(aWidth, aHeight, aMode) {
       
 10190         if (size3DCalled) {
       
 10191           throw "Multiple calls to size() for 3D renders are not allowed.";
       
 10192         }
       
 10193         size3DCalled = true;
       
 10194 
       
 10195         function getGLContext(canvas) {
       
 10196           var ctxNames = ['experimental-webgl', 'webgl', 'webkit-3d'],
       
 10197               gl;
       
 10198 
       
 10199           for (var i=0, l=ctxNames.length; i<l; i++) {
       
 10200             gl = canvas.getContext(ctxNames[i], {antialias: false});
       
 10201             if (gl) {
       
 10202               break;
       
 10203             }
       
 10204           }
       
 10205 
       
 10206           return gl;
       
 10207         }
       
 10208 
       
 10209         // get the 3D rendering context
       
 10210         try {
       
 10211           // If the HTML <canvas> dimensions differ from the
       
 10212           // dimensions specified in the size() call in the sketch, for
       
 10213           // 3D sketches, browsers will either not render or render the
       
 10214           // scene incorrectly. To fix this, we need to adjust the
       
 10215           // width and height attributes of the canvas.
       
 10216           curElement.width = p.width = aWidth || 100;
       
 10217           curElement.height = p.height = aHeight || 100;
       
 10218           curContext = getGLContext(curElement);
       
 10219           canTex = curContext.createTexture(); // texture
       
 10220           textTex = curContext.createTexture(); // texture
       
 10221         } catch(e_size) {
       
 10222           Processing.debug(e_size);
       
 10223         }
       
 10224 
       
 10225         if (!curContext) {
       
 10226           throw "WebGL context is not supported on this browser.";
       
 10227         }
       
 10228 
       
 10229         // Set defaults
       
 10230         curContext.viewport(0, 0, curElement.width, curElement.height);
       
 10231         curContext.enable(curContext.DEPTH_TEST);
       
 10232         curContext.enable(curContext.BLEND);
       
 10233         curContext.blendFunc(curContext.SRC_ALPHA, curContext.ONE_MINUS_SRC_ALPHA);
       
 10234 
       
 10235         // Create the program objects to render 2D (points, lines) and
       
 10236         // 3D (spheres, boxes) shapes. Because 2D shapes are not lit,
       
 10237         // lighting calculations could be ommitted from that program object.
       
 10238         programObject2D = createProgramObject(curContext, vertexShaderSource2D, fragmentShaderSource2D);
       
 10239 
       
 10240         programObjectUnlitShape = createProgramObject(curContext, vShaderSrcUnlitShape, fShaderSrcUnlitShape);
       
 10241 
       
 10242         // Set the default point and line width for the 2D and unlit shapes.
       
 10243         p.strokeWeight(1.0);
       
 10244 
       
 10245         // Now that the programs have been compiled, we can set the default
       
 10246         // states for the lights.
       
 10247         programObject3D = createProgramObject(curContext, vertexShaderSource3D, fragmentShaderSource3D);
       
 10248         curContext.useProgram(programObject3D);
       
 10249 
       
 10250         // assume we aren't using textures by default
       
 10251         uniformi("usingTexture3d", programObject3D, "usingTexture", usingTexture);
       
 10252         // assume that we arn't tinting by default
       
 10253         p.lightFalloff(1, 0, 0);
       
 10254         p.shininess(1);
       
 10255         p.ambient(255, 255, 255);
       
 10256         p.specular(0, 0, 0);
       
 10257         p.emissive(0, 0, 0);
       
 10258 
       
 10259         // Create buffers for 3D primitives
       
 10260         boxBuffer = curContext.createBuffer();
       
 10261         curContext.bindBuffer(curContext.ARRAY_BUFFER, boxBuffer);
       
 10262         curContext.bufferData(curContext.ARRAY_BUFFER, boxVerts, curContext.STATIC_DRAW);
       
 10263 
       
 10264         boxNormBuffer = curContext.createBuffer();
       
 10265         curContext.bindBuffer(curContext.ARRAY_BUFFER, boxNormBuffer);
       
 10266         curContext.bufferData(curContext.ARRAY_BUFFER, boxNorms, curContext.STATIC_DRAW);
       
 10267 
       
 10268         boxOutlineBuffer = curContext.createBuffer();
       
 10269         curContext.bindBuffer(curContext.ARRAY_BUFFER, boxOutlineBuffer);
       
 10270         curContext.bufferData(curContext.ARRAY_BUFFER, boxOutlineVerts, curContext.STATIC_DRAW);
       
 10271 
       
 10272         // used to draw the rectangle and the outline
       
 10273         rectBuffer = curContext.createBuffer();
       
 10274         curContext.bindBuffer(curContext.ARRAY_BUFFER, rectBuffer);
       
 10275         curContext.bufferData(curContext.ARRAY_BUFFER, rectVerts, curContext.STATIC_DRAW);
       
 10276 
       
 10277         rectNormBuffer = curContext.createBuffer();
       
 10278         curContext.bindBuffer(curContext.ARRAY_BUFFER, rectNormBuffer);
       
 10279         curContext.bufferData(curContext.ARRAY_BUFFER, rectNorms, curContext.STATIC_DRAW);
       
 10280 
       
 10281         // The sphere vertices are specified dynamically since the user
       
 10282         // can change the level of detail. Everytime the user does that
       
 10283         // using sphereDetail(), the new vertices are calculated.
       
 10284         sphereBuffer = curContext.createBuffer();
       
 10285 
       
 10286         lineBuffer = curContext.createBuffer();
       
 10287 
       
 10288         // Shape buffers
       
 10289         fillBuffer = curContext.createBuffer();
       
 10290         fillColorBuffer = curContext.createBuffer();
       
 10291         strokeColorBuffer = curContext.createBuffer();
       
 10292         shapeTexVBO = curContext.createBuffer();
       
 10293 
       
 10294         pointBuffer = curContext.createBuffer();
       
 10295         curContext.bindBuffer(curContext.ARRAY_BUFFER, pointBuffer);
       
 10296         curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array([0, 0, 0]), curContext.STATIC_DRAW);
       
 10297 
       
 10298         textBuffer = curContext.createBuffer();
       
 10299         curContext.bindBuffer(curContext.ARRAY_BUFFER, textBuffer );
       
 10300         curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array([1,1,0,-1,1,0,-1,-1,0,1,-1,0]), curContext.STATIC_DRAW);
       
 10301 
       
 10302         textureBuffer = curContext.createBuffer();
       
 10303         curContext.bindBuffer(curContext.ARRAY_BUFFER, textureBuffer);
       
 10304         curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array([0,0,1,0,1,1,0,1]), curContext.STATIC_DRAW);
       
 10305 
       
 10306         indexBuffer = curContext.createBuffer();
       
 10307         curContext.bindBuffer(curContext.ELEMENT_ARRAY_BUFFER, indexBuffer);
       
 10308         curContext.bufferData(curContext.ELEMENT_ARRAY_BUFFER, new Uint16Array([0,1,2,2,3,0]), curContext.STATIC_DRAW);
       
 10309 
       
 10310         cam = new PMatrix3D();
       
 10311         cameraInv = new PMatrix3D();
       
 10312         modelView = new PMatrix3D();
       
 10313         modelViewInv = new PMatrix3D();
       
 10314         projection = new PMatrix3D();
       
 10315         p.camera();
       
 10316         p.perspective();
       
 10317 
       
 10318         userMatrixStack = new PMatrixStack();
       
 10319         userReverseMatrixStack = new PMatrixStack();
       
 10320         // used by both curve and bezier, so just init here
       
 10321         curveBasisMatrix = new PMatrix3D();
       
 10322         curveToBezierMatrix = new PMatrix3D();
       
 10323         curveDrawMatrix = new PMatrix3D();
       
 10324         bezierDrawMatrix = new PMatrix3D();
       
 10325         bezierBasisInverse = new PMatrix3D();
       
 10326         bezierBasisMatrix = new PMatrix3D();
       
 10327         bezierBasisMatrix.set(-1, 3, -3, 1, 3, -6, 3, 0, -3, 3, 0, 0, 1, 0, 0, 0);
       
 10328 
       
 10329         DrawingShared.prototype.size.apply(this, arguments);
       
 10330       };
       
 10331     }());
       
 10332 
       
 10333     ////////////////////////////////////////////////////////////////////////////
       
 10334     // Lights
       
 10335     ////////////////////////////////////////////////////////////////////////////
       
 10336 
       
 10337     /**
       
 10338      * Adds an ambient light. Ambient light doesn't come from a specific direction,
       
 10339      * the rays have light have bounced around so much that objects are evenly lit
       
 10340      * from all sides. Ambient lights are almost always used in combination with
       
 10341      * other types of lights. Lights need to be included in the <b>draw()</b> to
       
 10342      * remain persistent in a looping program. Placing them in the <b>setup()</b>
       
 10343      * of a looping program will cause them to only have an effect the first time
       
 10344      * through the loop. The effect of the parameters is determined by the current
       
 10345      * color mode.
       
 10346      *
       
 10347      * @param {int | float} r red or hue value
       
 10348      * @param {int | float} g green or hue value
       
 10349      * @param {int | float} b blue or hue value
       
 10350      *
       
 10351      * @param {int | float} x x position of light (used for falloff)
       
 10352      * @param {int | float} y y position of light (used for falloff)
       
 10353      * @param {int | float} z z position of light (used for falloff)
       
 10354      *
       
 10355      * @returns none
       
 10356      *
       
 10357      * @see lights
       
 10358      * @see directionalLight
       
 10359      * @see pointLight
       
 10360      * @see spotLight
       
 10361     */
       
 10362     Drawing2D.prototype.ambientLight = DrawingShared.prototype.a3DOnlyFunction;
       
 10363 
       
 10364     Drawing3D.prototype.ambientLight = function(r, g, b, x, y, z) {
       
 10365       if (lightCount === PConstants.MAX_LIGHTS) {
       
 10366         throw "can only create " + PConstants.MAX_LIGHTS + " lights";
       
 10367       }
       
 10368 
       
 10369       var pos = new PVector(x, y, z);
       
 10370       var view = new PMatrix3D();
       
 10371       view.scale(1, -1, 1);
       
 10372       view.apply(modelView.array());
       
 10373       view.mult(pos, pos);
       
 10374 
       
 10375       // Instead of calling p.color, we do the calculations ourselves to 
       
 10376       // reduce property lookups.
       
 10377       var col = color$4(r, g, b, 0);
       
 10378       var normalizedCol = [ ((col & PConstants.RED_MASK) >>> 16) / 255,
       
 10379                             ((col & PConstants.GREEN_MASK) >>> 8) / 255,
       
 10380                              (col & PConstants.BLUE_MASK) / 255 ];
       
 10381 
       
 10382       curContext.useProgram(programObject3D);
       
 10383       uniformf("lights.color.3d." + lightCount, programObject3D, "lights" + lightCount + ".color", normalizedCol);
       
 10384       uniformf("lights.position.3d." + lightCount, programObject3D, "lights" + lightCount + ".position", pos.array());
       
 10385       uniformi("lights.type.3d." + lightCount, programObject3D, "lights" + lightCount + ".type", 0);
       
 10386       uniformi("lightCount3d", programObject3D, "lightCount", ++lightCount);
       
 10387     };
       
 10388 
       
 10389     /**
       
 10390      * Adds a directional light. Directional light comes from one direction and
       
 10391      * is stronger when hitting a surface squarely and weaker if it hits at a
       
 10392      * gentle angle. After hitting a surface, a directional lights scatters in
       
 10393      * all directions. Lights need to be included in the <b>draw()</b> to remain
       
 10394      * persistent in a looping program. Placing them in the <b>setup()</b> of a
       
 10395      * looping program will cause them to only have an effect the first time
       
 10396      * through the loop. The affect of the <br>r</b>, <br>g</b>, and <br>b</b>
       
 10397      * parameters is determined by the current color mode. The <b>nx</b>,
       
 10398      * <b>ny</b>, and <b>nz</b> parameters specify the direction the light is
       
 10399      * facing. For example, setting <b>ny</b> to -1 will cause the geometry to be
       
 10400      * lit from below (the light is facing directly upward).
       
 10401      *
       
 10402      * @param {int | float} r red or hue value
       
 10403      * @param {int | float} g green or hue value
       
 10404      * @param {int | float} b blue or hue value
       
 10405      *
       
 10406      * @param {int | float} nx direction along the x axis
       
 10407      * @param {int | float} ny direction along the y axis
       
 10408      * @param {int | float} nz direction along the z axis
       
 10409      *
       
 10410      * @returns none
       
 10411      *
       
 10412      * @see lights
       
 10413      * @see ambientLight
       
 10414      * @see pointLight
       
 10415      * @see spotLight
       
 10416     */
       
 10417     Drawing2D.prototype.directionalLight = DrawingShared.prototype.a3DOnlyFunction;
       
 10418 
       
 10419     Drawing3D.prototype.directionalLight = function(r, g, b, nx, ny, nz) {
       
 10420       if (lightCount === PConstants.MAX_LIGHTS) {
       
 10421         throw "can only create " + PConstants.MAX_LIGHTS + " lights";
       
 10422       }
       
 10423 
       
 10424       curContext.useProgram(programObject3D);
       
 10425 
       
 10426       var mvm = new PMatrix3D();
       
 10427       mvm.scale(1, -1, 1);
       
 10428       mvm.apply(modelView.array());
       
 10429       mvm = mvm.array();
       
 10430 
       
 10431       // We need to multiply the direction by the model view matrix, but
       
 10432       // the mult function checks the w component of the vector, if it isn't
       
 10433       // present, it uses 1, so we manually multiply.
       
 10434       var dir = [
       
 10435         mvm[0] * nx + mvm[4] * ny + mvm[8] * nz,
       
 10436         mvm[1] * nx + mvm[5] * ny + mvm[9] * nz,
       
 10437         mvm[2] * nx + mvm[6] * ny + mvm[10] * nz
       
 10438       ];
       
 10439 
       
 10440       // Instead of calling p.color, we do the calculations ourselves to 
       
 10441       // reduce property lookups.
       
 10442       var col = color$4(r, g, b, 0);
       
 10443       var normalizedCol = [ ((col & PConstants.RED_MASK) >>> 16) / 255,
       
 10444                             ((col & PConstants.GREEN_MASK) >>> 8) / 255,
       
 10445                              (col & PConstants.BLUE_MASK) / 255 ];
       
 10446 
       
 10447       uniformf("lights.color.3d." + lightCount, programObject3D, "lights" + lightCount + ".color", normalizedCol);
       
 10448       uniformf("lights.position.3d." + lightCount, programObject3D, "lights" + lightCount + ".position", dir);
       
 10449       uniformi("lights.type.3d." + lightCount, programObject3D, "lights" + lightCount + ".type", 1);
       
 10450       uniformi("lightCount3d", programObject3D, "lightCount", ++lightCount);
       
 10451     };
       
 10452 
       
 10453     /**
       
 10454      * Sets the falloff rates for point lights, spot lights, and ambient lights.
       
 10455      * The parameters are used to determine the falloff with the following equation:
       
 10456      *
       
 10457      * d = distance from light position to vertex position
       
 10458      * falloff = 1 / (CONSTANT + d * LINEAR + (d*d) * QUADRATIC)
       
 10459      *
       
 10460      * Like <b>fill()</b>, it affects only the elements which are created after it in the
       
 10461      * code. The default value if <b>LightFalloff(1.0, 0.0, 0.0)</b>. Thinking about an
       
 10462      * ambient light with a falloff can be tricky. It is used, for example, if you
       
 10463      * wanted a region of your scene to be lit ambiently one color and another region
       
 10464      * to be lit ambiently by another color, you would use an ambient light with location
       
 10465      * and falloff. You can think of it as a point light that doesn't care which direction
       
 10466      * a surface is facing.
       
 10467      *
       
 10468      * @param {int | float} constant constant value for determining falloff
       
 10469      * @param {int | float} linear linear value for determining falloff
       
 10470      * @param {int | float} quadratic quadratic value for determining falloff
       
 10471      *
       
 10472      * @returns none
       
 10473      *
       
 10474      * @see lights
       
 10475      * @see ambientLight
       
 10476      * @see pointLight
       
 10477      * @see spotLight
       
 10478      * @see lightSpecular
       
 10479     */
       
 10480     Drawing2D.prototype.lightFalloff = DrawingShared.prototype.a3DOnlyFunction;
       
 10481 
       
 10482     Drawing3D.prototype.lightFalloff = function(constant, linear, quadratic) {
       
 10483       curContext.useProgram(programObject3D);
       
 10484       uniformf("falloff3d", programObject3D, "falloff", [constant, linear, quadratic]);
       
 10485     };
       
 10486 
       
 10487     /**
       
 10488      * Sets the specular color for lights. Like <b>fill()</b>, it affects only the
       
 10489      * elements which are created after it in the code. Specular refers to light
       
 10490      * which bounces off a surface in a perferred direction (rather than bouncing
       
 10491      * in all directions like a diffuse light) and is used for creating highlights.
       
 10492      * The specular quality of a light interacts with the specular material qualities
       
 10493      * set through the <b>specular()</b> and <b>shininess()</b> functions.
       
 10494      *
       
 10495      * @param {int | float} r red or hue value
       
 10496      * @param {int | float} g green or hue value
       
 10497      * @param {int | float} b blue or hue value
       
 10498      *
       
 10499      * @returns none
       
 10500      *
       
 10501      * @see lights
       
 10502      * @see ambientLight
       
 10503      * @see pointLight
       
 10504      * @see spotLight
       
 10505     */
       
 10506     Drawing2D.prototype.lightSpecular = DrawingShared.prototype.a3DOnlyFunction;
       
 10507 
       
 10508     Drawing3D.prototype.lightSpecular = function(r, g, b) {
       
 10509 
       
 10510       // Instead of calling p.color, we do the calculations ourselves to 
       
 10511       // reduce property lookups.
       
 10512       var col = color$4(r, g, b, 0);
       
 10513       var normalizedCol = [ ((col & PConstants.RED_MASK) >>> 16) / 255,
       
 10514                             ((col & PConstants.GREEN_MASK) >>> 8) / 255,
       
 10515                              (col & PConstants.BLUE_MASK) / 255 ];
       
 10516 
       
 10517       curContext.useProgram(programObject3D);
       
 10518       uniformf("specular3d", programObject3D, "specular", normalizedCol);
       
 10519     };
       
 10520 
       
 10521     /**
       
 10522      * Sets the default ambient light, directional light, falloff, and specular
       
 10523      * values. The defaults are ambientLight(128, 128, 128) and
       
 10524      * directionalLight(128, 128, 128, 0, 0, -1), lightFalloff(1, 0, 0), and
       
 10525      * lightSpecular(0, 0, 0). Lights need to be included in the draw() to remain
       
 10526      * persistent in a looping program. Placing them in the setup() of a looping
       
 10527      * program will cause them to only have an effect the first time through the
       
 10528      * loop.
       
 10529      *
       
 10530      * @returns none
       
 10531      *
       
 10532      * @see ambientLight
       
 10533      * @see directionalLight
       
 10534      * @see pointLight
       
 10535      * @see spotLight
       
 10536      * @see noLights
       
 10537      *
       
 10538     */
       
 10539     p.lights = function() {
       
 10540       p.ambientLight(128, 128, 128);
       
 10541       p.directionalLight(128, 128, 128, 0, 0, -1);
       
 10542       p.lightFalloff(1, 0, 0);
       
 10543       p.lightSpecular(0, 0, 0);
       
 10544     };
       
 10545 
       
 10546     /**
       
 10547      * Adds a point light. Lights need to be included in the <b>draw()</b> to remain
       
 10548      * persistent in a looping program. Placing them in the <b>setup()</b> of a
       
 10549      * looping program will cause them to only have an effect the first time through
       
 10550      * the loop. The affect of the <b>r</b>, <b>g</b>, and <b>b</b> parameters
       
 10551      * is determined by the current color mode. The <b>x</b>, <b>y</b>, and <b>z</b>
       
 10552      * parameters set the position of the light.
       
 10553      *
       
 10554      * @param {int | float} r red or hue value
       
 10555      * @param {int | float} g green or hue value
       
 10556      * @param {int | float} b blue or hue value
       
 10557      * @param {int | float} x x coordinate of the light
       
 10558      * @param {int | float} y y coordinate of the light
       
 10559      * @param {int | float} z z coordinate of the light
       
 10560      *
       
 10561      * @returns none
       
 10562      *
       
 10563      * @see lights
       
 10564      * @see directionalLight
       
 10565      * @see ambientLight
       
 10566      * @see spotLight
       
 10567     */
       
 10568     Drawing2D.prototype.pointLight = DrawingShared.prototype.a3DOnlyFunction;
       
 10569 
       
 10570     Drawing3D.prototype.pointLight = function(r, g, b, x, y, z) {
       
 10571       if (lightCount === PConstants.MAX_LIGHTS) {
       
 10572         throw "can only create " + PConstants.MAX_LIGHTS + " lights";
       
 10573       }
       
 10574 
       
 10575       // Place the point in view space once instead of once per vertex
       
 10576       // in the shader.
       
 10577       var pos = new PVector(x, y, z);
       
 10578       var view = new PMatrix3D();
       
 10579       view.scale(1, -1, 1);
       
 10580       view.apply(modelView.array());
       
 10581       view.mult(pos, pos);
       
 10582 
       
 10583       // Instead of calling p.color, we do the calculations ourselves to 
       
 10584       // reduce property lookups.
       
 10585       var col = color$4(r, g, b, 0);
       
 10586       var normalizedCol = [ ((col & PConstants.RED_MASK) >>> 16) / 255,
       
 10587                             ((col & PConstants.GREEN_MASK) >>> 8) / 255,
       
 10588                              (col & PConstants.BLUE_MASK) / 255 ];
       
 10589 
       
 10590       curContext.useProgram(programObject3D);
       
 10591       uniformf("lights.color.3d." + lightCount, programObject3D, "lights" + lightCount + ".color", normalizedCol);
       
 10592       uniformf("lights.position.3d." + lightCount, programObject3D, "lights" + lightCount + ".position", pos.array());
       
 10593       uniformi("lights.type.3d." + lightCount, programObject3D, "lights" + lightCount + ".type", 2);
       
 10594       uniformi("lightCount3d", programObject3D, "lightCount", ++lightCount);
       
 10595     };
       
 10596 
       
 10597     /**
       
 10598      * Disable all lighting. Lighting is turned off by default and enabled with
       
 10599      * the lights() method. This function can be used to disable lighting so
       
 10600      * that 2D geometry (which does not require lighting) can be drawn after a
       
 10601      * set of lighted 3D geometry.
       
 10602      *
       
 10603      * @returns none
       
 10604      *
       
 10605      * @see lights
       
 10606     */
       
 10607     Drawing2D.prototype.noLights = DrawingShared.prototype.a3DOnlyFunction;
       
 10608 
       
 10609     Drawing3D.prototype.noLights = function() {
       
 10610       lightCount = 0;
       
 10611       curContext.useProgram(programObject3D);
       
 10612       uniformi("lightCount3d", programObject3D, "lightCount", lightCount);
       
 10613     };
       
 10614 
       
 10615     /**
       
 10616      * Adds a spot light. Lights need to be included in the <b>draw()</b> to
       
 10617      * remain persistent in a looping program. Placing them in the <b>setup()</b>
       
 10618      * of a looping program will cause them to only have an effect the first time
       
 10619      * through the loop. The affect of the <b>r</b>, <b>g</b>, and <b>b</b> parameters
       
 10620      * is determined by the current color mode. The <b>x</b>, <b>y</b>, and <b>z</b>
       
 10621      * parameters specify the position of the light and <b>nx</b>, <b>ny</b>, <b>nz</b>
       
 10622      * specify the direction or light. The angle parameter affects <b>angle</b> of the
       
 10623      * spotlight cone.
       
 10624      *
       
 10625      * @param {int | float} r red or hue value
       
 10626      * @param {int | float} g green or hue value
       
 10627      * @param {int | float} b blue or hue value
       
 10628      * @param {int | float} x coordinate of the light
       
 10629      * @param {int | float} y coordinate of the light
       
 10630      * @param {int | float} z coordinate of the light
       
 10631      * @param {int | float} nx direction along the x axis
       
 10632      * @param {int | float} ny direction along the y axis
       
 10633      * @param {int | float} nz direction along the z axis
       
 10634      * @param {float} angle angle of the spotlight cone
       
 10635      * @param {float} concentration exponent determining the center bias of the cone
       
 10636      *
       
 10637      * @returns none
       
 10638      *
       
 10639      * @see lights
       
 10640      * @see directionalLight
       
 10641      * @see ambientLight
       
 10642      * @see pointLight
       
 10643     */
       
 10644     Drawing2D.prototype.spotLight = DrawingShared.prototype.a3DOnlyFunction;
       
 10645 
       
 10646     Drawing3D.prototype.spotLight = function(r, g, b, x, y, z, nx, ny, nz, angle, concentration) {
       
 10647       if (lightCount === PConstants.MAX_LIGHTS) {
       
 10648         throw "can only create " + PConstants.MAX_LIGHTS + " lights";
       
 10649       }
       
 10650 
       
 10651       curContext.useProgram(programObject3D);
       
 10652 
       
 10653       // multiply the position and direction by the model view matrix
       
 10654       // once per object rather than once per vertex.
       
 10655       var pos = new PVector(x, y, z);
       
 10656       var mvm = new PMatrix3D();
       
 10657       mvm.scale(1, -1, 1);
       
 10658       mvm.apply(modelView.array());
       
 10659       mvm.mult(pos, pos);
       
 10660 
       
 10661       // Convert to array since we need to directly access the elements.
       
 10662       mvm = mvm.array();
       
 10663 
       
 10664       // We need to multiply the direction by the model view matrix, but
       
 10665       // the mult function checks the w component of the vector, if it isn't
       
 10666       // present, it uses 1, so we use a very small value as a work around.
       
 10667       var dir = [
       
 10668           mvm[0] * nx + mvm[4] * ny + mvm[8] * nz,
       
 10669           mvm[1] * nx + mvm[5] * ny + mvm[9] * nz,
       
 10670           mvm[2] * nx + mvm[6] * ny + mvm[10] * nz
       
 10671       ];
       
 10672 
       
 10673       // Instead of calling p.color, we do the calculations ourselves to 
       
 10674       // reduce property lookups.
       
 10675       var col = color$4(r, g, b, 0);
       
 10676       var normalizedCol = [ ((col & PConstants.RED_MASK) >>> 16) / 255,
       
 10677                             ((col & PConstants.GREEN_MASK) >>> 8) / 255,
       
 10678                              (col & PConstants.BLUE_MASK) / 255 ];
       
 10679 
       
 10680       uniformf("lights.color.3d." + lightCount, programObject3D, "lights" + lightCount + ".color", normalizedCol);
       
 10681       uniformf("lights.position.3d." + lightCount, programObject3D, "lights" + lightCount + ".position", pos.array());
       
 10682       uniformf("lights.direction.3d." + lightCount, programObject3D, "lights" + lightCount + ".direction", dir);
       
 10683       uniformf("lights.concentration.3d." + lightCount, programObject3D, "lights" + lightCount + ".concentration", concentration);
       
 10684       uniformf("lights.angle.3d." + lightCount, programObject3D, "lights" + lightCount + ".angle", angle);
       
 10685       uniformi("lights.type.3d." + lightCount, programObject3D, "lights" + lightCount + ".type", 3);
       
 10686       uniformi("lightCount3d", programObject3D, "lightCount", ++lightCount);
       
 10687     };
       
 10688 
       
 10689     ////////////////////////////////////////////////////////////////////////////
       
 10690     // Camera functions
       
 10691     ////////////////////////////////////////////////////////////////////////////
       
 10692 
       
 10693     /**
       
 10694      * The <b>beginCamera()</b> and <b>endCamera()</b> functions enable advanced customization of the camera space.
       
 10695      * The functions are useful if you want to more control over camera movement, however for most users, the <b>camera()</b>
       
 10696      * function will be sufficient.<br /><br />The camera functions will replace any transformations (such as <b>rotate()</b>
       
 10697      * or <b>translate()</b>) that occur before them in <b>draw()</b>, but they will not automatically replace the camera
       
 10698      * transform itself. For this reason, camera functions should be placed at the beginning of <b>draw()</b> (so that
       
 10699      * transformations happen afterwards), and the <b>camera()</b> function can be used after <b>beginCamera()</b> if
       
 10700      * you want to reset the camera before applying transformations.<br /><br />This function sets the matrix mode to the
       
 10701      * camera matrix so calls such as <b>translate()</b>, <b>rotate()</b>, applyMatrix() and resetMatrix() affect the camera.
       
 10702      * <b>beginCamera()</b> should always be used with a following <b>endCamera()</b> and pairs of <b>beginCamera()</b> and
       
 10703      * <b>endCamera()</b> cannot be nested.
       
 10704      *
       
 10705      * @see camera
       
 10706      * @see endCamera
       
 10707      * @see applyMatrix
       
 10708      * @see resetMatrix
       
 10709      * @see translate
       
 10710      * @see rotate
       
 10711      * @see scale
       
 10712      */
       
 10713     Drawing2D.prototype.beginCamera = function() {
       
 10714       throw ("beginCamera() is not available in 2D mode");
       
 10715     };
       
 10716 
       
 10717     Drawing3D.prototype.beginCamera = function() {
       
 10718       if (manipulatingCamera) {
       
 10719         throw ("You cannot call beginCamera() again before calling endCamera()");
       
 10720       }
       
 10721       manipulatingCamera = true;
       
 10722       modelView = cameraInv;
       
 10723       modelViewInv = cam;
       
 10724     };
       
 10725 
       
 10726     /**
       
 10727      * The <b>beginCamera()</b> and <b>endCamera()</b> functions enable advanced customization of the camera space.
       
 10728      * Please see the reference for <b>beginCamera()</b> for a description of how the functions are used.
       
 10729      *
       
 10730      * @see beginCamera
       
 10731      */
       
 10732     Drawing2D.prototype.endCamera = function() {
       
 10733       throw ("endCamera() is not available in 2D mode");
       
 10734     };
       
 10735 
       
 10736     Drawing3D.prototype.endCamera = function() {
       
 10737       if (!manipulatingCamera) {
       
 10738         throw ("You cannot call endCamera() before calling beginCamera()");
       
 10739       }
       
 10740       modelView.set(cam);
       
 10741       modelViewInv.set(cameraInv);
       
 10742       manipulatingCamera = false;
       
 10743     };
       
 10744 
       
 10745     /**
       
 10746      * Sets the position of the camera through setting the eye position, the center of the scene, and which axis is facing
       
 10747      * upward. Moving the eye position and the direction it is pointing (the center of the scene) allows the images to be
       
 10748      * seen from different angles. The version without any parameters sets the camera to the default position, pointing to
       
 10749      * the center of the display window with the Y axis as up. The default values are camera(width/2.0, height/2.0,
       
 10750      * (height/2.0) / tan(PI*60.0 / 360.0), width/2.0, height/2.0, 0, 0, 1, 0). This function is similar to gluLookAt()
       
 10751      * in OpenGL, but it first clears the current camera settings.
       
 10752      *
       
 10753      * @param {float} eyeX    x-coordinate for the eye
       
 10754      * @param {float} eyeY    y-coordinate for the eye
       
 10755      * @param {float} eyeZ    z-coordinate for the eye
       
 10756      * @param {float} centerX x-coordinate for the center of the scene
       
 10757      * @param {float} centerY y-coordinate for the center of the scene
       
 10758      * @param {float} centerZ z-coordinate for the center of the scene
       
 10759      * @param {float} upX     usually 0.0, 1.0, -1.0
       
 10760      * @param {float} upY     usually 0.0, 1.0, -1.0
       
 10761      * @param {float} upZ     usually 0.0, 1.0, -1.0
       
 10762      *
       
 10763      * @see beginCamera
       
 10764      * @see endCamera
       
 10765      * @see frustum
       
 10766      */
       
 10767     p.camera = function(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ) {
       
 10768       if (eyeX === undef) {
       
 10769         // Workaround if createGraphics is used. 
       
 10770         cameraX = p.width / 2;
       
 10771         cameraY = p.height / 2;
       
 10772         cameraZ = cameraY / Math.tan(cameraFOV / 2);
       
 10773         eyeX = cameraX;
       
 10774         eyeY = cameraY;
       
 10775         eyeZ = cameraZ;
       
 10776         centerX = cameraX;
       
 10777         centerY = cameraY;
       
 10778         centerZ = 0;
       
 10779         upX = 0;
       
 10780         upY = 1;
       
 10781         upZ = 0;
       
 10782       }
       
 10783 
       
 10784       var z = new PVector(eyeX - centerX, eyeY - centerY, eyeZ - centerZ);
       
 10785       var y = new PVector(upX, upY, upZ);
       
 10786       z.normalize();
       
 10787       var x = PVector.cross(y, z);
       
 10788       y = PVector.cross(z, x);
       
 10789       x.normalize();
       
 10790       y.normalize();
       
 10791 
       
 10792       var xX = x.x,
       
 10793           xY = x.y,
       
 10794           xZ = x.z;
       
 10795 
       
 10796       var yX = y.x,
       
 10797           yY = y.y,
       
 10798           yZ = y.z;
       
 10799 
       
 10800       var zX = z.x,
       
 10801           zY = z.y,
       
 10802           zZ = z.z;
       
 10803 
       
 10804       cam.set(xX, xY, xZ, 0, yX, yY, yZ, 0, zX, zY, zZ, 0, 0, 0, 0, 1);
       
 10805 
       
 10806       cam.translate(-eyeX, -eyeY, -eyeZ);
       
 10807 
       
 10808       cameraInv.reset();
       
 10809       cameraInv.invApply(xX, xY, xZ, 0, yX, yY, yZ, 0, zX, zY, zZ, 0, 0, 0, 0, 1);
       
 10810 
       
 10811       cameraInv.translate(eyeX, eyeY, eyeZ);
       
 10812 
       
 10813       modelView.set(cam);
       
 10814       modelViewInv.set(cameraInv);
       
 10815     };
       
 10816 
       
 10817     /**
       
 10818      * Sets a perspective projection applying foreshortening, making distant objects appear smaller than closer ones. The
       
 10819      * parameters define a viewing volume with the shape of truncated pyramid. Objects near to the front of the volume appear
       
 10820      * their actual size, while farther objects appear smaller. This projection simulates the perspective of the world more
       
 10821      * accurately than orthographic projection. The version of perspective without parameters sets the default perspective and
       
 10822      * the version with four parameters allows the programmer to set the area precisely. The default values are:
       
 10823      * perspective(PI/3.0, width/height, cameraZ/10.0, cameraZ*10.0) where cameraZ is ((height/2.0) / tan(PI*60.0/360.0));
       
 10824      *
       
 10825      * @param {float} fov     field-of-view angle (in radians) for vertical direction
       
 10826      * @param {float} aspect  ratio of width to height
       
 10827      * @param {float} zNear   z-position of nearest clipping plane
       
 10828      * @param {float} zFar    z-positions of farthest clipping plane
       
 10829      */
       
 10830     p.perspective = function(fov, aspect, near, far) {
       
 10831       if (arguments.length === 0) {
       
 10832         //in case canvas is resized
       
 10833         cameraY = curElement.height / 2;
       
 10834         cameraZ = cameraY / Math.tan(cameraFOV / 2);
       
 10835         cameraNear = cameraZ / 10;
       
 10836         cameraFar = cameraZ * 10;
       
 10837         cameraAspect = p.width / p.height;
       
 10838         fov = cameraFOV;
       
 10839         aspect = cameraAspect;
       
 10840         near = cameraNear;
       
 10841         far = cameraFar;
       
 10842       }
       
 10843 
       
 10844       var yMax, yMin, xMax, xMin;
       
 10845       yMax = near * Math.tan(fov / 2);
       
 10846       yMin = -yMax;
       
 10847       xMax = yMax * aspect;
       
 10848       xMin = yMin * aspect;
       
 10849       p.frustum(xMin, xMax, yMin, yMax, near, far);
       
 10850     };
       
 10851 
       
 10852     /**
       
 10853      * Sets a perspective matrix defined through the parameters. Works like glFrustum, except it wipes out the current
       
 10854      * perspective matrix rather than muliplying itself with it.
       
 10855      *
       
 10856      * @param {float} left   left coordinate of the clipping plane
       
 10857      * @param {float} right  right coordinate of the clipping plane
       
 10858      * @param {float} bottom bottom coordinate of the clipping plane
       
 10859      * @param {float} top    top coordinate of the clipping plane
       
 10860      * @param {float} near   near coordinate of the clipping plane
       
 10861      * @param {float} far    far coordinate of the clipping plane
       
 10862      *
       
 10863      * @see beginCamera
       
 10864      * @see camera
       
 10865      * @see endCamera
       
 10866      * @see perspective
       
 10867      */
       
 10868     Drawing2D.prototype.frustum = function() {
       
 10869       throw("Processing.js: frustum() is not supported in 2D mode");
       
 10870     };
       
 10871 
       
 10872     Drawing3D.prototype.frustum = function(left, right, bottom, top, near, far) {
       
 10873       frustumMode = true;
       
 10874       projection = new PMatrix3D();
       
 10875       projection.set((2 * near) / (right - left), 0, (right + left) / (right - left),
       
 10876                      0, 0, (2 * near) / (top - bottom), (top + bottom) / (top - bottom),
       
 10877                      0, 0, 0, -(far + near) / (far - near), -(2 * far * near) / (far - near),
       
 10878                      0, 0, -1, 0);
       
 10879       var proj = new PMatrix3D();
       
 10880       proj.set(projection);
       
 10881       proj.transpose();
       
 10882       curContext.useProgram(programObject2D);
       
 10883       uniformMatrix("projection2d", programObject2D, "projection", false, proj.array());
       
 10884       curContext.useProgram(programObject3D);
       
 10885       uniformMatrix("projection3d", programObject3D, "projection", false, proj.array());
       
 10886       curContext.useProgram(programObjectUnlitShape);
       
 10887       uniformMatrix("uProjectionUS", programObjectUnlitShape, "uProjection", false, proj.array());
       
 10888     };
       
 10889 
       
 10890     /**
       
 10891      * Sets an orthographic projection and defines a parallel clipping volume. All objects with the same dimension appear
       
 10892      * the same size, regardless of whether they are near or far from the camera. The parameters to this function specify
       
 10893      * the clipping volume where left and right are the minimum and maximum x values, top and bottom are the minimum and
       
 10894      * maximum y values, and near and far are the minimum and maximum z values. If no parameters are given, the default
       
 10895      * is used: ortho(0, width, 0, height, -10, 10).
       
 10896      *
       
 10897      * @param {float} left   left plane of the clipping volume
       
 10898      * @param {float} right  right plane of the clipping volume
       
 10899      * @param {float} bottom bottom plane of the clipping volume
       
 10900      * @param {float} top    top plane of the clipping volume
       
 10901      * @param {float} near   maximum distance from the origin to the viewer
       
 10902      * @param {float} far    maximum distance from the origin away from the viewer
       
 10903      */
       
 10904     p.ortho = function(left, right, bottom, top, near, far) {
       
 10905       if (arguments.length === 0) {
       
 10906         left = 0;
       
 10907         right = p.width;
       
 10908         bottom = 0;
       
 10909         top = p.height;
       
 10910         near = -10;
       
 10911         far = 10;
       
 10912       }
       
 10913 
       
 10914       var x = 2 / (right - left);
       
 10915       var y = 2 / (top - bottom);
       
 10916       var z = -2 / (far - near);
       
 10917 
       
 10918       var tx = -(right + left) / (right - left);
       
 10919       var ty = -(top + bottom) / (top - bottom);
       
 10920       var tz = -(far + near) / (far - near);
       
 10921 
       
 10922       projection = new PMatrix3D();
       
 10923       projection.set(x, 0, 0, tx, 0, y, 0, ty, 0, 0, z, tz, 0, 0, 0, 1);
       
 10924 
       
 10925       var proj = new PMatrix3D();
       
 10926       proj.set(projection);
       
 10927       proj.transpose();
       
 10928       curContext.useProgram(programObject2D);
       
 10929       uniformMatrix("projection2d", programObject2D, "projection", false, proj.array());
       
 10930       curContext.useProgram(programObject3D);
       
 10931       uniformMatrix("projection3d", programObject3D, "projection", false, proj.array());
       
 10932       curContext.useProgram(programObjectUnlitShape);
       
 10933       uniformMatrix("uProjectionUS", programObjectUnlitShape, "uProjection", false, proj.array());
       
 10934       frustumMode = false;
       
 10935     };
       
 10936     /**
       
 10937      * The printProjection() prints the current projection matrix to the text window.
       
 10938      */
       
 10939     p.printProjection = function() {
       
 10940       projection.print();
       
 10941     };
       
 10942     /**
       
 10943      * The printCamera() function prints the current camera matrix.
       
 10944      */
       
 10945     p.printCamera = function() {
       
 10946       cam.print();
       
 10947     };
       
 10948 
       
 10949     ////////////////////////////////////////////////////////////////////////////
       
 10950     // Shapes
       
 10951     ////////////////////////////////////////////////////////////////////////////
       
 10952     /**
       
 10953      * The box() function renders a box. A box is an extruded rectangle. A box with equal dimension on all sides is a cube.
       
 10954      * Calling this function with only one parameter will create a cube.
       
 10955      *
       
 10956      * @param {int|float} w  dimension of the box in the x-dimension
       
 10957      * @param {int|float} h  dimension of the box in the y-dimension
       
 10958      * @param {int|float} d  dimension of the box in the z-dimension
       
 10959      */
       
 10960     Drawing2D.prototype.box = DrawingShared.prototype.a3DOnlyFunction;
       
 10961 
       
 10962     Drawing3D.prototype.box = function(w, h, d) {
       
 10963       // user can uniformly scale the box by
       
 10964       // passing in only one argument.
       
 10965       if (!h || !d) {
       
 10966         h = d = w;
       
 10967       }
       
 10968 
       
 10969       // Modeling transformation
       
 10970       var model = new PMatrix3D();
       
 10971       model.scale(w, h, d);
       
 10972 
       
 10973       // viewing transformation needs to have Y flipped
       
 10974       // becuase that's what Processing does.
       
 10975       var view = new PMatrix3D();
       
 10976       view.scale(1, -1, 1);
       
 10977       view.apply(modelView.array());
       
 10978       view.transpose();
       
 10979 
       
 10980       if (doFill) {
       
 10981         curContext.useProgram(programObject3D);
       
 10982         uniformMatrix("model3d", programObject3D, "model", false, model.array());
       
 10983         uniformMatrix("view3d", programObject3D, "view", false, view.array());
       
 10984         // fix stitching problems. (lines get occluded by triangles
       
 10985         // since they share the same depth values). This is not entirely
       
 10986         // working, but it's a start for drawing the outline. So
       
 10987         // developers can start playing around with styles.
       
 10988         curContext.enable(curContext.POLYGON_OFFSET_FILL);
       
 10989         curContext.polygonOffset(1, 1);
       
 10990         uniformf("color3d", programObject3D, "color", fillStyle);
       
 10991 
       
 10992         // Calculating the normal matrix can be expensive, so only
       
 10993         // do it if it's necessary
       
 10994         if(lightCount > 0){
       
 10995           // Create the normal transformation matrix
       
 10996           var v = new PMatrix3D();
       
 10997           v.set(view);
       
 10998 
       
 10999           var m = new PMatrix3D();
       
 11000           m.set(model);
       
 11001 
       
 11002           v.mult(m);
       
 11003 
       
 11004           var normalMatrix = new PMatrix3D();
       
 11005           normalMatrix.set(v);
       
 11006           normalMatrix.invert();
       
 11007           normalMatrix.transpose();
       
 11008 
       
 11009           uniformMatrix("normalTransform3d", programObject3D, "normalTransform", false, normalMatrix.array());
       
 11010           vertexAttribPointer("normal3d", programObject3D, "Normal", 3, boxNormBuffer);
       
 11011         }
       
 11012         else{
       
 11013           disableVertexAttribPointer("normal3d", programObject3D, "Normal");
       
 11014         }
       
 11015 
       
 11016         vertexAttribPointer("vertex3d", programObject3D, "Vertex", 3, boxBuffer);
       
 11017 
       
 11018         // Turn off per vertex colors
       
 11019         disableVertexAttribPointer("aColor3d", programObject3D, "aColor");
       
 11020         disableVertexAttribPointer("aTexture3d", programObject3D, "aTexture");
       
 11021 
       
 11022         curContext.drawArrays(curContext.TRIANGLES, 0, boxVerts.length / 3);
       
 11023         curContext.disable(curContext.POLYGON_OFFSET_FILL);
       
 11024       }
       
 11025 
       
 11026       if (lineWidth > 0 && doStroke) {
       
 11027         curContext.useProgram(programObject2D);
       
 11028         uniformMatrix("model2d", programObject2D, "model", false, model.array());
       
 11029         uniformMatrix("view2d", programObject2D, "view", false, view.array());
       
 11030         uniformf("color2d", programObject2D, "color", strokeStyle);
       
 11031         uniformi("picktype2d", programObject2D, "picktype", 0);
       
 11032         vertexAttribPointer("vertex2d", programObject2D, "Vertex", 3, boxOutlineBuffer);
       
 11033         disableVertexAttribPointer("aTextureCoord2d", programObject2D, "aTextureCoord");
       
 11034         curContext.drawArrays(curContext.LINES, 0, boxOutlineVerts.length / 3);
       
 11035       }
       
 11036     };
       
 11037 
       
 11038     /**
       
 11039      * The initSphere() function is a helper function used by <b>sphereDetail()</b>
       
 11040      * This function creates and stores sphere vertices every time the user changes sphere detail.
       
 11041      *
       
 11042      * @see #sphereDetail
       
 11043      */
       
 11044     var initSphere = function() {
       
 11045       var i;
       
 11046       sphereVerts = [];
       
 11047 
       
 11048       for (i = 0; i < sphereDetailU; i++) {
       
 11049         sphereVerts.push(0);
       
 11050         sphereVerts.push(-1);
       
 11051         sphereVerts.push(0);
       
 11052         sphereVerts.push(sphereX[i]);
       
 11053         sphereVerts.push(sphereY[i]);
       
 11054         sphereVerts.push(sphereZ[i]);
       
 11055       }
       
 11056       sphereVerts.push(0);
       
 11057       sphereVerts.push(-1);
       
 11058       sphereVerts.push(0);
       
 11059       sphereVerts.push(sphereX[0]);
       
 11060       sphereVerts.push(sphereY[0]);
       
 11061       sphereVerts.push(sphereZ[0]);
       
 11062 
       
 11063       var v1, v11, v2;
       
 11064 
       
 11065       // middle rings
       
 11066       var voff = 0;
       
 11067       for (i = 2; i < sphereDetailV; i++) {
       
 11068         v1 = v11 = voff;
       
 11069         voff += sphereDetailU;
       
 11070         v2 = voff;
       
 11071         for (var j = 0; j < sphereDetailU; j++) {
       
 11072           sphereVerts.push(sphereX[v1]);
       
 11073           sphereVerts.push(sphereY[v1]);
       
 11074           sphereVerts.push(sphereZ[v1++]);
       
 11075           sphereVerts.push(sphereX[v2]);
       
 11076           sphereVerts.push(sphereY[v2]);
       
 11077           sphereVerts.push(sphereZ[v2++]);
       
 11078         }
       
 11079 
       
 11080         // close each ring
       
 11081         v1 = v11;
       
 11082         v2 = voff;
       
 11083 
       
 11084         sphereVerts.push(sphereX[v1]);
       
 11085         sphereVerts.push(sphereY[v1]);
       
 11086         sphereVerts.push(sphereZ[v1]);
       
 11087         sphereVerts.push(sphereX[v2]);
       
 11088         sphereVerts.push(sphereY[v2]);
       
 11089         sphereVerts.push(sphereZ[v2]);
       
 11090       }
       
 11091 
       
 11092       // add the northern cap
       
 11093       for (i = 0; i < sphereDetailU; i++) {
       
 11094         v2 = voff + i;
       
 11095 
       
 11096         sphereVerts.push(sphereX[v2]);
       
 11097         sphereVerts.push(sphereY[v2]);
       
 11098         sphereVerts.push(sphereZ[v2]);
       
 11099         sphereVerts.push(0);
       
 11100         sphereVerts.push(1);
       
 11101         sphereVerts.push(0);
       
 11102       }
       
 11103 
       
 11104       sphereVerts.push(sphereX[voff]);
       
 11105       sphereVerts.push(sphereY[voff]);
       
 11106       sphereVerts.push(sphereZ[voff]);
       
 11107       sphereVerts.push(0);
       
 11108       sphereVerts.push(1);
       
 11109       sphereVerts.push(0);
       
 11110 
       
 11111       //set the buffer data
       
 11112       curContext.bindBuffer(curContext.ARRAY_BUFFER, sphereBuffer);
       
 11113       curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array(sphereVerts), curContext.STATIC_DRAW);
       
 11114     };
       
 11115 
       
 11116     /**
       
 11117      * The sphereDetail() function controls the detail used to render a sphere by adjusting the number of
       
 11118      * vertices of the sphere mesh. The default resolution is 30, which creates
       
 11119      * a fairly detailed sphere definition with vertices every 360/30 = 12
       
 11120      * degrees. If you're going to render a great number of spheres per frame,
       
 11121      * it is advised to reduce the level of detail using this function.
       
 11122      * The setting stays active until <b>sphereDetail()</b> is called again with
       
 11123      * a new parameter and so should <i>not</i> be called prior to every
       
 11124      * <b>sphere()</b> statement, unless you wish to render spheres with
       
 11125      * different settings, e.g. using less detail for smaller spheres or ones
       
 11126      * further away from the camera. To control the detail of the horizontal
       
 11127      * and vertical resolution independently, use the version of the functions
       
 11128      * with two parameters. Calling this function with one parameter sets the number of segments
       
 11129      *(minimum of 3) used per full circle revolution. This is equivalent to calling the function with
       
 11130      * two identical values.
       
 11131      *
       
 11132      * @param {int} ures    number of segments used horizontally (longitudinally) per full circle revolution
       
 11133      * @param {int} vres    number of segments used vertically (latitudinally) from top to bottom
       
 11134      *
       
 11135      * @see #sphere()
       
 11136      */
       
 11137     p.sphereDetail = function(ures, vres) {
       
 11138       var i;
       
 11139 
       
 11140       if (arguments.length === 1) {
       
 11141         ures = vres = arguments[0];
       
 11142       }
       
 11143 
       
 11144       if (ures < 3) {
       
 11145         ures = 3;
       
 11146       } // force a minimum res
       
 11147       if (vres < 2) {
       
 11148         vres = 2;
       
 11149       } // force a minimum res
       
 11150       // if it hasn't changed do nothing
       
 11151       if ((ures === sphereDetailU) && (vres === sphereDetailV)) {
       
 11152         return;
       
 11153       }
       
 11154 
       
 11155       var delta = PConstants.SINCOS_LENGTH / ures;
       
 11156       var cx = new Float32Array(ures);
       
 11157       var cz = new Float32Array(ures);
       
 11158       // calc unit circle in XZ plane
       
 11159       for (i = 0; i < ures; i++) {
       
 11160         cx[i] = cosLUT[((i * delta) % PConstants.SINCOS_LENGTH) | 0];
       
 11161         cz[i] = sinLUT[((i * delta) % PConstants.SINCOS_LENGTH) | 0];
       
 11162       }
       
 11163 
       
 11164       // computing vertexlist
       
 11165       // vertexlist starts at south pole
       
 11166       var vertCount = ures * (vres - 1) + 2;
       
 11167       var currVert = 0;
       
 11168 
       
 11169       // re-init arrays to store vertices
       
 11170       sphereX = new Float32Array(vertCount);
       
 11171       sphereY = new Float32Array(vertCount);
       
 11172       sphereZ = new Float32Array(vertCount);
       
 11173 
       
 11174       var angle_step = (PConstants.SINCOS_LENGTH * 0.5) / vres;
       
 11175       var angle = angle_step;
       
 11176 
       
 11177       // step along Y axis
       
 11178       for (i = 1; i < vres; i++) {
       
 11179         var curradius = sinLUT[(angle % PConstants.SINCOS_LENGTH) | 0];
       
 11180         var currY = -cosLUT[(angle % PConstants.SINCOS_LENGTH) | 0];
       
 11181         for (var j = 0; j < ures; j++) {
       
 11182           sphereX[currVert] = cx[j] * curradius;
       
 11183           sphereY[currVert] = currY;
       
 11184           sphereZ[currVert++] = cz[j] * curradius;
       
 11185         }
       
 11186         angle += angle_step;
       
 11187       }
       
 11188       sphereDetailU = ures;
       
 11189       sphereDetailV = vres;
       
 11190 
       
 11191       // make the sphere verts and norms
       
 11192       initSphere();
       
 11193     };
       
 11194 
       
 11195     /**
       
 11196      * The sphere() function draws a sphere with radius r centered at coordinate 0, 0, 0.
       
 11197      * A sphere is a hollow ball made from tessellated triangles.
       
 11198      *
       
 11199      * @param {int|float} r the radius of the sphere
       
 11200      */
       
 11201     Drawing2D.prototype.sphere = DrawingShared.prototype.a3DOnlyFunction;
       
 11202 
       
 11203     Drawing3D.prototype.sphere = function() {
       
 11204       var sRad = arguments[0];
       
 11205 
       
 11206       if ((sphereDetailU < 3) || (sphereDetailV < 2)) {
       
 11207         p.sphereDetail(30);
       
 11208       }
       
 11209 
       
 11210       // Modeling transformation
       
 11211       var model = new PMatrix3D();
       
 11212       model.scale(sRad, sRad, sRad);
       
 11213 
       
 11214       // viewing transformation needs to have Y flipped
       
 11215       // becuase that's what Processing does.
       
 11216       var view = new PMatrix3D();
       
 11217       view.scale(1, -1, 1);
       
 11218       view.apply(modelView.array());
       
 11219       view.transpose();
       
 11220 
       
 11221       if (doFill) {
       
 11222         // Calculating the normal matrix can be expensive, so only
       
 11223         // do it if it's necessary
       
 11224         if(lightCount > 0){
       
 11225           // Create a normal transformation matrix
       
 11226           var v = new PMatrix3D();
       
 11227           v.set(view);
       
 11228 
       
 11229           var m = new PMatrix3D();
       
 11230           m.set(model);
       
 11231 
       
 11232           v.mult(m);
       
 11233 
       
 11234           var normalMatrix = new PMatrix3D();
       
 11235           normalMatrix.set(v);
       
 11236           normalMatrix.invert();
       
 11237           normalMatrix.transpose();
       
 11238 
       
 11239           uniformMatrix("normalTransform3d", programObject3D, "normalTransform", false, normalMatrix.array());
       
 11240           vertexAttribPointer("normal3d", programObject3D, "Normal", 3, sphereBuffer);
       
 11241         }
       
 11242         else{
       
 11243           disableVertexAttribPointer("normal3d", programObject3D, "Normal");
       
 11244         }
       
 11245 
       
 11246         curContext.useProgram(programObject3D);
       
 11247         disableVertexAttribPointer("aTexture3d", programObject3D, "aTexture");
       
 11248 
       
 11249         uniformMatrix("model3d", programObject3D, "model", false, model.array());
       
 11250         uniformMatrix("view3d", programObject3D, "view", false, view.array());
       
 11251         vertexAttribPointer("vertex3d", programObject3D, "Vertex", 3, sphereBuffer);
       
 11252 
       
 11253         // Turn off per vertex colors
       
 11254         disableVertexAttribPointer("aColor3d", programObject3D, "aColor");
       
 11255 
       
 11256         // fix stitching problems. (lines get occluded by triangles
       
 11257         // since they share the same depth values). This is not entirely
       
 11258         // working, but it's a start for drawing the outline. So
       
 11259         // developers can start playing around with styles.
       
 11260         curContext.enable(curContext.POLYGON_OFFSET_FILL);
       
 11261         curContext.polygonOffset(1, 1);
       
 11262         uniformf("color3d", programObject3D, "color", fillStyle);
       
 11263         curContext.drawArrays(curContext.TRIANGLE_STRIP, 0, sphereVerts.length / 3);
       
 11264         curContext.disable(curContext.POLYGON_OFFSET_FILL);
       
 11265       }
       
 11266 
       
 11267       if (lineWidth > 0 && doStroke) {
       
 11268         curContext.useProgram(programObject2D);
       
 11269         uniformMatrix("model2d", programObject2D, "model", false, model.array());
       
 11270         uniformMatrix("view2d", programObject2D, "view", false, view.array());
       
 11271         vertexAttribPointer("vertex2d", programObject2D, "Vertex", 3, sphereBuffer);
       
 11272         disableVertexAttribPointer("aTextureCoord2d", programObject2D, "aTextureCoord");
       
 11273         uniformf("color2d", programObject2D, "color", strokeStyle);
       
 11274         uniformi("picktype2d", programObject2D, "picktype", 0);
       
 11275         curContext.drawArrays(curContext.LINE_STRIP, 0, sphereVerts.length / 3);
       
 11276       }
       
 11277     };
       
 11278 
       
 11279     ////////////////////////////////////////////////////////////////////////////
       
 11280     // Coordinates
       
 11281     ////////////////////////////////////////////////////////////////////////////
       
 11282 
       
 11283     /**
       
 11284      * Returns the three-dimensional X, Y, Z position in model space. This returns
       
 11285      * the X value for a given coordinate based on the current set of transformations
       
 11286      * (scale, rotate, translate, etc.) The X value can be used to place an object
       
 11287      * in space relative to the location of the original point once the transformations
       
 11288      * are no longer in use.<br />
       
 11289      * <br />
       
 11290      *
       
 11291      * @param {int | float} x 3D x coordinate to be mapped
       
 11292      * @param {int | float} y 3D y coordinate to be mapped
       
 11293      * @param {int | float} z 3D z coordinate to be mapped
       
 11294      *
       
 11295      * @returns {float}
       
 11296      *
       
 11297      * @see modelY
       
 11298      * @see modelZ
       
 11299     */
       
 11300     p.modelX = function(x, y, z) {
       
 11301       var mv = modelView.array();
       
 11302       var ci = cameraInv.array();
       
 11303 
       
 11304       var ax = mv[0] * x + mv[1] * y + mv[2] * z + mv[3];
       
 11305       var ay = mv[4] * x + mv[5] * y + mv[6] * z + mv[7];
       
 11306       var az = mv[8] * x + mv[9] * y + mv[10] * z + mv[11];
       
 11307       var aw = mv[12] * x + mv[13] * y + mv[14] * z + mv[15];
       
 11308 
       
 11309       var ox = ci[0] * ax + ci[1] * ay + ci[2] * az + ci[3] * aw;
       
 11310       var ow = ci[12] * ax + ci[13] * ay + ci[14] * az + ci[15] * aw;
       
 11311 
       
 11312       return (ow !== 0) ? ox / ow : ox;
       
 11313     };
       
 11314 
       
 11315     /**
       
 11316      * Returns the three-dimensional X, Y, Z position in model space. This returns
       
 11317      * the Y value for a given coordinate based on the current set of transformations
       
 11318      * (scale, rotate, translate, etc.) The Y value can be used to place an object in
       
 11319      * space relative to the location of the original point once the transformations
       
 11320      * are no longer in use.<br />
       
 11321      * <br />
       
 11322      *
       
 11323      * @param {int | float} x 3D x coordinate to be mapped
       
 11324      * @param {int | float} y 3D y coordinate to be mapped
       
 11325      * @param {int | float} z 3D z coordinate to be mapped
       
 11326      *
       
 11327      * @returns {float}
       
 11328      *
       
 11329      * @see modelX
       
 11330      * @see modelZ
       
 11331     */
       
 11332     p.modelY = function(x, y, z) {
       
 11333       var mv = modelView.array();
       
 11334       var ci = cameraInv.array();
       
 11335 
       
 11336       var ax = mv[0] * x + mv[1] * y + mv[2] * z + mv[3];
       
 11337       var ay = mv[4] * x + mv[5] * y + mv[6] * z + mv[7];
       
 11338       var az = mv[8] * x + mv[9] * y + mv[10] * z + mv[11];
       
 11339       var aw = mv[12] * x + mv[13] * y + mv[14] * z + mv[15];
       
 11340 
       
 11341       var oy = ci[4] * ax + ci[5] * ay + ci[6] * az + ci[7] * aw;
       
 11342       var ow = ci[12] * ax + ci[13] * ay + ci[14] * az + ci[15] * aw;
       
 11343 
       
 11344       return (ow !== 0) ? oy / ow : oy;
       
 11345     };
       
 11346 
       
 11347     /**
       
 11348      * Returns the three-dimensional X, Y, Z position in model space. This returns
       
 11349      * the Z value for a given coordinate based on the current set of transformations
       
 11350      * (scale, rotate, translate, etc.) The Z value can be used to place an object in
       
 11351      * space relative to the location of the original point once the transformations
       
 11352      * are no longer in use.
       
 11353      *
       
 11354      * @param {int | float} x 3D x coordinate to be mapped
       
 11355      * @param {int | float} y 3D y coordinate to be mapped
       
 11356      * @param {int | float} z 3D z coordinate to be mapped
       
 11357      *
       
 11358      * @returns {float}
       
 11359      *
       
 11360      * @see modelX
       
 11361      * @see modelY
       
 11362     */
       
 11363     p.modelZ = function(x, y, z) {
       
 11364       var mv = modelView.array();
       
 11365       var ci = cameraInv.array();
       
 11366 
       
 11367       var ax = mv[0] * x + mv[1] * y + mv[2] * z + mv[3];
       
 11368       var ay = mv[4] * x + mv[5] * y + mv[6] * z + mv[7];
       
 11369       var az = mv[8] * x + mv[9] * y + mv[10] * z + mv[11];
       
 11370       var aw = mv[12] * x + mv[13] * y + mv[14] * z + mv[15];
       
 11371 
       
 11372       var oz = ci[8] * ax + ci[9] * ay + ci[10] * az + ci[11] * aw;
       
 11373       var ow = ci[12] * ax + ci[13] * ay + ci[14] * az + ci[15] * aw;
       
 11374 
       
 11375       return (ow !== 0) ? oz / ow : oz;
       
 11376     };
       
 11377 
       
 11378     ////////////////////////////////////////////////////////////////////////////
       
 11379     // Material Properties
       
 11380     ////////////////////////////////////////////////////////////////////////////
       
 11381 
       
 11382     /**
       
 11383      * Sets the ambient reflectance for shapes drawn to the screen. This is
       
 11384      * combined with the ambient light component of environment. The color
       
 11385      * components set through the parameters define the reflectance. For example in
       
 11386      * the default color mode, setting v1=255, v2=126, v3=0, would cause all the
       
 11387      * red light to reflect and half of the green light to reflect. Used in combination
       
 11388      * with <b>emissive()</b>, <b>specular()</b>, and <b>shininess()</b> in setting
       
 11389      * the materal properties of shapes.
       
 11390      *
       
 11391      * @param {int | float} gray
       
 11392      *
       
 11393      * @returns none
       
 11394      *
       
 11395      * @see emissive
       
 11396      * @see specular
       
 11397      * @see shininess
       
 11398     */
       
 11399     Drawing2D.prototype.ambient = DrawingShared.prototype.a3DOnlyFunction;
       
 11400 
       
 11401     Drawing3D.prototype.ambient = function(v1, v2, v3) {
       
 11402       curContext.useProgram(programObject3D);
       
 11403       uniformi("usingMat3d", programObject3D, "usingMat", true);
       
 11404       var col = p.color(v1, v2, v3);
       
 11405       uniformf("mat_ambient3d", programObject3D, "mat_ambient", p.color.toGLArray(col).slice(0, 3));
       
 11406     };
       
 11407 
       
 11408     /**
       
 11409      * Sets the emissive color of the material used for drawing shapes
       
 11410      * drawn to the screen. Used in combination with ambient(), specular(),
       
 11411      * and shininess() in setting the material properties of shapes.
       
 11412      *
       
 11413      * Can be called in the following ways:
       
 11414      *
       
 11415      * emissive(gray)
       
 11416      * @param {int | float} gray number specifying value between white and black
       
 11417      *
       
 11418      * emissive(color)
       
 11419      * @param {color} color any value of the color datatype
       
 11420      *
       
 11421      * emissive(v1, v2, v3)
       
 11422      * @param {int | float} v1 red or hue value
       
 11423      * @param {int | float} v2 green or saturation value
       
 11424      * @param {int | float} v3 blue or brightness value
       
 11425      *
       
 11426      * @returns none
       
 11427      *
       
 11428      * @see ambient
       
 11429      * @see specular
       
 11430      * @see shininess
       
 11431     */
       
 11432     Drawing2D.prototype.emissive = DrawingShared.prototype.a3DOnlyFunction;
       
 11433 
       
 11434     Drawing3D.prototype.emissive = function(v1, v2, v3) {
       
 11435       curContext.useProgram(programObject3D);
       
 11436       uniformi("usingMat3d", programObject3D, "usingMat", true);
       
 11437       var col = p.color(v1, v2, v3);
       
 11438       uniformf("mat_emissive3d", programObject3D, "mat_emissive", p.color.toGLArray(col).slice(0, 3));
       
 11439     };
       
 11440 
       
 11441     /**
       
 11442      * Sets the amount of gloss in the surface of shapes. Used in combination with
       
 11443      * <b>ambient()</b>, <b>specular()</b>, and <b>emissive()</b> in setting the
       
 11444      * material properties of shapes.
       
 11445      *
       
 11446      * @param {float} shine degree of shininess
       
 11447      *
       
 11448      * @returns none
       
 11449     */
       
 11450     Drawing2D.prototype.shininess = DrawingShared.prototype.a3DOnlyFunction;
       
 11451 
       
 11452     Drawing3D.prototype.shininess = function(shine) {
       
 11453       curContext.useProgram(programObject3D);
       
 11454       uniformi("usingMat3d", programObject3D, "usingMat", true);
       
 11455       uniformf("shininess3d", programObject3D, "shininess", shine);
       
 11456     };
       
 11457 
       
 11458     /**
       
 11459      * Sets the specular color of the materials used for shapes drawn to the screen,
       
 11460      * which sets the color of hightlights. Specular refers to light which bounces
       
 11461      * off a surface in a perferred direction (rather than bouncing in all directions
       
 11462      * like a diffuse light). Used in combination with emissive(), ambient(), and
       
 11463      * shininess() in setting the material properties of shapes.
       
 11464      *
       
 11465      * Can be called in the following ways:
       
 11466      *
       
 11467      * specular(gray)
       
 11468      * @param {int | float} gray number specifying value between white and black
       
 11469      *
       
 11470      * specular(gray, alpha)
       
 11471      * @param {int | float} gray number specifying value between white and black
       
 11472      * @param {int | float} alpha opacity
       
 11473      *
       
 11474      * specular(color)
       
 11475      * @param {color} color any value of the color datatype
       
 11476      *
       
 11477      * specular(v1, v2, v3)
       
 11478      * @param {int | float} v1 red or hue value
       
 11479      * @param {int | float} v2 green or saturation value
       
 11480      * @param {int | float} v3 blue or brightness value
       
 11481      *
       
 11482      * specular(v1, v2, v3, alpha)
       
 11483      * @param {int | float} v1 red or hue value
       
 11484      * @param {int | float} v2 green or saturation value
       
 11485      * @param {int | float} v3 blue or brightness value
       
 11486      * @param {int | float} alpha opacity
       
 11487      *
       
 11488      * @returns none
       
 11489      *
       
 11490      * @see ambient
       
 11491      * @see emissive
       
 11492      * @see shininess
       
 11493     */
       
 11494     Drawing2D.prototype.specular = DrawingShared.prototype.a3DOnlyFunction;
       
 11495 
       
 11496     Drawing3D.prototype.specular = function(v1, v2, v3) {
       
 11497       curContext.useProgram(programObject3D);
       
 11498       uniformi("usingMat3d", programObject3D, "usingMat", true);
       
 11499       var col = p.color(v1, v2, v3);
       
 11500       uniformf("mat_specular3d", programObject3D, "mat_specular", p.color.toGLArray(col).slice(0, 3));
       
 11501     };
       
 11502 
       
 11503     ////////////////////////////////////////////////////////////////////////////
       
 11504     // Coordinates
       
 11505     ////////////////////////////////////////////////////////////////////////////
       
 11506 
       
 11507     /**
       
 11508      * Takes a three-dimensional X, Y, Z position and returns the X value for
       
 11509      * where it will appear on a (two-dimensional) screen.
       
 11510      *
       
 11511      * @param {int | float} x 3D x coordinate to be mapped
       
 11512      * @param {int | float} y 3D y coordinate to be mapped
       
 11513      * @param {int | float} z 3D z optional coordinate to be mapped
       
 11514      *
       
 11515      * @returns {float}
       
 11516      *
       
 11517      * @see screenY
       
 11518      * @see screenZ
       
 11519     */
       
 11520     p.screenX = function( x, y, z ) {
       
 11521       var mv = modelView.array();
       
 11522       if( mv.length === 16 )
       
 11523       {
       
 11524         var ax = mv[ 0]*x + mv[ 1]*y + mv[ 2]*z + mv[ 3];
       
 11525         var ay = mv[ 4]*x + mv[ 5]*y + mv[ 6]*z + mv[ 7];
       
 11526         var az = mv[ 8]*x + mv[ 9]*y + mv[10]*z + mv[11];
       
 11527         var aw = mv[12]*x + mv[13]*y + mv[14]*z + mv[15];
       
 11528 
       
 11529         var pj = projection.array();
       
 11530 
       
 11531         var ox = pj[ 0]*ax + pj[ 1]*ay + pj[ 2]*az + pj[ 3]*aw;
       
 11532         var ow = pj[12]*ax + pj[13]*ay + pj[14]*az + pj[15]*aw;
       
 11533 
       
 11534         if ( ow !== 0 ){
       
 11535           ox /= ow;
       
 11536         }
       
 11537         return p.width * ( 1 + ox ) / 2.0;
       
 11538       }
       
 11539       // We assume that we're in 2D
       
 11540       return modelView.multX(x, y);
       
 11541     };
       
 11542 
       
 11543     /**
       
 11544      * Takes a three-dimensional X, Y, Z position and returns the Y value for
       
 11545      * where it will appear on a (two-dimensional) screen.
       
 11546      *
       
 11547      * @param {int | float} x 3D x coordinate to be mapped
       
 11548      * @param {int | float} y 3D y coordinate to be mapped
       
 11549      * @param {int | float} z 3D z optional coordinate to be mapped
       
 11550      *
       
 11551      * @returns {float}
       
 11552      *
       
 11553      * @see screenX
       
 11554      * @see screenZ
       
 11555     */
       
 11556     p.screenY = function screenY( x, y, z ) {
       
 11557       var mv = modelView.array();
       
 11558       if( mv.length === 16 ) {
       
 11559         var ax = mv[ 0]*x + mv[ 1]*y + mv[ 2]*z + mv[ 3];
       
 11560         var ay = mv[ 4]*x + mv[ 5]*y + mv[ 6]*z + mv[ 7];
       
 11561         var az = mv[ 8]*x + mv[ 9]*y + mv[10]*z + mv[11];
       
 11562         var aw = mv[12]*x + mv[13]*y + mv[14]*z + mv[15];
       
 11563 
       
 11564         var pj = projection.array();
       
 11565 
       
 11566         var oy = pj[ 4]*ax + pj[ 5]*ay + pj[ 6]*az + pj[ 7]*aw;
       
 11567         var ow = pj[12]*ax + pj[13]*ay + pj[14]*az + pj[15]*aw;
       
 11568 
       
 11569         if ( ow !== 0 ){
       
 11570           oy /= ow;
       
 11571         }
       
 11572         return p.height * ( 1 + oy ) / 2.0;
       
 11573       }
       
 11574       // We assume that we're in 2D
       
 11575       return modelView.multY(x, y);
       
 11576     };
       
 11577 
       
 11578     /**
       
 11579      * Takes a three-dimensional X, Y, Z position and returns the Z value for
       
 11580      * where it will appear on a (two-dimensional) screen.
       
 11581      *
       
 11582      * @param {int | float} x 3D x coordinate to be mapped
       
 11583      * @param {int | float} y 3D y coordinate to be mapped
       
 11584      * @param {int | float} z 3D z coordinate to be mapped
       
 11585      *
       
 11586      * @returns {float}
       
 11587      *
       
 11588      * @see screenX
       
 11589      * @see screenY
       
 11590     */
       
 11591     p.screenZ = function screenZ( x, y, z ) {
       
 11592       var mv = modelView.array();
       
 11593       if( mv.length !== 16 ) {
       
 11594         return 0;
       
 11595       }
       
 11596 
       
 11597       var pj = projection.array();
       
 11598 
       
 11599       var ax = mv[ 0]*x + mv[ 1]*y + mv[ 2]*z + mv[ 3];
       
 11600       var ay = mv[ 4]*x + mv[ 5]*y + mv[ 6]*z + mv[ 7];
       
 11601       var az = mv[ 8]*x + mv[ 9]*y + mv[10]*z + mv[11];
       
 11602       var aw = mv[12]*x + mv[13]*y + mv[14]*z + mv[15];
       
 11603 
       
 11604       var oz = pj[ 8]*ax + pj[ 9]*ay + pj[10]*az + pj[11]*aw;
       
 11605       var ow = pj[12]*ax + pj[13]*ay + pj[14]*az + pj[15]*aw;
       
 11606 
       
 11607       if ( ow !== 0 ) {
       
 11608         oz /= ow;
       
 11609       }
       
 11610       return ( oz + 1 ) / 2.0;
       
 11611     };
       
 11612 
       
 11613     ////////////////////////////////////////////////////////////////////////////
       
 11614     // Style functions
       
 11615     ////////////////////////////////////////////////////////////////////////////
       
 11616     /**
       
 11617      * The fill() function sets the color used to fill shapes. For example, if you run <b>fill(204, 102, 0)</b>, all subsequent shapes will be filled with orange.
       
 11618      * This color is either specified in terms of the RGB or HSB color depending on the current <b>colorMode()</b>
       
 11619      *(the default color space is RGB, with each value in the range from 0 to 255).
       
 11620      * <br><br>When using hexadecimal notation to specify a color, use "#" or "0x" before the values (e.g. #CCFFAA, 0xFFCCFFAA).
       
 11621      * The # syntax uses six digits to specify a color (the way colors are specified in HTML and CSS). When using the hexadecimal notation starting with "0x",
       
 11622      * the hexadecimal value must be specified with eight characters; the first two characters define the alpha component and the remainder the red, green, and blue components.
       
 11623      * <br><br>The value for the parameter "gray" must be less than or equal to the current maximum value as specified by <b>colorMode()</b>. The default maximum value is 255.
       
 11624      * <br><br>To change the color of an image (or a texture), use tint().
       
 11625      *
       
 11626      * @param {int|float} gray    number specifying value between white and black
       
 11627      * @param {int|float} value1  red or hue value
       
 11628      * @param {int|float} value2  green or saturation value
       
 11629      * @param {int|float} value3  blue or brightness value
       
 11630      * @param {int|float} alpha   opacity of the fill
       
 11631      * @param {Color} color       any value of the color datatype
       
 11632      * @param {int} hex           color value in hexadecimal notation (i.e. #FFCC00 or 0xFFFFCC00)
       
 11633      *
       
 11634      * @see #noFill()
       
 11635      * @see #stroke()
       
 11636      * @see #tint()
       
 11637      * @see #background()
       
 11638      * @see #colorMode()
       
 11639      */
       
 11640     DrawingShared.prototype.fill = function() {
       
 11641       var color = p.color(arguments[0], arguments[1], arguments[2], arguments[3]);
       
 11642       if(color === currentFillColor && doFill) {
       
 11643         return;
       
 11644       }
       
 11645       doFill = true;
       
 11646       currentFillColor = color;
       
 11647     };
       
 11648 
       
 11649     Drawing2D.prototype.fill = function() {
       
 11650       DrawingShared.prototype.fill.apply(this, arguments);
       
 11651       isFillDirty = true;
       
 11652     };
       
 11653 
       
 11654     Drawing3D.prototype.fill = function() {
       
 11655       DrawingShared.prototype.fill.apply(this, arguments);
       
 11656       fillStyle = p.color.toGLArray(currentFillColor);
       
 11657     };
       
 11658 
       
 11659     function executeContextFill() {
       
 11660       if(doFill) {
       
 11661         if(isFillDirty) {
       
 11662           curContext.fillStyle = p.color.toString(currentFillColor);
       
 11663           isFillDirty = false;
       
 11664         }
       
 11665         curContext.fill();
       
 11666       }
       
 11667     }
       
 11668 
       
 11669     /**
       
 11670      * The noFill() function disables filling geometry. If both <b>noStroke()</b> and <b>noFill()</b>
       
 11671      * are called, no shapes will be drawn to the screen.
       
 11672      *
       
 11673      * @see #fill()
       
 11674      *
       
 11675      */
       
 11676     p.noFill = function() {
       
 11677       doFill = false;
       
 11678     };
       
 11679 
       
 11680     /**
       
 11681      * The stroke() function sets the color used to draw lines and borders around shapes. This color
       
 11682      * is either specified in terms of the RGB or HSB color depending on the
       
 11683      * current <b>colorMode()</b> (the default color space is RGB, with each
       
 11684      * value in the range from 0 to 255).
       
 11685      * <br><br>When using hexadecimal notation to specify a color, use "#" or
       
 11686      * "0x" before the values (e.g. #CCFFAA, 0xFFCCFFAA). The # syntax uses six
       
 11687      * digits to specify a color (the way colors are specified in HTML and CSS).
       
 11688      * When using the hexadecimal notation starting with "0x", the hexadecimal
       
 11689      * value must be specified with eight characters; the first two characters
       
 11690      * define the alpha component and the remainder the red, green, and blue
       
 11691      * components.
       
 11692      * <br><br>The value for the parameter "gray" must be less than or equal
       
 11693      * to the current maximum value as specified by <b>colorMode()</b>.
       
 11694      * The default maximum value is 255.
       
 11695      *
       
 11696      * @param {int|float} gray    number specifying value between white and black
       
 11697      * @param {int|float} value1  red or hue value
       
 11698      * @param {int|float} value2  green or saturation value
       
 11699      * @param {int|float} value3  blue or brightness value
       
 11700      * @param {int|float} alpha   opacity of the stroke
       
 11701      * @param {Color} color       any value of the color datatype
       
 11702      * @param {int} hex           color value in hexadecimal notation (i.e. #FFCC00 or 0xFFFFCC00)
       
 11703      *
       
 11704      * @see #fill()
       
 11705      * @see #noStroke()
       
 11706      * @see #tint()
       
 11707      * @see #background()
       
 11708      * @see #colorMode()
       
 11709      */
       
 11710     DrawingShared.prototype.stroke = function() {
       
 11711       var color = p.color(arguments[0], arguments[1], arguments[2], arguments[3]);
       
 11712       if(color === currentStrokeColor && doStroke) {
       
 11713         return;
       
 11714       }
       
 11715       doStroke = true;
       
 11716       currentStrokeColor = color;
       
 11717     };
       
 11718 
       
 11719     Drawing2D.prototype.stroke = function() {
       
 11720       DrawingShared.prototype.stroke.apply(this, arguments);
       
 11721       isStrokeDirty = true;
       
 11722     };
       
 11723 
       
 11724     Drawing3D.prototype.stroke = function() {
       
 11725       DrawingShared.prototype.stroke.apply(this, arguments);
       
 11726       strokeStyle = p.color.toGLArray(currentStrokeColor);
       
 11727     };
       
 11728 
       
 11729     function executeContextStroke() {
       
 11730       if(doStroke) {
       
 11731         if(isStrokeDirty) {
       
 11732           curContext.strokeStyle = p.color.toString(currentStrokeColor);
       
 11733           isStrokeDirty = false;
       
 11734         }
       
 11735         curContext.stroke();
       
 11736       }
       
 11737     }
       
 11738 
       
 11739     /**
       
 11740      * The noStroke() function disables drawing the stroke (outline). If both <b>noStroke()</b> and
       
 11741      * <b>noFill()</b> are called, no shapes will be drawn to the screen.
       
 11742      *
       
 11743      * @see #stroke()
       
 11744      */
       
 11745     p.noStroke = function() {
       
 11746       doStroke = false;
       
 11747     };
       
 11748 
       
 11749     /**
       
 11750      * The strokeWeight() function sets the width of the stroke used for lines, points, and the border around shapes.
       
 11751      * All widths are set in units of pixels.
       
 11752      *
       
 11753      * @param {int|float} w the weight (in pixels) of the stroke
       
 11754      */
       
 11755     DrawingShared.prototype.strokeWeight = function(w) {
       
 11756       lineWidth = w;
       
 11757     };
       
 11758 
       
 11759     Drawing2D.prototype.strokeWeight = function(w) {
       
 11760       DrawingShared.prototype.strokeWeight.apply(this, arguments);
       
 11761       curContext.lineWidth = w;
       
 11762     };
       
 11763 
       
 11764     Drawing3D.prototype.strokeWeight = function(w) {
       
 11765       DrawingShared.prototype.strokeWeight.apply(this, arguments);
       
 11766 
       
 11767       // Processing groups the weight of points and lines under this one function,
       
 11768       // but for WebGL, we need to set a uniform for points and call a function for line.
       
 11769 
       
 11770       curContext.useProgram(programObject2D);
       
 11771       uniformf("pointSize2d", programObject2D, "pointSize", w);
       
 11772 
       
 11773       curContext.useProgram(programObjectUnlitShape);
       
 11774       uniformf("pointSizeUnlitShape", programObjectUnlitShape, "pointSize", w);
       
 11775 
       
 11776       curContext.lineWidth(w);
       
 11777     };
       
 11778 
       
 11779     /**
       
 11780      * The strokeCap() function sets the style for rendering line endings. These ends are either squared, extended, or rounded and
       
 11781      * specified with the corresponding parameters SQUARE, PROJECT, and ROUND. The default cap is ROUND.
       
 11782      * This function is not available with the P2D, P3D, or OPENGL renderers
       
 11783      *
       
 11784      * @param {int} value Either SQUARE, PROJECT, or ROUND
       
 11785      */
       
 11786     p.strokeCap = function(value) {
       
 11787       drawing.$ensureContext().lineCap = value;
       
 11788     };
       
 11789 
       
 11790     /**
       
 11791      * The strokeJoin() function sets the style of the joints which connect line segments.
       
 11792      * These joints are either mitered, beveled, or rounded and specified with the corresponding parameters MITER, BEVEL, and ROUND. The default joint is MITER.
       
 11793      * This function is not available with the P2D, P3D, or OPENGL renderers
       
 11794      *
       
 11795      * @param {int} value Either SQUARE, PROJECT, or ROUND
       
 11796      */
       
 11797     p.strokeJoin = function(value) {
       
 11798       drawing.$ensureContext().lineJoin = value;
       
 11799     };
       
 11800 
       
 11801     /**
       
 11802      * The smooth() function draws all geometry with smooth (anti-aliased) edges. This will slow down the frame rate of the application,
       
 11803      * but will enhance the visual refinement. <br/><br/>
       
 11804      * Note that smooth() will also improve image quality of resized images, and noSmooth() will disable image (and font) smoothing altogether.
       
 11805      *
       
 11806      * @see #noSmooth()
       
 11807      * @see #hint()
       
 11808      * @see #size()
       
 11809      */
       
 11810 
       
 11811     Drawing2D.prototype.smooth = function() {
       
 11812       renderSmooth = true;
       
 11813       var style = curElement.style;
       
 11814       style.setProperty("image-rendering", "optimizeQuality", "important");
       
 11815       style.setProperty("-ms-interpolation-mode", "bicubic", "important");
       
 11816       if (curContext.hasOwnProperty("mozImageSmoothingEnabled")) {
       
 11817         curContext.mozImageSmoothingEnabled = true;
       
 11818       }
       
 11819     };
       
 11820 
       
 11821     Drawing3D.prototype.smooth = nop;
       
 11822 
       
 11823     /**
       
 11824      * The noSmooth() function draws all geometry with jagged (aliased) edges.
       
 11825      *
       
 11826      * @see #smooth()
       
 11827      */
       
 11828 
       
 11829     Drawing2D.prototype.noSmooth = function() {
       
 11830       renderSmooth = false;
       
 11831       var style = curElement.style;
       
 11832       style.setProperty("image-rendering", "optimizeSpeed", "important");
       
 11833       style.setProperty("image-rendering", "-moz-crisp-edges", "important");
       
 11834       style.setProperty("image-rendering", "-webkit-optimize-contrast", "important");
       
 11835       style.setProperty("image-rendering", "optimize-contrast", "important");
       
 11836       style.setProperty("-ms-interpolation-mode", "nearest-neighbor", "important");
       
 11837       if (curContext.hasOwnProperty("mozImageSmoothingEnabled")) {
       
 11838         curContext.mozImageSmoothingEnabled = false;
       
 11839       }
       
 11840     };
       
 11841 
       
 11842     Drawing3D.prototype.noSmooth = nop;
       
 11843 
       
 11844     ////////////////////////////////////////////////////////////////////////////
       
 11845     // Vector drawing functions
       
 11846     ////////////////////////////////////////////////////////////////////////////
       
 11847     /**
       
 11848      * The point() function draws a point, a coordinate in space at the dimension of one pixel.
       
 11849      * The first parameter is the horizontal value for the point, the second
       
 11850      * value is the vertical value for the point, and the optional third value
       
 11851      * is the depth value. Drawing this shape in 3D using the <b>z</b>
       
 11852      * parameter requires the P3D or OPENGL parameter in combination with
       
 11853      * size as shown in the above example.
       
 11854      *
       
 11855      * @param {int|float} x x-coordinate of the point
       
 11856      * @param {int|float} y y-coordinate of the point
       
 11857      * @param {int|float} z z-coordinate of the point
       
 11858      *
       
 11859      * @see #beginShape()
       
 11860      */
       
 11861     Drawing2D.prototype.point = function(x, y) {
       
 11862       if (!doStroke) {
       
 11863         return;
       
 11864       }
       
 11865 
       
 11866       x = Math.round(x);
       
 11867       y = Math.round(y);
       
 11868       curContext.fillStyle = p.color.toString(currentStrokeColor);
       
 11869       isFillDirty = true;
       
 11870       // Draw a circle for any point larger than 1px
       
 11871       if (lineWidth > 1) {
       
 11872         curContext.beginPath();
       
 11873         curContext.arc(x, y, lineWidth / 2, 0, PConstants.TWO_PI, false);
       
 11874         curContext.fill();
       
 11875       } else {
       
 11876         curContext.fillRect(x, y, 1, 1);
       
 11877       }
       
 11878     };
       
 11879 
       
 11880     Drawing3D.prototype.point = function(x, y, z) {
       
 11881       var model = new PMatrix3D();
       
 11882 
       
 11883       // move point to position
       
 11884       model.translate(x, y, z || 0);
       
 11885       model.transpose();
       
 11886 
       
 11887       var view = new PMatrix3D();
       
 11888       view.scale(1, -1, 1);
       
 11889       view.apply(modelView.array());
       
 11890       view.transpose();
       
 11891 
       
 11892       curContext.useProgram(programObject2D);
       
 11893       uniformMatrix("model2d", programObject2D, "model", false, model.array());
       
 11894       uniformMatrix("view2d", programObject2D, "view", false, view.array());
       
 11895 
       
 11896       if (lineWidth > 0 && doStroke) {
       
 11897         // this will be replaced with the new bit shifting color code
       
 11898         uniformf("color2d", programObject2D, "color", strokeStyle);
       
 11899         uniformi("picktype2d", programObject2D, "picktype", 0);
       
 11900         vertexAttribPointer("vertex2d", programObject2D, "Vertex", 3, pointBuffer);
       
 11901         disableVertexAttribPointer("aTextureCoord2d", programObject2D, "aTextureCoord");
       
 11902         curContext.drawArrays(curContext.POINTS, 0, 1);
       
 11903       }
       
 11904     };
       
 11905 
       
 11906     /**
       
 11907      * Using the <b>beginShape()</b> and <b>endShape()</b> functions allow creating more complex forms.
       
 11908      * <b>beginShape()</b> begins recording vertices for a shape and <b>endShape()</b> stops recording.
       
 11909      * The value of the <b>MODE</b> parameter tells it which types of shapes to create from the provided vertices.
       
 11910      * With no mode specified, the shape can be any irregular polygon. After calling the <b>beginShape()</b> function,
       
 11911      * a series of <b>vertex()</b> commands must follow. To stop drawing the shape, call <b>endShape()</b>.
       
 11912      * The <b>vertex()</b> function with two parameters specifies a position in 2D and the <b>vertex()</b>
       
 11913      * function with three parameters specifies a position in 3D. Each shape will be outlined with the current
       
 11914      * stroke color and filled with the fill color.
       
 11915      *
       
 11916      * @param {int} MODE either POINTS, LINES, TRIANGLES, TRIANGLE_FAN, TRIANGLE_STRIP, QUADS, and QUAD_STRIP.
       
 11917      *
       
 11918      * @see endShape
       
 11919      * @see vertex
       
 11920      * @see curveVertex
       
 11921      * @see bezierVertex
       
 11922      */
       
 11923     p.beginShape = function(type) {
       
 11924       curShape = type;
       
 11925       curvePoints = [];
       
 11926       vertArray = [];
       
 11927     };
       
 11928 
       
 11929     /**
       
 11930      * All shapes are constructed by connecting a series of vertices. <b>vertex()</b> is used to specify the vertex
       
 11931      * coordinates for points, lines, triangles, quads, and polygons and is used exclusively within the <b>beginShape()</b>
       
 11932      * and <b>endShape()</b> function. <br /><br />Drawing a vertex in 3D using the <b>z</b> parameter requires the P3D or
       
 11933      * OPENGL parameter in combination with size as shown in the above example.<br /><br />This function is also used to map a
       
 11934      * texture onto the geometry. The <b>texture()</b> function declares the texture to apply to the geometry and the <b>u</b>
       
 11935      * and <b>v</b> coordinates set define the mapping of this texture to the form. By default, the coordinates used for
       
 11936      * <b>u</b> and <b>v</b> are specified in relation to the image's size in pixels, but this relation can be changed with
       
 11937      * <b>textureMode()</b>.
       
 11938      *
       
 11939      * @param {int | float} x x-coordinate of the vertex
       
 11940      * @param {int | float} y y-coordinate of the vertex
       
 11941      * @param {int | float} z z-coordinate of the vertex
       
 11942      * @param {int | float} u horizontal coordinate for the texture mapping
       
 11943      * @param {int | float} v vertical coordinate for the texture mapping
       
 11944      *
       
 11945      * @see beginShape
       
 11946      * @see endShape
       
 11947      * @see bezierVertex
       
 11948      * @see curveVertex
       
 11949      * @see texture
       
 11950      */
       
 11951 
       
 11952     Drawing2D.prototype.vertex = function(x, y, u, v) {
       
 11953       var vert = [];
       
 11954 
       
 11955       if (firstVert) { firstVert = false; }
       
 11956       vert["isVert"] = true;
       
 11957 
       
 11958       vert[0] = x;
       
 11959       vert[1] = y;
       
 11960       vert[2] = 0;
       
 11961       vert[3] = u;
       
 11962       vert[4] = v;
       
 11963 
       
 11964       // fill and stroke color
       
 11965       vert[5] = currentFillColor;
       
 11966       vert[6] = currentStrokeColor;
       
 11967 
       
 11968       vertArray.push(vert);
       
 11969     };
       
 11970 
       
 11971     Drawing3D.prototype.vertex = function(x, y, z, u, v) {
       
 11972       var vert = [];
       
 11973 
       
 11974       if (firstVert) { firstVert = false; }
       
 11975       vert["isVert"] = true;
       
 11976 
       
 11977       if (v === undef && usingTexture) {
       
 11978         v = u;
       
 11979         u = z;
       
 11980         z = 0;
       
 11981       }
       
 11982 
       
 11983       // Convert u and v to normalized coordinates
       
 11984       if (u !== undef && v !== undef) {
       
 11985         if (curTextureMode === PConstants.IMAGE) {
       
 11986           u /= curTexture.width;
       
 11987           v /= curTexture.height;
       
 11988         }
       
 11989         u = u > 1 ? 1 : u;
       
 11990         u = u < 0 ? 0 : u;
       
 11991         v = v > 1 ? 1 : v;
       
 11992         v = v < 0 ? 0 : v;
       
 11993       }
       
 11994 
       
 11995       vert[0] = x;
       
 11996       vert[1] = y;
       
 11997       vert[2] = z || 0;
       
 11998       vert[3] = u || 0;
       
 11999       vert[4] = v || 0;
       
 12000 
       
 12001       // fill rgba
       
 12002       vert[5] = fillStyle[0];
       
 12003       vert[6] = fillStyle[1];
       
 12004       vert[7] = fillStyle[2];
       
 12005       vert[8] = fillStyle[3];
       
 12006       // stroke rgba
       
 12007       vert[9] = strokeStyle[0];
       
 12008       vert[10] = strokeStyle[1];
       
 12009       vert[11] = strokeStyle[2];
       
 12010       vert[12] = strokeStyle[3];
       
 12011       //normals
       
 12012       vert[13] = normalX;
       
 12013       vert[14] = normalY;
       
 12014       vert[15] = normalZ;
       
 12015 
       
 12016       vertArray.push(vert);
       
 12017     };
       
 12018 
       
 12019     /**
       
 12020      * @private
       
 12021      * Renders 3D points created from calls to vertex and beginShape/endShape
       
 12022      *
       
 12023      * @param {Array} vArray an array of vertex coordinate
       
 12024      * @param {Array} cArray an array of colours used for the vertices
       
 12025      *
       
 12026      * @see beginShape
       
 12027      * @see endShape
       
 12028      * @see vertex
       
 12029      */
       
 12030     var point3D = function(vArray, cArray){
       
 12031       var view = new PMatrix3D();
       
 12032       view.scale(1, -1, 1);
       
 12033       view.apply(modelView.array());
       
 12034       view.transpose();
       
 12035 
       
 12036       curContext.useProgram(programObjectUnlitShape);
       
 12037 
       
 12038       uniformMatrix("uViewUS", programObjectUnlitShape, "uView", false, view.array());
       
 12039 
       
 12040       vertexAttribPointer("aVertexUS", programObjectUnlitShape, "aVertex", 3, pointBuffer);
       
 12041       curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array(vArray), curContext.STREAM_DRAW);
       
 12042 
       
 12043       vertexAttribPointer("aColorUS", programObjectUnlitShape, "aColor", 4, fillColorBuffer);
       
 12044       curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array(cArray), curContext.STREAM_DRAW);
       
 12045 
       
 12046       curContext.drawArrays(curContext.POINTS, 0, vArray.length/3);
       
 12047     };
       
 12048 
       
 12049     /**
       
 12050      * @private
       
 12051      * Renders 3D lines created from calls to beginShape/vertex/endShape - based on the mode specified LINES, LINE_LOOP, etc.
       
 12052      *
       
 12053      * @param {Array} vArray an array of vertex coordinate
       
 12054      * @param {String} mode  either LINES, LINE_LOOP, or LINE_STRIP
       
 12055      * @param {Array} cArray an array of colours used for the vertices
       
 12056      *
       
 12057      * @see beginShape
       
 12058      * @see endShape
       
 12059      * @see vertex
       
 12060      */
       
 12061     var line3D = function(vArray, mode, cArray){
       
 12062       var ctxMode;
       
 12063       if (mode === "LINES"){
       
 12064         ctxMode = curContext.LINES;
       
 12065       }
       
 12066       else if(mode === "LINE_LOOP"){
       
 12067         ctxMode = curContext.LINE_LOOP;
       
 12068       }
       
 12069       else{
       
 12070         ctxMode = curContext.LINE_STRIP;
       
 12071       }
       
 12072 
       
 12073       var view = new PMatrix3D();
       
 12074       view.scale(1, -1, 1);
       
 12075       view.apply(modelView.array());
       
 12076       view.transpose();
       
 12077 
       
 12078       curContext.useProgram(programObjectUnlitShape);
       
 12079       uniformMatrix("uViewUS", programObjectUnlitShape, "uView", false, view.array());
       
 12080       vertexAttribPointer("aVertexUS", programObjectUnlitShape, "aVertex", 3, lineBuffer);
       
 12081       curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array(vArray), curContext.STREAM_DRAW);
       
 12082       vertexAttribPointer("aColorUS", programObjectUnlitShape, "aColor", 4, strokeColorBuffer);
       
 12083       curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array(cArray), curContext.STREAM_DRAW);
       
 12084       curContext.drawArrays(ctxMode, 0, vArray.length/3);
       
 12085     };
       
 12086 
       
 12087     /**
       
 12088      * @private
       
 12089      * Render filled shapes created from calls to beginShape/vertex/endShape - based on the mode specified TRIANGLES, etc.
       
 12090      *
       
 12091      * @param {Array} vArray an array of vertex coordinate
       
 12092      * @param {String} mode  either LINES, LINE_LOOP, or LINE_STRIP
       
 12093      * @param {Array} cArray an array of colours used for the vertices
       
 12094      * @param {Array} tArray an array of u,v coordinates for textures
       
 12095      *
       
 12096      * @see beginShape
       
 12097      * @see endShape
       
 12098      * @see vertex
       
 12099      */
       
 12100     var fill3D = function(vArray, mode, cArray, tArray){
       
 12101       var ctxMode;
       
 12102       if (mode === "TRIANGLES") {
       
 12103         ctxMode = curContext.TRIANGLES;
       
 12104       } else if(mode === "TRIANGLE_FAN") {
       
 12105         ctxMode = curContext.TRIANGLE_FAN;
       
 12106       } else {
       
 12107         ctxMode = curContext.TRIANGLE_STRIP;
       
 12108       }
       
 12109 
       
 12110       var view = new PMatrix3D();
       
 12111       view.scale(1, -1, 1);
       
 12112       view.apply(modelView.array());
       
 12113       view.transpose();
       
 12114 
       
 12115       curContext.useProgram( programObject3D );
       
 12116       uniformMatrix("model3d", programObject3D, "model", false,  [1,0,0,0,  0,1,0,0,   0,0,1,0,   0,0,0,1] );
       
 12117       uniformMatrix("view3d", programObject3D, "view", false, view.array() );
       
 12118       curContext.enable( curContext.POLYGON_OFFSET_FILL );
       
 12119       curContext.polygonOffset( 1, 1 );
       
 12120       uniformf("color3d", programObject3D, "color", [-1,0,0,0]);
       
 12121       vertexAttribPointer("vertex3d", programObject3D, "Vertex", 3, fillBuffer);
       
 12122       curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array(vArray), curContext.STREAM_DRAW);
       
 12123 
       
 12124       // if we are using a texture and a tint, then overwrite the
       
 12125       // contents of the color buffer with the current tint
       
 12126       if (usingTexture && curTint !== null){
       
 12127         curTint3d(cArray);
       
 12128       }
       
 12129 
       
 12130       vertexAttribPointer("aColor3d", programObject3D, "aColor", 4, fillColorBuffer);
       
 12131       curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array(cArray), curContext.STREAM_DRAW);
       
 12132 
       
 12133       // No support for lights....yet
       
 12134       disableVertexAttribPointer("normal3d", programObject3D, "Normal");
       
 12135 
       
 12136       if (usingTexture) {
       
 12137         uniformi("usingTexture3d", programObject3D, "usingTexture", usingTexture);
       
 12138         vertexAttribPointer("aTexture3d", programObject3D, "aTexture", 2, shapeTexVBO);
       
 12139         curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array(tArray), curContext.STREAM_DRAW);
       
 12140       }
       
 12141 
       
 12142       curContext.drawArrays( ctxMode, 0, vArray.length/3 );
       
 12143       curContext.disable( curContext.POLYGON_OFFSET_FILL );
       
 12144     };
       
 12145 
       
 12146     /**
       
 12147      * this series of three operations is used a lot in Drawing2D.prototype.endShape
       
 12148      * and has been split off as its own function, to tighten the code and allow for
       
 12149      * fewer bugs.
       
 12150      */
       
 12151     function fillStrokeClose() {
       
 12152       executeContextFill();
       
 12153       executeContextStroke();
       
 12154       curContext.closePath();
       
 12155     }
       
 12156 
       
 12157     /**
       
 12158      * The endShape() function is the companion to beginShape() and may only be called after beginShape().
       
 12159      * When endshape() is called, all of image data defined since the previous call to beginShape() is written
       
 12160      * into the image buffer.
       
 12161      *
       
 12162      * @param {int} MODE Use CLOSE to close the shape
       
 12163      *
       
 12164      * @see beginShape
       
 12165      */
       
 12166     Drawing2D.prototype.endShape = function(mode) {
       
 12167       // Duplicated in Drawing3D; too many variables used
       
 12168       if (vertArray.length === 0) { return; }
       
 12169 
       
 12170       var closeShape = mode === PConstants.CLOSE;
       
 12171 
       
 12172       // if the shape is closed, the first element is also the last element
       
 12173       if (closeShape) {
       
 12174         vertArray.push(vertArray[0]);
       
 12175       }
       
 12176 
       
 12177       var lineVertArray = [];
       
 12178       var fillVertArray = [];
       
 12179       var colorVertArray = [];
       
 12180       var strokeVertArray = [];
       
 12181       var texVertArray = [];
       
 12182       var cachedVertArray;
       
 12183 
       
 12184       firstVert = true;
       
 12185       var i, j, k;
       
 12186       var vertArrayLength = vertArray.length;
       
 12187 
       
 12188       for (i = 0; i < vertArrayLength; i++) {
       
 12189         cachedVertArray = vertArray[i];
       
 12190         for (j = 0; j < 3; j++) {
       
 12191           fillVertArray.push(cachedVertArray[j]);
       
 12192         }
       
 12193       }
       
 12194 
       
 12195       // 5,6,7,8
       
 12196       // R,G,B,A - fill colour
       
 12197       for (i = 0; i < vertArrayLength; i++) {
       
 12198         cachedVertArray = vertArray[i];
       
 12199         for (j = 5; j < 9; j++) {
       
 12200           colorVertArray.push(cachedVertArray[j]);
       
 12201         }
       
 12202       }
       
 12203 
       
 12204       // 9,10,11,12
       
 12205       // R, G, B, A - stroke colour
       
 12206       for (i = 0; i < vertArrayLength; i++) {
       
 12207         cachedVertArray = vertArray[i];
       
 12208         for (j = 9; j < 13; j++) {
       
 12209           strokeVertArray.push(cachedVertArray[j]);
       
 12210         }
       
 12211       }
       
 12212 
       
 12213       // texture u,v
       
 12214       for (i = 0; i < vertArrayLength; i++) {
       
 12215         cachedVertArray = vertArray[i];
       
 12216         texVertArray.push(cachedVertArray[3]);
       
 12217         texVertArray.push(cachedVertArray[4]);
       
 12218       }
       
 12219 
       
 12220       // curveVertex
       
 12221       if ( isCurve && (curShape === PConstants.POLYGON || curShape === undef) ) {
       
 12222         if (vertArrayLength > 3) {
       
 12223           var b = [],
       
 12224               s = 1 - curTightness;
       
 12225           curContext.beginPath();
       
 12226           curContext.moveTo(vertArray[1][0], vertArray[1][1]);
       
 12227             /*
       
 12228             * Matrix to convert from Catmull-Rom to cubic Bezier
       
 12229             * where t = curTightness
       
 12230             * |0         1          0         0       |
       
 12231             * |(t-1)/6   1          (1-t)/6   0       |
       
 12232             * |0         (1-t)/6    1         (t-1)/6 |
       
 12233             * |0         0          0         0       |
       
 12234             */
       
 12235           for (i = 1; (i+2) < vertArrayLength; i++) {
       
 12236             cachedVertArray = vertArray[i];
       
 12237             b[0] = [cachedVertArray[0], cachedVertArray[1]];
       
 12238             b[1] = [cachedVertArray[0] + (s * vertArray[i+1][0] - s * vertArray[i-1][0]) / 6,
       
 12239                    cachedVertArray[1] + (s * vertArray[i+1][1] - s * vertArray[i-1][1]) / 6];
       
 12240             b[2] = [vertArray[i+1][0] + (s * vertArray[i][0] - s * vertArray[i+2][0]) / 6,
       
 12241                    vertArray[i+1][1] + (s * vertArray[i][1] - s * vertArray[i+2][1]) / 6];
       
 12242             b[3] = [vertArray[i+1][0], vertArray[i+1][1]];
       
 12243             curContext.bezierCurveTo(b[1][0], b[1][1], b[2][0], b[2][1], b[3][0], b[3][1]);
       
 12244           }
       
 12245           fillStrokeClose();
       
 12246         }
       
 12247       }
       
 12248 
       
 12249       // bezierVertex
       
 12250       else if ( isBezier && (curShape === PConstants.POLYGON || curShape === undef) ) {
       
 12251         curContext.beginPath();
       
 12252         for (i = 0; i < vertArrayLength; i++) {
       
 12253           cachedVertArray = vertArray[i];
       
 12254           if (vertArray[i]["isVert"]) { //if it is a vertex move to the position
       
 12255             if (vertArray[i]["moveTo"]) {
       
 12256               curContext.moveTo(cachedVertArray[0], cachedVertArray[1]);
       
 12257             } else {
       
 12258               curContext.lineTo(cachedVertArray[0], cachedVertArray[1]);
       
 12259             }
       
 12260           } else { //otherwise continue drawing bezier
       
 12261             curContext.bezierCurveTo(vertArray[i][0], vertArray[i][1], vertArray[i][2], vertArray[i][3], vertArray[i][4], vertArray[i][5]);
       
 12262           }
       
 12263         }
       
 12264         fillStrokeClose();
       
 12265       }
       
 12266 
       
 12267       // render the vertices provided
       
 12268       else {
       
 12269         if (curShape === PConstants.POINTS) {
       
 12270           for (i = 0; i < vertArrayLength; i++) {
       
 12271             cachedVertArray = vertArray[i];
       
 12272             if (doStroke) {
       
 12273               p.stroke(cachedVertArray[6]);
       
 12274             }
       
 12275             p.point(cachedVertArray[0], cachedVertArray[1]);
       
 12276           }
       
 12277         } else if (curShape === PConstants.LINES) {
       
 12278           for (i = 0; (i + 1) < vertArrayLength; i+=2) {
       
 12279             cachedVertArray = vertArray[i];
       
 12280             if (doStroke) {
       
 12281               p.stroke(vertArray[i+1][6]);
       
 12282             }
       
 12283             p.line(cachedVertArray[0], cachedVertArray[1], vertArray[i+1][0], vertArray[i+1][1]);
       
 12284           }
       
 12285         } else if (curShape === PConstants.TRIANGLES) {
       
 12286           for (i = 0; (i + 2) < vertArrayLength; i+=3) {
       
 12287             cachedVertArray = vertArray[i];
       
 12288             curContext.beginPath();
       
 12289             curContext.moveTo(cachedVertArray[0], cachedVertArray[1]);
       
 12290             curContext.lineTo(vertArray[i+1][0], vertArray[i+1][1]);
       
 12291             curContext.lineTo(vertArray[i+2][0], vertArray[i+2][1]);
       
 12292             curContext.lineTo(cachedVertArray[0], cachedVertArray[1]);
       
 12293 
       
 12294             if (doFill) {
       
 12295               p.fill(vertArray[i+2][5]);
       
 12296               executeContextFill();
       
 12297             }
       
 12298             if (doStroke) {
       
 12299               p.stroke(vertArray[i+2][6]);
       
 12300               executeContextStroke();
       
 12301             }
       
 12302 
       
 12303             curContext.closePath();
       
 12304           }
       
 12305         } else if (curShape === PConstants.TRIANGLE_STRIP) {
       
 12306           for (i = 0; (i+1) < vertArrayLength; i++) {
       
 12307             cachedVertArray = vertArray[i];
       
 12308             curContext.beginPath();
       
 12309             curContext.moveTo(vertArray[i+1][0], vertArray[i+1][1]);
       
 12310             curContext.lineTo(cachedVertArray[0], cachedVertArray[1]);
       
 12311 
       
 12312             if (doStroke) {
       
 12313               p.stroke(vertArray[i+1][6]);
       
 12314             }
       
 12315             if (doFill) {
       
 12316               p.fill(vertArray[i+1][5]);
       
 12317             }
       
 12318 
       
 12319             if (i + 2 < vertArrayLength) {
       
 12320               curContext.lineTo(vertArray[i+2][0], vertArray[i+2][1]);
       
 12321               if (doStroke) {
       
 12322                 p.stroke(vertArray[i+2][6]);
       
 12323               }
       
 12324               if (doFill) {
       
 12325                 p.fill(vertArray[i+2][5]);
       
 12326               }
       
 12327             }
       
 12328             fillStrokeClose();
       
 12329           }
       
 12330         } else if (curShape === PConstants.TRIANGLE_FAN) {
       
 12331           if (vertArrayLength > 2) {
       
 12332             curContext.beginPath();
       
 12333             curContext.moveTo(vertArray[0][0], vertArray[0][1]);
       
 12334             curContext.lineTo(vertArray[1][0], vertArray[1][1]);
       
 12335             curContext.lineTo(vertArray[2][0], vertArray[2][1]);
       
 12336 
       
 12337             if (doFill) {
       
 12338               p.fill(vertArray[2][5]);
       
 12339               executeContextFill();
       
 12340             }
       
 12341             if (doStroke) {
       
 12342               p.stroke(vertArray[2][6]);
       
 12343               executeContextStroke();
       
 12344             }
       
 12345 
       
 12346             curContext.closePath();
       
 12347             for (i = 3; i < vertArrayLength; i++) {
       
 12348               cachedVertArray = vertArray[i];
       
 12349               curContext.beginPath();
       
 12350               curContext.moveTo(vertArray[0][0], vertArray[0][1]);
       
 12351               curContext.lineTo(vertArray[i-1][0], vertArray[i-1][1]);
       
 12352               curContext.lineTo(cachedVertArray[0], cachedVertArray[1]);
       
 12353 
       
 12354               if (doFill) {
       
 12355                 p.fill(cachedVertArray[5]);
       
 12356                 executeContextFill();
       
 12357               }
       
 12358               if (doStroke) {
       
 12359                 p.stroke(cachedVertArray[6]);
       
 12360                 executeContextStroke();
       
 12361               }
       
 12362 
       
 12363               curContext.closePath();
       
 12364             }
       
 12365           }
       
 12366         } else if (curShape === PConstants.QUADS) {
       
 12367           for (i = 0; (i + 3) < vertArrayLength; i+=4) {
       
 12368             cachedVertArray = vertArray[i];
       
 12369             curContext.beginPath();
       
 12370             curContext.moveTo(cachedVertArray[0], cachedVertArray[1]);
       
 12371             for (j = 1; j < 4; j++) {
       
 12372               curContext.lineTo(vertArray[i+j][0], vertArray[i+j][1]);
       
 12373             }
       
 12374             curContext.lineTo(cachedVertArray[0], cachedVertArray[1]);
       
 12375 
       
 12376             if (doFill) {
       
 12377               p.fill(vertArray[i+3][5]);
       
 12378               executeContextFill();
       
 12379             }
       
 12380             if (doStroke) {
       
 12381               p.stroke(vertArray[i+3][6]);
       
 12382               executeContextStroke();
       
 12383             }
       
 12384 
       
 12385             curContext.closePath();
       
 12386           }
       
 12387         } else if (curShape === PConstants.QUAD_STRIP) {
       
 12388           if (vertArrayLength > 3) {
       
 12389             for (i = 0; (i+1) < vertArrayLength; i+=2) {
       
 12390               cachedVertArray = vertArray[i];
       
 12391               curContext.beginPath();
       
 12392               if (i+3 < vertArrayLength) {
       
 12393                 curContext.moveTo(vertArray[i+2][0], vertArray[i+2][1]);
       
 12394                 curContext.lineTo(cachedVertArray[0], cachedVertArray[1]);
       
 12395                 curContext.lineTo(vertArray[i+1][0], vertArray[i+1][1]);
       
 12396                 curContext.lineTo(vertArray[i+3][0], vertArray[i+3][1]);
       
 12397 
       
 12398                 if (doFill) {
       
 12399                   p.fill(vertArray[i+3][5]);
       
 12400                 }
       
 12401                 if (doStroke) {
       
 12402                   p.stroke(vertArray[i+3][6]);
       
 12403                 }
       
 12404               } else {
       
 12405                 curContext.moveTo(cachedVertArray[0], cachedVertArray[1]);
       
 12406                 curContext.lineTo(vertArray[i+1][0], vertArray[i+1][1]);
       
 12407               }
       
 12408               fillStrokeClose();
       
 12409             }
       
 12410           }
       
 12411         } else {
       
 12412           curContext.beginPath();
       
 12413           curContext.moveTo(vertArray[0][0], vertArray[0][1]);
       
 12414           for (i = 1; i < vertArrayLength; i++) {
       
 12415             cachedVertArray = vertArray[i];
       
 12416             if (cachedVertArray["isVert"]) { //if it is a vertex move to the position
       
 12417               if (cachedVertArray["moveTo"]) {
       
 12418                 curContext.moveTo(cachedVertArray[0], cachedVertArray[1]);
       
 12419               } else {
       
 12420                 curContext.lineTo(cachedVertArray[0], cachedVertArray[1]);
       
 12421               }
       
 12422             }
       
 12423           }
       
 12424           fillStrokeClose();
       
 12425         }
       
 12426       }
       
 12427 
       
 12428       // Reset some settings
       
 12429       isCurve = false;
       
 12430       isBezier = false;
       
 12431       curveVertArray = [];
       
 12432       curveVertCount = 0;
       
 12433 
       
 12434       // If the shape is closed, the first element was added as last element.
       
 12435       // We must remove it again to prevent the list of vertices from growing
       
 12436       // over successive calls to endShape(CLOSE)
       
 12437       if (closeShape) {
       
 12438         vertArray.pop();
       
 12439       }
       
 12440     };
       
 12441 
       
 12442     Drawing3D.prototype.endShape = function(mode) {
       
 12443       // Duplicated in Drawing3D; too many variables used
       
 12444       if (vertArray.length === 0) { return; }
       
 12445 
       
 12446       var closeShape = mode === PConstants.CLOSE;
       
 12447       var lineVertArray = [];
       
 12448       var fillVertArray = [];
       
 12449       var colorVertArray = [];
       
 12450       var strokeVertArray = [];
       
 12451       var texVertArray = [];
       
 12452       var cachedVertArray;
       
 12453 
       
 12454       firstVert = true;
       
 12455       var i, j, k;
       
 12456       var vertArrayLength = vertArray.length;
       
 12457 
       
 12458       for (i = 0; i < vertArrayLength; i++) {
       
 12459         cachedVertArray = vertArray[i];
       
 12460         for (j = 0; j < 3; j++) {
       
 12461           fillVertArray.push(cachedVertArray[j]);
       
 12462         }
       
 12463       }
       
 12464 
       
 12465       // 5,6,7,8
       
 12466       // R,G,B,A - fill colour
       
 12467       for (i = 0; i < vertArrayLength; i++) {
       
 12468         cachedVertArray = vertArray[i];
       
 12469         for (j = 5; j < 9; j++) {
       
 12470           colorVertArray.push(cachedVertArray[j]);
       
 12471         }
       
 12472       }
       
 12473 
       
 12474       // 9,10,11,12
       
 12475       // R, G, B, A - stroke colour
       
 12476       for (i = 0; i < vertArrayLength; i++) {
       
 12477         cachedVertArray = vertArray[i];
       
 12478         for (j = 9; j < 13; j++) {
       
 12479           strokeVertArray.push(cachedVertArray[j]);
       
 12480         }
       
 12481       }
       
 12482 
       
 12483       // texture u,v
       
 12484       for (i = 0; i < vertArrayLength; i++) {
       
 12485         cachedVertArray = vertArray[i];
       
 12486         texVertArray.push(cachedVertArray[3]);
       
 12487         texVertArray.push(cachedVertArray[4]);
       
 12488       }
       
 12489 
       
 12490       // if shape is closed, push the first point into the last point (including colours)
       
 12491       if (closeShape) {
       
 12492         fillVertArray.push(vertArray[0][0]);
       
 12493         fillVertArray.push(vertArray[0][1]);
       
 12494         fillVertArray.push(vertArray[0][2]);
       
 12495 
       
 12496         for (i = 5; i < 9; i++) {
       
 12497           colorVertArray.push(vertArray[0][i]);
       
 12498         }
       
 12499 
       
 12500         for (i = 9; i < 13; i++) {
       
 12501           strokeVertArray.push(vertArray[0][i]);
       
 12502         }
       
 12503 
       
 12504         texVertArray.push(vertArray[0][3]);
       
 12505         texVertArray.push(vertArray[0][4]);
       
 12506       }
       
 12507       // End duplication
       
 12508 
       
 12509       // curveVertex
       
 12510       if ( isCurve && (curShape === PConstants.POLYGON || curShape === undef) ) {
       
 12511         lineVertArray = fillVertArray;
       
 12512         if (doStroke) {
       
 12513           line3D(lineVertArray, null, strokeVertArray);
       
 12514         }
       
 12515         if (doFill) {
       
 12516           fill3D(fillVertArray, null, colorVertArray);
       
 12517         }
       
 12518       }
       
 12519       // bezierVertex
       
 12520       else if ( isBezier && (curShape === PConstants.POLYGON || curShape === undef) ) {
       
 12521         lineVertArray = fillVertArray;
       
 12522         lineVertArray.splice(lineVertArray.length - 3);
       
 12523         strokeVertArray.splice(strokeVertArray.length - 4);
       
 12524         if (doStroke) {
       
 12525           line3D(lineVertArray, null, strokeVertArray);
       
 12526         }
       
 12527         if (doFill) {
       
 12528           fill3D(fillVertArray, "TRIANGLES", colorVertArray);
       
 12529         }
       
 12530       }
       
 12531 
       
 12532       // render the vertices provided
       
 12533       else {
       
 12534         if (curShape === PConstants.POINTS) {       // if POINTS was the specified parameter in beginShape
       
 12535           for (i = 0; i < vertArrayLength; i++) {  // loop through and push the point location information to the array
       
 12536             cachedVertArray = vertArray[i];
       
 12537             for (j = 0; j < 3; j++) {
       
 12538               lineVertArray.push(cachedVertArray[j]);
       
 12539             }
       
 12540           }
       
 12541           point3D(lineVertArray, strokeVertArray);  // render function for points
       
 12542         } else if (curShape === PConstants.LINES) { // if LINES was the specified parameter in beginShape
       
 12543           for (i = 0; i < vertArrayLength; i++) {  // loop through and push the point location information to the array
       
 12544             cachedVertArray = vertArray[i];
       
 12545             for (j = 0; j < 3; j++) {
       
 12546               lineVertArray.push(cachedVertArray[j]);
       
 12547             }
       
 12548           }
       
 12549           for (i = 0; i < vertArrayLength; i++) {  // loop through and push the color information to the array
       
 12550             cachedVertArray = vertArray[i];
       
 12551             for (j = 5; j < 9; j++) {
       
 12552               colorVertArray.push(cachedVertArray[j]);
       
 12553             }
       
 12554           }
       
 12555           line3D(lineVertArray, "LINES", strokeVertArray);  // render function for lines
       
 12556         } else if (curShape === PConstants.TRIANGLES) {     // if TRIANGLES was the specified parameter in beginShape
       
 12557           if (vertArrayLength > 2) {
       
 12558             for (i = 0; (i+2) < vertArrayLength; i+=3) {   // loop through the array per triangle
       
 12559               fillVertArray = [];
       
 12560               texVertArray = [];
       
 12561               lineVertArray = [];
       
 12562               colorVertArray = [];
       
 12563               strokeVertArray = [];
       
 12564               for (j = 0; j < 3; j++) {
       
 12565                 for (k = 0; k < 3; k++) {                   // loop through and push
       
 12566                   lineVertArray.push(vertArray[i+j][k]);    // the line point location information
       
 12567                   fillVertArray.push(vertArray[i+j][k]);    // and fill point location information
       
 12568                 }
       
 12569               }
       
 12570               for (j = 0; j < 3; j++) {                     // loop through and push the texture information
       
 12571                 for (k = 3; k < 5; k++) {
       
 12572                   texVertArray.push(vertArray[i+j][k]);
       
 12573                 }
       
 12574               }
       
 12575               for (j = 0; j < 3; j++) {
       
 12576                 for (k = 5; k < 9; k++) {                   // loop through and push
       
 12577                   colorVertArray.push(vertArray[i+j][k]);   // the colour information
       
 12578                   strokeVertArray.push(vertArray[i+j][k+4]);// and the stroke information
       
 12579                 }
       
 12580               }
       
 12581               if (doStroke) {
       
 12582                 line3D(lineVertArray, "LINE_LOOP", strokeVertArray );               // line render function
       
 12583               }
       
 12584               if (doFill || usingTexture) {
       
 12585                 fill3D(fillVertArray, "TRIANGLES", colorVertArray, texVertArray);   // fill shape render function
       
 12586               }
       
 12587             }
       
 12588           }
       
 12589         } else if (curShape === PConstants.TRIANGLE_STRIP) {    // if TRIANGLE_STRIP was the specified parameter in beginShape
       
 12590           if (vertArrayLength > 2) {
       
 12591             for (i = 0; (i+2) < vertArrayLength; i++) {
       
 12592               lineVertArray = [];
       
 12593               fillVertArray = [];
       
 12594               strokeVertArray = [];
       
 12595               colorVertArray = [];
       
 12596               texVertArray = [];
       
 12597               for (j = 0; j < 3; j++) {
       
 12598                 for (k = 0; k < 3; k++) {
       
 12599                   lineVertArray.push(vertArray[i+j][k]);
       
 12600                   fillVertArray.push(vertArray[i+j][k]);
       
 12601                 }
       
 12602               }
       
 12603               for (j = 0; j < 3; j++) {
       
 12604                 for (k = 3; k < 5; k++) {
       
 12605                   texVertArray.push(vertArray[i+j][k]);
       
 12606                 }
       
 12607               }
       
 12608               for (j = 0; j < 3; j++) {
       
 12609                 for (k = 5; k < 9; k++) {
       
 12610                   strokeVertArray.push(vertArray[i+j][k+4]);
       
 12611                   colorVertArray.push(vertArray[i+j][k]);
       
 12612                 }
       
 12613               }
       
 12614 
       
 12615               if (doFill || usingTexture) {
       
 12616                 fill3D(fillVertArray, "TRIANGLE_STRIP", colorVertArray, texVertArray);
       
 12617               }
       
 12618               if (doStroke) {
       
 12619                 line3D(lineVertArray, "LINE_LOOP", strokeVertArray);
       
 12620               }
       
 12621             }
       
 12622           }
       
 12623         } else if (curShape === PConstants.TRIANGLE_FAN) {
       
 12624           if (vertArrayLength > 2) {
       
 12625             for (i = 0; i < 3; i++) {
       
 12626               cachedVertArray = vertArray[i];
       
 12627               for (j = 0; j < 3; j++) {
       
 12628                 lineVertArray.push(cachedVertArray[j]);
       
 12629               }
       
 12630             }
       
 12631             for (i = 0; i < 3; i++) {
       
 12632               cachedVertArray = vertArray[i];
       
 12633               for (j = 9; j < 13; j++) {
       
 12634                 strokeVertArray.push(cachedVertArray[j]);
       
 12635               }
       
 12636             }
       
 12637             if (doStroke) {
       
 12638               line3D(lineVertArray, "LINE_LOOP", strokeVertArray);
       
 12639             }
       
 12640 
       
 12641             for (i = 2; (i+1) < vertArrayLength; i++) {
       
 12642               lineVertArray = [];
       
 12643               strokeVertArray = [];
       
 12644               lineVertArray.push(vertArray[0][0]);
       
 12645               lineVertArray.push(vertArray[0][1]);
       
 12646               lineVertArray.push(vertArray[0][2]);
       
 12647 
       
 12648               strokeVertArray.push(vertArray[0][9]);
       
 12649               strokeVertArray.push(vertArray[0][10]);
       
 12650               strokeVertArray.push(vertArray[0][11]);
       
 12651               strokeVertArray.push(vertArray[0][12]);
       
 12652 
       
 12653               for (j = 0; j < 2; j++) {
       
 12654                 for (k = 0; k < 3; k++) {
       
 12655                   lineVertArray.push(vertArray[i+j][k]);
       
 12656                 }
       
 12657               }
       
 12658               for (j = 0; j < 2; j++) {
       
 12659                 for (k = 9; k < 13; k++) {
       
 12660                   strokeVertArray.push(vertArray[i+j][k]);
       
 12661                 }
       
 12662               }
       
 12663               if (doStroke) {
       
 12664                 line3D(lineVertArray, "LINE_STRIP",strokeVertArray);
       
 12665               }
       
 12666             }
       
 12667             if (doFill || usingTexture) {
       
 12668               fill3D(fillVertArray, "TRIANGLE_FAN", colorVertArray, texVertArray);
       
 12669             }
       
 12670           }
       
 12671         } else if (curShape === PConstants.QUADS) {
       
 12672           for (i = 0; (i + 3) < vertArrayLength; i+=4) {
       
 12673             lineVertArray = [];
       
 12674             for (j = 0; j < 4; j++) {
       
 12675               cachedVertArray = vertArray[i+j];
       
 12676               for (k = 0; k < 3; k++) {
       
 12677                 lineVertArray.push(cachedVertArray[k]);
       
 12678               }
       
 12679             }
       
 12680             if (doStroke) {
       
 12681               line3D(lineVertArray, "LINE_LOOP",strokeVertArray);
       
 12682             }
       
 12683 
       
 12684             if (doFill) {
       
 12685               fillVertArray = [];
       
 12686               colorVertArray = [];
       
 12687               texVertArray = [];
       
 12688               for (j = 0; j < 3; j++) {
       
 12689                 fillVertArray.push(vertArray[i][j]);
       
 12690               }
       
 12691               for (j = 5; j < 9; j++) {
       
 12692                 colorVertArray.push(vertArray[i][j]);
       
 12693               }
       
 12694 
       
 12695               for (j = 0; j < 3; j++) {
       
 12696                 fillVertArray.push(vertArray[i+1][j]);
       
 12697               }
       
 12698               for (j = 5; j < 9; j++) {
       
 12699                 colorVertArray.push(vertArray[i+1][j]);
       
 12700               }
       
 12701 
       
 12702               for (j = 0; j < 3; j++) {
       
 12703                 fillVertArray.push(vertArray[i+3][j]);
       
 12704               }
       
 12705               for (j = 5; j < 9; j++) {
       
 12706                 colorVertArray.push(vertArray[i+3][j]);
       
 12707               }
       
 12708 
       
 12709               for (j = 0; j < 3; j++) {
       
 12710                 fillVertArray.push(vertArray[i+2][j]);
       
 12711               }
       
 12712               for (j = 5; j < 9; j++) {
       
 12713                 colorVertArray.push(vertArray[i+2][j]);
       
 12714               }
       
 12715 
       
 12716               if (usingTexture) {
       
 12717                 texVertArray.push(vertArray[i+0][3]);
       
 12718                 texVertArray.push(vertArray[i+0][4]);
       
 12719                 texVertArray.push(vertArray[i+1][3]);
       
 12720                 texVertArray.push(vertArray[i+1][4]);
       
 12721                 texVertArray.push(vertArray[i+3][3]);
       
 12722                 texVertArray.push(vertArray[i+3][4]);
       
 12723                 texVertArray.push(vertArray[i+2][3]);
       
 12724                 texVertArray.push(vertArray[i+2][4]);
       
 12725               }
       
 12726 
       
 12727               fill3D(fillVertArray, "TRIANGLE_STRIP", colorVertArray, texVertArray);
       
 12728             }
       
 12729           }
       
 12730         } else if (curShape === PConstants.QUAD_STRIP) {
       
 12731           var tempArray = [];
       
 12732           if (vertArrayLength > 3) {
       
 12733             for (i = 0; i < 2; i++) {
       
 12734               cachedVertArray = vertArray[i];
       
 12735               for (j = 0; j < 3; j++) {
       
 12736                 lineVertArray.push(cachedVertArray[j]);
       
 12737               }
       
 12738             }
       
 12739 
       
 12740             for (i = 0; i < 2; i++) {
       
 12741               cachedVertArray = vertArray[i];
       
 12742               for (j = 9; j < 13; j++) {
       
 12743                 strokeVertArray.push(cachedVertArray[j]);
       
 12744               }
       
 12745             }
       
 12746 
       
 12747             line3D(lineVertArray, "LINE_STRIP", strokeVertArray);
       
 12748             if (vertArrayLength > 4 && vertArrayLength % 2 > 0) {
       
 12749               tempArray = fillVertArray.splice(fillVertArray.length - 3);
       
 12750               vertArray.pop();
       
 12751             }
       
 12752             for (i = 0; (i+3) < vertArrayLength; i+=2) {
       
 12753               lineVertArray = [];
       
 12754               strokeVertArray = [];
       
 12755               for (j = 0; j < 3; j++) {
       
 12756                 lineVertArray.push(vertArray[i+1][j]);
       
 12757               }
       
 12758               for (j = 0; j < 3; j++) {
       
 12759                 lineVertArray.push(vertArray[i+3][j]);
       
 12760               }
       
 12761               for (j = 0; j < 3; j++) {
       
 12762                 lineVertArray.push(vertArray[i+2][j]);
       
 12763               }
       
 12764               for (j = 0; j < 3; j++) {
       
 12765                 lineVertArray.push(vertArray[i+0][j]);
       
 12766               }
       
 12767               for (j = 9; j < 13; j++) {
       
 12768                 strokeVertArray.push(vertArray[i+1][j]);
       
 12769               }
       
 12770               for (j = 9; j < 13; j++) {
       
 12771                 strokeVertArray.push(vertArray[i+3][j]);
       
 12772               }
       
 12773               for (j = 9; j < 13; j++) {
       
 12774                 strokeVertArray.push(vertArray[i+2][j]);
       
 12775               }
       
 12776               for (j = 9; j < 13; j++) {
       
 12777                 strokeVertArray.push(vertArray[i+0][j]);
       
 12778               }
       
 12779               if (doStroke) {
       
 12780                 line3D(lineVertArray, "LINE_STRIP", strokeVertArray);
       
 12781               }
       
 12782             }
       
 12783 
       
 12784             if (doFill || usingTexture) {
       
 12785               fill3D(fillVertArray, "TRIANGLE_LIST", colorVertArray, texVertArray);
       
 12786             }
       
 12787           }
       
 12788         }
       
 12789         // If the user didn't specify a type (LINES, TRIANGLES, etc)
       
 12790         else {
       
 12791           // If only one vertex was specified, it must be a point
       
 12792           if (vertArrayLength === 1) {
       
 12793             for (j = 0; j < 3; j++) {
       
 12794               lineVertArray.push(vertArray[0][j]);
       
 12795             }
       
 12796             for (j = 9; j < 13; j++) {
       
 12797               strokeVertArray.push(vertArray[0][j]);
       
 12798             }
       
 12799             point3D(lineVertArray,strokeVertArray);
       
 12800           } else {
       
 12801             for (i = 0; i < vertArrayLength; i++) {
       
 12802               cachedVertArray = vertArray[i];
       
 12803               for (j = 0; j < 3; j++) {
       
 12804                 lineVertArray.push(cachedVertArray[j]);
       
 12805               }
       
 12806               for (j = 5; j < 9; j++) {
       
 12807                 strokeVertArray.push(cachedVertArray[j]);
       
 12808               }
       
 12809             }
       
 12810             if (doStroke && closeShape) {
       
 12811               line3D(lineVertArray, "LINE_LOOP", strokeVertArray);
       
 12812             } else if (doStroke && !closeShape) {
       
 12813               line3D(lineVertArray, "LINE_STRIP", strokeVertArray);
       
 12814             }
       
 12815 
       
 12816             // fill is ignored if textures are used
       
 12817             if (doFill || usingTexture) {
       
 12818               fill3D(fillVertArray, "TRIANGLE_FAN", colorVertArray, texVertArray);
       
 12819             }
       
 12820           }
       
 12821         }
       
 12822         // everytime beginShape is followed by a call to
       
 12823         // texture(), texturing it turned back on. We do this to
       
 12824         // figure out if the shape should be textured or filled
       
 12825         // with a color.
       
 12826         usingTexture = false;
       
 12827         curContext.useProgram(programObject3D);
       
 12828         uniformi("usingTexture3d", programObject3D, "usingTexture", usingTexture);
       
 12829       }
       
 12830 
       
 12831       // Reset some settings
       
 12832       isCurve = false;
       
 12833       isBezier = false;
       
 12834       curveVertArray = [];
       
 12835       curveVertCount = 0;
       
 12836     };
       
 12837 
       
 12838     /**
       
 12839      * The function splineForward() setup forward-differencing matrix to be used for speedy
       
 12840      * curve rendering. It's based on using a specific number
       
 12841      * of curve segments and just doing incremental adds for each
       
 12842      * vertex of the segment, rather than running the mathematically
       
 12843      * expensive cubic equation. This function is used by both curveDetail and bezierDetail.
       
 12844      *
       
 12845      * @param {int} segments      number of curve segments to use when drawing
       
 12846      * @param {PMatrix3D} matrix  target object for the new matrix
       
 12847      */
       
 12848     var splineForward = function(segments, matrix) {
       
 12849       var f = 1.0 / segments;
       
 12850       var ff = f * f;
       
 12851       var fff = ff * f;
       
 12852 
       
 12853       matrix.set(0, 0, 0, 1, fff, ff, f, 0, 6 * fff, 2 * ff, 0, 0, 6 * fff, 0, 0, 0);
       
 12854     };
       
 12855 
       
 12856     /**
       
 12857      * The curveInit() function set the number of segments to use when drawing a Catmull-Rom
       
 12858      * curve, and setting the s parameter, which defines how tightly
       
 12859      * the curve fits to each vertex. Catmull-Rom curves are actually
       
 12860      * a subset of this curve type where the s is set to zero.
       
 12861      * This in an internal function used by curveDetail() and curveTightness().
       
 12862      */
       
 12863     var curveInit = function() {
       
 12864       // allocate only if/when used to save startup time
       
 12865       if (!curveDrawMatrix) {
       
 12866         curveBasisMatrix = new PMatrix3D();
       
 12867         curveDrawMatrix = new PMatrix3D();
       
 12868         curveInited = true;
       
 12869       }
       
 12870 
       
 12871       var s = curTightness;
       
 12872       curveBasisMatrix.set((s - 1) / 2, (s + 3) / 2, (-3 - s) / 2, (1 - s) / 2,
       
 12873                            (1 - s), (-5 - s) / 2, (s + 2), (s - 1) / 2,
       
 12874                            (s - 1) / 2, 0, (1 - s) / 2, 0, 0, 1, 0, 0);
       
 12875 
       
 12876       splineForward(curveDet, curveDrawMatrix);
       
 12877 
       
 12878       if (!bezierBasisInverse) {
       
 12879         //bezierBasisInverse = bezierBasisMatrix.get();
       
 12880         //bezierBasisInverse.invert();
       
 12881         curveToBezierMatrix = new PMatrix3D();
       
 12882       }
       
 12883 
       
 12884       // TODO only needed for PGraphicsJava2D? if so, move it there
       
 12885       // actually, it's generally useful for other renderers, so keep it
       
 12886       // or hide the implementation elsewhere.
       
 12887       curveToBezierMatrix.set(curveBasisMatrix);
       
 12888       curveToBezierMatrix.preApply(bezierBasisInverse);
       
 12889 
       
 12890       // multiply the basis and forward diff matrices together
       
 12891       // saves much time since this needn't be done for each curve
       
 12892       curveDrawMatrix.apply(curveBasisMatrix);
       
 12893     };
       
 12894 
       
 12895     /**
       
 12896      * Specifies vertex coordinates for Bezier curves. Each call to <b>bezierVertex()</b> defines the position of two control
       
 12897      * points and one anchor point of a Bezier curve, adding a new segment to a line or shape. The first time
       
 12898      * <b>bezierVertex()</b> is used within a <b>beginShape()</b> call, it must be prefaced with a call to <b>vertex()</b>
       
 12899      * to set the first anchor point. This function must be used between <b>beginShape()</b> and <b>endShape()</b> and only
       
 12900      * when there is no MODE parameter specified to <b>beginShape()</b>. Using the 3D version of requires rendering with P3D
       
 12901      * or OPENGL (see the Environment reference for more information). <br /> <br /> <b>NOTE: </b> Fill does not work properly yet.
       
 12902      *
       
 12903      * @param {float | int} cx1 The x-coordinate of 1st control point
       
 12904      * @param {float | int} cy1 The y-coordinate of 1st control point
       
 12905      * @param {float | int} cz1 The z-coordinate of 1st control point
       
 12906      * @param {float | int} cx2 The x-coordinate of 2nd control point
       
 12907      * @param {float | int} cy2 The y-coordinate of 2nd control point
       
 12908      * @param {float | int} cz2 The z-coordinate of 2nd control point
       
 12909      * @param {float | int} x   The x-coordinate of the anchor point
       
 12910      * @param {float | int} y   The y-coordinate of the anchor point
       
 12911      * @param {float | int} z   The z-coordinate of the anchor point
       
 12912      *
       
 12913      * @see curveVertex
       
 12914      * @see vertex
       
 12915      * @see bezier
       
 12916      */
       
 12917     Drawing2D.prototype.bezierVertex = function() {
       
 12918       isBezier = true;
       
 12919       var vert = [];
       
 12920       if (firstVert) {
       
 12921         throw ("vertex() must be used at least once before calling bezierVertex()");
       
 12922       }
       
 12923 
       
 12924       for (var i = 0; i < arguments.length; i++) {
       
 12925         vert[i] = arguments[i];
       
 12926       }
       
 12927       vertArray.push(vert);
       
 12928       vertArray[vertArray.length -1]["isVert"] = false;
       
 12929     };
       
 12930 
       
 12931     Drawing3D.prototype.bezierVertex = function() {
       
 12932       isBezier = true;
       
 12933       var vert = [];
       
 12934       if (firstVert) {
       
 12935         throw ("vertex() must be used at least once before calling bezierVertex()");
       
 12936       }
       
 12937 
       
 12938       if (arguments.length === 9) {
       
 12939         if (bezierDrawMatrix === undef) {
       
 12940           bezierDrawMatrix = new PMatrix3D();
       
 12941         }
       
 12942         // setup matrix for forward differencing to speed up drawing
       
 12943         var lastPoint = vertArray.length - 1;
       
 12944         splineForward( bezDetail, bezierDrawMatrix );
       
 12945         bezierDrawMatrix.apply( bezierBasisMatrix );
       
 12946         var draw = bezierDrawMatrix.array();
       
 12947         var x1 = vertArray[lastPoint][0],
       
 12948             y1 = vertArray[lastPoint][1],
       
 12949             z1 = vertArray[lastPoint][2];
       
 12950         var xplot1 = draw[4] * x1 + draw[5] * arguments[0] + draw[6] * arguments[3] + draw[7] * arguments[6];
       
 12951         var xplot2 = draw[8] * x1 + draw[9] * arguments[0] + draw[10]* arguments[3] + draw[11]* arguments[6];
       
 12952         var xplot3 = draw[12]* x1 + draw[13]* arguments[0] + draw[14]* arguments[3] + draw[15]* arguments[6];
       
 12953 
       
 12954         var yplot1 = draw[4] * y1 + draw[5] * arguments[1] + draw[6] * arguments[4] + draw[7] * arguments[7];
       
 12955         var yplot2 = draw[8] * y1 + draw[9] * arguments[1] + draw[10]* arguments[4] + draw[11]* arguments[7];
       
 12956         var yplot3 = draw[12]* y1 + draw[13]* arguments[1] + draw[14]* arguments[4] + draw[15]* arguments[7];
       
 12957 
       
 12958         var zplot1 = draw[4] * z1 + draw[5] * arguments[2] + draw[6] * arguments[5] + draw[7] * arguments[8];
       
 12959         var zplot2 = draw[8] * z1 + draw[9] * arguments[2] + draw[10]* arguments[5] + draw[11]* arguments[8];
       
 12960         var zplot3 = draw[12]* z1 + draw[13]* arguments[2] + draw[14]* arguments[5] + draw[15]* arguments[8];
       
 12961         for (var j = 0; j < bezDetail; j++) {
       
 12962           x1 += xplot1; xplot1 += xplot2; xplot2 += xplot3;
       
 12963           y1 += yplot1; yplot1 += yplot2; yplot2 += yplot3;
       
 12964           z1 += zplot1; zplot1 += zplot2; zplot2 += zplot3;
       
 12965           p.vertex(x1, y1, z1);
       
 12966         }
       
 12967         p.vertex(arguments[6], arguments[7], arguments[8]);
       
 12968       }
       
 12969     };
       
 12970 
       
 12971     /**
       
 12972      * Sets a texture to be applied to vertex points. The <b>texture()</b> function
       
 12973      * must be called between <b>beginShape()</b> and <b>endShape()</b> and before
       
 12974      * any calls to vertex().
       
 12975      *
       
 12976      * When textures are in use, the fill color is ignored. Instead, use tint() to
       
 12977      * specify the color of the texture as it is applied to the shape.
       
 12978      *
       
 12979      * @param {PImage} pimage the texture to apply
       
 12980      *
       
 12981      * @returns none
       
 12982      *
       
 12983      * @see textureMode
       
 12984      * @see beginShape
       
 12985      * @see endShape
       
 12986      * @see vertex
       
 12987     */
       
 12988     p.texture = function(pimage) {
       
 12989       var curContext = drawing.$ensureContext();
       
 12990 
       
 12991       if (pimage.__texture) {
       
 12992         curContext.bindTexture(curContext.TEXTURE_2D, pimage.__texture);
       
 12993       } else if (pimage.localName === "canvas") {
       
 12994         curContext.bindTexture(curContext.TEXTURE_2D, canTex);
       
 12995         curContext.texImage2D(curContext.TEXTURE_2D, 0, curContext.RGBA, curContext.RGBA, curContext.UNSIGNED_BYTE, pimage);
       
 12996         curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_MAG_FILTER, curContext.LINEAR);
       
 12997         curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_MIN_FILTER, curContext.LINEAR);
       
 12998         curContext.generateMipmap(curContext.TEXTURE_2D);
       
 12999         curTexture.width = pimage.width;
       
 13000         curTexture.height = pimage.height;
       
 13001       } else {
       
 13002         var texture = curContext.createTexture(),
       
 13003             cvs = document.createElement('canvas'),
       
 13004             cvsTextureCtx = cvs.getContext('2d'),
       
 13005             pot;
       
 13006 
       
 13007         // WebGL requires power of two textures
       
 13008         if (pimage.width & (pimage.width-1) === 0) {
       
 13009           cvs.width = pimage.width;
       
 13010         } else {
       
 13011           pot = 1;
       
 13012           while (pot < pimage.width) {
       
 13013             pot *= 2;
       
 13014           }
       
 13015           cvs.width = pot;
       
 13016         }
       
 13017 
       
 13018         if (pimage.height & (pimage.height-1) === 0) {
       
 13019           cvs.height = pimage.height;
       
 13020         } else {
       
 13021           pot = 1;
       
 13022           while (pot < pimage.height) {
       
 13023             pot *= 2;
       
 13024           }
       
 13025           cvs.height = pot;
       
 13026         }
       
 13027 
       
 13028         cvsTextureCtx.drawImage(pimage.sourceImg, 0, 0, pimage.width, pimage.height, 0, 0, cvs.width, cvs.height);
       
 13029 
       
 13030         curContext.bindTexture(curContext.TEXTURE_2D, texture);
       
 13031         curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_MIN_FILTER, curContext.LINEAR_MIPMAP_LINEAR);
       
 13032         curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_MAG_FILTER, curContext.LINEAR);
       
 13033         curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_WRAP_T, curContext.CLAMP_TO_EDGE);
       
 13034         curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_WRAP_S, curContext.CLAMP_TO_EDGE);
       
 13035         curContext.texImage2D(curContext.TEXTURE_2D, 0, curContext.RGBA, curContext.RGBA, curContext.UNSIGNED_BYTE, cvs);
       
 13036         curContext.generateMipmap(curContext.TEXTURE_2D);
       
 13037 
       
 13038         pimage.__texture = texture;
       
 13039         curTexture.width = pimage.width;
       
 13040         curTexture.height = pimage.height;
       
 13041       }
       
 13042 
       
 13043       usingTexture = true;
       
 13044       curContext.useProgram(programObject3D);
       
 13045       uniformi("usingTexture3d", programObject3D, "usingTexture", usingTexture);
       
 13046     };
       
 13047 
       
 13048     /**
       
 13049      * Sets the coordinate space for texture mapping. There are two options, IMAGE,
       
 13050      * which refers to the actual coordinates of the image, and NORMALIZED, which
       
 13051      * refers to a normalized space of values ranging from 0 to 1. The default mode
       
 13052      * is IMAGE. In IMAGE, if an image is 100 x 200 pixels, mapping the image onto
       
 13053      * the entire size of a quad would require the points (0,0) (0,100) (100,200) (0,200).
       
 13054      * The same mapping in NORMAL_SPACE is (0,0) (0,1) (1,1) (0,1).
       
 13055      *
       
 13056      * @param MODE either IMAGE or NORMALIZED
       
 13057      *
       
 13058      * @returns none
       
 13059      *
       
 13060      * @see texture
       
 13061     */
       
 13062     p.textureMode = function(mode){
       
 13063       curTextureMode = mode;
       
 13064     };
       
 13065     /**
       
 13066      * The curveVertexSegment() function handle emitting a specific segment of Catmull-Rom curve. Internal helper function used by <b>curveVertex()</b>.
       
 13067      */
       
 13068     var curveVertexSegment = function(x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4) {
       
 13069       var x0 = x2;
       
 13070       var y0 = y2;
       
 13071       var z0 = z2;
       
 13072 
       
 13073       var draw = curveDrawMatrix.array();
       
 13074 
       
 13075       var xplot1 = draw[4] * x1 + draw[5] * x2 + draw[6] * x3 + draw[7] * x4;
       
 13076       var xplot2 = draw[8] * x1 + draw[9] * x2 + draw[10] * x3 + draw[11] * x4;
       
 13077       var xplot3 = draw[12] * x1 + draw[13] * x2 + draw[14] * x3 + draw[15] * x4;
       
 13078 
       
 13079       var yplot1 = draw[4] * y1 + draw[5] * y2 + draw[6] * y3 + draw[7] * y4;
       
 13080       var yplot2 = draw[8] * y1 + draw[9] * y2 + draw[10] * y3 + draw[11] * y4;
       
 13081       var yplot3 = draw[12] * y1 + draw[13] * y2 + draw[14] * y3 + draw[15] * y4;
       
 13082 
       
 13083       var zplot1 = draw[4] * z1 + draw[5] * z2 + draw[6] * z3 + draw[7] * z4;
       
 13084       var zplot2 = draw[8] * z1 + draw[9] * z2 + draw[10] * z3 + draw[11] * z4;
       
 13085       var zplot3 = draw[12] * z1 + draw[13] * z2 + draw[14] * z3 + draw[15] * z4;
       
 13086 
       
 13087       p.vertex(x0, y0, z0);
       
 13088       for (var j = 0; j < curveDet; j++) {
       
 13089         x0 += xplot1; xplot1 += xplot2; xplot2 += xplot3;
       
 13090         y0 += yplot1; yplot1 += yplot2; yplot2 += yplot3;
       
 13091         z0 += zplot1; zplot1 += zplot2; zplot2 += zplot3;
       
 13092         p.vertex(x0, y0, z0);
       
 13093       }
       
 13094     };
       
 13095 
       
 13096     /**
       
 13097      * Specifies vertex coordinates for curves. This function may only be used between <b>beginShape()</b> and
       
 13098      * <b>endShape()</b> and only when there is no MODE parameter specified to <b>beginShape()</b>. The first and last points
       
 13099      * in a series of <b>curveVertex()</b> lines will be used to guide the beginning and end of a the curve. A minimum of four
       
 13100      * points is required to draw a tiny curve between the second and third points. Adding a fifth point with
       
 13101      * <b>curveVertex()</b> will draw the curve between the second, third, and fourth points. The <b>curveVertex()</b> function
       
 13102      * is an implementation of Catmull-Rom splines. Using the 3D version of requires rendering with P3D or OPENGL (see the
       
 13103      * Environment reference for more information). <br /> <br /><b>NOTE: </b> Fill does not work properly yet.
       
 13104      *
       
 13105      * @param {float | int} x The x-coordinate of the vertex
       
 13106      * @param {float | int} y The y-coordinate of the vertex
       
 13107      * @param {float | int} z The z-coordinate of the vertex
       
 13108      *
       
 13109      * @see curve
       
 13110      * @see beginShape
       
 13111      * @see endShape
       
 13112      * @see vertex
       
 13113      * @see bezierVertex
       
 13114      */
       
 13115     Drawing2D.prototype.curveVertex = function(x, y) {
       
 13116       isCurve = true;
       
 13117 
       
 13118       p.vertex(x, y);
       
 13119     };
       
 13120 
       
 13121     Drawing3D.prototype.curveVertex = function(x, y, z) {
       
 13122       isCurve = true;
       
 13123 
       
 13124       if (!curveInited) {
       
 13125         curveInit();
       
 13126       }
       
 13127       var vert = [];
       
 13128       vert[0] = x;
       
 13129       vert[1] = y;
       
 13130       vert[2] = z;
       
 13131       curveVertArray.push(vert);
       
 13132       curveVertCount++;
       
 13133 
       
 13134       if (curveVertCount > 3) {
       
 13135         curveVertexSegment( curveVertArray[curveVertCount-4][0],
       
 13136                             curveVertArray[curveVertCount-4][1],
       
 13137                             curveVertArray[curveVertCount-4][2],
       
 13138                             curveVertArray[curveVertCount-3][0],
       
 13139                             curveVertArray[curveVertCount-3][1],
       
 13140                             curveVertArray[curveVertCount-3][2],
       
 13141                             curveVertArray[curveVertCount-2][0],
       
 13142                             curveVertArray[curveVertCount-2][1],
       
 13143                             curveVertArray[curveVertCount-2][2],
       
 13144                             curveVertArray[curveVertCount-1][0],
       
 13145                             curveVertArray[curveVertCount-1][1],
       
 13146                             curveVertArray[curveVertCount-1][2] );
       
 13147       }
       
 13148     };
       
 13149 
       
 13150     /**
       
 13151      * The curve() function draws a curved line on the screen. The first and second parameters
       
 13152      * specify the beginning control point and the last two parameters specify
       
 13153      * the ending control point. The middle parameters specify the start and
       
 13154      * stop of the curve. Longer curves can be created by putting a series of
       
 13155      * <b>curve()</b> functions together or using <b>curveVertex()</b>.
       
 13156      * An additional function called <b>curveTightness()</b> provides control
       
 13157      * for the visual quality of the curve. The <b>curve()</b> function is an
       
 13158      * implementation of Catmull-Rom splines. Using the 3D version of requires
       
 13159      * rendering with P3D or OPENGL (see the Environment reference for more
       
 13160      * information).
       
 13161      *
       
 13162      * @param {int|float} x1 coordinates for the beginning control point
       
 13163      * @param {int|float} y1 coordinates for the beginning control point
       
 13164      * @param {int|float} z1 coordinates for the beginning control point
       
 13165      * @param {int|float} x2 coordinates for the first point
       
 13166      * @param {int|float} y2 coordinates for the first point
       
 13167      * @param {int|float} z2 coordinates for the first point
       
 13168      * @param {int|float} x3 coordinates for the second point
       
 13169      * @param {int|float} y3 coordinates for the second point
       
 13170      * @param {int|float} z3 coordinates for the second point
       
 13171      * @param {int|float} x4 coordinates for the ending control point
       
 13172      * @param {int|float} y4 coordinates for the ending control point
       
 13173      * @param {int|float} z4 coordinates for the ending control point
       
 13174      *
       
 13175      * @see #curveVertex()
       
 13176      * @see #curveTightness()
       
 13177      * @see #bezier()
       
 13178      */
       
 13179     Drawing2D.prototype.curve = function() {
       
 13180       if (arguments.length === 8) { // curve(x1, y1, x2, y2, x3, y3, x4, y4)
       
 13181         p.beginShape();
       
 13182         p.curveVertex(arguments[0], arguments[1]);
       
 13183         p.curveVertex(arguments[2], arguments[3]);
       
 13184         p.curveVertex(arguments[4], arguments[5]);
       
 13185         p.curveVertex(arguments[6], arguments[7]);
       
 13186         p.endShape();
       
 13187       }
       
 13188     };
       
 13189 
       
 13190     Drawing3D.prototype.curve = function() {
       
 13191       if (arguments.length === 12) { // curve( x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4);
       
 13192         p.beginShape();
       
 13193         p.curveVertex(arguments[0], arguments[1], arguments[2]);
       
 13194         p.curveVertex(arguments[3], arguments[4], arguments[5]);
       
 13195         p.curveVertex(arguments[6], arguments[7], arguments[8]);
       
 13196         p.curveVertex(arguments[9], arguments[10], arguments[11]);
       
 13197         p.endShape();
       
 13198       }
       
 13199     };
       
 13200 
       
 13201     /**
       
 13202      * The curveTightness() function modifies the quality of forms created with <b>curve()</b> and
       
 13203      * <b>curveVertex()</b>. The parameter <b>squishy</b> determines how the
       
 13204      * curve fits to the vertex points. The value 0.0 is the default value for
       
 13205      * <b>squishy</b> (this value defines the curves to be Catmull-Rom splines)
       
 13206      * and the value 1.0 connects all the points with straight lines.
       
 13207      * Values within the range -5.0 and 5.0 will deform the curves but
       
 13208      * will leave them recognizable and as values increase in magnitude,
       
 13209      * they will continue to deform.
       
 13210      *
       
 13211      * @param {float} tightness amount of deformation from the original vertices
       
 13212      *
       
 13213      * @see #curve()
       
 13214      * @see #curveVertex()
       
 13215      *
       
 13216      */
       
 13217     p.curveTightness = function(tightness) {
       
 13218       curTightness = tightness;
       
 13219     };
       
 13220 
       
 13221     /**
       
 13222      * The curveDetail() function sets the resolution at which curves display. The default value is 20.
       
 13223      * This function is only useful when using the P3D or OPENGL renderer.
       
 13224      *
       
 13225      * @param {int} detail resolution of the curves
       
 13226      *
       
 13227      * @see curve()
       
 13228      * @see curveVertex()
       
 13229      * @see curveTightness()
       
 13230      */
       
 13231     p.curveDetail = function(detail) {
       
 13232       curveDet = detail;
       
 13233       curveInit();
       
 13234     };
       
 13235 
       
 13236     /**
       
 13237     * Modifies the location from which rectangles draw. The default mode is rectMode(CORNER), which
       
 13238     * specifies the location to be the upper left corner of the shape and uses the third and fourth
       
 13239     * parameters of rect() to specify the width and height. The syntax rectMode(CORNERS) uses the
       
 13240     * first and second parameters of rect() to set the location of one corner and uses the third and
       
 13241     * fourth parameters to set the opposite corner. The syntax rectMode(CENTER) draws the image from
       
 13242     * its center point and uses the third and forth parameters of rect() to specify the image's width
       
 13243     * and height. The syntax rectMode(RADIUS) draws the image from its center point and uses the third
       
 13244     * and forth parameters of rect()  to specify half of the image's width and height. The parameter must
       
 13245     * be written in ALL CAPS because Processing is a case sensitive language. Note: In version 125, the
       
 13246     * mode named CENTER_RADIUS was shortened to RADIUS.
       
 13247     *
       
 13248     * @param {MODE} MODE      Either CORNER, CORNERS, CENTER, or RADIUS
       
 13249     *
       
 13250     * @see rect
       
 13251     */
       
 13252     p.rectMode = function(aRectMode) {
       
 13253       curRectMode = aRectMode;
       
 13254     };
       
 13255 
       
 13256     /**
       
 13257     * Modifies the location from which images draw. The default mode is imageMode(CORNER), which specifies
       
 13258     * the location to be the upper left corner and uses the fourth and fifth parameters of image() to set
       
 13259     * the image's width and height. The syntax imageMode(CORNERS) uses the second and third parameters of
       
 13260     * image() to set the location of one corner of the image and uses the fourth and fifth parameters to
       
 13261     * set the opposite corner. Use imageMode(CENTER) to draw images centered at the given x and y position.
       
 13262     * The parameter to imageMode() must be written in ALL CAPS because Processing is a case sensitive language.
       
 13263     *
       
 13264     * @param {MODE} MODE      Either CORNER, CORNERS, or CENTER
       
 13265     *
       
 13266     * @see loadImage
       
 13267     * @see PImage
       
 13268     * @see image
       
 13269     * @see background
       
 13270     */
       
 13271     p.imageMode = function(mode) {
       
 13272       switch (mode) {
       
 13273       case PConstants.CORNER:
       
 13274         imageModeConvert = imageModeCorner;
       
 13275         break;
       
 13276       case PConstants.CORNERS:
       
 13277         imageModeConvert = imageModeCorners;
       
 13278         break;
       
 13279       case PConstants.CENTER:
       
 13280         imageModeConvert = imageModeCenter;
       
 13281         break;
       
 13282       default:
       
 13283         throw "Invalid imageMode";
       
 13284       }
       
 13285     };
       
 13286 
       
 13287     /**
       
 13288     * The origin of the ellipse is modified by the ellipseMode() function. The default configuration is
       
 13289     * ellipseMode(CENTER), which specifies the location of the ellipse as the center of the shape. The RADIUS
       
 13290     * mode is the same, but the width and height parameters to ellipse()  specify the radius of the ellipse,
       
 13291     * rather than the diameter. The CORNER mode draws the shape from the upper-left corner of its bounding box.
       
 13292     * The CORNERS mode uses the four parameters to ellipse() to set two opposing corners of the ellipse's bounding
       
 13293     * box. The parameter must be written in "ALL CAPS" because Processing is a case sensitive language.
       
 13294     *
       
 13295     * @param {MODE} MODE      Either CENTER, RADIUS, CORNER, or CORNERS.
       
 13296     *
       
 13297     * @see ellipse
       
 13298     */
       
 13299     p.ellipseMode = function(aEllipseMode) {
       
 13300       curEllipseMode = aEllipseMode;
       
 13301     };
       
 13302 
       
 13303     /**
       
 13304      * The arc() function draws an arc in the display window.
       
 13305      * Arcs are drawn along the outer edge of an ellipse defined by the
       
 13306      * <b>x</b>, <b>y</b>, <b>width</b> and <b>height</b> parameters.
       
 13307      * The origin or the arc's ellipse may be changed with the
       
 13308      * <b>ellipseMode()</b> function.
       
 13309      * The <b>start</b> and <b>stop</b> parameters specify the angles
       
 13310      * at which to draw the arc.
       
 13311      *
       
 13312      * @param {float} a       x-coordinate of the arc's ellipse
       
 13313      * @param {float} b       y-coordinate of the arc's ellipse
       
 13314      * @param {float} c       width of the arc's ellipse
       
 13315      * @param {float} d       height of the arc's ellipse
       
 13316      * @param {float} start   angle to start the arc, specified in radians
       
 13317      * @param {float} stop    angle to stop the arc, specified in radians
       
 13318      *
       
 13319      * @see #ellipseMode()
       
 13320      * @see #ellipse()
       
 13321      */
       
 13322     p.arc = function(x, y, width, height, start, stop) {
       
 13323       if (width <= 0 || stop < start) { return; }
       
 13324 
       
 13325       if (curEllipseMode === PConstants.CORNERS) {
       
 13326         width = width - x;
       
 13327         height = height - y;
       
 13328       } else if (curEllipseMode === PConstants.RADIUS) {
       
 13329         x = x - width;
       
 13330         y = y - height;
       
 13331         width = width * 2;
       
 13332         height = height * 2;
       
 13333       } else if (curEllipseMode === PConstants.CENTER) {
       
 13334         x = x - width/2;
       
 13335         y = y - height/2;
       
 13336       }
       
 13337       // make sure that we're starting at a useful point
       
 13338       while (start < 0) {
       
 13339         start += PConstants.TWO_PI;
       
 13340         stop += PConstants.TWO_PI;
       
 13341       }
       
 13342       if (stop - start > PConstants.TWO_PI) {
       
 13343         start = 0;
       
 13344         stop = PConstants.TWO_PI;
       
 13345       }
       
 13346       var hr = width / 2;
       
 13347       var vr = height / 2;
       
 13348       var centerX = x + hr;
       
 13349       var centerY = y + vr;
       
 13350       var startLUT = 0 | (-0.5 + start * p.RAD_TO_DEG * 2);
       
 13351       var stopLUT  = 0 | (0.5 + stop * p.RAD_TO_DEG * 2);
       
 13352       var i, j;
       
 13353       if (doFill) {
       
 13354         // shut off stroke for a minute
       
 13355         var savedStroke = doStroke;
       
 13356         doStroke = false;
       
 13357         p.beginShape();
       
 13358         p.vertex(centerX, centerY);
       
 13359         for (i = startLUT; i <= stopLUT; i++) {
       
 13360           j = i % PConstants.SINCOS_LENGTH;
       
 13361           p.vertex(centerX + cosLUT[j] * hr, centerY + sinLUT[j] * vr);
       
 13362         }
       
 13363         p.endShape(PConstants.CLOSE);
       
 13364         doStroke = savedStroke;
       
 13365       }
       
 13366 
       
 13367       if (doStroke) {
       
 13368         // and doesn't include the first (center) vertex.
       
 13369         var savedFill = doFill;
       
 13370         doFill = false;
       
 13371         p.beginShape();
       
 13372         for (i = startLUT; i <= stopLUT; i++) {
       
 13373           j = i % PConstants.SINCOS_LENGTH;
       
 13374           p.vertex(centerX + cosLUT[j] * hr, centerY + sinLUT[j] * vr);
       
 13375         }
       
 13376         p.endShape();
       
 13377         doFill = savedFill;
       
 13378       }
       
 13379     };
       
 13380 
       
 13381     /**
       
 13382     * Draws a line (a direct path between two points) to the screen. The version of line() with four parameters
       
 13383     * draws the line in 2D. To color a line, use the stroke() function. A line cannot be filled, therefore the
       
 13384     * fill()  method will not affect the color of a line. 2D lines are drawn with a width of one pixel by default,
       
 13385     * but this can be changed with the strokeWeight()  function. The version with six parameters allows the line
       
 13386     * to be placed anywhere within XYZ space. Drawing this shape in 3D using the z parameter requires the P3D or
       
 13387     * OPENGL parameter in combination with size.
       
 13388     *
       
 13389     * @param {int|float} x1       x-coordinate of the first point
       
 13390     * @param {int|float} y1       y-coordinate of the first point
       
 13391     * @param {int|float} z1       z-coordinate of the first point
       
 13392     * @param {int|float} x2       x-coordinate of the second point
       
 13393     * @param {int|float} y2       y-coordinate of the second point
       
 13394     * @param {int|float} z2       z-coordinate of the second point
       
 13395     *
       
 13396     * @see strokeWeight
       
 13397     * @see strokeJoin
       
 13398     * @see strokeCap
       
 13399     * @see beginShape
       
 13400     */
       
 13401     Drawing2D.prototype.line = function(x1, y1, x2, y2) {
       
 13402       if (!doStroke) {
       
 13403         return;
       
 13404       }
       
 13405       x1 = Math.round(x1);
       
 13406       x2 = Math.round(x2);
       
 13407       y1 = Math.round(y1);
       
 13408       y2 = Math.round(y2);
       
 13409       // A line is only defined if it has different start and end coordinates.
       
 13410       // If they are the same, we call point instead.
       
 13411       if (x1 === x2 && y1 === y2) {
       
 13412         p.point(x1, y1);
       
 13413         return;
       
 13414       }
       
 13415 
       
 13416       var swap = undef,
       
 13417           lineCap = undef,
       
 13418           drawCrisp = true,
       
 13419           currentModelView = modelView.array(),
       
 13420           identityMatrix = [1, 0, 0, 0, 1, 0];
       
 13421       // Test if any transformations have been applied to the sketch
       
 13422       for (var i = 0; i < 6 && drawCrisp; i++) {
       
 13423         drawCrisp = currentModelView[i] === identityMatrix[i];
       
 13424       }
       
 13425       /* Draw crisp lines if the line is vertical or horizontal with the following method
       
 13426        * If any transformations have been applied to the sketch, don't make the line crisp
       
 13427        * If the line is directed up or to the left, reverse it by swapping x1/x2 or y1/y2
       
 13428        * Make the line 1 pixel longer to work around cross-platform canvas implementations
       
 13429        * If the lineWidth is odd, translate the line by 0.5 in the perpendicular direction
       
 13430        * Even lineWidths do not need to be translated because the canvas will draw them on pixel boundaries
       
 13431        * Change the cap to butt-end to work around cross-platform canvas implementations
       
 13432        * Reverse the translate and lineCap canvas state changes after drawing the line
       
 13433        */
       
 13434       if (drawCrisp) {
       
 13435         if (x1 === x2) {
       
 13436           if (y1 > y2) {
       
 13437             swap = y1;
       
 13438             y1 = y2;
       
 13439             y2 = swap;
       
 13440           }
       
 13441           y2++;
       
 13442           if (lineWidth % 2 === 1) {
       
 13443             curContext.translate(0.5, 0.0);
       
 13444           }
       
 13445         } else if (y1 === y2) {
       
 13446           if (x1 > x2) {
       
 13447             swap = x1;
       
 13448             x1 = x2;
       
 13449             x2 = swap;
       
 13450           }
       
 13451           x2++;
       
 13452           if (lineWidth % 2 === 1) {
       
 13453             curContext.translate(0.0, 0.5);
       
 13454           }
       
 13455         }
       
 13456         if (lineWidth === 1) {
       
 13457           lineCap = curContext.lineCap;
       
 13458           curContext.lineCap = 'butt';
       
 13459         }
       
 13460       }
       
 13461       curContext.beginPath();
       
 13462       curContext.moveTo(x1 || 0, y1 || 0);
       
 13463       curContext.lineTo(x2 || 0, y2 || 0);
       
 13464       executeContextStroke();
       
 13465       if (drawCrisp) {
       
 13466         if (x1 === x2 && lineWidth % 2 === 1) {
       
 13467           curContext.translate(-0.5, 0.0);
       
 13468         } else if (y1 === y2 && lineWidth % 2 === 1) {
       
 13469           curContext.translate(0.0, -0.5);
       
 13470         }
       
 13471         if (lineWidth === 1) {
       
 13472           curContext.lineCap = lineCap;
       
 13473         }
       
 13474       }
       
 13475     };
       
 13476 
       
 13477     Drawing3D.prototype.line = function(x1, y1, z1, x2, y2, z2) {
       
 13478       if (y2 === undef || z2 === undef) { // 2D line called in 3D context
       
 13479         z2 = 0;
       
 13480         y2 = x2;
       
 13481         x2 = z1;
       
 13482         z1 = 0;
       
 13483       }
       
 13484 
       
 13485       // a line is only defined if it has different start and end coordinates.
       
 13486       // If they are the same, we call point instead.
       
 13487       if (x1===x2 && y1===y2 && z1===z2) {
       
 13488         p.point(x1,y1,z1);
       
 13489         return;
       
 13490       }
       
 13491 
       
 13492       var lineVerts = [x1, y1, z1, x2, y2, z2];
       
 13493 
       
 13494       var view = new PMatrix3D();
       
 13495       view.scale(1, -1, 1);
       
 13496       view.apply(modelView.array());
       
 13497       view.transpose();
       
 13498 
       
 13499       if (lineWidth > 0 && doStroke) {
       
 13500         curContext.useProgram(programObject2D);
       
 13501 
       
 13502         uniformMatrix("model2d", programObject2D, "model", false, [1,0,0,0,  0,1,0,0,  0,0,1,0,  0,0,0,1]);
       
 13503         uniformMatrix("view2d", programObject2D, "view", false, view.array());
       
 13504 
       
 13505         uniformf("color2d", programObject2D, "color", strokeStyle);
       
 13506         uniformi("picktype2d", programObject2D, "picktype", 0);
       
 13507 
       
 13508         vertexAttribPointer("vertex2d", programObject2D, "Vertex", 3, lineBuffer);
       
 13509         disableVertexAttribPointer("aTextureCoord2d", programObject2D, "aTextureCoord");
       
 13510 
       
 13511         curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array(lineVerts), curContext.STREAM_DRAW);
       
 13512         curContext.drawArrays(curContext.LINES, 0, 2);
       
 13513       }
       
 13514     };
       
 13515 
       
 13516     /**
       
 13517      * Draws a Bezier curve on the screen. These curves are defined by a series of anchor and control points. The first
       
 13518      * two parameters specify the first anchor point and the last two parameters specify the other anchor point. The
       
 13519      * middle parameters specify the control points which define the shape of the curve. Bezier curves were developed
       
 13520      * by French engineer Pierre Bezier. Using the 3D version of requires rendering with P3D or OPENGL (see the
       
 13521      * Environment reference for more information).
       
 13522      *
       
 13523      * @param {int | float} x1,y1,z1    coordinates for the first anchor point
       
 13524      * @param {int | float} cx1,cy1,cz1 coordinates for the first control point
       
 13525      * @param {int | float} cx2,cy2,cz2 coordinates for the second control point
       
 13526      * @param {int | float} x2,y2,z2    coordinates for the second anchor point
       
 13527      *
       
 13528      * @see bezierVertex
       
 13529      * @see curve
       
 13530      */
       
 13531     Drawing2D.prototype.bezier = function() {
       
 13532       if (arguments.length !== 8) {
       
 13533         throw("You must use 8 parameters for bezier() in 2D mode");
       
 13534       }
       
 13535 
       
 13536       p.beginShape();
       
 13537       p.vertex( arguments[0], arguments[1] );
       
 13538       p.bezierVertex( arguments[2], arguments[3],
       
 13539                       arguments[4], arguments[5],
       
 13540                       arguments[6], arguments[7] );
       
 13541       p.endShape();
       
 13542     };
       
 13543 
       
 13544     Drawing3D.prototype.bezier = function() {
       
 13545       if (arguments.length !== 12) {
       
 13546         throw("You must use 12 parameters for bezier() in 3D mode");
       
 13547       }
       
 13548 
       
 13549       p.beginShape();
       
 13550       p.vertex( arguments[0], arguments[1], arguments[2] );
       
 13551       p.bezierVertex( arguments[3], arguments[4], arguments[5],
       
 13552                       arguments[6], arguments[7], arguments[8],
       
 13553                       arguments[9], arguments[10], arguments[11] );
       
 13554       p.endShape();
       
 13555     };
       
 13556 
       
 13557     /**
       
 13558      * Sets the resolution at which Beziers display. The default value is 20. This function is only useful when using the P3D
       
 13559      * or OPENGL renderer as the default (JAVA2D) renderer does not use this information.
       
 13560      *
       
 13561      * @param {int} detail resolution of the curves
       
 13562      *
       
 13563      * @see curve
       
 13564      * @see curveVertex
       
 13565      * @see curveTightness
       
 13566      */
       
 13567     p.bezierDetail = function( detail ){
       
 13568       bezDetail = detail;
       
 13569     };
       
 13570 
       
 13571     /**
       
 13572      * The bezierPoint() function evalutes quadratic bezier at point t for points a, b, c, d.
       
 13573      * The parameter t varies between 0 and 1. The a and d parameters are the
       
 13574      * on-curve points, b and c are the control points. To make a two-dimensional
       
 13575      * curve, call this function once with the x coordinates and a second time
       
 13576      * with the y coordinates to get the location of a bezier curve at t.
       
 13577      *
       
 13578      * @param {float} a   coordinate of first point on the curve
       
 13579      * @param {float} b   coordinate of first control point
       
 13580      * @param {float} c   coordinate of second control point
       
 13581      * @param {float} d   coordinate of second point on the curve
       
 13582      * @param {float} t   value between 0 and 1
       
 13583      *
       
 13584      * @see #bezier()
       
 13585      * @see #bezierVertex()
       
 13586      * @see #curvePoint()
       
 13587      */
       
 13588     p.bezierPoint = function(a, b, c, d, t) {
       
 13589       return (1 - t) * (1 - t) * (1 - t) * a + 3 * (1 - t) * (1 - t) * t * b + 3 * (1 - t) * t * t * c + t * t * t * d;
       
 13590     };
       
 13591 
       
 13592     /**
       
 13593      * The bezierTangent() function calculates the tangent of a point on a Bezier curve. There is a good
       
 13594      * definition of "tangent" at Wikipedia: <a href="http://en.wikipedia.org/wiki/Tangent" target="new">http://en.wikipedia.org/wiki/Tangent</a>
       
 13595      *
       
 13596      * @param {float} a   coordinate of first point on the curve
       
 13597      * @param {float} b   coordinate of first control point
       
 13598      * @param {float} c   coordinate of second control point
       
 13599      * @param {float} d   coordinate of second point on the curve
       
 13600      * @param {float} t   value between 0 and 1
       
 13601      *
       
 13602      * @see #bezier()
       
 13603      * @see #bezierVertex()
       
 13604      * @see #curvePoint()
       
 13605      */
       
 13606     p.bezierTangent = function(a, b, c, d, t) {
       
 13607       return (3 * t * t * (-a + 3 * b - 3 * c + d) + 6 * t * (a - 2 * b + c) + 3 * (-a + b));
       
 13608     };
       
 13609 
       
 13610     /**
       
 13611      * The curvePoint() function evalutes the Catmull-Rom curve at point t for points a, b, c, d. The
       
 13612      * parameter t varies between 0 and 1, a and d are points on the curve,
       
 13613      * and b and c are the control points. This can be done once with the x
       
 13614      * coordinates and a second time with the y coordinates to get the
       
 13615      * location of a curve at t.
       
 13616      *
       
 13617      * @param {int|float} a   coordinate of first point on the curve
       
 13618      * @param {int|float} b   coordinate of second point on the curve
       
 13619      * @param {int|float} c   coordinate of third point on the curve
       
 13620      * @param {int|float} d   coordinate of fourth point on the curve
       
 13621      * @param {float} t       value between 0 and 1
       
 13622      *
       
 13623      * @see #curve()
       
 13624      * @see #curveVertex()
       
 13625      * @see #bezierPoint()
       
 13626      */
       
 13627     p.curvePoint = function(a, b, c, d, t) {
       
 13628       return 0.5 * ((2 * b) + (-a + c) * t + (2 * a - 5 * b + 4 * c - d) * t * t + (-a + 3 * b - 3 * c + d) * t * t * t);
       
 13629     };
       
 13630 
       
 13631     /**
       
 13632      * The curveTangent() function calculates the tangent of a point on a Catmull-Rom curve.
       
 13633      * There is a good definition of "tangent" at Wikipedia: <a href="http://en.wikipedia.org/wiki/Tangent" target="new">http://en.wikipedia.org/wiki/Tangent</a>.
       
 13634      *
       
 13635      * @param {int|float} a   coordinate of first point on the curve
       
 13636      * @param {int|float} b   coordinate of first control point
       
 13637      * @param {int|float} c   coordinate of second control point
       
 13638      * @param {int|float} d   coordinate of second point on the curve
       
 13639      * @param {float} t       value between 0 and 1
       
 13640      *
       
 13641      * @see #curve()
       
 13642      * @see #curveVertex()
       
 13643      * @see #curvePoint()
       
 13644      * @see #bezierTangent()
       
 13645      */
       
 13646     p.curveTangent = function(a, b, c, d, t) {
       
 13647       return 0.5 * ((-a + c) + 2 * (2 * a - 5 * b + 4 * c - d) * t + 3 * (-a + 3 * b - 3 * c + d) * t * t);
       
 13648     };
       
 13649 
       
 13650     /**
       
 13651      * A triangle is a plane created by connecting three points. The first two arguments specify the first point,
       
 13652      * the middle two arguments specify the second point, and the last two arguments specify the third point.
       
 13653      *
       
 13654      * @param {int | float} x1 x-coordinate of the first point
       
 13655      * @param {int | float} y1 y-coordinate of the first point
       
 13656      * @param {int | float} x2 x-coordinate of the second point
       
 13657      * @param {int | float} y2 y-coordinate of the second point
       
 13658      * @param {int | float} x3 x-coordinate of the third point
       
 13659      * @param {int | float} y3 y-coordinate of the third point
       
 13660      */
       
 13661     p.triangle = function(x1, y1, x2, y2, x3, y3) {
       
 13662       p.beginShape(PConstants.TRIANGLES);
       
 13663       p.vertex(x1, y1, 0);
       
 13664       p.vertex(x2, y2, 0);
       
 13665       p.vertex(x3, y3, 0);
       
 13666       p.endShape();
       
 13667     };
       
 13668 
       
 13669     /**
       
 13670      * A quad is a quadrilateral, a four sided polygon. It is similar to a rectangle, but the angles between its
       
 13671      * edges are not constrained to ninety degrees. The first pair of parameters (x1,y1) sets the first vertex
       
 13672      * and the subsequent pairs should proceed clockwise or counter-clockwise around the defined shape.
       
 13673      *
       
 13674      * @param {float | int} x1 x-coordinate of the first corner
       
 13675      * @param {float | int} y1 y-coordinate of the first corner
       
 13676      * @param {float | int} x2 x-coordinate of the second corner
       
 13677      * @param {float | int} y2 y-coordinate of the second corner
       
 13678      * @param {float | int} x3 x-coordinate of the third corner
       
 13679      * @param {float | int} y3 y-coordinate of the third corner
       
 13680      * @param {float | int} x4 x-coordinate of the fourth corner
       
 13681      * @param {float | int} y4 y-coordinate of the fourth corner
       
 13682      */
       
 13683     p.quad = function(x1, y1, x2, y2, x3, y3, x4, y4) {
       
 13684       p.beginShape(PConstants.QUADS);
       
 13685       p.vertex(x1, y1, 0);
       
 13686       p.vertex(x2, y2, 0);
       
 13687       p.vertex(x3, y3, 0);
       
 13688       p.vertex(x4, y4, 0);
       
 13689       p.endShape();
       
 13690     };
       
 13691 
       
 13692     var roundedRect$2d = function(x, y, width, height, tl, tr, br, bl) {
       
 13693       if (bl === undef) {
       
 13694         tr = tl;
       
 13695         br = tl;
       
 13696         bl = tl;
       
 13697       }
       
 13698       var halfWidth = width / 2, 
       
 13699           halfHeight = height / 2;
       
 13700       if (tl > halfWidth || tl > halfHeight) {
       
 13701         tl = Math.min(halfWidth, halfHeight);
       
 13702       }
       
 13703       if (tr > halfWidth || tr > halfHeight) {
       
 13704         tr = Math.min(halfWidth, halfHeight);
       
 13705       }
       
 13706       if (br > halfWidth || br > halfHeight) {
       
 13707         br = Math.min(halfWidth, halfHeight);
       
 13708       }
       
 13709       if (bl > halfWidth || bl > halfHeight) {
       
 13710         bl = Math.min(halfWidth, halfHeight);
       
 13711       }
       
 13712       // Translate the stroke by (0.5, 0.5) to draw a crisp border
       
 13713       if (!doFill || doStroke) {
       
 13714         curContext.translate(0.5, 0.5);
       
 13715       }
       
 13716       curContext.beginPath();
       
 13717       curContext.moveTo(x + tl, y);
       
 13718       curContext.lineTo(x + width - tr, y);
       
 13719       curContext.quadraticCurveTo(x + width, y, x + width, y + tr);
       
 13720       curContext.lineTo(x + width, y + height - br);
       
 13721       curContext.quadraticCurveTo(x + width, y + height, x + width - br, y + height);
       
 13722       curContext.lineTo(x + bl, y + height);
       
 13723       curContext.quadraticCurveTo(x, y + height, x, y + height - bl);
       
 13724       curContext.lineTo(x, y + tl);
       
 13725       curContext.quadraticCurveTo(x, y, x + tl, y);
       
 13726       if (!doFill || doStroke) {
       
 13727         curContext.translate(-0.5, -0.5);
       
 13728       }
       
 13729       executeContextFill();
       
 13730       executeContextStroke();
       
 13731     };
       
 13732 
       
 13733     /**
       
 13734     * Draws a rectangle to the screen. A rectangle is a four-sided shape with every angle at ninety
       
 13735     * degrees. The first two parameters set the location, the third sets the width, and the fourth
       
 13736     * sets the height. The origin is changed with the rectMode() function.
       
 13737     *
       
 13738     * @param {int|float} x        x-coordinate of the rectangle
       
 13739     * @param {int|float} y        y-coordinate of the rectangle
       
 13740     * @param {int|float} width    width of the rectangle
       
 13741     * @param {int|float} height   height of the rectangle
       
 13742     *
       
 13743     * @see rectMode
       
 13744     * @see quad
       
 13745     */
       
 13746     Drawing2D.prototype.rect = function(x, y, width, height, tl, tr, br, bl) {
       
 13747       if (!width && !height) {
       
 13748         return;
       
 13749       }
       
 13750 
       
 13751       if (curRectMode === PConstants.CORNERS) {
       
 13752         width -= x;
       
 13753         height -= y;
       
 13754       } else if (curRectMode === PConstants.RADIUS) {
       
 13755         width *= 2;
       
 13756         height *= 2;
       
 13757         x -= width / 2;
       
 13758         y -= height / 2;
       
 13759       } else if (curRectMode === PConstants.CENTER) {
       
 13760         x -= width / 2;
       
 13761         y -= height / 2;
       
 13762       }
       
 13763 
       
 13764       x = Math.round(x);
       
 13765       y = Math.round(y);
       
 13766       width = Math.round(width);
       
 13767       height = Math.round(height);
       
 13768       if (tl !== undef) {
       
 13769         roundedRect$2d(x, y, width, height, tl, tr, br, bl);
       
 13770         return;
       
 13771       }
       
 13772 
       
 13773       // Translate the line by (0.5, 0.5) to draw a crisp rectangle border
       
 13774       if (doStroke && lineWidth % 2 === 1) {
       
 13775         curContext.translate(0.5, 0.5);
       
 13776       }
       
 13777       curContext.beginPath();
       
 13778       curContext.rect(x, y, width, height);
       
 13779       executeContextFill();
       
 13780       executeContextStroke();
       
 13781       if (doStroke && lineWidth % 2 === 1) {
       
 13782         curContext.translate(-0.5, -0.5);
       
 13783       }
       
 13784     };
       
 13785 
       
 13786     Drawing3D.prototype.rect = function(x, y, width, height, tl, tr, br, bl) {
       
 13787       if (tl !== undef) {
       
 13788         throw "rect() with rounded corners is not supported in 3D mode";
       
 13789       }
       
 13790 
       
 13791       if (curRectMode === PConstants.CORNERS) {
       
 13792         width -= x;
       
 13793         height -= y;
       
 13794       } else if (curRectMode === PConstants.RADIUS) {
       
 13795         width *= 2;
       
 13796         height *= 2;
       
 13797         x -= width / 2;
       
 13798         y -= height / 2;
       
 13799       } else if (curRectMode === PConstants.CENTER) {
       
 13800         x -= width / 2;
       
 13801         y -= height / 2;
       
 13802       }
       
 13803 
       
 13804       // Modeling transformation
       
 13805       var model = new PMatrix3D();
       
 13806       model.translate(x, y, 0);
       
 13807       model.scale(width, height, 1);
       
 13808       model.transpose();
       
 13809 
       
 13810       // viewing transformation needs to have Y flipped
       
 13811       // becuase that's what Processing does.
       
 13812       var view = new PMatrix3D();
       
 13813       view.scale(1, -1, 1);
       
 13814       view.apply(modelView.array());
       
 13815       view.transpose();
       
 13816 
       
 13817       if (lineWidth > 0 && doStroke) {
       
 13818         curContext.useProgram(programObject2D);
       
 13819         uniformMatrix("model2d", programObject2D, "model", false, model.array());
       
 13820         uniformMatrix("view2d", programObject2D, "view", false, view.array());
       
 13821         uniformf("color2d", programObject2D, "color", strokeStyle);
       
 13822         uniformi("picktype2d", programObject2D, "picktype", 0);
       
 13823         vertexAttribPointer("vertex2d", programObject2D, "Vertex", 3, rectBuffer);
       
 13824         disableVertexAttribPointer("aTextureCoord2d", programObject2D, "aTextureCoord");
       
 13825         curContext.drawArrays(curContext.LINE_LOOP, 0, rectVerts.length / 3);
       
 13826       }
       
 13827 
       
 13828       if (doFill) {
       
 13829         curContext.useProgram(programObject3D);
       
 13830         uniformMatrix("model3d", programObject3D, "model", false, model.array());
       
 13831         uniformMatrix("view3d", programObject3D, "view", false, view.array());
       
 13832 
       
 13833         // fix stitching problems. (lines get occluded by triangles
       
 13834         // since they share the same depth values). This is not entirely
       
 13835         // working, but it's a start for drawing the outline. So
       
 13836         // developers can start playing around with styles.
       
 13837         curContext.enable(curContext.POLYGON_OFFSET_FILL);
       
 13838         curContext.polygonOffset(1, 1);
       
 13839 
       
 13840         uniformf("color3d", programObject3D, "color", fillStyle);
       
 13841 
       
 13842         if(lightCount > 0){
       
 13843           var v = new PMatrix3D();
       
 13844           v.set(view);
       
 13845 
       
 13846           var m = new PMatrix3D();
       
 13847           m.set(model);
       
 13848 
       
 13849           v.mult(m);
       
 13850 
       
 13851           var normalMatrix = new PMatrix3D();
       
 13852           normalMatrix.set(v);
       
 13853           normalMatrix.invert();
       
 13854           normalMatrix.transpose();
       
 13855 
       
 13856           uniformMatrix("normalTransform3d", programObject3D, "normalTransform", false, normalMatrix.array());
       
 13857           vertexAttribPointer("normal3d", programObject3D, "Normal", 3, rectNormBuffer);
       
 13858         }
       
 13859         else{
       
 13860           disableVertexAttribPointer("normal3d", programObject3D, "Normal");
       
 13861         }
       
 13862 
       
 13863         vertexAttribPointer("vertex3d", programObject3D, "Vertex", 3, rectBuffer);
       
 13864 
       
 13865         curContext.drawArrays(curContext.TRIANGLE_FAN, 0, rectVerts.length / 3);
       
 13866         curContext.disable(curContext.POLYGON_OFFSET_FILL);
       
 13867       }
       
 13868     };
       
 13869 
       
 13870     /**
       
 13871      * Draws an ellipse (oval) in the display window. An ellipse with an equal <b>width</b> and <b>height</b> is a circle.
       
 13872      * The first two parameters set the location, the third sets the width, and the fourth sets the height. The origin may be
       
 13873      * changed with the <b>ellipseMode()</b> function.
       
 13874      *
       
 13875      * @param {float|int} x      x-coordinate of the ellipse
       
 13876      * @param {float|int} y      y-coordinate of the ellipse
       
 13877      * @param {float|int} width  width of the ellipse
       
 13878      * @param {float|int} height height of the ellipse
       
 13879      *
       
 13880      * @see ellipseMode
       
 13881      */
       
 13882     Drawing2D.prototype.ellipse = function(x, y, width, height) {
       
 13883       x = x || 0;
       
 13884       y = y || 0;
       
 13885 
       
 13886       if (width <= 0 && height <= 0) {
       
 13887         return;
       
 13888       }
       
 13889 
       
 13890       if (curEllipseMode === PConstants.RADIUS) {
       
 13891         width *= 2;
       
 13892         height *= 2;
       
 13893       } else if (curEllipseMode === PConstants.CORNERS) {
       
 13894         width = width - x;
       
 13895         height = height - y;
       
 13896         x += width / 2;
       
 13897         y += height / 2;
       
 13898       } else if (curEllipseMode === PConstants.CORNER) {
       
 13899         x += width / 2;
       
 13900         y += height / 2;
       
 13901       }
       
 13902 
       
 13903       // Shortcut for drawing a 2D circle
       
 13904       if (width === height) {
       
 13905         curContext.beginPath();
       
 13906         curContext.arc(x, y, width / 2, 0, PConstants.TWO_PI, false);
       
 13907         executeContextFill();
       
 13908         executeContextStroke();
       
 13909       } else {
       
 13910         var w = width / 2,
       
 13911             h = height / 2,
       
 13912             C = 0.5522847498307933,
       
 13913             c_x = C * w,
       
 13914             c_y = C * h;
       
 13915 
       
 13916         p.beginShape();
       
 13917         p.vertex(x + w, y);
       
 13918         p.bezierVertex(x + w, y - c_y, x + c_x, y - h, x, y - h);
       
 13919         p.bezierVertex(x - c_x, y - h, x - w, y - c_y, x - w, y);
       
 13920         p.bezierVertex(x - w, y + c_y, x - c_x, y + h, x, y + h);
       
 13921         p.bezierVertex(x + c_x, y + h, x + w, y + c_y, x + w, y);
       
 13922         p.endShape();
       
 13923       }
       
 13924     };
       
 13925 
       
 13926     Drawing3D.prototype.ellipse = function(x, y, width, height) {
       
 13927       x = x || 0;
       
 13928       y = y || 0;
       
 13929 
       
 13930       if (width <= 0 && height <= 0) {
       
 13931         return;
       
 13932       }
       
 13933 
       
 13934       if (curEllipseMode === PConstants.RADIUS) {
       
 13935         width *= 2;
       
 13936         height *= 2;
       
 13937       } else if (curEllipseMode === PConstants.CORNERS) {
       
 13938         width = width - x;
       
 13939         height = height - y;
       
 13940         x += width / 2;
       
 13941         y += height / 2;
       
 13942       } else if (curEllipseMode === PConstants.CORNER) {
       
 13943         x += width / 2;
       
 13944         y += height / 2;
       
 13945       }
       
 13946 
       
 13947       var w = width / 2,
       
 13948           h = height / 2,
       
 13949           C = 0.5522847498307933,
       
 13950           c_x = C * w,
       
 13951           c_y = C * h;
       
 13952 
       
 13953       p.beginShape();
       
 13954       p.vertex(x + w, y);
       
 13955       p.bezierVertex(x + w, y - c_y, 0, x + c_x, y - h, 0, x, y - h, 0);
       
 13956       p.bezierVertex(x - c_x, y - h, 0, x - w, y - c_y, 0, x - w, y, 0);
       
 13957       p.bezierVertex(x - w, y + c_y, 0, x - c_x, y + h, 0, x, y + h, 0);
       
 13958       p.bezierVertex(x + c_x, y + h, 0, x + w, y + c_y, 0, x + w, y, 0);
       
 13959       p.endShape();
       
 13960 
       
 13961       if (doFill) {
       
 13962         //temporary workaround to not working fills for bezier -- will fix later
       
 13963         var xAv = 0, yAv = 0, i, j;
       
 13964         for (i = 0; i < vertArray.length; i++) {
       
 13965           xAv += vertArray[i][0];
       
 13966           yAv += vertArray[i][1];
       
 13967         }
       
 13968         xAv /= vertArray.length;
       
 13969         yAv /= vertArray.length;
       
 13970         var vert = [],
       
 13971             fillVertArray = [],
       
 13972             colorVertArray = [];
       
 13973         vert[0] = xAv;
       
 13974         vert[1] = yAv;
       
 13975         vert[2] = 0;
       
 13976         vert[3] = 0;
       
 13977         vert[4] = 0;
       
 13978         vert[5] = fillStyle[0];
       
 13979         vert[6] = fillStyle[1];
       
 13980         vert[7] = fillStyle[2];
       
 13981         vert[8] = fillStyle[3];
       
 13982         vert[9] = strokeStyle[0];
       
 13983         vert[10] = strokeStyle[1];
       
 13984         vert[11] = strokeStyle[2];
       
 13985         vert[12] = strokeStyle[3];
       
 13986         vert[13] = normalX;
       
 13987         vert[14] = normalY;
       
 13988         vert[15] = normalZ;
       
 13989         vertArray.unshift(vert);
       
 13990         for (i = 0; i < vertArray.length; i++) {
       
 13991           for (j = 0; j < 3; j++) {
       
 13992             fillVertArray.push(vertArray[i][j]);
       
 13993           }
       
 13994           for (j = 5; j < 9; j++) {
       
 13995             colorVertArray.push(vertArray[i][j]);
       
 13996           }
       
 13997         }
       
 13998         fill3D(fillVertArray, "TRIANGLE_FAN", colorVertArray);
       
 13999       }
       
 14000     };
       
 14001 
       
 14002     /**
       
 14003     * Sets the current normal vector. This is for drawing three dimensional shapes and surfaces and
       
 14004     * specifies a vector perpendicular to the surface of the shape which determines how lighting affects
       
 14005     * it. Processing attempts to automatically assign normals to shapes, but since that's imperfect,
       
 14006     * this is a better option when you want more control. This function is identical to glNormal3f() in OpenGL.
       
 14007     *
       
 14008     * @param {float} nx       x direction
       
 14009     * @param {float} ny       y direction
       
 14010     * @param {float} nz       z direction
       
 14011     *
       
 14012     * @see beginShape
       
 14013     * @see endShape
       
 14014     * @see lights
       
 14015     */
       
 14016     p.normal = function(nx, ny, nz) {
       
 14017       if (arguments.length !== 3 || !(typeof nx === "number" && typeof ny === "number" && typeof nz === "number")) {
       
 14018         throw "normal() requires three numeric arguments.";
       
 14019       }
       
 14020 
       
 14021       normalX = nx;
       
 14022       normalY = ny;
       
 14023       normalZ = nz;
       
 14024 
       
 14025       if (curShape !== 0) {
       
 14026         if (normalMode === PConstants.NORMAL_MODE_AUTO) {
       
 14027           normalMode = PConstants.NORMAL_MODE_SHAPE;
       
 14028         } else if (normalMode === PConstants.NORMAL_MODE_SHAPE) {
       
 14029           normalMode = PConstants.NORMAL_MODE_VERTEX;
       
 14030         }
       
 14031       }
       
 14032     };
       
 14033 
       
 14034     ////////////////////////////////////////////////////////////////////////////
       
 14035     // Raster drawing functions
       
 14036     ////////////////////////////////////////////////////////////////////////////
       
 14037 
       
 14038     /**
       
 14039     * Saves an image from the display window. Images are saved in TIFF, TARGA, JPEG, and PNG format
       
 14040     * depending on the extension within the filename  parameter. For example, "image.tif" will have
       
 14041     * a TIFF image and "image.png" will save a PNG image. If no extension is included in the filename,
       
 14042     * the image will save in TIFF format and .tif will be added to the name. These files are saved to
       
 14043     * the sketch's folder, which may be opened by selecting "Show sketch folder" from the "Sketch" menu.
       
 14044     * It is not possible to use save() while running the program in a web browser.  All images saved
       
 14045     * from the main drawing window will be opaque. To save images without a background, use createGraphics().
       
 14046     *
       
 14047     * @param {String} filename      any sequence of letters and numbers
       
 14048     *
       
 14049     * @see saveFrame
       
 14050     * @see createGraphics
       
 14051     */
       
 14052     p.save = function(file, img) {
       
 14053       // file is unused at the moment
       
 14054       // may implement this differently in later release
       
 14055       if (img !== undef) {
       
 14056         return window.open(img.toDataURL(),"_blank");
       
 14057       }
       
 14058       return window.open(p.externals.canvas.toDataURL(),"_blank");
       
 14059     };
       
 14060 
       
 14061     var saveNumber = 0;
       
 14062 
       
 14063     p.saveFrame = function(file) {
       
 14064       if(file === undef) {
       
 14065         // use default name template if parameter is not specified
       
 14066         file = "screen-####.png";
       
 14067       }
       
 14068       // Increment changeable part: screen-0000.png, screen-0001.png, ...
       
 14069       var frameFilename = file.replace(/#+/, function(all) {
       
 14070         var s = "" + (saveNumber++);
       
 14071         while(s.length < all.length) {
       
 14072           s = "0" + s;
       
 14073         }
       
 14074         return s;
       
 14075       });
       
 14076       p.save(frameFilename);
       
 14077     };
       
 14078 
       
 14079     var utilityContext2d = document.createElement("canvas").getContext("2d");
       
 14080 
       
 14081     var canvasDataCache = [undef, undef, undef]; // we need three for now
       
 14082 
       
 14083     function getCanvasData(obj, w, h) {
       
 14084       var canvasData = canvasDataCache.shift();
       
 14085 
       
 14086       if (canvasData === undef) {
       
 14087         canvasData = {};
       
 14088         canvasData.canvas = document.createElement("canvas");
       
 14089         canvasData.context = canvasData.canvas.getContext('2d');
       
 14090       }
       
 14091 
       
 14092       canvasDataCache.push(canvasData);
       
 14093 
       
 14094       var canvas = canvasData.canvas, context = canvasData.context,
       
 14095           width = w || obj.width, height = h || obj.height;
       
 14096 
       
 14097       canvas.width = width;
       
 14098       canvas.height = height;
       
 14099 
       
 14100       if (!obj) {
       
 14101         context.clearRect(0, 0, width, height);
       
 14102       } else if ("data" in obj) { // ImageData
       
 14103         context.putImageData(obj, 0, 0);
       
 14104       } else {
       
 14105         context.clearRect(0, 0, width, height);
       
 14106         context.drawImage(obj, 0, 0, width, height);
       
 14107       }
       
 14108       return canvasData;
       
 14109     }
       
 14110 
       
 14111     /**
       
 14112      * Handle the sketch code for pixels[] and pixels.length
       
 14113      * parser code converts pixels[] to getPixels()
       
 14114      * or setPixels(), .length becomes getLength()
       
 14115      */
       
 14116     function buildPixelsObject(pImage) {
       
 14117       return {
       
 14118 
       
 14119         getLength: (function(aImg) {
       
 14120           return function() {
       
 14121             if (aImg.isRemote) {
       
 14122               throw "Image is loaded remotely. Cannot get length.";
       
 14123             } else {
       
 14124               return aImg.imageData.data.length ? aImg.imageData.data.length/4 : 0;
       
 14125             }
       
 14126           };
       
 14127         }(pImage)),
       
 14128 
       
 14129         getPixel: (function(aImg) {
       
 14130           return function(i) {
       
 14131             var offset = i*4,
       
 14132               data = aImg.imageData.data;
       
 14133 
       
 14134             if (aImg.isRemote) {
       
 14135               throw "Image is loaded remotely. Cannot get pixels.";
       
 14136             }
       
 14137 
       
 14138             return (data[offset+3] << 24) & PConstants.ALPHA_MASK |
       
 14139                    (data[offset] << 16) & PConstants.RED_MASK |
       
 14140                    (data[offset+1] << 8) & PConstants.GREEN_MASK |
       
 14141                    data[offset+2] & PConstants.BLUE_MASK;
       
 14142           };
       
 14143         }(pImage)),
       
 14144 
       
 14145         setPixel: (function(aImg) {
       
 14146           return function(i, c) {
       
 14147             var offset = i*4,
       
 14148               data = aImg.imageData.data;
       
 14149 
       
 14150             if (aImg.isRemote) {
       
 14151               throw "Image is loaded remotely. Cannot set pixel.";
       
 14152             }
       
 14153 
       
 14154             data[offset+0] = (c & PConstants.RED_MASK) >>> 16;
       
 14155             data[offset+1] = (c & PConstants.GREEN_MASK) >>> 8;
       
 14156             data[offset+2] = (c & PConstants.BLUE_MASK);
       
 14157             data[offset+3] = (c & PConstants.ALPHA_MASK) >>> 24;
       
 14158             aImg.__isDirty = true;
       
 14159           };
       
 14160         }(pImage)),
       
 14161 
       
 14162         toArray: (function(aImg) {
       
 14163           return function() {
       
 14164             var arr = [],
       
 14165               data = aImg.imageData.data,
       
 14166               length = aImg.width * aImg.height;
       
 14167 
       
 14168             if (aImg.isRemote) {
       
 14169               throw "Image is loaded remotely. Cannot get pixels.";
       
 14170             }
       
 14171 
       
 14172             for (var i = 0, offset = 0; i < length; i++, offset += 4) {
       
 14173               arr.push( (data[offset+3] << 24) & PConstants.ALPHA_MASK |
       
 14174                         (data[offset] << 16) & PConstants.RED_MASK |
       
 14175                         (data[offset+1] << 8) & PConstants.GREEN_MASK |
       
 14176                         data[offset+2] & PConstants.BLUE_MASK );
       
 14177             }
       
 14178             return arr;
       
 14179           };
       
 14180         }(pImage)),
       
 14181 
       
 14182         set: (function(aImg) {
       
 14183           return function(arr) {
       
 14184             var offset,
       
 14185               data,
       
 14186               c;
       
 14187             if (this.isRemote) {
       
 14188               throw "Image is loaded remotely. Cannot set pixels.";
       
 14189             }
       
 14190 
       
 14191             data = aImg.imageData.data;
       
 14192             for (var i = 0, aL = arr.length; i < aL; i++) {
       
 14193               c = arr[i];
       
 14194               offset = i*4;
       
 14195 
       
 14196               data[offset+0] = (c & PConstants.RED_MASK) >>> 16;
       
 14197               data[offset+1] = (c & PConstants.GREEN_MASK) >>> 8;
       
 14198               data[offset+2] = (c & PConstants.BLUE_MASK);
       
 14199               data[offset+3] = (c & PConstants.ALPHA_MASK) >>> 24;
       
 14200             }
       
 14201             aImg.__isDirty = true;
       
 14202           };
       
 14203         }(pImage))
       
 14204 
       
 14205       };
       
 14206     }
       
 14207 
       
 14208     /**
       
 14209     * Datatype for storing images. Processing can display .gif, .jpg, .tga, and .png images. Images may be
       
 14210     * displayed in 2D and 3D space. Before an image is used, it must be loaded with the loadImage() function.
       
 14211     * The PImage object contains fields for the width and height of the image, as well as an array called
       
 14212     * pixels[]  which contains the values for every pixel in the image. A group of methods, described below,
       
 14213     * allow easy access to the image's pixels and alpha channel and simplify the process of compositing.
       
 14214     * Before using the pixels[] array, be sure to use the loadPixels() method on the image to make sure that the
       
 14215     * pixel data is properly loaded. To create a new image, use the createImage() function (do not use new PImage()).
       
 14216     *
       
 14217     * @param {int} width                image width
       
 14218     * @param {int} height               image height
       
 14219     * @param {MODE} format              Either RGB, ARGB, ALPHA (grayscale alpha channel)
       
 14220     *
       
 14221     * @returns {PImage}
       
 14222     *
       
 14223     * @see loadImage
       
 14224     * @see imageMode
       
 14225     * @see createImage
       
 14226     */
       
 14227     var PImage = function(aWidth, aHeight, aFormat) {
       
 14228 
       
 14229       // Keep track of whether or not the cached imageData has been touched.
       
 14230       this.__isDirty = false;
       
 14231 
       
 14232       if (aWidth instanceof HTMLImageElement) {
       
 14233         // convert an <img> to a PImage
       
 14234         this.fromHTMLImageData(aWidth);
       
 14235       } else if (aHeight || aFormat) {
       
 14236         this.width = aWidth || 1;
       
 14237         this.height = aHeight || 1;
       
 14238 
       
 14239         // Stuff a canvas into sourceImg so image() calls can use drawImage like an <img>
       
 14240         var canvas = this.sourceImg = document.createElement("canvas");
       
 14241         canvas.width = this.width;
       
 14242         canvas.height = this.height;
       
 14243 
       
 14244         var imageData = this.imageData = canvas.getContext('2d').createImageData(this.width, this.height);
       
 14245         this.format = (aFormat === PConstants.ARGB || aFormat === PConstants.ALPHA) ? aFormat : PConstants.RGB;
       
 14246         if (this.format === PConstants.RGB) {
       
 14247           // Set the alpha channel of an RGB image to opaque.
       
 14248           for (var i = 3, data = this.imageData.data, len = data.length; i < len; i += 4) {
       
 14249             data[i] = 255;
       
 14250           }
       
 14251         }
       
 14252 
       
 14253         this.__isDirty = true;
       
 14254         this.updatePixels();
       
 14255       } else {
       
 14256         this.width = 0;
       
 14257         this.height = 0;
       
 14258         this.imageData = utilityContext2d.createImageData(1, 1);
       
 14259         this.format = PConstants.ARGB;
       
 14260       }
       
 14261 
       
 14262       this.pixels = buildPixelsObject(this);
       
 14263     };
       
 14264     PImage.prototype = {
       
 14265 
       
 14266       /**
       
 14267        * Temporary hack to deal with cross-Processing-instance created PImage.  See
       
 14268        * tickets #1623 and #1644.
       
 14269        */
       
 14270       __isPImage: true,
       
 14271 
       
 14272       /**
       
 14273       * @member PImage
       
 14274       * Updates the image with the data in its pixels[] array. Use in conjunction with loadPixels(). If
       
 14275       * you're only reading pixels from the array, there's no need to call updatePixels().
       
 14276       * Certain renderers may or may not seem to require loadPixels() or updatePixels(). However, the rule
       
 14277       * is that any time you want to manipulate the pixels[] array, you must first call loadPixels(), and
       
 14278       * after changes have been made, call updatePixels(). Even if the renderer may not seem to use this
       
 14279       * function in the current Processing release, this will always be subject to change.
       
 14280       * Currently, none of the renderers use the additional parameters to updatePixels().
       
 14281       */
       
 14282       updatePixels: function() {
       
 14283         var canvas = this.sourceImg;
       
 14284         if (canvas && canvas instanceof HTMLCanvasElement && this.__isDirty) {
       
 14285           canvas.getContext('2d').putImageData(this.imageData, 0, 0);
       
 14286         }
       
 14287         this.__isDirty = false;
       
 14288       },
       
 14289 
       
 14290       fromHTMLImageData: function(htmlImg) {
       
 14291         // convert an <img> to a PImage
       
 14292         var canvasData = getCanvasData(htmlImg);
       
 14293         try {
       
 14294           var imageData = canvasData.context.getImageData(0, 0, htmlImg.width, htmlImg.height);
       
 14295           this.fromImageData(imageData);
       
 14296         } catch(e) {
       
 14297           if (htmlImg.width && htmlImg.height) {
       
 14298             this.isRemote = true;
       
 14299             this.width = htmlImg.width;
       
 14300             this.height = htmlImg.height;
       
 14301           }
       
 14302         }
       
 14303         this.sourceImg = htmlImg;
       
 14304       },
       
 14305 
       
 14306       'get': function(x, y, w, h) {
       
 14307         if (!arguments.length) {
       
 14308           return p.get(this);
       
 14309         }
       
 14310         if (arguments.length === 2) {
       
 14311           return p.get(x, y, this);
       
 14312         }
       
 14313         if (arguments.length === 4) {
       
 14314           return p.get(x, y, w, h, this);
       
 14315         }
       
 14316       },
       
 14317 
       
 14318       /**
       
 14319       * @member PImage
       
 14320       * Changes the color of any pixel or writes an image directly into the image. The x and y parameter
       
 14321       * specify the pixel or the upper-left corner of the image. The color parameter specifies the color value.
       
 14322       * Setting the color of a single pixel with set(x, y) is easy, but not as fast as putting the data
       
 14323       * directly into pixels[]. The equivalent statement to "set(x, y, #000000)" using pixels[] is
       
 14324       * "pixels[y*width+x] = #000000". Processing requires calling loadPixels() to load the display window
       
 14325       * data into the pixels[] array before getting the values and calling updatePixels() to update the window.
       
 14326       *
       
 14327       * @param {int} x        x-coordinate of the pixel or upper-left corner of the image
       
 14328       * @param {int} y        y-coordinate of the pixel or upper-left corner of the image
       
 14329       * @param {color} color  any value of the color datatype
       
 14330       *
       
 14331       * @see get
       
 14332       * @see pixels[]
       
 14333       * @see copy
       
 14334       */
       
 14335       'set': function(x, y, c) {
       
 14336         p.set(x, y, c, this);
       
 14337         this.__isDirty = true;
       
 14338       },
       
 14339 
       
 14340       /**
       
 14341       * @member PImage
       
 14342       * Blends a region of pixels into the image specified by the img parameter. These copies utilize full
       
 14343       * alpha channel support and a choice of the following modes to blend the colors of source pixels (A)
       
 14344       * with the ones of pixels in the destination image (B):
       
 14345       * BLEND - linear interpolation of colours: C = A*factor + B
       
 14346       * ADD - additive blending with white clip: C = min(A*factor + B, 255)
       
 14347       * SUBTRACT - subtractive blending with black clip: C = max(B - A*factor, 0)
       
 14348       * DARKEST - only the darkest colour succeeds: C = min(A*factor, B)
       
 14349       * LIGHTEST - only the lightest colour succeeds: C = max(A*factor, B)
       
 14350       * DIFFERENCE - subtract colors from underlying image.
       
 14351       * EXCLUSION - similar to DIFFERENCE, but less extreme.
       
 14352       * MULTIPLY - Multiply the colors, result will always be darker.
       
 14353       * SCREEN - Opposite multiply, uses inverse values of the colors.
       
 14354       * OVERLAY - A mix of MULTIPLY and SCREEN. Multiplies dark values, and screens light values.
       
 14355       * HARD_LIGHT - SCREEN when greater than 50% gray, MULTIPLY when lower.
       
 14356       * SOFT_LIGHT - Mix of DARKEST and LIGHTEST. Works like OVERLAY, but not as harsh.
       
 14357       * DODGE - Lightens light tones and increases contrast, ignores darks. Called "Color Dodge" in Illustrator and Photoshop.
       
 14358       * BURN - Darker areas are applied, increasing contrast, ignores lights. Called "Color Burn" in Illustrator and Photoshop.
       
 14359       * All modes use the alpha information (highest byte) of source image pixels as the blending factor.
       
 14360       * If the source and destination regions are different sizes, the image will be automatically resized to
       
 14361       * match the destination size. If the srcImg parameter is not used, the display window is used as the source image.
       
 14362       * This function ignores imageMode().
       
 14363       *
       
 14364       * @param {int} x              X coordinate of the source's upper left corner
       
 14365       * @param {int} y              Y coordinate of the source's upper left corner
       
 14366       * @param {int} width          source image width
       
 14367       * @param {int} height         source image height
       
 14368       * @param {int} dx             X coordinate of the destinations's upper left corner
       
 14369       * @param {int} dy             Y coordinate of the destinations's upper left corner
       
 14370       * @param {int} dwidth         destination image width
       
 14371       * @param {int} dheight        destination image height
       
 14372       * @param {PImage} srcImg      an image variable referring to the source image
       
 14373       * @param {MODE} MODE          Either BLEND, ADD, SUBTRACT, LIGHTEST, DARKEST, DIFFERENCE, EXCLUSION,
       
 14374       * MULTIPLY, SCREEN, OVERLAY, HARD_LIGHT, SOFT_LIGHT, DODGE, BURN
       
 14375       *
       
 14376       * @see alpha
       
 14377       * @see copy
       
 14378       */
       
 14379       blend: function(srcImg, x, y, width, height, dx, dy, dwidth, dheight, MODE) {
       
 14380         if (arguments.length === 9) {
       
 14381           p.blend(this, srcImg, x, y, width, height, dx, dy, dwidth, dheight, this);
       
 14382         } else if (arguments.length === 10) {
       
 14383           p.blend(srcImg, x, y, width, height, dx, dy, dwidth, dheight, MODE, this);
       
 14384         }
       
 14385         delete this.sourceImg;
       
 14386       },
       
 14387 
       
 14388       /**
       
 14389       * @member PImage
       
 14390       * Copies a region of pixels from one image into another. If the source and destination regions
       
 14391       * aren't the same size, it will automatically resize source pixels to fit the specified target region.
       
 14392       * No alpha information is used in the process, however if the source image has an alpha channel set,
       
 14393       * it will be copied as well. This function ignores imageMode().
       
 14394       *
       
 14395       * @param {int} sx             X coordinate of the source's upper left corner
       
 14396       * @param {int} sy             Y coordinate of the source's upper left corner
       
 14397       * @param {int} swidth         source image width
       
 14398       * @param {int} sheight        source image height
       
 14399       * @param {int} dx             X coordinate of the destinations's upper left corner
       
 14400       * @param {int} dy             Y coordinate of the destinations's upper left corner
       
 14401       * @param {int} dwidth         destination image width
       
 14402       * @param {int} dheight        destination image height
       
 14403       * @param {PImage} srcImg      an image variable referring to the source image
       
 14404       *
       
 14405       * @see alpha
       
 14406       * @see blend
       
 14407       */
       
 14408       copy: function(srcImg, sx, sy, swidth, sheight, dx, dy, dwidth, dheight) {
       
 14409         if (arguments.length === 8) {
       
 14410           p.blend(this, srcImg, sx, sy, swidth, sheight, dx, dy, dwidth, PConstants.REPLACE, this);
       
 14411         } else if (arguments.length === 9) {
       
 14412           p.blend(srcImg, sx, sy, swidth, sheight, dx, dy, dwidth, dheight, PConstants.REPLACE, this);
       
 14413         }
       
 14414         delete this.sourceImg;
       
 14415       },
       
 14416 
       
 14417       /**
       
 14418       * @member PImage
       
 14419       * Filters an image as defined by one of the following modes:
       
 14420       * THRESHOLD - converts the image to black and white pixels depending if they are above or below
       
 14421       * the threshold defined by the level parameter. The level must be between 0.0 (black) and 1.0(white).
       
 14422       * If no level is specified, 0.5 is used.
       
 14423       * GRAY - converts any colors in the image to grayscale equivalents
       
 14424       * INVERT - sets each pixel to its inverse value
       
 14425       * POSTERIZE - limits each channel of the image to the number of colors specified as the level parameter
       
 14426       * BLUR - executes a Guassian blur with the level parameter specifying the extent of the blurring.
       
 14427       * If no level parameter is used, the blur is equivalent to Guassian blur of radius 1.
       
 14428       * OPAQUE - sets the alpha channel to entirely opaque.
       
 14429       * ERODE - reduces the light areas with the amount defined by the level parameter.
       
 14430       * DILATE - increases the light areas with the amount defined by the level parameter
       
 14431       *
       
 14432       * @param {MODE} MODE        Either THRESHOLD, GRAY, INVERT, POSTERIZE, BLUR, OPAQUE, ERODE, or DILATE
       
 14433       * @param {int|float} param  in the range from 0 to 1
       
 14434       */
       
 14435       filter: function(mode, param) {
       
 14436         if (arguments.length === 2) {
       
 14437           p.filter(mode, param, this);
       
 14438         } else if (arguments.length === 1) {
       
 14439           // no param specified, send null to show its invalid
       
 14440           p.filter(mode, null, this);
       
 14441         }
       
 14442         delete this.sourceImg;
       
 14443       },
       
 14444 
       
 14445       /**
       
 14446       * @member PImage
       
 14447       * Saves the image into a file. Images are saved in TIFF, TARGA, JPEG, and PNG format depending on
       
 14448       * the extension within the filename  parameter. For example, "image.tif" will have a TIFF image and
       
 14449       * "image.png" will save a PNG image. If no extension is included in the filename, the image will save
       
 14450       * in TIFF format and .tif will be added to the name. These files are saved to the sketch's folder,
       
 14451       * which may be opened by selecting "Show sketch folder" from the "Sketch" menu. It is not possible to
       
 14452       * use save() while running the program in a web browser.
       
 14453       * To save an image created within the code, rather than through loading, it's necessary to make the
       
 14454       * image with the createImage() function so it is aware of the location of the program and can therefore
       
 14455       * save the file to the right place. See the createImage() reference for more information.
       
 14456       *
       
 14457       * @param {String} filename        a sequence of letters and numbers
       
 14458       */
       
 14459       save: function(file){
       
 14460         p.save(file,this);
       
 14461       },
       
 14462 
       
 14463       /**
       
 14464       * @member PImage
       
 14465       * Resize the image to a new width and height. To make the image scale proportionally, use 0 as the
       
 14466       * value for the wide or high parameter.
       
 14467       *
       
 14468       * @param {int} wide         the resized image width
       
 14469       * @param {int} high         the resized image height
       
 14470       *
       
 14471       * @see get
       
 14472       */
       
 14473       resize: function(w, h) {
       
 14474         if (this.isRemote) { // Remote images cannot access imageData
       
 14475           throw "Image is loaded remotely. Cannot resize.";
       
 14476         }
       
 14477         if (this.width !== 0 || this.height !== 0) {
       
 14478           // make aspect ratio if w or h is 0
       
 14479           if (w === 0 && h !== 0) {
       
 14480             w = Math.floor(this.width / this.height * h);
       
 14481           } else if (h === 0 && w !== 0) {
       
 14482             h = Math.floor(this.height / this.width * w);
       
 14483           }
       
 14484           // put 'this.imageData' into a new canvas
       
 14485           var canvas = getCanvasData(this.imageData).canvas;
       
 14486           // pull imageData object out of canvas into ImageData object
       
 14487           var imageData = getCanvasData(canvas, w, h).context.getImageData(0, 0, w, h);
       
 14488           // set this as new pimage
       
 14489           this.fromImageData(imageData);
       
 14490         }
       
 14491       },
       
 14492 
       
 14493       /**
       
 14494       * @member PImage
       
 14495       * Masks part of an image from displaying by loading another image and using it as an alpha channel.
       
 14496       * This mask image should only contain grayscale data, but only the blue color channel is used. The
       
 14497       * mask image needs to be the same size as the image to which it is applied.
       
 14498       * In addition to using a mask image, an integer array containing the alpha channel data can be
       
 14499       * specified directly. This method is useful for creating dynamically generated alpha masks. This
       
 14500       * array must be of the same length as the target image's pixels array and should contain only grayscale
       
 14501       * data of values between 0-255.
       
 14502       *
       
 14503       * @param {PImage} maskImg         any PImage object used as the alpha channel for "img", needs to be same
       
 14504       *                                 size as "img"
       
 14505       * @param {int[]} maskArray        any array of Integer numbers used as the alpha channel, needs to be same
       
 14506       *                                 length as the image's pixel array
       
 14507       */
       
 14508       mask: function(mask) {
       
 14509         var obj = this.toImageData(),
       
 14510             i,
       
 14511             size;
       
 14512 
       
 14513         if (mask instanceof PImage || mask.__isPImage) {
       
 14514           if (mask.width === this.width && mask.height === this.height) {
       
 14515             mask = mask.toImageData();
       
 14516 
       
 14517             for (i = 2, size = this.width * this.height * 4; i < size; i += 4) {
       
 14518               // using it as an alpha channel
       
 14519               obj.data[i + 1] = mask.data[i];
       
 14520               // but only the blue color channel
       
 14521             }
       
 14522           } else {
       
 14523             throw "mask must have the same dimensions as PImage.";
       
 14524           }
       
 14525         } else if (mask instanceof Array) {
       
 14526           if (this.width * this.height === mask.length) {
       
 14527             for (i = 0, size = mask.length; i < size; ++i) {
       
 14528               obj.data[i * 4 + 3] = mask[i];
       
 14529             }
       
 14530           } else {
       
 14531             throw "mask array must be the same length as PImage pixels array.";
       
 14532           }
       
 14533         }
       
 14534 
       
 14535         this.fromImageData(obj);
       
 14536       },
       
 14537 
       
 14538       // These are intentionally left blank for PImages, we work live with pixels and draw as necessary
       
 14539       /**
       
 14540       * @member PImage
       
 14541       * Loads the pixel data for the image into its pixels[] array. This function must always be called
       
 14542       * before reading from or writing to pixels[].
       
 14543       * Certain renderers may or may not seem to require loadPixels() or updatePixels(). However, the
       
 14544       * rule is that any time you want to manipulate the pixels[] array, you must first call loadPixels(),
       
 14545       * and after changes have been made, call updatePixels(). Even if the renderer may not seem to use
       
 14546       * this function in the current Processing release, this will always be subject to change.
       
 14547       */
       
 14548       loadPixels: nop,
       
 14549 
       
 14550       toImageData: function() {
       
 14551         if (this.isRemote) {
       
 14552           return this.sourceImg;
       
 14553         }
       
 14554 
       
 14555         if (!this.__isDirty) {
       
 14556           return this.imageData;
       
 14557         }
       
 14558 
       
 14559         var canvasData = getCanvasData(this.imageData);
       
 14560         return canvasData.context.getImageData(0, 0, this.width, this.height);
       
 14561       },
       
 14562 
       
 14563       toDataURL: function() {
       
 14564         if (this.isRemote) { // Remote images cannot access imageData
       
 14565           throw "Image is loaded remotely. Cannot create dataURI.";
       
 14566         }
       
 14567         var canvasData = getCanvasData(this.imageData);
       
 14568         return canvasData.canvas.toDataURL();
       
 14569       },
       
 14570 
       
 14571       fromImageData: function(canvasImg) {
       
 14572         var w = canvasImg.width,
       
 14573           h = canvasImg.height,
       
 14574           canvas = document.createElement('canvas'),
       
 14575           ctx = canvas.getContext('2d');
       
 14576 
       
 14577         this.width = canvas.width = w;
       
 14578         this.height = canvas.height = h;
       
 14579 
       
 14580         ctx.putImageData(canvasImg, 0, 0);
       
 14581 
       
 14582         // changed for 0.9
       
 14583         this.format = PConstants.ARGB;
       
 14584 
       
 14585         this.imageData = canvasImg;
       
 14586         this.sourceImg = canvas;
       
 14587       }
       
 14588     };
       
 14589 
       
 14590     p.PImage = PImage;
       
 14591 
       
 14592     /**
       
 14593     * Creates a new PImage (the datatype for storing images). This provides a fresh buffer of pixels to play
       
 14594     * with. Set the size of the buffer with the width and height parameters. The format parameter defines how
       
 14595     * the pixels are stored. See the PImage reference for more information.
       
 14596     * Be sure to include all three parameters, specifying only the width and height (but no format) will
       
 14597     * produce a strange error.
       
 14598     * Advanced users please note that createImage() should be used instead of the syntax new PImage().
       
 14599     *
       
 14600     * @param {int} width                image width
       
 14601     * @param {int} height               image height
       
 14602     * @param {MODE} format              Either RGB, ARGB, ALPHA (grayscale alpha channel)
       
 14603     *
       
 14604     * @returns {PImage}
       
 14605     *
       
 14606     * @see PImage
       
 14607     * @see PGraphics
       
 14608     */
       
 14609     p.createImage = function(w, h, mode) {
       
 14610       return new PImage(w,h,mode);
       
 14611     };
       
 14612 
       
 14613     // Loads an image for display. Type is an extension. Callback is fired on load.
       
 14614     /**
       
 14615     * Loads an image into a variable of type PImage. Four types of images ( .gif, .jpg, .tga, .png) images may
       
 14616     * be loaded. To load correctly, images must be located in the data directory of the current sketch. In most
       
 14617     * cases, load all images in setup() to preload them at the start of the program. Loading images inside draw()
       
 14618     * will reduce the speed of a program.
       
 14619     * The filename parameter can also be a URL to a file found online. For security reasons, a Processing sketch
       
 14620     * found online can only download files from the same server from which it came. Getting around this restriction
       
 14621     * requires a signed applet.
       
 14622     * The extension parameter is used to determine the image type in cases where the image filename does not end
       
 14623     * with a proper extension. Specify the extension as the second parameter to loadImage(), as shown in the
       
 14624     * third example on this page.
       
 14625     * If an image is not loaded successfully, the null value is returned and an error message will be printed to
       
 14626     * the console. The error message does not halt the program, however the null value may cause a NullPointerException
       
 14627     * if your code does not check whether the value returned from loadImage() is null.
       
 14628     * Depending on the type of error, a PImage object may still be returned, but the width and height of the image
       
 14629     * will be set to -1. This happens if bad image data is returned or cannot be decoded properly. Sometimes this happens
       
 14630     * with image URLs that produce a 403 error or that redirect to a password prompt, because loadImage() will attempt
       
 14631     * to interpret the HTML as image data.
       
 14632     *
       
 14633     * @param {String} filename        name of file to load, can be .gif, .jpg, .tga, or a handful of other image
       
 14634     *                                 types depending on your platform.
       
 14635     * @param {String} extension       the type of image to load, for example "png", "gif", "jpg"
       
 14636     *
       
 14637     * @returns {PImage}
       
 14638     *
       
 14639     * @see PImage
       
 14640     * @see image
       
 14641     * @see imageMode
       
 14642     * @see background
       
 14643     */
       
 14644     p.loadImage = function(file, type, callback) {
       
 14645       // if type is specified add it with a . to file to make the filename
       
 14646       if (type) {
       
 14647         file = file + "." + type;
       
 14648       }
       
 14649       var pimg;
       
 14650       // if image is in the preloader cache return a new PImage
       
 14651       if (curSketch.imageCache.images[file]) {
       
 14652         pimg = new PImage(curSketch.imageCache.images[file]);
       
 14653         pimg.loaded = true;
       
 14654         return pimg;
       
 14655       }
       
 14656       // else async load it
       
 14657       pimg = new PImage();
       
 14658       var img = document.createElement('img');
       
 14659 
       
 14660       pimg.sourceImg = img;
       
 14661 
       
 14662       img.onload = (function(aImage, aPImage, aCallback) {
       
 14663         var image = aImage;
       
 14664         var pimg = aPImage;
       
 14665         var callback = aCallback;
       
 14666         return function() {
       
 14667           // change the <img> object into a PImage now that its loaded
       
 14668           pimg.fromHTMLImageData(image);
       
 14669           pimg.loaded = true;
       
 14670           if (callback) {
       
 14671             callback();
       
 14672           }
       
 14673         };
       
 14674       }(img, pimg, callback));
       
 14675 
       
 14676       img.src = file; // needs to be called after the img.onload function is declared or it wont work in opera
       
 14677       return pimg;
       
 14678     };
       
 14679 
       
 14680     // async loading of large images, same functionality as loadImage above
       
 14681     /**
       
 14682     * This function load images on a separate thread so that your sketch does not freeze while images load during
       
 14683     * setup(). While the image is loading, its width and height will be 0. If an error occurs while loading the image,
       
 14684     * its width and height will be set to -1. You'll know when the image has loaded properly because its width and
       
 14685     * height will be greater than 0. Asynchronous image loading (particularly when downloading from a server) can
       
 14686     * dramatically improve performance.
       
 14687     * The extension parameter is used to determine the image type in cases where the image filename does not end
       
 14688     * with a proper extension. Specify the extension as the second parameter to requestImage().
       
 14689     *
       
 14690     * @param {String} filename        name of file to load, can be .gif, .jpg, .tga, or a handful of other image
       
 14691     *                                 types depending on your platform.
       
 14692     * @param {String} extension       the type of image to load, for example "png", "gif", "jpg"
       
 14693     *
       
 14694     * @returns {PImage}
       
 14695     *
       
 14696     * @see PImage
       
 14697     * @see loadImage
       
 14698     */
       
 14699     p.requestImage = p.loadImage;
       
 14700 
       
 14701     function get$2(x,y) {
       
 14702       var data;
       
 14703       // return the color at x,y (int) of curContext
       
 14704       if (x >= p.width || x < 0 || y < 0 || y >= p.height) {
       
 14705         // x,y is outside image return transparent black
       
 14706         return 0;
       
 14707       }
       
 14708 
       
 14709       // loadPixels() has been called
       
 14710       if (isContextReplaced) {
       
 14711         var offset = ((0|x) + p.width * (0|y)) * 4;
       
 14712         data = p.imageData.data;
       
 14713         return (data[offset + 3] << 24) & PConstants.ALPHA_MASK |
       
 14714                (data[offset] << 16) & PConstants.RED_MASK |
       
 14715                (data[offset + 1] << 8) & PConstants.GREEN_MASK |
       
 14716                data[offset + 2] & PConstants.BLUE_MASK;
       
 14717       }
       
 14718 
       
 14719       // x,y is inside canvas space
       
 14720       data = p.toImageData(0|x, 0|y, 1, 1).data;
       
 14721       return (data[3] << 24) & PConstants.ALPHA_MASK |
       
 14722              (data[0] << 16) & PConstants.RED_MASK |
       
 14723              (data[1] << 8) & PConstants.GREEN_MASK |
       
 14724              data[2] & PConstants.BLUE_MASK;
       
 14725     }
       
 14726     function get$3(x,y,img) {
       
 14727       if (img.isRemote) { // Remote images cannot access imageData
       
 14728         throw "Image is loaded remotely. Cannot get x,y.";
       
 14729       }
       
 14730       // PImage.get(x,y) was called, return the color (int) at x,y of img
       
 14731       var offset = y * img.width * 4 + (x * 4),
       
 14732           data = img.imageData.data;
       
 14733       return (data[offset + 3] << 24) & PConstants.ALPHA_MASK |
       
 14734              (data[offset] << 16) & PConstants.RED_MASK |
       
 14735              (data[offset + 1] << 8) & PConstants.GREEN_MASK |
       
 14736              data[offset + 2] & PConstants.BLUE_MASK;
       
 14737     }
       
 14738     function get$4(x, y, w, h) {
       
 14739       // return a PImage of w and h from cood x,y of curContext
       
 14740       var c = new PImage(w, h, PConstants.ARGB);
       
 14741       c.fromImageData(p.toImageData(x, y, w, h));
       
 14742       return c;
       
 14743     }
       
 14744     function get$5(x, y, w, h, img) {
       
 14745       if (img.isRemote) { // Remote images cannot access imageData
       
 14746         throw "Image is loaded remotely. Cannot get x,y,w,h.";
       
 14747       }
       
 14748       // PImage.get(x,y,w,h) was called, return x,y,w,h PImage of img
       
 14749       // offset start point needs to be *4
       
 14750       var c = new PImage(w, h, PConstants.ARGB), cData = c.imageData.data,
       
 14751         imgWidth = img.width, imgHeight = img.height, imgData = img.imageData.data;
       
 14752       // Don't need to copy pixels from the image outside ranges.
       
 14753       var startRow = Math.max(0, -y), startColumn = Math.max(0, -x),
       
 14754         stopRow = Math.min(h, imgHeight - y), stopColumn = Math.min(w, imgWidth - x);
       
 14755       for (var i = startRow; i < stopRow; ++i) {
       
 14756         var sourceOffset = ((y + i) * imgWidth + (x + startColumn)) * 4;
       
 14757         var targetOffset = (i * w + startColumn) * 4;
       
 14758         for (var j = startColumn; j < stopColumn; ++j) {
       
 14759           cData[targetOffset++] = imgData[sourceOffset++];
       
 14760           cData[targetOffset++] = imgData[sourceOffset++];
       
 14761           cData[targetOffset++] = imgData[sourceOffset++];
       
 14762           cData[targetOffset++] = imgData[sourceOffset++];
       
 14763         }
       
 14764       }
       
 14765       c.__isDirty = true;
       
 14766       return c;
       
 14767     }
       
 14768 
       
 14769     // Gets a single pixel or block of pixels from the current Canvas Context or a PImage
       
 14770     /**
       
 14771     * Reads the color of any pixel or grabs a section of an image. If no parameters are specified, the entire
       
 14772     * image is returned. Get the value of one pixel by specifying an x,y coordinate. Get a section of the display
       
 14773     * window by specifying an additional width and height parameter. If the pixel requested is outside of the image
       
 14774     * window, black is returned. The numbers returned are scaled according to the current color ranges, but only RGB
       
 14775     * values are returned by this function. For example, even though you may have drawn a shape with colorMode(HSB),
       
 14776     * the numbers returned will be in RGB.
       
 14777     * Getting the color of a single pixel with get(x, y) is easy, but not as fast as grabbing the data directly
       
 14778     * from pixels[]. The equivalent statement to "get(x, y)" using pixels[] is "pixels[y*width+x]". Processing
       
 14779     * requires calling loadPixels() to load the display window data into the pixels[] array before getting the values.
       
 14780     * This function ignores imageMode().
       
 14781     *
       
 14782     * @param {int} x            x-coordinate of the pixel
       
 14783     * @param {int} y            y-coordinate of the pixel
       
 14784     * @param {int} width        width of pixel rectangle to get
       
 14785     * @param {int} height       height of pixel rectangle to get
       
 14786     *
       
 14787     * @returns {Color|PImage}
       
 14788     *
       
 14789     * @see set
       
 14790     * @see pixels[]
       
 14791     * @see imageMode
       
 14792     */
       
 14793     p.get = function(x, y, w, h, img) {
       
 14794       // for 0 2 and 4 arguments use curContext, otherwise PImage.get was called
       
 14795       if (img !== undefined) {
       
 14796         return get$5(x, y, w, h, img);
       
 14797       }
       
 14798       if (h !== undefined) {
       
 14799         return get$4(x, y, w, h);
       
 14800       }
       
 14801       if (w !== undefined) {
       
 14802         return get$3(x, y, w);
       
 14803       }
       
 14804       if (y !== undefined) {
       
 14805         return get$2(x, y);
       
 14806       }
       
 14807       if (x !== undefined) {
       
 14808         // PImage.get() was called, return a new PImage
       
 14809         return get$5(0, 0, x.width, x.height, x);
       
 14810       }
       
 14811 
       
 14812       return get$4(0, 0, p.width, p.height);
       
 14813     };
       
 14814 
       
 14815     /**
       
 14816      * Creates and returns a new <b>PGraphics</b> object of the types P2D, P3D, and JAVA2D. Use this class if you need to draw
       
 14817      * into an off-screen graphics buffer. It's not possible to use <b>createGraphics()</b> with OPENGL, because it doesn't
       
 14818      * allow offscreen use. The DXF and PDF renderers require the filename parameter. <br /><br /> It's important to call
       
 14819      * any drawing commands between beginDraw() and endDraw() statements. This is also true for any commands that affect
       
 14820      * drawing, such as smooth() or colorMode().<br /><br /> Unlike the main drawing surface which is completely opaque,
       
 14821      * surfaces created with createGraphics() can have transparency. This makes it possible to draw into a graphics and
       
 14822      * maintain the alpha channel.
       
 14823      *
       
 14824      * @param {int} width       width in pixels
       
 14825      * @param {int} height      height in pixels
       
 14826      * @param {int} renderer    Either P2D, P3D, JAVA2D, PDF, DXF
       
 14827      * @param {String} filename the name of the file (not supported yet)
       
 14828      */
       
 14829     p.createGraphics = function(w, h, render) {
       
 14830       var pg = new Processing();
       
 14831       pg.size(w, h, render);
       
 14832       return pg;
       
 14833     };
       
 14834 
       
 14835     // pixels caching
       
 14836     function resetContext() {
       
 14837       if(isContextReplaced) {
       
 14838         curContext = originalContext;
       
 14839         isContextReplaced = false;
       
 14840 
       
 14841         p.updatePixels();
       
 14842       }
       
 14843     }
       
 14844     function SetPixelContextWrapper() {
       
 14845       function wrapFunction(newContext, name) {
       
 14846         function wrapper() {
       
 14847           resetContext();
       
 14848           curContext[name].apply(curContext, arguments);
       
 14849         }
       
 14850         newContext[name] = wrapper;
       
 14851       }
       
 14852       function wrapProperty(newContext, name) {
       
 14853         function getter() {
       
 14854           resetContext();
       
 14855           return curContext[name];
       
 14856         }
       
 14857         function setter(value) {
       
 14858           resetContext();
       
 14859           curContext[name] = value;
       
 14860         }
       
 14861         p.defineProperty(newContext, name, { get: getter, set: setter });
       
 14862       }
       
 14863       for(var n in curContext) {
       
 14864         if(typeof curContext[n] === 'function') {
       
 14865           wrapFunction(this, n);
       
 14866         } else {
       
 14867           wrapProperty(this, n);
       
 14868         }
       
 14869       }
       
 14870     }
       
 14871     function replaceContext() {
       
 14872       if(isContextReplaced) {
       
 14873         return;
       
 14874       }
       
 14875       p.loadPixels();
       
 14876       if(proxyContext === null) {
       
 14877         originalContext = curContext;
       
 14878         proxyContext = new SetPixelContextWrapper();
       
 14879       }
       
 14880       isContextReplaced = true;
       
 14881       curContext = proxyContext;
       
 14882       setPixelsCached = 0;
       
 14883     }
       
 14884 
       
 14885     function set$3(x, y, c) {
       
 14886       if (x < p.width && x >= 0 && y >= 0 && y < p.height) {
       
 14887         replaceContext();
       
 14888         p.pixels.setPixel((0|x)+p.width*(0|y), c);
       
 14889         if(++setPixelsCached > maxPixelsCached) {
       
 14890           resetContext();
       
 14891         }
       
 14892       }
       
 14893     }
       
 14894     function set$4(x, y, obj, img) {
       
 14895       if (img.isRemote) { // Remote images cannot access imageData
       
 14896         throw "Image is loaded remotely. Cannot set x,y.";
       
 14897       }
       
 14898       var c = p.color.toArray(obj);
       
 14899       var offset = y * img.width * 4 + (x*4);
       
 14900       var data = img.imageData.data;
       
 14901       data[offset] = c[0];
       
 14902       data[offset+1] = c[1];
       
 14903       data[offset+2] = c[2];
       
 14904       data[offset+3] = c[3];
       
 14905     }
       
 14906 
       
 14907     // Paints a pixel array into the canvas
       
 14908     /**
       
 14909     * Changes the color of any pixel or writes an image directly into the display window. The x and y parameters
       
 14910     * specify the pixel to change and the color  parameter specifies the color value. The color parameter is affected
       
 14911     * by the current color mode (the default is RGB values from 0 to 255). When setting an image, the x and y
       
 14912     * parameters define the coordinates for the upper-left corner of the image.
       
 14913     * Setting the color of a single pixel with set(x, y) is easy, but not as fast as putting the data directly
       
 14914     * into pixels[]. The equivalent statement to "set(x, y, #000000)" using pixels[] is "pixels[y*width+x] = #000000".
       
 14915     * You must call loadPixels() to load the display window data into the pixels[] array before setting the values
       
 14916     * and calling updatePixels() to update the window with any changes. This function ignores imageMode().
       
 14917     *
       
 14918     * @param {int} x            x-coordinate of the pixel
       
 14919     * @param {int} y            y-coordinate of the pixel
       
 14920     * @param {Color} obj        any value of the color datatype
       
 14921     * @param {PImage} img       any valid variable of type PImage
       
 14922     *
       
 14923     * @see get
       
 14924     * @see pixels[]
       
 14925     * @see imageMode
       
 14926     */
       
 14927     p.set = function(x, y, obj, img) {
       
 14928       var color, oldFill;
       
 14929       if (arguments.length === 3) {
       
 14930         // called p.set(), was it with a color or a img ?
       
 14931         if (typeof obj === "number") {
       
 14932           set$3(x, y, obj);
       
 14933         } else if (obj instanceof PImage || obj.__isPImage) {
       
 14934           p.image(obj, x, y);
       
 14935         }
       
 14936       } else if (arguments.length === 4) {
       
 14937         // PImage.set(x,y,c) was called, set coordinate x,y color to c of img
       
 14938         set$4(x, y, obj, img);
       
 14939       }
       
 14940     };
       
 14941     p.imageData = {};
       
 14942 
       
 14943     // handle the sketch code for pixels[]
       
 14944     // parser code converts pixels[] to getPixels() or setPixels(),
       
 14945     // .length becomes getLength()
       
 14946     /**
       
 14947     * Array containing the values for all the pixels in the display window. These values are of the color datatype.
       
 14948     * This array is the size of the display window. For example, if the image is 100x100 pixels, there will be 10000
       
 14949     * values and if the window is 200x300 pixels, there will be 60000 values. The index value defines the position
       
 14950     * of a value within the array. For example, the statment color b = pixels[230] will set the variable b to be
       
 14951     * equal to the value at that location in the array.
       
 14952     * Before accessing this array, the data must loaded with the loadPixels() function. After the array data has
       
 14953     * been modified, the updatePixels() function must be run to update the changes.
       
 14954     *
       
 14955     * @param {int} index      must not exceed the size of the array
       
 14956     *
       
 14957     * @see loadPixels
       
 14958     * @see updatePixels
       
 14959     * @see get
       
 14960     * @see set
       
 14961     * @see PImage
       
 14962     */
       
 14963     p.pixels = {
       
 14964       getLength: function() { return p.imageData.data.length ? p.imageData.data.length/4 : 0; },
       
 14965       getPixel: function(i) {
       
 14966         var offset = i*4, data = p.imageData.data;
       
 14967         return (data[offset+3] << 24) & 0xff000000 |
       
 14968                (data[offset+0] << 16) & 0x00ff0000 |
       
 14969                (data[offset+1] << 8) & 0x0000ff00 |
       
 14970                data[offset+2] & 0x000000ff;
       
 14971       },
       
 14972       setPixel: function(i,c) {
       
 14973         var offset = i*4, data = p.imageData.data;
       
 14974         data[offset+0] = (c & 0x00ff0000) >>> 16; // RED_MASK
       
 14975         data[offset+1] = (c & 0x0000ff00) >>> 8;  // GREEN_MASK
       
 14976         data[offset+2] = (c & 0x000000ff);        // BLUE_MASK
       
 14977         data[offset+3] = (c & 0xff000000) >>> 24; // ALPHA_MASK
       
 14978       },
       
 14979       toArray: function() {
       
 14980         var arr = [], length = p.imageData.width * p.imageData.height, data = p.imageData.data;
       
 14981         for (var i = 0, offset = 0; i < length; i++, offset += 4) {
       
 14982           arr.push((data[offset+3] << 24) & 0xff000000 |
       
 14983                    (data[offset+0] << 16) & 0x00ff0000 |
       
 14984                    (data[offset+1] << 8) & 0x0000ff00 |
       
 14985                    data[offset+2] & 0x000000ff);
       
 14986         }
       
 14987         return arr;
       
 14988       },
       
 14989       set: function(arr) {
       
 14990         for (var i = 0, aL = arr.length; i < aL; i++) {
       
 14991           this.setPixel(i, arr[i]);
       
 14992         }
       
 14993       }
       
 14994     };
       
 14995 
       
 14996     // Gets a 1-Dimensional pixel array from Canvas
       
 14997     /**
       
 14998     * Loads the pixel data for the display window into the pixels[] array. This function must always be called
       
 14999     * before reading from or writing to pixels[].
       
 15000     * Certain renderers may or may not seem to require loadPixels() or updatePixels(). However, the rule is that
       
 15001     * any time you want to manipulate the pixels[] array, you must first call loadPixels(), and after changes
       
 15002     * have been made, call updatePixels(). Even if the renderer may not seem to use this function in the current
       
 15003     * Processing release, this will always be subject to change.
       
 15004     *
       
 15005     * @see pixels[]
       
 15006     * @see updatePixels
       
 15007     */
       
 15008     p.loadPixels = function() {
       
 15009       p.imageData = drawing.$ensureContext().getImageData(0, 0, p.width, p.height);
       
 15010     };
       
 15011 
       
 15012     // Draws a 1-Dimensional pixel array to Canvas
       
 15013     /**
       
 15014     * Updates the display window with the data in the pixels[] array. Use in conjunction with loadPixels(). If
       
 15015     * you're only reading pixels from the array, there's no need to call updatePixels() unless there are changes.
       
 15016     * Certain renderers may or may not seem to require loadPixels() or updatePixels(). However, the rule is that
       
 15017     * any time you want to manipulate the pixels[] array, you must first call loadPixels(), and after changes
       
 15018     * have been made, call updatePixels(). Even if the renderer may not seem to use this function in the current
       
 15019     * Processing release, this will always be subject to change.
       
 15020     * Currently, none of the renderers use the additional parameters to updatePixels(), however this may be
       
 15021     * implemented in the future.
       
 15022     *
       
 15023     * @see loadPixels
       
 15024     * @see pixels[]
       
 15025     */
       
 15026     p.updatePixels = function() {
       
 15027       if (p.imageData) {
       
 15028         drawing.$ensureContext().putImageData(p.imageData, 0, 0);
       
 15029       }
       
 15030     };
       
 15031 
       
 15032     /**
       
 15033     * Set various hints and hacks for the renderer. This is used to handle obscure rendering features that cannot be
       
 15034     * implemented in a consistent manner across renderers. Many options will often graduate to standard features
       
 15035     * instead of hints over time.
       
 15036     * hint(ENABLE_OPENGL_4X_SMOOTH) - Enable 4x anti-aliasing for OpenGL. This can help force anti-aliasing if
       
 15037     * it has not been enabled by the user. On some graphics cards, this can also be set by the graphics driver's
       
 15038     * control panel, however not all cards make this available. This hint must be called immediately after the
       
 15039     * size() command because it resets the renderer, obliterating any settings and anything drawn (and like size(),
       
 15040     * re-running the code that came before it again).
       
 15041     * hint(DISABLE_OPENGL_2X_SMOOTH) - In Processing 1.0, Processing always enables 2x smoothing when the OpenGL
       
 15042     * renderer is used. This hint disables the default 2x smoothing and returns the smoothing behavior found in
       
 15043     * earlier releases, where smooth() and noSmooth() could be used to enable and disable smoothing, though the
       
 15044     * quality was inferior.
       
 15045     * hint(ENABLE_NATIVE_FONTS) - Use the native version fonts when they are installed, rather than the bitmapped
       
 15046     * version from a .vlw file. This is useful with the JAVA2D renderer setting, as it will improve font rendering
       
 15047     * speed. This is not enabled by default, because it can be misleading while testing because the type will look
       
 15048     * great on your machine (because you have the font installed) but lousy on others' machines if the identical
       
 15049     * font is unavailable. This option can only be set per-sketch, and must be called before any use of textFont().
       
 15050     * hint(DISABLE_DEPTH_TEST) - Disable the zbuffer, allowing you to draw on top of everything at will. When depth
       
 15051     * testing is disabled, items will be drawn to the screen sequentially, like a painting. This hint is most often
       
 15052     * used to draw in 3D, then draw in 2D on top of it (for instance, to draw GUI controls in 2D on top of a 3D
       
 15053     * interface). Starting in release 0149, this will also clear the depth buffer. Restore the default with
       
 15054     * hint(ENABLE_DEPTH_TEST), but note that with the depth buffer cleared, any 3D drawing that happens later in
       
 15055     * draw() will ignore existing shapes on the screen.
       
 15056     * hint(ENABLE_DEPTH_SORT) - Enable primitive z-sorting of triangles and lines in P3D and OPENGL. This can slow
       
 15057     * performance considerably, and the algorithm is not yet perfect. Restore the default with hint(DISABLE_DEPTH_SORT).
       
 15058     * hint(DISABLE_OPENGL_ERROR_REPORT) - Speeds up the OPENGL renderer setting by not checking for errors while
       
 15059     * running. Undo with hint(ENABLE_OPENGL_ERROR_REPORT).
       
 15060     * As of release 0149, unhint() has been removed in favor of adding additional ENABLE/DISABLE constants to reset
       
 15061     * the default behavior. This prevents the double negatives, and also reinforces which hints can be enabled or disabled.
       
 15062     *
       
 15063     * @param {MODE} item          constant: name of the hint to be enabled or disabled
       
 15064     *
       
 15065     * @see PGraphics
       
 15066     * @see createGraphics
       
 15067     * @see size
       
 15068     */
       
 15069     p.hint = function(which) {
       
 15070       var curContext = drawing.$ensureContext();
       
 15071       if (which === PConstants.DISABLE_DEPTH_TEST) {
       
 15072          curContext.disable(curContext.DEPTH_TEST);
       
 15073          curContext.depthMask(false);
       
 15074          curContext.clear(curContext.DEPTH_BUFFER_BIT);
       
 15075       }
       
 15076       else if (which === PConstants.ENABLE_DEPTH_TEST) {
       
 15077          curContext.enable(curContext.DEPTH_TEST);
       
 15078          curContext.depthMask(true);
       
 15079       }
       
 15080     };
       
 15081 
       
 15082     /**
       
 15083      * The background() function sets the color used for the background of the Processing window.
       
 15084      * The default background is light gray. In the <b>draw()</b> function, the background color is used to clear the display window at the beginning of each frame.
       
 15085      * An image can also be used as the background for a sketch, however its width and height must be the same size as the sketch window.
       
 15086      * To resize an image 'b' to the size of the sketch window, use b.resize(width, height).
       
 15087      * Images used as background will ignore the current <b>tint()</b> setting.
       
 15088      * For the main drawing surface, the alpha value will be ignored. However,
       
 15089      * alpha can be used on PGraphics objects from <b>createGraphics()</b>. This is
       
 15090      * the only way to set all the pixels partially transparent, for instance.
       
 15091      * If the 'gray' parameter is passed in the function sets the background to a grayscale value, based on the
       
 15092      * current colorMode.
       
 15093      * <p>
       
 15094      * Note that background() should be called before any transformations occur,
       
 15095      * because some implementations may require the current transformation matrix
       
 15096      * to be identity before drawing.
       
 15097      *
       
 15098      * @param {int|float} gray    specifies a value between white and black
       
 15099      * @param {int|float} value1  red or hue value (depending on the current color mode)
       
 15100      * @param {int|float} value2  green or saturation value (depending on the current color mode)
       
 15101      * @param {int|float} value3  blue or brightness value (depending on the current color mode)
       
 15102      * @param {int|float} alpha   opacity of the background
       
 15103      * @param {Color} color       any value of the color datatype
       
 15104      * @param {int} hex           color value in hexadecimal notation (i.e. #FFCC00 or 0xFFFFCC00)
       
 15105      * @param {PImage} image      an instance of a PImage to use as a background
       
 15106      *
       
 15107      * @see #stroke()
       
 15108      * @see #fill()
       
 15109      * @see #tint()
       
 15110      * @see #colorMode()
       
 15111      */
       
 15112     var backgroundHelper = function(arg1, arg2, arg3, arg4) {
       
 15113       var obj;
       
 15114 
       
 15115       if (arg1 instanceof PImage || arg1.__isPImage) {
       
 15116         obj = arg1;
       
 15117 
       
 15118         if (!obj.loaded) {
       
 15119           throw "Error using image in background(): PImage not loaded.";
       
 15120         }
       
 15121         if(obj.width !== p.width || obj.height !== p.height){
       
 15122           throw "Background image must be the same dimensions as the canvas.";
       
 15123         }
       
 15124       } else {
       
 15125         obj = p.color(arg1, arg2, arg3, arg4);
       
 15126       }
       
 15127 
       
 15128       backgroundObj = obj;
       
 15129     };
       
 15130 
       
 15131     Drawing2D.prototype.background = function(arg1, arg2, arg3, arg4) {
       
 15132       if (arg1 !== undef) {
       
 15133         backgroundHelper(arg1, arg2, arg3, arg4);
       
 15134       }
       
 15135 
       
 15136       if (backgroundObj instanceof PImage || backgroundObj.__isPImage) {
       
 15137         saveContext();
       
 15138         curContext.setTransform(1, 0, 0, 1, 0, 0);
       
 15139         p.image(backgroundObj, 0, 0);
       
 15140         restoreContext();
       
 15141       } else {
       
 15142         saveContext();
       
 15143         curContext.setTransform(1, 0, 0, 1, 0, 0);
       
 15144 
       
 15145         // If the background is transparent
       
 15146         if (p.alpha(backgroundObj) !== colorModeA) {
       
 15147           curContext.clearRect(0,0, p.width, p.height);
       
 15148         }
       
 15149         curContext.fillStyle = p.color.toString(backgroundObj);
       
 15150         curContext.fillRect(0, 0, p.width, p.height);
       
 15151         isFillDirty = true;
       
 15152         restoreContext();
       
 15153       }
       
 15154     };
       
 15155 
       
 15156     Drawing3D.prototype.background = function(arg1, arg2, arg3, arg4) {
       
 15157       if (arguments.length > 0) {
       
 15158         backgroundHelper(arg1, arg2, arg3, arg4);
       
 15159       }
       
 15160 
       
 15161       var c = p.color.toGLArray(backgroundObj);
       
 15162       curContext.clearColor(c[0], c[1], c[2], c[3]);
       
 15163       curContext.clear(curContext.COLOR_BUFFER_BIT | curContext.DEPTH_BUFFER_BIT);
       
 15164 
       
 15165       // An image as a background in 3D is not implemented yet
       
 15166     };
       
 15167 
       
 15168     // Draws an image to the Canvas
       
 15169     /**
       
 15170     * Displays images to the screen. The images must be in the sketch's "data" directory to load correctly. Select "Add
       
 15171     * file..." from the "Sketch" menu to add the image. Processing currently works with GIF, JPEG, and Targa images. The
       
 15172     * color of an image may be modified with the tint() function and if a GIF has transparency, it will maintain its
       
 15173     * transparency. The img parameter specifies the image to display and the x and y parameters define the location of
       
 15174     * the image from its upper-left corner. The image is displayed at its original size unless the width and height
       
 15175     * parameters specify a different size. The imageMode() function changes the way the parameters work. A call to
       
 15176     * imageMode(CORNERS) will change the width and height parameters to define the x and y values of the opposite
       
 15177     * corner of the image.
       
 15178     *
       
 15179     * @param {PImage} img            the image to display
       
 15180     * @param {int|float} x           x-coordinate of the image
       
 15181     * @param {int|float} y           y-coordinate of the image
       
 15182     * @param {int|float} width       width to display the image
       
 15183     * @param {int|float} height      height to display the image
       
 15184     *
       
 15185     * @see loadImage
       
 15186     * @see PImage
       
 15187     * @see imageMode
       
 15188     * @see tint
       
 15189     * @see background
       
 15190     * @see alpha
       
 15191     */
       
 15192     Drawing2D.prototype.image = function(img, x, y, w, h) {
       
 15193       // Fix fractional positions
       
 15194       x = Math.round(x);
       
 15195       y = Math.round(y);
       
 15196 
       
 15197       if (img.width > 0) {
       
 15198         var wid = w || img.width;
       
 15199         var hgt = h || img.height;
       
 15200 
       
 15201         var bounds = imageModeConvert(x || 0, y || 0, w || img.width, h || img.height, arguments.length < 4);
       
 15202         var fastImage = !!img.sourceImg && curTint === null;
       
 15203         if (fastImage) {
       
 15204           var htmlElement = img.sourceImg;
       
 15205           if (img.__isDirty) {
       
 15206             img.updatePixels();
       
 15207           }
       
 15208           // Using HTML element's width and height in case if the image was resized.
       
 15209           curContext.drawImage(htmlElement, 0, 0,
       
 15210             htmlElement.width, htmlElement.height, bounds.x, bounds.y, bounds.w, bounds.h);
       
 15211         } else {
       
 15212           var obj = img.toImageData();
       
 15213 
       
 15214           // Tint the image
       
 15215           if (curTint !== null) {
       
 15216             curTint(obj);
       
 15217             img.__isDirty = true;
       
 15218           }
       
 15219 
       
 15220           curContext.drawImage(getCanvasData(obj).canvas, 0, 0,
       
 15221             img.width, img.height, bounds.x, bounds.y, bounds.w, bounds.h);
       
 15222         }
       
 15223       }
       
 15224     };
       
 15225 
       
 15226     Drawing3D.prototype.image = function(img, x, y, w, h) {
       
 15227       if (img.width > 0) {
       
 15228         // Fix fractional positions
       
 15229         x = Math.round(x);
       
 15230         y = Math.round(y);
       
 15231         w = w || img.width;
       
 15232         h = h || img.height;
       
 15233 
       
 15234         p.beginShape(p.QUADS);
       
 15235         p.texture(img);
       
 15236         p.vertex(x, y, 0, 0, 0);
       
 15237         p.vertex(x, y+h, 0, 0, h);
       
 15238         p.vertex(x+w, y+h, 0, w, h);
       
 15239         p.vertex(x+w, y, 0, w, 0);
       
 15240         p.endShape();
       
 15241       }
       
 15242     };
       
 15243 
       
 15244     /**
       
 15245      * The tint() function sets the fill value for displaying images. Images can be tinted to
       
 15246      * specified colors or made transparent by setting the alpha.
       
 15247      * <br><br>To make an image transparent, but not change it's color,
       
 15248      * use white as the tint color and specify an alpha value. For instance,
       
 15249      * tint(255, 128) will make an image 50% transparent (unless
       
 15250      * <b>colorMode()</b> has been used).
       
 15251      *
       
 15252      * <br><br>When using hexadecimal notation to specify a color, use "#" or
       
 15253      * "0x" before the values (e.g. #CCFFAA, 0xFFCCFFAA). The # syntax uses six
       
 15254      * digits to specify a color (the way colors are specified in HTML and CSS).
       
 15255      * When using the hexadecimal notation starting with "0x", the hexadecimal
       
 15256      * value must be specified with eight characters; the first two characters
       
 15257      * define the alpha component and the remainder the red, green, and blue
       
 15258      * components.
       
 15259      * <br><br>The value for the parameter "gray" must be less than or equal
       
 15260      * to the current maximum value as specified by <b>colorMode()</b>.
       
 15261      * The default maximum value is 255.
       
 15262      * <br><br>The tint() method is also used to control the coloring of
       
 15263      * textures in 3D.
       
 15264      *
       
 15265      * @param {int|float} gray    any valid number
       
 15266      * @param {int|float} alpha    opacity of the image
       
 15267      * @param {int|float} value1  red or hue value
       
 15268      * @param {int|float} value2  green or saturation value
       
 15269      * @param {int|float} value3  blue or brightness value
       
 15270      * @param {int|float} color    any value of the color datatype
       
 15271      * @param {int} hex            color value in hexadecimal notation (i.e. #FFCC00 or 0xFFFFCC00)
       
 15272      *
       
 15273      * @see #noTint()
       
 15274      * @see #image()
       
 15275      */
       
 15276     p.tint = function(a1, a2, a3, a4) {
       
 15277       var tintColor = p.color(a1, a2, a3, a4);
       
 15278       var r = p.red(tintColor) / colorModeX;
       
 15279       var g = p.green(tintColor) / colorModeY;
       
 15280       var b = p.blue(tintColor) / colorModeZ;
       
 15281       var a = p.alpha(tintColor) / colorModeA;
       
 15282       curTint = function(obj) {
       
 15283         var data = obj.data,
       
 15284             length = 4 * obj.width * obj.height;
       
 15285         for (var i = 0; i < length;) {
       
 15286           data[i++] *= r;
       
 15287           data[i++] *= g;
       
 15288           data[i++] *= b;
       
 15289           data[i++] *= a;
       
 15290         }
       
 15291       };
       
 15292       // for overriding the color buffer when 3d rendering
       
 15293       curTint3d = function(data){
       
 15294         for (var i = 0; i < data.length;) {
       
 15295           data[i++] = r;
       
 15296           data[i++] = g;
       
 15297           data[i++] = b;
       
 15298           data[i++] = a;
       
 15299         }
       
 15300       };
       
 15301     };
       
 15302 
       
 15303     /**
       
 15304      * The noTint() function removes the current fill value for displaying images and reverts to displaying images with their original hues.
       
 15305      *
       
 15306      * @see #tint()
       
 15307      * @see #image()
       
 15308      */
       
 15309     p.noTint = function() {
       
 15310       curTint = null;
       
 15311       curTint3d = null;
       
 15312     };
       
 15313 
       
 15314     /**
       
 15315     * Copies a region of pixels from the display window to another area of the display window and copies a region of pixels from an
       
 15316     * image used as the srcImg  parameter into the display window. If the source and destination regions aren't the same size, it will
       
 15317     * automatically resize the source pixels to fit the specified target region. No alpha information is used in the process, however
       
 15318     * if the source image has an alpha channel set, it will be copied as well. This function ignores imageMode().
       
 15319     *
       
 15320     * @param {int} x            X coordinate of the source's upper left corner
       
 15321     * @param {int} y            Y coordinate of the source's upper left corner
       
 15322     * @param {int} width        source image width
       
 15323     * @param {int} height       source image height
       
 15324     * @param {int} dx           X coordinate of the destination's upper left corner
       
 15325     * @param {int} dy           Y coordinate of the destination's upper left corner
       
 15326     * @param {int} dwidth       destination image width
       
 15327     * @param {int} dheight      destination image height
       
 15328     * @param {PImage} srcImg    image variable referring to the source image
       
 15329     *
       
 15330     * @see blend
       
 15331     * @see get
       
 15332     */
       
 15333     p.copy = function(src, sx, sy, sw, sh, dx, dy, dw, dh) {
       
 15334       if (dh === undef) {
       
 15335         // shift everything, and introduce p
       
 15336         dh = dw;
       
 15337         dw = dy;
       
 15338         dy = dx;
       
 15339         dx = sh;
       
 15340         sh = sw;
       
 15341         sw = sy;
       
 15342         sy = sx;
       
 15343         sx = src;
       
 15344         src = p;
       
 15345       }
       
 15346       p.blend(src, sx, sy, sw, sh, dx, dy, dw, dh, PConstants.REPLACE);
       
 15347     };
       
 15348 
       
 15349     /**
       
 15350     * Blends a region of pixels from one image into another (or in itself again) with full alpha channel support. There
       
 15351     * is a choice of the following modes to blend the source pixels (A) with the ones of pixels in the destination image (B):
       
 15352     * BLEND - linear interpolation of colours: C = A*factor + B
       
 15353     * ADD - additive blending with white clip: C = min(A*factor + B, 255)
       
 15354     * SUBTRACT - subtractive blending with black clip: C = max(B - A*factor, 0)
       
 15355     * DARKEST - only the darkest colour succeeds: C = min(A*factor, B)
       
 15356     * LIGHTEST - only the lightest colour succeeds: C = max(A*factor, B)
       
 15357     * DIFFERENCE - subtract colors from underlying image.
       
 15358     * EXCLUSION - similar to DIFFERENCE, but less extreme.
       
 15359     * MULTIPLY - Multiply the colors, result will always be darker.
       
 15360     * SCREEN - Opposite multiply, uses inverse values of the colors.
       
 15361     * OVERLAY - A mix of MULTIPLY and SCREEN. Multiplies dark values, and screens light values.
       
 15362     * HARD_LIGHT - SCREEN when greater than 50% gray, MULTIPLY when lower.
       
 15363     * SOFT_LIGHT - Mix of DARKEST and LIGHTEST. Works like OVERLAY, but not as harsh.
       
 15364     * DODGE - Lightens light tones and increases contrast, ignores darks. Called "Color Dodge" in Illustrator and Photoshop.
       
 15365     * BURN - Darker areas are applied, increasing contrast, ignores lights. Called "Color Burn" in Illustrator and Photoshop.
       
 15366     * All modes use the alpha information (highest byte) of source image pixels as the blending factor. If the source and
       
 15367     * destination regions are different sizes, the image will be automatically resized to match the destination size. If the
       
 15368     * srcImg parameter is not used, the display window is used as the source image.  This function ignores imageMode().
       
 15369     *
       
 15370     * @param {int} x            X coordinate of the source's upper left corner
       
 15371     * @param {int} y            Y coordinate of the source's upper left corner
       
 15372     * @param {int} width        source image width
       
 15373     * @param {int} height       source image height
       
 15374     * @param {int} dx           X coordinate of the destination's upper left corner
       
 15375     * @param {int} dy           Y coordinate of the destination's upper left corner
       
 15376     * @param {int} dwidth       destination image width
       
 15377     * @param {int} dheight      destination image height
       
 15378     * @param {PImage} srcImg    image variable referring to the source image
       
 15379     * @param {PImage} MODE      Either BLEND, ADD, SUBTRACT, LIGHTEST, DARKEST, DIFFERENCE, EXCLUSION, MULTIPLY, SCREEN,
       
 15380     *                           OVERLAY, HARD_LIGHT, SOFT_LIGHT, DODGE, BURN
       
 15381     * @see filter
       
 15382     */
       
 15383     p.blend = function(src, sx, sy, sw, sh, dx, dy, dw, dh, mode, pimgdest) {
       
 15384       if (src.isRemote) {
       
 15385         throw "Image is loaded remotely. Cannot blend image.";
       
 15386       }
       
 15387 
       
 15388       if (mode === undef) {
       
 15389         // shift everything, and introduce p
       
 15390         mode = dh;
       
 15391         dh = dw;
       
 15392         dw = dy;
       
 15393         dy = dx;
       
 15394         dx = sh;
       
 15395         sh = sw;
       
 15396         sw = sy;
       
 15397         sy = sx;
       
 15398         sx = src;
       
 15399         src = p;
       
 15400       }
       
 15401 
       
 15402       var sx2 = sx + sw,
       
 15403         sy2 = sy + sh,
       
 15404         dx2 = dx + dw,
       
 15405         dy2 = dy + dh,
       
 15406         dest = pimgdest || p;
       
 15407 
       
 15408       // check if pimgdest is there and pixels, if so this was a call from pimg.blend
       
 15409       if (pimgdest === undef || mode === undef) {
       
 15410         p.loadPixels();
       
 15411       }
       
 15412 
       
 15413       src.loadPixels();
       
 15414 
       
 15415       if (src === p && p.intersect(sx, sy, sx2, sy2, dx, dy, dx2, dy2)) {
       
 15416         p.blit_resize(p.get(sx, sy, sx2 - sx, sy2 - sy), 0, 0, sx2 - sx - 1, sy2 - sy - 1,
       
 15417                       dest.imageData.data, dest.width, dest.height, dx, dy, dx2, dy2, mode);
       
 15418       } else {
       
 15419         p.blit_resize(src, sx, sy, sx2, sy2, dest.imageData.data, dest.width, dest.height, dx, dy, dx2, dy2, mode);
       
 15420       }
       
 15421 
       
 15422       if (pimgdest === undef) {
       
 15423         p.updatePixels();
       
 15424       }
       
 15425     };
       
 15426 
       
 15427     // helper function for filter()
       
 15428     var buildBlurKernel = function(r) {
       
 15429       var radius = p.floor(r * 3.5), i, radiusi;
       
 15430       radius = (radius < 1) ? 1 : ((radius < 248) ? radius : 248);
       
 15431       if (p.shared.blurRadius !== radius) {
       
 15432         p.shared.blurRadius = radius;
       
 15433         p.shared.blurKernelSize = 1 + (p.shared.blurRadius<<1);
       
 15434         p.shared.blurKernel = new Float32Array(p.shared.blurKernelSize);
       
 15435         var sharedBlurKernal = p.shared.blurKernel;
       
 15436         var sharedBlurKernelSize = p.shared.blurKernelSize;
       
 15437         var sharedBlurRadius = p.shared.blurRadius;
       
 15438         // init blurKernel
       
 15439         for (i = 0; i < sharedBlurKernelSize; i++) {
       
 15440           sharedBlurKernal[i] = 0;
       
 15441         }
       
 15442         var radiusiSquared = (radius - 1) * (radius - 1);
       
 15443         for (i = 1; i < radius; i++) {
       
 15444           sharedBlurKernal[radius + i] = sharedBlurKernal[radiusi] = radiusiSquared;
       
 15445         }
       
 15446         sharedBlurKernal[radius] = radius * radius;
       
 15447       }
       
 15448     };
       
 15449 
       
 15450     var blurARGB = function(r, aImg) {
       
 15451       var sum, cr, cg, cb, ca, c, m;
       
 15452       var read, ri, ym, ymi, bk0;
       
 15453       var wh = aImg.pixels.getLength();
       
 15454       var r2 = new Float32Array(wh);
       
 15455       var g2 = new Float32Array(wh);
       
 15456       var b2 = new Float32Array(wh);
       
 15457       var a2 = new Float32Array(wh);
       
 15458       var yi = 0;
       
 15459       var x, y, i, offset;
       
 15460 
       
 15461       buildBlurKernel(r);
       
 15462 
       
 15463       var aImgHeight = aImg.height;
       
 15464       var aImgWidth = aImg.width;
       
 15465       var sharedBlurKernelSize = p.shared.blurKernelSize;
       
 15466       var sharedBlurRadius = p.shared.blurRadius;
       
 15467       var sharedBlurKernal = p.shared.blurKernel;
       
 15468       var pix = aImg.imageData.data;
       
 15469 
       
 15470       for (y = 0; y < aImgHeight; y++) {
       
 15471         for (x = 0; x < aImgWidth; x++) {
       
 15472           cb = cg = cr = ca = sum = 0;
       
 15473           read = x - sharedBlurRadius;
       
 15474           if (read<0) {
       
 15475             bk0 = -read;
       
 15476             read = 0;
       
 15477           } else {
       
 15478             if (read >= aImgWidth) {
       
 15479               break;
       
 15480             }
       
 15481             bk0=0;
       
 15482           }
       
 15483           for (i = bk0; i < sharedBlurKernelSize; i++) {
       
 15484             if (read >= aImgWidth) {
       
 15485               break;
       
 15486             }
       
 15487             offset = (read + yi) *4;
       
 15488             m = sharedBlurKernal[i];
       
 15489             ca += m * pix[offset + 3];
       
 15490             cr += m * pix[offset];
       
 15491             cg += m * pix[offset + 1];
       
 15492             cb += m * pix[offset + 2];
       
 15493             sum += m;
       
 15494             read++;
       
 15495           }
       
 15496           ri = yi + x;
       
 15497           a2[ri] = ca / sum;
       
 15498           r2[ri] = cr / sum;
       
 15499           g2[ri] = cg / sum;
       
 15500           b2[ri] = cb / sum;
       
 15501         }
       
 15502         yi += aImgWidth;
       
 15503       }
       
 15504 
       
 15505       yi = 0;
       
 15506       ym = -sharedBlurRadius;
       
 15507       ymi = ym*aImgWidth;
       
 15508 
       
 15509       for (y = 0; y < aImgHeight; y++) {
       
 15510         for (x = 0; x < aImgWidth; x++) {
       
 15511           cb = cg = cr = ca = sum = 0;
       
 15512           if (ym<0) {
       
 15513             bk0 = ri = -ym;
       
 15514             read = x;
       
 15515           } else {
       
 15516             if (ym >= aImgHeight) {
       
 15517               break;
       
 15518             }
       
 15519             bk0 = 0;
       
 15520             ri = ym;
       
 15521             read = x + ymi;
       
 15522           }
       
 15523           for (i = bk0; i < sharedBlurKernelSize; i++) {
       
 15524             if (ri >= aImgHeight) {
       
 15525               break;
       
 15526             }
       
 15527             m = sharedBlurKernal[i];
       
 15528             ca += m * a2[read];
       
 15529             cr += m * r2[read];
       
 15530             cg += m * g2[read];
       
 15531             cb += m * b2[read];
       
 15532             sum += m;
       
 15533             ri++;
       
 15534             read += aImgWidth;
       
 15535           }
       
 15536           offset = (x + yi) *4;
       
 15537           pix[offset] = cr / sum;
       
 15538           pix[offset + 1] = cg / sum;
       
 15539           pix[offset + 2] = cb / sum;
       
 15540           pix[offset + 3] = ca / sum;
       
 15541         }
       
 15542         yi += aImgWidth;
       
 15543         ymi += aImgWidth;
       
 15544         ym++;
       
 15545       }
       
 15546     };
       
 15547 
       
 15548     // helper funtion for ERODE and DILATE modes of filter()
       
 15549     var dilate = function(isInverted, aImg) {
       
 15550       var currIdx = 0;
       
 15551       var maxIdx = aImg.pixels.getLength();
       
 15552       var out = new Int32Array(maxIdx);
       
 15553       var currRowIdx, maxRowIdx, colOrig, colOut, currLum;
       
 15554       var idxRight, idxLeft, idxUp, idxDown,
       
 15555           colRight, colLeft, colUp, colDown,
       
 15556           lumRight, lumLeft, lumUp, lumDown;
       
 15557 
       
 15558       if (!isInverted) {
       
 15559         // erosion (grow light areas)
       
 15560         while (currIdx<maxIdx) {
       
 15561           currRowIdx = currIdx;
       
 15562           maxRowIdx = currIdx + aImg.width;
       
 15563           while (currIdx < maxRowIdx) {
       
 15564             colOrig = colOut = aImg.pixels.getPixel(currIdx);
       
 15565             idxLeft = currIdx - 1;
       
 15566             idxRight = currIdx + 1;
       
 15567             idxUp = currIdx - aImg.width;
       
 15568             idxDown = currIdx + aImg.width;
       
 15569             if (idxLeft < currRowIdx) {
       
 15570               idxLeft = currIdx;
       
 15571             }
       
 15572             if (idxRight >= maxRowIdx) {
       
 15573               idxRight = currIdx;
       
 15574             }
       
 15575             if (idxUp < 0) {
       
 15576               idxUp = 0;
       
 15577             }
       
 15578             if (idxDown >= maxIdx) {
       
 15579               idxDown = currIdx;
       
 15580             }
       
 15581             colUp = aImg.pixels.getPixel(idxUp);
       
 15582             colLeft = aImg.pixels.getPixel(idxLeft);
       
 15583             colDown = aImg.pixels.getPixel(idxDown);
       
 15584             colRight = aImg.pixels.getPixel(idxRight);
       
 15585 
       
 15586             // compute luminance
       
 15587             currLum = 77*(colOrig>>16&0xff) + 151*(colOrig>>8&0xff) + 28*(colOrig&0xff);
       
 15588             lumLeft = 77*(colLeft>>16&0xff) + 151*(colLeft>>8&0xff) + 28*(colLeft&0xff);
       
 15589             lumRight = 77*(colRight>>16&0xff) + 151*(colRight>>8&0xff) + 28*(colRight&0xff);
       
 15590             lumUp = 77*(colUp>>16&0xff) + 151*(colUp>>8&0xff) + 28*(colUp&0xff);
       
 15591             lumDown = 77*(colDown>>16&0xff) + 151*(colDown>>8&0xff) + 28*(colDown&0xff);
       
 15592 
       
 15593             if (lumLeft > currLum) {
       
 15594               colOut = colLeft;
       
 15595               currLum = lumLeft;
       
 15596             }
       
 15597             if (lumRight > currLum) {
       
 15598               colOut = colRight;
       
 15599               currLum = lumRight;
       
 15600             }
       
 15601             if (lumUp > currLum) {
       
 15602               colOut = colUp;
       
 15603               currLum = lumUp;
       
 15604             }
       
 15605             if (lumDown > currLum) {
       
 15606               colOut = colDown;
       
 15607               currLum = lumDown;
       
 15608             }
       
 15609             out[currIdx++] = colOut;
       
 15610           }
       
 15611         }
       
 15612       } else {
       
 15613         // dilate (grow dark areas)
       
 15614         while (currIdx < maxIdx) {
       
 15615           currRowIdx = currIdx;
       
 15616           maxRowIdx = currIdx + aImg.width;
       
 15617           while (currIdx < maxRowIdx) {
       
 15618             colOrig = colOut = aImg.pixels.getPixel(currIdx);
       
 15619             idxLeft = currIdx - 1;
       
 15620             idxRight = currIdx + 1;
       
 15621             idxUp = currIdx - aImg.width;
       
 15622             idxDown = currIdx + aImg.width;
       
 15623             if (idxLeft < currRowIdx) {
       
 15624               idxLeft = currIdx;
       
 15625             }
       
 15626             if (idxRight >= maxRowIdx) {
       
 15627               idxRight = currIdx;
       
 15628             }
       
 15629             if (idxUp < 0) {
       
 15630               idxUp = 0;
       
 15631             }
       
 15632             if (idxDown >= maxIdx) {
       
 15633               idxDown = currIdx;
       
 15634             }
       
 15635             colUp = aImg.pixels.getPixel(idxUp);
       
 15636             colLeft = aImg.pixels.getPixel(idxLeft);
       
 15637             colDown = aImg.pixels.getPixel(idxDown);
       
 15638             colRight = aImg.pixels.getPixel(idxRight);
       
 15639 
       
 15640             // compute luminance
       
 15641             currLum = 77*(colOrig>>16&0xff) + 151*(colOrig>>8&0xff) + 28*(colOrig&0xff);
       
 15642             lumLeft = 77*(colLeft>>16&0xff) + 151*(colLeft>>8&0xff) + 28*(colLeft&0xff);
       
 15643             lumRight = 77*(colRight>>16&0xff) + 151*(colRight>>8&0xff) + 28*(colRight&0xff);
       
 15644             lumUp = 77*(colUp>>16&0xff) + 151*(colUp>>8&0xff) + 28*(colUp&0xff);
       
 15645             lumDown = 77*(colDown>>16&0xff) + 151*(colDown>>8&0xff) + 28*(colDown&0xff);
       
 15646 
       
 15647             if (lumLeft < currLum) {
       
 15648               colOut = colLeft;
       
 15649               currLum = lumLeft;
       
 15650             }
       
 15651             if (lumRight < currLum) {
       
 15652               colOut = colRight;
       
 15653               currLum = lumRight;
       
 15654             }
       
 15655             if (lumUp < currLum) {
       
 15656               colOut = colUp;
       
 15657               currLum = lumUp;
       
 15658             }
       
 15659             if (lumDown < currLum) {
       
 15660               colOut = colDown;
       
 15661               currLum = lumDown;
       
 15662             }
       
 15663             out[currIdx++]=colOut;
       
 15664           }
       
 15665         }
       
 15666       }
       
 15667       aImg.pixels.set(out);
       
 15668       //p.arraycopy(out,0,pixels,0,maxIdx);
       
 15669     };
       
 15670 
       
 15671     /**
       
 15672     * Filters the display window as defined by one of the following modes:
       
 15673     * THRESHOLD - converts the image to black and white pixels depending if they are above or below the threshold
       
 15674     * defined by the level parameter. The level must be between 0.0 (black) and 1.0(white). If no level is specified, 0.5 is used.
       
 15675     * GRAY - converts any colors in the image to grayscale equivalents
       
 15676     * INVERT - sets each pixel to its inverse value
       
 15677     * POSTERIZE - limits each channel of the image to the number of colors specified as the level parameter
       
 15678     * BLUR - executes a Guassian blur with the level parameter specifying the extent of the blurring. If no level parameter is
       
 15679     * used, the blur is equivalent to Guassian blur of radius 1.
       
 15680     * OPAQUE - sets the alpha channel to entirely opaque.
       
 15681     * ERODE - reduces the light areas with the amount defined by the level parameter.
       
 15682     * DILATE - increases the light areas with the amount defined by the level parameter.
       
 15683     *
       
 15684     * @param {MODE} MODE          Either THRESHOLD, GRAY, INVERT, POSTERIZE, BLUR, OPAQUE, ERODE, or DILATE
       
 15685     * @param {int|float} level    defines the quality of the filter
       
 15686     *
       
 15687     * @see blend
       
 15688     */
       
 15689     p.filter = function(kind, param, aImg){
       
 15690       var img, col, lum, i;
       
 15691 
       
 15692       if (arguments.length === 3) {
       
 15693         aImg.loadPixels();
       
 15694         img = aImg;
       
 15695       } else {
       
 15696         p.loadPixels();
       
 15697         img = p;
       
 15698       }
       
 15699 
       
 15700       if (param === undef) {
       
 15701         param = null;
       
 15702       }
       
 15703       if (img.isRemote) { // Remote images cannot access imageData
       
 15704         throw "Image is loaded remotely. Cannot filter image.";
       
 15705       }
       
 15706       // begin filter process
       
 15707       var imglen = img.pixels.getLength();
       
 15708       switch (kind) {
       
 15709         case PConstants.BLUR:
       
 15710           var radius = param || 1; // if no param specified, use 1 (default for p5)
       
 15711           blurARGB(radius, img);
       
 15712           break;
       
 15713 
       
 15714         case PConstants.GRAY:
       
 15715           if (img.format === PConstants.ALPHA) { //trouble
       
 15716             // for an alpha image, convert it to an opaque grayscale
       
 15717             for (i = 0; i < imglen; i++) {
       
 15718               col = 255 - img.pixels.getPixel(i);
       
 15719               img.pixels.setPixel(i,(0xff000000 | (col << 16) | (col << 8) | col));
       
 15720             }
       
 15721             img.format = PConstants.RGB; //trouble
       
 15722           } else {
       
 15723             for (i = 0; i < imglen; i++) {
       
 15724               col = img.pixels.getPixel(i);
       
 15725               lum = (77*(col>>16&0xff) + 151*(col>>8&0xff) + 28*(col&0xff))>>8;
       
 15726               img.pixels.setPixel(i,((col & PConstants.ALPHA_MASK) | lum<<16 | lum<<8 | lum));
       
 15727             }
       
 15728           }
       
 15729           break;
       
 15730 
       
 15731         case PConstants.INVERT:
       
 15732           for (i = 0; i < imglen; i++) {
       
 15733             img.pixels.setPixel(i, (img.pixels.getPixel(i) ^ 0xffffff));
       
 15734           }
       
 15735           break;
       
 15736 
       
 15737         case PConstants.POSTERIZE:
       
 15738           if (param === null) {
       
 15739             throw "Use filter(POSTERIZE, int levels) instead of filter(POSTERIZE)";
       
 15740           }
       
 15741           var levels = p.floor(param);
       
 15742           if ((levels < 2) || (levels > 255)) {
       
 15743             throw "Levels must be between 2 and 255 for filter(POSTERIZE, levels)";
       
 15744           }
       
 15745           var levels1 = levels - 1;
       
 15746           for (i = 0; i < imglen; i++) {
       
 15747             var rlevel = (img.pixels.getPixel(i) >> 16) & 0xff;
       
 15748             var glevel = (img.pixels.getPixel(i) >> 8) & 0xff;
       
 15749             var blevel = img.pixels.getPixel(i) & 0xff;
       
 15750             rlevel = (((rlevel * levels) >> 8) * 255) / levels1;
       
 15751             glevel = (((glevel * levels) >> 8) * 255) / levels1;
       
 15752             blevel = (((blevel * levels) >> 8) * 255) / levels1;
       
 15753             img.pixels.setPixel(i, ((0xff000000 & img.pixels.getPixel(i)) | (rlevel << 16) | (glevel << 8) | blevel));
       
 15754           }
       
 15755           break;
       
 15756 
       
 15757         case PConstants.OPAQUE:
       
 15758           for (i = 0; i < imglen; i++) {
       
 15759             img.pixels.setPixel(i, (img.pixels.getPixel(i) | 0xff000000));
       
 15760           }
       
 15761           img.format = PConstants.RGB; //trouble
       
 15762           break;
       
 15763 
       
 15764         case PConstants.THRESHOLD:
       
 15765           if (param === null) {
       
 15766             param = 0.5;
       
 15767           }
       
 15768           if ((param < 0) || (param > 1)) {
       
 15769             throw "Level must be between 0 and 1 for filter(THRESHOLD, level)";
       
 15770           }
       
 15771           var thresh = p.floor(param * 255);
       
 15772           for (i = 0; i < imglen; i++) {
       
 15773             var max = p.max((img.pixels.getPixel(i) & PConstants.RED_MASK) >> 16, p.max((img.pixels.getPixel(i) & PConstants.GREEN_MASK) >> 8, (img.pixels.getPixel(i) & PConstants.BLUE_MASK)));
       
 15774             img.pixels.setPixel(i, ((img.pixels.getPixel(i) & PConstants.ALPHA_MASK) | ((max < thresh) ? 0x000000 : 0xffffff)));
       
 15775           }
       
 15776           break;
       
 15777 
       
 15778         case PConstants.ERODE:
       
 15779           dilate(true, img);
       
 15780           break;
       
 15781 
       
 15782         case PConstants.DILATE:
       
 15783           dilate(false, img);
       
 15784           break;
       
 15785       }
       
 15786       img.updatePixels();
       
 15787     };
       
 15788 
       
 15789 
       
 15790     // shared variables for blit_resize(), filter_new_scanline(), filter_bilinear(), filter()
       
 15791     // change this in the future to not be exposed to p
       
 15792     p.shared = {
       
 15793       fracU: 0,
       
 15794       ifU: 0,
       
 15795       fracV: 0,
       
 15796       ifV: 0,
       
 15797       u1: 0,
       
 15798       u2: 0,
       
 15799       v1: 0,
       
 15800       v2: 0,
       
 15801       sX: 0,
       
 15802       sY: 0,
       
 15803       iw: 0,
       
 15804       iw1: 0,
       
 15805       ih1: 0,
       
 15806       ul: 0,
       
 15807       ll: 0,
       
 15808       ur: 0,
       
 15809       lr: 0,
       
 15810       cUL: 0,
       
 15811       cLL: 0,
       
 15812       cUR: 0,
       
 15813       cLR: 0,
       
 15814       srcXOffset: 0,
       
 15815       srcYOffset: 0,
       
 15816       r: 0,
       
 15817       g: 0,
       
 15818       b: 0,
       
 15819       a: 0,
       
 15820       srcBuffer: null,
       
 15821       blurRadius: 0,
       
 15822       blurKernelSize: 0,
       
 15823       blurKernel: null
       
 15824     };
       
 15825 
       
 15826     p.intersect = function(sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2) {
       
 15827       var sw = sx2 - sx1 + 1;
       
 15828       var sh = sy2 - sy1 + 1;
       
 15829       var dw = dx2 - dx1 + 1;
       
 15830       var dh = dy2 - dy1 + 1;
       
 15831       if (dx1 < sx1) {
       
 15832         dw += dx1 - sx1;
       
 15833         if (dw > sw) {
       
 15834           dw = sw;
       
 15835         }
       
 15836       } else {
       
 15837         var w = sw + sx1 - dx1;
       
 15838         if (dw > w) {
       
 15839           dw = w;
       
 15840         }
       
 15841       }
       
 15842       if (dy1 < sy1) {
       
 15843         dh += dy1 - sy1;
       
 15844         if (dh > sh) {
       
 15845           dh = sh;
       
 15846         }
       
 15847       } else {
       
 15848         var h = sh + sy1 - dy1;
       
 15849         if (dh > h) {
       
 15850           dh = h;
       
 15851         }
       
 15852       }
       
 15853       return ! (dw <= 0 || dh <= 0);
       
 15854     };
       
 15855 
       
 15856     var blendFuncs = {};
       
 15857     blendFuncs[PConstants.BLEND] = p.modes.blend;
       
 15858     blendFuncs[PConstants.ADD] = p.modes.add;
       
 15859     blendFuncs[PConstants.SUBTRACT] = p.modes.subtract;
       
 15860     blendFuncs[PConstants.LIGHTEST] = p.modes.lightest;
       
 15861     blendFuncs[PConstants.DARKEST] = p.modes.darkest;
       
 15862     blendFuncs[PConstants.REPLACE] = p.modes.replace;
       
 15863     blendFuncs[PConstants.DIFFERENCE] = p.modes.difference;
       
 15864     blendFuncs[PConstants.EXCLUSION] = p.modes.exclusion;
       
 15865     blendFuncs[PConstants.MULTIPLY] = p.modes.multiply;
       
 15866     blendFuncs[PConstants.SCREEN] = p.modes.screen;
       
 15867     blendFuncs[PConstants.OVERLAY] = p.modes.overlay;
       
 15868     blendFuncs[PConstants.HARD_LIGHT] = p.modes.hard_light;
       
 15869     blendFuncs[PConstants.SOFT_LIGHT] = p.modes.soft_light;
       
 15870     blendFuncs[PConstants.DODGE] = p.modes.dodge;
       
 15871     blendFuncs[PConstants.BURN] = p.modes.burn;
       
 15872 
       
 15873     p.blit_resize = function(img, srcX1, srcY1, srcX2, srcY2, destPixels,
       
 15874                              screenW, screenH, destX1, destY1, destX2, destY2, mode) {
       
 15875       var x, y;
       
 15876       if (srcX1 < 0) {
       
 15877         srcX1 = 0;
       
 15878       }
       
 15879       if (srcY1 < 0) {
       
 15880         srcY1 = 0;
       
 15881       }
       
 15882       if (srcX2 >= img.width) {
       
 15883         srcX2 = img.width - 1;
       
 15884       }
       
 15885       if (srcY2 >= img.height) {
       
 15886         srcY2 = img.height - 1;
       
 15887       }
       
 15888       var srcW = srcX2 - srcX1;
       
 15889       var srcH = srcY2 - srcY1;
       
 15890       var destW = destX2 - destX1;
       
 15891       var destH = destY2 - destY1;
       
 15892 
       
 15893       if (destW <= 0 || destH <= 0 || srcW <= 0 || srcH <= 0 || destX1 >= screenW ||
       
 15894           destY1 >= screenH || srcX1 >= img.width || srcY1 >= img.height) {
       
 15895         return;
       
 15896       }
       
 15897 
       
 15898       var dx = Math.floor(srcW / destW * PConstants.PRECISIONF);
       
 15899       var dy = Math.floor(srcH / destH * PConstants.PRECISIONF);
       
 15900 
       
 15901       var pshared = p.shared;
       
 15902 
       
 15903       pshared.srcXOffset = Math.floor(destX1 < 0 ? -destX1 * dx : srcX1 * PConstants.PRECISIONF);
       
 15904       pshared.srcYOffset = Math.floor(destY1 < 0 ? -destY1 * dy : srcY1 * PConstants.PRECISIONF);
       
 15905       if (destX1 < 0) {
       
 15906         destW += destX1;
       
 15907         destX1 = 0;
       
 15908       }
       
 15909       if (destY1 < 0) {
       
 15910         destH += destY1;
       
 15911         destY1 = 0;
       
 15912       }
       
 15913       destW = Math.min(destW, screenW - destX1);
       
 15914       destH = Math.min(destH, screenH - destY1);
       
 15915 
       
 15916       var destOffset = destY1 * screenW + destX1;
       
 15917       var destColor;
       
 15918 
       
 15919       pshared.srcBuffer = img.imageData.data;
       
 15920       pshared.iw = img.width;
       
 15921       pshared.iw1 = img.width - 1;
       
 15922       pshared.ih1 = img.height - 1;
       
 15923 
       
 15924       // cache for speed
       
 15925       var filterBilinear = p.filter_bilinear,
       
 15926         filterNewScanline = p.filter_new_scanline,
       
 15927         blendFunc = blendFuncs[mode],
       
 15928         blendedColor,
       
 15929         idx,
       
 15930         cULoffset,
       
 15931         cURoffset,
       
 15932         cLLoffset,
       
 15933         cLRoffset,
       
 15934         ALPHA_MASK = PConstants.ALPHA_MASK,
       
 15935         RED_MASK = PConstants.RED_MASK,
       
 15936         GREEN_MASK = PConstants.GREEN_MASK,
       
 15937         BLUE_MASK = PConstants.BLUE_MASK,
       
 15938         PREC_MAXVAL = PConstants.PREC_MAXVAL,
       
 15939         PRECISIONB = PConstants.PRECISIONB,
       
 15940         PREC_RED_SHIFT = PConstants.PREC_RED_SHIFT,
       
 15941         PREC_ALPHA_SHIFT = PConstants.PREC_ALPHA_SHIFT,
       
 15942         srcBuffer = pshared.srcBuffer,
       
 15943         min = Math.min;
       
 15944 
       
 15945       for (y = 0; y < destH; y++) {
       
 15946 
       
 15947         pshared.sX = pshared.srcXOffset;
       
 15948         pshared.fracV = pshared.srcYOffset & PREC_MAXVAL;
       
 15949         pshared.ifV = PREC_MAXVAL - pshared.fracV;
       
 15950         pshared.v1 = (pshared.srcYOffset >> PRECISIONB) * pshared.iw;
       
 15951         pshared.v2 = min((pshared.srcYOffset >> PRECISIONB) + 1, pshared.ih1) * pshared.iw;
       
 15952 
       
 15953         for (x = 0; x < destW; x++) {
       
 15954           idx = (destOffset + x) * 4;
       
 15955 
       
 15956           destColor = (destPixels[idx + 3] << 24) &
       
 15957                       ALPHA_MASK | (destPixels[idx] << 16) &
       
 15958                       RED_MASK   | (destPixels[idx + 1] << 8) &
       
 15959                       GREEN_MASK |  destPixels[idx + 2] & BLUE_MASK;
       
 15960 
       
 15961           pshared.fracU = pshared.sX & PREC_MAXVAL;
       
 15962           pshared.ifU = PREC_MAXVAL - pshared.fracU;
       
 15963           pshared.ul = (pshared.ifU * pshared.ifV) >> PRECISIONB;
       
 15964           pshared.ll = (pshared.ifU * pshared.fracV) >> PRECISIONB;
       
 15965           pshared.ur = (pshared.fracU * pshared.ifV) >> PRECISIONB;
       
 15966           pshared.lr = (pshared.fracU * pshared.fracV) >> PRECISIONB;
       
 15967           pshared.u1 = (pshared.sX >> PRECISIONB);
       
 15968           pshared.u2 = min(pshared.u1 + 1, pshared.iw1);
       
 15969 
       
 15970           cULoffset = (pshared.v1 + pshared.u1) * 4;
       
 15971           cURoffset = (pshared.v1 + pshared.u2) * 4;
       
 15972           cLLoffset = (pshared.v2 + pshared.u1) * 4;
       
 15973           cLRoffset = (pshared.v2 + pshared.u2) * 4;
       
 15974 
       
 15975           pshared.cUL = (srcBuffer[cULoffset + 3] << 24) &
       
 15976                         ALPHA_MASK | (srcBuffer[cULoffset] << 16) &
       
 15977                         RED_MASK   | (srcBuffer[cULoffset + 1] << 8) &
       
 15978                         GREEN_MASK |  srcBuffer[cULoffset + 2] & BLUE_MASK;
       
 15979 
       
 15980           pshared.cUR = (srcBuffer[cURoffset + 3] << 24) &
       
 15981                         ALPHA_MASK | (srcBuffer[cURoffset] << 16) &
       
 15982                         RED_MASK   | (srcBuffer[cURoffset + 1] << 8) &
       
 15983                         GREEN_MASK |  srcBuffer[cURoffset + 2] & BLUE_MASK;
       
 15984 
       
 15985           pshared.cLL = (srcBuffer[cLLoffset + 3] << 24) &
       
 15986                         ALPHA_MASK | (srcBuffer[cLLoffset] << 16) &
       
 15987                         RED_MASK   | (srcBuffer[cLLoffset + 1] << 8) &
       
 15988                         GREEN_MASK |  srcBuffer[cLLoffset + 2] & BLUE_MASK;
       
 15989 
       
 15990           pshared.cLR = (srcBuffer[cLRoffset + 3] << 24) &
       
 15991                         ALPHA_MASK | (srcBuffer[cLRoffset] << 16) &
       
 15992                         RED_MASK   | (srcBuffer[cLRoffset + 1] << 8) &
       
 15993                         GREEN_MASK |  srcBuffer[cLRoffset + 2] & BLUE_MASK;
       
 15994 
       
 15995           pshared.r = ((pshared.ul * ((pshared.cUL & RED_MASK) >> 16) +
       
 15996                        pshared.ll * ((pshared.cLL & RED_MASK) >> 16) +
       
 15997                        pshared.ur * ((pshared.cUR & RED_MASK) >> 16) +
       
 15998                        pshared.lr * ((pshared.cLR & RED_MASK) >> 16)) << PREC_RED_SHIFT) & RED_MASK;
       
 15999           pshared.g = ((pshared.ul * (pshared.cUL & GREEN_MASK) +
       
 16000                        pshared.ll * (pshared.cLL & GREEN_MASK) +
       
 16001                        pshared.ur * (pshared.cUR & GREEN_MASK) +
       
 16002                        pshared.lr * (pshared.cLR & GREEN_MASK)) >>> PRECISIONB) & GREEN_MASK;
       
 16003           pshared.b = (pshared.ul * (pshared.cUL & BLUE_MASK) +
       
 16004                        pshared.ll * (pshared.cLL & BLUE_MASK) +
       
 16005                        pshared.ur * (pshared.cUR & BLUE_MASK) +
       
 16006                        pshared.lr * (pshared.cLR & BLUE_MASK)) >>> PRECISIONB;
       
 16007           pshared.a = ((pshared.ul * ((pshared.cUL & ALPHA_MASK) >>> 24) +
       
 16008                        pshared.ll * ((pshared.cLL & ALPHA_MASK) >>> 24) +
       
 16009                        pshared.ur * ((pshared.cUR & ALPHA_MASK) >>> 24) +
       
 16010                        pshared.lr * ((pshared.cLR & ALPHA_MASK) >>> 24)) << PREC_ALPHA_SHIFT) & ALPHA_MASK;
       
 16011 
       
 16012           blendedColor = blendFunc(destColor, (pshared.a | pshared.r | pshared.g | pshared.b));
       
 16013 
       
 16014           destPixels[idx]     = (blendedColor & RED_MASK) >>> 16;
       
 16015           destPixels[idx + 1] = (blendedColor & GREEN_MASK) >>> 8;
       
 16016           destPixels[idx + 2] = (blendedColor & BLUE_MASK);
       
 16017           destPixels[idx + 3] = (blendedColor & ALPHA_MASK) >>> 24;
       
 16018 
       
 16019           pshared.sX += dx;
       
 16020         }
       
 16021         destOffset += screenW;
       
 16022         pshared.srcYOffset += dy;
       
 16023       }
       
 16024     };
       
 16025 
       
 16026     ////////////////////////////////////////////////////////////////////////////
       
 16027     // Font handling
       
 16028     ////////////////////////////////////////////////////////////////////////////
       
 16029 
       
 16030     /**
       
 16031      * loadFont() Loads a font into a variable of type PFont.
       
 16032      *
       
 16033      * @param {String} name filename of the font to load
       
 16034      * @param {int|float} size option font size (used internally)
       
 16035      *
       
 16036      * @returns {PFont} new PFont object
       
 16037      *
       
 16038      * @see #PFont
       
 16039      * @see #textFont
       
 16040      * @see #text
       
 16041      * @see #createFont
       
 16042      */
       
 16043     p.loadFont = function(name, size) {
       
 16044       if (name === undef) {
       
 16045         throw("font name required in loadFont.");
       
 16046       }
       
 16047       if (name.indexOf(".svg") === -1) {
       
 16048         if (size === undef) {
       
 16049           size = curTextFont.size;
       
 16050         }
       
 16051         return PFont.get(name, size);
       
 16052       }
       
 16053       // If the font is a glyph, calculate by SVG table
       
 16054       var font = p.loadGlyphs(name);
       
 16055 
       
 16056       return {
       
 16057         name: name,
       
 16058         css: '12px sans-serif',
       
 16059         glyph: true,
       
 16060         units_per_em: font.units_per_em,
       
 16061         horiz_adv_x: 1 / font.units_per_em * font.horiz_adv_x,
       
 16062         ascent: font.ascent,
       
 16063         descent: font.descent,
       
 16064         width: function(str) {
       
 16065           var width = 0;
       
 16066           var len = str.length;
       
 16067           for (var i = 0; i < len; i++) {
       
 16068             try {
       
 16069               width += parseFloat(p.glyphLook(p.glyphTable[name], str[i]).horiz_adv_x);
       
 16070             }
       
 16071             catch(e) {
       
 16072               Processing.debug(e);
       
 16073             }
       
 16074           }
       
 16075           return width / p.glyphTable[name].units_per_em;
       
 16076         }
       
 16077       };
       
 16078     };
       
 16079 
       
 16080     /**
       
 16081      * createFont() Loads a font into a variable of type PFont.
       
 16082      * Smooth and charset are ignored in Processing.js.
       
 16083      *
       
 16084      * @param {String}    name    filename of the font to load
       
 16085      * @param {int|float} size    font size in pixels
       
 16086      * @param {boolean}   smooth  not used in Processing.js
       
 16087      * @param {char[]}    charset not used in Processing.js
       
 16088      *
       
 16089      * @returns {PFont} new PFont object
       
 16090      *
       
 16091      * @see #PFont
       
 16092      * @see #textFont
       
 16093      * @see #text
       
 16094      * @see #loadFont
       
 16095      */
       
 16096     p.createFont = function(name, size) {
       
 16097       // because Processing.js only deals with real fonts,
       
 16098       // createFont is simply a wrapper for loadFont/2
       
 16099       return p.loadFont(name, size);
       
 16100     };
       
 16101 
       
 16102     /**
       
 16103      * textFont() Sets the current font.
       
 16104      *
       
 16105      * @param {PFont}     pfont the PFont to load as current text font
       
 16106      * @param {int|float} size optional font size in pixels
       
 16107      *
       
 16108      * @see #createFont
       
 16109      * @see #loadFont
       
 16110      * @see #PFont
       
 16111      * @see #text
       
 16112      */
       
 16113     p.textFont = function(pfont, size) {
       
 16114       if (size !== undef) {
       
 16115         // If we're using an SVG glyph font, don't load from cache
       
 16116         if (!pfont.glyph) {
       
 16117           pfont = PFont.get(pfont.name, size);
       
 16118         }
       
 16119         curTextSize = size;
       
 16120       }
       
 16121       curTextFont = pfont;
       
 16122       curFontName = curTextFont.name;
       
 16123       curTextAscent = curTextFont.ascent;
       
 16124       curTextDescent = curTextFont.descent;
       
 16125       curTextLeading = curTextFont.leading;
       
 16126       var curContext = drawing.$ensureContext();
       
 16127       curContext.font = curTextFont.css;
       
 16128     };
       
 16129 
       
 16130     /**
       
 16131      * textSize() Sets the current font size in pixels.
       
 16132      *
       
 16133      * @param {int|float} size font size in pixels
       
 16134      *
       
 16135      * @see #textFont
       
 16136      * @see #loadFont
       
 16137      * @see #PFont
       
 16138      * @see #text
       
 16139      */
       
 16140     p.textSize = function(size) {
       
 16141       if (size !== curTextSize) {
       
 16142         curTextFont = PFont.get(curFontName, size);
       
 16143         curTextSize = size;
       
 16144         // recache metrics
       
 16145         curTextAscent = curTextFont.ascent;
       
 16146         curTextDescent = curTextFont.descent;
       
 16147         curTextLeading = curTextFont.leading;
       
 16148         var curContext = drawing.$ensureContext();
       
 16149         curContext.font = curTextFont.css;
       
 16150       }
       
 16151     };
       
 16152 
       
 16153     /**
       
 16154      * textAscent() returns the maximum height a character extends above the baseline of the
       
 16155      * current font at its current size, in pixels.
       
 16156      *
       
 16157      * @returns {float} height of the current font above the baseline, at its current size, in pixels
       
 16158      *
       
 16159      * @see #textDescent
       
 16160      */
       
 16161     p.textAscent = function() {
       
 16162       return curTextAscent;
       
 16163     };
       
 16164 
       
 16165     /**
       
 16166      * textDescent() returns the maximum depth a character will protrude below the baseline of
       
 16167      * the current font at its current size, in pixels.
       
 16168      *
       
 16169      * @returns {float} depth of the current font below the baseline, at its current size, in pixels
       
 16170      *
       
 16171      * @see #textAscent
       
 16172      */
       
 16173     p.textDescent = function() {
       
 16174       return curTextDescent;
       
 16175     };
       
 16176 
       
 16177     /**
       
 16178      * textLeading() Sets the current font's leading, which is the distance
       
 16179      * from baseline to baseline over consecutive lines, with additional vertical
       
 16180      * spacing taking into account. Usually this value is 1.2 or 1.25 times the
       
 16181      * textsize, but this value can be changed to effect vertically compressed
       
 16182      * or stretched text.
       
 16183      *
       
 16184      * @param {int|float} the desired baseline-to-baseline size in pixels
       
 16185      */
       
 16186     p.textLeading = function(leading) {
       
 16187       curTextLeading = leading;
       
 16188     };
       
 16189 
       
 16190     /**
       
 16191      * textAlign() Sets the current alignment for drawing text.
       
 16192      *
       
 16193      * @param {int} ALIGN  Horizontal alignment, either LEFT, CENTER, or RIGHT
       
 16194      * @param {int} YALIGN optional vertical alignment, either TOP, BOTTOM, CENTER, or BASELINE
       
 16195      *
       
 16196      * @see #loadFont
       
 16197      * @see #PFont
       
 16198      * @see #text
       
 16199      */
       
 16200     p.textAlign = function(xalign, yalign) {
       
 16201       horizontalTextAlignment = xalign;
       
 16202       verticalTextAlignment = yalign || PConstants.BASELINE;
       
 16203     };
       
 16204 
       
 16205     /**
       
 16206      * toP5String converts things with arbitrary data type into
       
 16207      * string values, for text rendering.
       
 16208      *
       
 16209      * @param {any} any object that can be converted into a string
       
 16210      *
       
 16211      * @return {String} the string representation of the input
       
 16212      */
       
 16213     function toP5String(obj) {
       
 16214       if(obj instanceof String) {
       
 16215         return obj;
       
 16216       }
       
 16217       if(typeof obj === 'number') {
       
 16218         // check if an int
       
 16219         if(obj === (0 | obj)) {
       
 16220           return obj.toString();
       
 16221         }
       
 16222         return p.nf(obj, 0, 3);
       
 16223       }
       
 16224       if(obj === null || obj === undef) {
       
 16225         return "";
       
 16226       }
       
 16227       return obj.toString();
       
 16228     }
       
 16229 
       
 16230     /**
       
 16231      * textWidth() Calculates and returns the width of any character or text string in pixels.
       
 16232      *
       
 16233      * @param {char|String} str char or String to be measured
       
 16234      *
       
 16235      * @return {float} width of char or String in pixels
       
 16236      *
       
 16237      * @see #loadFont
       
 16238      * @see #PFont
       
 16239      * @see #text
       
 16240      * @see #textFont
       
 16241      */
       
 16242     Drawing2D.prototype.textWidth = function(str) {
       
 16243       var lines = toP5String(str).split(/\r?\n/g), width = 0;
       
 16244       var i, linesCount = lines.length;
       
 16245 
       
 16246       curContext.font = curTextFont.css;
       
 16247       for (i = 0; i < linesCount; ++i) {
       
 16248         width = Math.max(width, curTextFont.measureTextWidth(lines[i]));
       
 16249       }
       
 16250       return width | 0;
       
 16251     };
       
 16252 
       
 16253     Drawing3D.prototype.textWidth = function(str) {
       
 16254       var lines = toP5String(str).split(/\r?\n/g), width = 0;
       
 16255       var i, linesCount = lines.length;
       
 16256       if (textcanvas === undef) {
       
 16257         textcanvas = document.createElement("canvas");
       
 16258       }
       
 16259 
       
 16260       var textContext = textcanvas.getContext("2d");
       
 16261       textContext.font = curTextFont.css;
       
 16262 
       
 16263       for (i = 0; i < linesCount; ++i) {
       
 16264         width = Math.max(width, textContext.measureText(lines[i]).width);
       
 16265       }
       
 16266       return width | 0;
       
 16267     };
       
 16268 
       
 16269     // A lookup table for characters that can not be referenced by Object
       
 16270     p.glyphLook = function(font, chr) {
       
 16271       try {
       
 16272         switch (chr) {
       
 16273         case "1":
       
 16274           return font.one;
       
 16275         case "2":
       
 16276           return font.two;
       
 16277         case "3":
       
 16278           return font.three;
       
 16279         case "4":
       
 16280           return font.four;
       
 16281         case "5":
       
 16282           return font.five;
       
 16283         case "6":
       
 16284           return font.six;
       
 16285         case "7":
       
 16286           return font.seven;
       
 16287         case "8":
       
 16288           return font.eight;
       
 16289         case "9":
       
 16290           return font.nine;
       
 16291         case "0":
       
 16292           return font.zero;
       
 16293         case " ":
       
 16294           return font.space;
       
 16295         case "$":
       
 16296           return font.dollar;
       
 16297         case "!":
       
 16298           return font.exclam;
       
 16299         case '"':
       
 16300           return font.quotedbl;
       
 16301         case "#":
       
 16302           return font.numbersign;
       
 16303         case "%":
       
 16304           return font.percent;
       
 16305         case "&":
       
 16306           return font.ampersand;
       
 16307         case "'":
       
 16308           return font.quotesingle;
       
 16309         case "(":
       
 16310           return font.parenleft;
       
 16311         case ")":
       
 16312           return font.parenright;
       
 16313         case "*":
       
 16314           return font.asterisk;
       
 16315         case "+":
       
 16316           return font.plus;
       
 16317         case ",":
       
 16318           return font.comma;
       
 16319         case "-":
       
 16320           return font.hyphen;
       
 16321         case ".":
       
 16322           return font.period;
       
 16323         case "/":
       
 16324           return font.slash;
       
 16325         case "_":
       
 16326           return font.underscore;
       
 16327         case ":":
       
 16328           return font.colon;
       
 16329         case ";":
       
 16330           return font.semicolon;
       
 16331         case "<":
       
 16332           return font.less;
       
 16333         case "=":
       
 16334           return font.equal;
       
 16335         case ">":
       
 16336           return font.greater;
       
 16337         case "?":
       
 16338           return font.question;
       
 16339         case "@":
       
 16340           return font.at;
       
 16341         case "[":
       
 16342           return font.bracketleft;
       
 16343         case "\\":
       
 16344           return font.backslash;
       
 16345         case "]":
       
 16346           return font.bracketright;
       
 16347         case "^":
       
 16348           return font.asciicircum;
       
 16349         case "`":
       
 16350           return font.grave;
       
 16351         case "{":
       
 16352           return font.braceleft;
       
 16353         case "|":
       
 16354           return font.bar;
       
 16355         case "}":
       
 16356           return font.braceright;
       
 16357         case "~":
       
 16358           return font.asciitilde;
       
 16359           // If the character is not 'special', access it by object reference
       
 16360         default:
       
 16361           return font[chr];
       
 16362         }
       
 16363       } catch(e) {
       
 16364         Processing.debug(e);
       
 16365       }
       
 16366     };
       
 16367 
       
 16368     // Print some text to the Canvas
       
 16369     Drawing2D.prototype.text$line = function(str, x, y, z, align) {
       
 16370       var textWidth = 0, xOffset = 0;
       
 16371       // If the font is a standard Canvas font...
       
 16372       if (!curTextFont.glyph) {
       
 16373         if (str && ("fillText" in curContext)) {
       
 16374           if (isFillDirty) {
       
 16375             curContext.fillStyle = p.color.toString(currentFillColor);
       
 16376             isFillDirty = false;
       
 16377           }
       
 16378 
       
 16379           // horizontal offset/alignment
       
 16380           if(align === PConstants.RIGHT || align === PConstants.CENTER) {
       
 16381             textWidth = curTextFont.measureTextWidth(str);
       
 16382 
       
 16383             if(align === PConstants.RIGHT) {
       
 16384               xOffset = -textWidth;
       
 16385             } else { // if(align === PConstants.CENTER)
       
 16386               xOffset = -textWidth/2;
       
 16387             }
       
 16388           }
       
 16389 
       
 16390           curContext.fillText(str, x+xOffset, y);
       
 16391         }
       
 16392       } else {
       
 16393         // If the font is a Batik SVG font...
       
 16394         var font = p.glyphTable[curFontName];
       
 16395         saveContext();
       
 16396         curContext.translate(x, y + curTextSize);
       
 16397 
       
 16398         // horizontal offset/alignment
       
 16399         if(align === PConstants.RIGHT || align === PConstants.CENTER) {
       
 16400           textWidth = font.width(str);
       
 16401 
       
 16402           if(align === PConstants.RIGHT) {
       
 16403             xOffset = -textWidth;
       
 16404           } else { // if(align === PConstants.CENTER)
       
 16405             xOffset = -textWidth/2;
       
 16406           }
       
 16407         }
       
 16408 
       
 16409         var upem   = font.units_per_em,
       
 16410           newScale = 1 / upem * curTextSize;
       
 16411 
       
 16412         curContext.scale(newScale, newScale);
       
 16413 
       
 16414         for (var i=0, len=str.length; i < len; i++) {
       
 16415           // Test character against glyph table
       
 16416           try {
       
 16417             p.glyphLook(font, str[i]).draw();
       
 16418           } catch(e) {
       
 16419             Processing.debug(e);
       
 16420           }
       
 16421         }
       
 16422         restoreContext();
       
 16423       }
       
 16424     };
       
 16425 
       
 16426     Drawing3D.prototype.text$line = function(str, x, y, z, align) {
       
 16427       // handle case for 3d text
       
 16428       if (textcanvas === undef) {
       
 16429         textcanvas = document.createElement("canvas");
       
 16430       }
       
 16431       var oldContext = curContext;
       
 16432       curContext = textcanvas.getContext("2d");
       
 16433       curContext.font = curTextFont.css;
       
 16434       var textWidth = curTextFont.measureTextWidth(str);
       
 16435       textcanvas.width = textWidth;
       
 16436       textcanvas.height = curTextSize;
       
 16437       curContext = textcanvas.getContext("2d"); // refreshes curContext
       
 16438       curContext.font = curTextFont.css;
       
 16439       curContext.textBaseline="top";
       
 16440 
       
 16441       // paint on 2D canvas
       
 16442       Drawing2D.prototype.text$line(str,0,0,0,PConstants.LEFT);
       
 16443 
       
 16444       // use it as a texture
       
 16445       var aspect = textcanvas.width/textcanvas.height;
       
 16446       curContext = oldContext;
       
 16447 
       
 16448       curContext.bindTexture(curContext.TEXTURE_2D, textTex);
       
 16449       curContext.texImage2D(curContext.TEXTURE_2D, 0, curContext.RGBA, curContext.RGBA, curContext.UNSIGNED_BYTE, textcanvas);
       
 16450       curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_MAG_FILTER, curContext.LINEAR);
       
 16451       curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_MIN_FILTER, curContext.LINEAR);
       
 16452       curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_WRAP_T, curContext.CLAMP_TO_EDGE);
       
 16453       curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_WRAP_S, curContext.CLAMP_TO_EDGE);
       
 16454       // If we don't have a power of two texture, we can't mipmap it.
       
 16455       // curContext.generateMipmap(curContext.TEXTURE_2D);
       
 16456 
       
 16457       // horizontal offset/alignment
       
 16458       var xOffset = 0;
       
 16459       if (align === PConstants.RIGHT) {
       
 16460         xOffset = -textWidth;
       
 16461       } else if(align === PConstants.CENTER) {
       
 16462         xOffset = -textWidth/2;
       
 16463       }
       
 16464       var model = new PMatrix3D();
       
 16465       var scalefactor = curTextSize * 0.5;
       
 16466       model.translate(x+xOffset-scalefactor/2, y-scalefactor, z);
       
 16467       model.scale(-aspect*scalefactor, -scalefactor, scalefactor);
       
 16468       model.translate(-1, -1, -1);
       
 16469       model.transpose();
       
 16470 
       
 16471       var view = new PMatrix3D();
       
 16472       view.scale(1, -1, 1);
       
 16473       view.apply(modelView.array());
       
 16474       view.transpose();
       
 16475 
       
 16476       curContext.useProgram(programObject2D);
       
 16477       vertexAttribPointer("vertex2d", programObject2D, "Vertex", 3, textBuffer);
       
 16478       vertexAttribPointer("aTextureCoord2d", programObject2D, "aTextureCoord", 2, textureBuffer);
       
 16479       uniformi("uSampler2d", programObject2D, "uSampler", [0]);
       
 16480       uniformi("picktype2d", programObject2D, "picktype", 1);
       
 16481       uniformMatrix("model2d", programObject2D, "model", false,  model.array());
       
 16482       uniformMatrix("view2d", programObject2D, "view", false, view.array());
       
 16483       uniformf("color2d", programObject2D, "color", fillStyle);
       
 16484       curContext.bindBuffer(curContext.ELEMENT_ARRAY_BUFFER, indexBuffer);
       
 16485       curContext.drawElements(curContext.TRIANGLES, 6, curContext.UNSIGNED_SHORT, 0);
       
 16486     };
       
 16487 
       
 16488 
       
 16489     /**
       
 16490     * unbounded text function (z is an optional argument)
       
 16491     */
       
 16492     function text$4(str, x, y, z) {
       
 16493       var lines, linesCount;
       
 16494       if(str.indexOf('\n') < 0) {
       
 16495         lines = [str];
       
 16496         linesCount = 1;
       
 16497       } else {
       
 16498         lines = str.split(/\r?\n/g);
       
 16499         linesCount = lines.length;
       
 16500       }
       
 16501       // handle text line-by-line
       
 16502 
       
 16503       var yOffset = 0;
       
 16504       if(verticalTextAlignment === PConstants.TOP) {
       
 16505         yOffset = curTextAscent + curTextDescent;
       
 16506       } else if(verticalTextAlignment === PConstants.CENTER) {
       
 16507         yOffset = curTextAscent/2 - (linesCount-1)*curTextLeading/2;
       
 16508       } else if(verticalTextAlignment === PConstants.BOTTOM) {
       
 16509         yOffset = -(curTextDescent + (linesCount-1)*curTextLeading);
       
 16510       }
       
 16511 
       
 16512       for(var i=0;i<linesCount;++i) {
       
 16513         var line = lines[i];
       
 16514         drawing.text$line(line, x, y + yOffset, z, horizontalTextAlignment);
       
 16515         yOffset += curTextLeading;
       
 16516       }
       
 16517     }
       
 16518 
       
 16519 
       
 16520     /**
       
 16521     * box-bounded text function (z is an optional argument)
       
 16522     */
       
 16523     function text$6(str, x, y, width, height, z) {
       
 16524       // 'fail' on 0-valued dimensions
       
 16525       if (str.length === 0 || width === 0 || height === 0) {
       
 16526         return;
       
 16527       }
       
 16528       // also 'fail' if the text height is larger than the bounding height
       
 16529       if(curTextSize > height) {
       
 16530         return;
       
 16531       }
       
 16532 
       
 16533       var spaceMark = -1;
       
 16534       var start = 0;
       
 16535       var lineWidth = 0;
       
 16536       var drawCommands = [];
       
 16537 
       
 16538       // run through text, character-by-character
       
 16539       for (var charPos=0, len=str.length; charPos < len; charPos++)
       
 16540       {
       
 16541         var currentChar = str[charPos];
       
 16542         var spaceChar = (currentChar === " ");
       
 16543         var letterWidth = curTextFont.measureTextWidth(currentChar);
       
 16544 
       
 16545         // if we aren't looking at a newline, and the text still fits, keep processing
       
 16546         if (currentChar !== "\n" && (lineWidth + letterWidth <= width)) {
       
 16547           if (spaceChar) { spaceMark = charPos; }
       
 16548           lineWidth += letterWidth;
       
 16549         }
       
 16550 
       
 16551         // if we're looking at a newline, or the text no longer fits, push the section that fit into the drawcommand list
       
 16552         else
       
 16553         {
       
 16554           if (spaceMark + 1 === start) {
       
 16555             if(charPos>0) {
       
 16556               // Whole line without spaces so far.
       
 16557               spaceMark = charPos;
       
 16558             } else {
       
 16559               // 'fail', because the line can't even fit the first character
       
 16560               return;
       
 16561             }
       
 16562           }
       
 16563 
       
 16564           if (currentChar === "\n") {
       
 16565             drawCommands.push({text:str.substring(start, charPos), width: lineWidth});
       
 16566             start = charPos + 1;
       
 16567           } else {
       
 16568             // current is not a newline, which means the line doesn't fit in box. push text.
       
 16569             // In Processing 1.5.1, the space is also pushed, so we push up to spaceMark+1,
       
 16570             // rather than up to spaceMark, as was the case for Processing 1.5 and earlier.
       
 16571             drawCommands.push({text:str.substring(start, spaceMark+1), width: lineWidth});
       
 16572             start = spaceMark + 1;
       
 16573           }
       
 16574 
       
 16575           // newline + return
       
 16576           lineWidth = 0;
       
 16577           charPos = start - 1;
       
 16578         }
       
 16579       }
       
 16580 
       
 16581       // push the remaining text
       
 16582       if (start < len) {
       
 16583         drawCommands.push({text:str.substring(start), width: lineWidth});
       
 16584       }
       
 16585 
       
 16586       // resolve horizontal alignment
       
 16587       var xOffset = 1,
       
 16588           yOffset = curTextAscent;
       
 16589       if (horizontalTextAlignment === PConstants.CENTER) {
       
 16590         xOffset = width/2;
       
 16591       } else if (horizontalTextAlignment === PConstants.RIGHT) {
       
 16592         xOffset = width;
       
 16593       }
       
 16594 
       
 16595       // resolve vertical alignment
       
 16596       var linesCount = drawCommands.length,
       
 16597           visibleLines = Math.min(linesCount, Math.floor(height/curTextLeading));
       
 16598       if(verticalTextAlignment === PConstants.TOP) {
       
 16599         yOffset = curTextAscent + curTextDescent;
       
 16600       } else if(verticalTextAlignment === PConstants.CENTER) {
       
 16601         yOffset = (height/2) - curTextLeading * (visibleLines/2 - 1);
       
 16602       } else if(verticalTextAlignment === PConstants.BOTTOM) {
       
 16603         yOffset = curTextDescent + curTextLeading;
       
 16604       }
       
 16605 
       
 16606       var command,
       
 16607           drawCommand,
       
 16608           leading;
       
 16609       for (command = 0; command < linesCount; command++) {
       
 16610         leading = command * curTextLeading;
       
 16611         // stop if not enough space for one more line draw
       
 16612         if (yOffset + leading > height - curTextDescent) {
       
 16613           break;
       
 16614         }
       
 16615         drawCommand = drawCommands[command];
       
 16616         drawing.text$line(drawCommand.text, x + xOffset, y + yOffset + leading, z, horizontalTextAlignment);
       
 16617       }
       
 16618     }
       
 16619 
       
 16620     /**
       
 16621      * text() Draws text to the screen.
       
 16622      *
       
 16623      * @param {String|char|int|float} data       the alphanumeric symbols to be displayed
       
 16624      * @param {int|float}             x          x-coordinate of text
       
 16625      * @param {int|float}             y          y-coordinate of text
       
 16626      * @param {int|float}             z          optional z-coordinate of text
       
 16627      * @param {String}                stringdata optional letters to be displayed
       
 16628      * @param {int|float}             width      optional width of text box
       
 16629      * @param {int|float}             height     optional height of text box
       
 16630      *
       
 16631      * @see #textAlign
       
 16632      * @see #textMode
       
 16633      * @see #loadFont
       
 16634      * @see #PFont
       
 16635      * @see #textFont
       
 16636      */
       
 16637     p.text = function() {
       
 16638       if (textMode === PConstants.SHAPE) {
       
 16639         // TODO: requires beginRaw function
       
 16640         return;
       
 16641       }
       
 16642       if (arguments.length === 3) { // for text( str, x, y)
       
 16643         text$4(toP5String(arguments[0]), arguments[1], arguments[2], 0);
       
 16644       } else if (arguments.length === 4) { // for text( str, x, y, z)
       
 16645         text$4(toP5String(arguments[0]), arguments[1], arguments[2], arguments[3]);
       
 16646       } else if (arguments.length === 5) { // for text( str, x, y , width, height)
       
 16647         text$6(toP5String(arguments[0]), arguments[1], arguments[2], arguments[3], arguments[4], 0);
       
 16648       } else if (arguments.length === 6) { // for text( stringdata, x, y , width, height, z)
       
 16649         text$6(toP5String(arguments[0]), arguments[1], arguments[2], arguments[3], arguments[4], arguments[5]);
       
 16650       }
       
 16651     };
       
 16652 
       
 16653     /**
       
 16654      * Sets the way text draws to the screen. In the default configuration (the MODEL mode), it's possible to rotate,
       
 16655      * scale, and place letters in two and three dimensional space. <br /><br /> Changing to SCREEN mode draws letters
       
 16656      * directly to the front of the window and greatly increases rendering quality and speed when used with the P2D and
       
 16657      * P3D renderers. textMode(SCREEN) with OPENGL and JAVA2D (the default) renderers will generally be slower, though
       
 16658      * pixel accurate with P2D and P3D. With textMode(SCREEN), the letters draw at the actual size of the font (in pixels)
       
 16659      * and therefore calls to <b>textSize()</b> will not affect the size of the letters. To create a font at the size you
       
 16660      * desire, use the "Create font..." option in the Tools menu, or use the createFont() function. When using textMode(SCREEN),
       
 16661      * any z-coordinate passed to a text() command will be ignored, because your computer screen is...flat!
       
 16662      *
       
 16663      * @param {int} MODE Either MODEL, SCREEN or SHAPE (not yet supported)
       
 16664      *
       
 16665      * @see loadFont
       
 16666      * @see PFont
       
 16667      * @see text
       
 16668      * @see textFont
       
 16669      * @see createFont
       
 16670      */
       
 16671     p.textMode = function(mode){
       
 16672       textMode = mode;
       
 16673     };
       
 16674 
       
 16675     // Load Batik SVG Fonts and parse to pre-def objects for quick rendering
       
 16676     p.loadGlyphs = function(url) {
       
 16677       var x, y, cx, cy, nx, ny, d, a, lastCom, lenC, horiz_adv_x, getXY = '[0-9\\-]+', path;
       
 16678 
       
 16679       // Return arrays of SVG commands and coords
       
 16680       // get this to use p.matchAll() - will need to work around the lack of null return
       
 16681       var regex = function(needle, hay) {
       
 16682         var i = 0,
       
 16683           results = [],
       
 16684           latest, regexp = new RegExp(needle, "g");
       
 16685         latest = results[i] = regexp.exec(hay);
       
 16686         while (latest) {
       
 16687           i++;
       
 16688           latest = results[i] = regexp.exec(hay);
       
 16689         }
       
 16690         return results;
       
 16691       };
       
 16692 
       
 16693       var buildPath = function(d) {
       
 16694         var c = regex("[A-Za-z][0-9\\- ]+|Z", d);
       
 16695         var beforePathDraw = function() {
       
 16696           saveContext();
       
 16697           return drawing.$ensureContext();
       
 16698         };
       
 16699         var afterPathDraw = function() {
       
 16700           executeContextFill();
       
 16701           executeContextStroke();
       
 16702           restoreContext();
       
 16703         };
       
 16704 
       
 16705         // Begin storing path object
       
 16706         path = "return {draw:function(){var curContext=beforePathDraw();curContext.beginPath();";
       
 16707 
       
 16708         x = 0;
       
 16709         y = 0;
       
 16710         cx = 0;
       
 16711         cy = 0;
       
 16712         nx = 0;
       
 16713         ny = 0;
       
 16714         d = 0;
       
 16715         a = 0;
       
 16716         lastCom = "";
       
 16717         lenC = c.length - 1;
       
 16718 
       
 16719         // Loop through SVG commands translating to canvas eqivs functions in path object
       
 16720         for (var j = 0; j < lenC; j++) {
       
 16721           var com = c[j][0], xy = regex(getXY, com);
       
 16722 
       
 16723           switch (com[0]) {
       
 16724             case "M":
       
 16725               //curContext.moveTo(x,-y);
       
 16726               x = parseFloat(xy[0][0]);
       
 16727               y = parseFloat(xy[1][0]);
       
 16728               path += "curContext.moveTo(" + x + "," + (-y) + ");";
       
 16729               break;
       
 16730 
       
 16731             case "L":
       
 16732               //curContext.lineTo(x,-y);
       
 16733               x = parseFloat(xy[0][0]);
       
 16734               y = parseFloat(xy[1][0]);
       
 16735               path += "curContext.lineTo(" + x + "," + (-y) + ");";
       
 16736               break;
       
 16737 
       
 16738             case "H":
       
 16739               //curContext.lineTo(x,-y)
       
 16740               x = parseFloat(xy[0][0]);
       
 16741               path += "curContext.lineTo(" + x + "," + (-y) + ");";
       
 16742               break;
       
 16743 
       
 16744             case "V":
       
 16745               //curContext.lineTo(x,-y);
       
 16746               y = parseFloat(xy[0][0]);
       
 16747               path += "curContext.lineTo(" + x + "," + (-y) + ");";
       
 16748               break;
       
 16749 
       
 16750             case "T":
       
 16751               //curContext.quadraticCurveTo(cx,-cy,nx,-ny);
       
 16752               nx = parseFloat(xy[0][0]);
       
 16753               ny = parseFloat(xy[1][0]);
       
 16754 
       
 16755               if (lastCom === "Q" || lastCom === "T") {
       
 16756                 d = Math.sqrt(Math.pow(x - cx, 2) + Math.pow(cy - y, 2));
       
 16757                 a = Math.PI + Math.atan2(cx - x, cy - y);
       
 16758                 cx = x + (Math.sin(a) * (d));
       
 16759                 cy = y + (Math.cos(a) * (d));
       
 16760               } else {
       
 16761                 cx = x;
       
 16762                 cy = y;
       
 16763               }
       
 16764 
       
 16765               path += "curContext.quadraticCurveTo(" + cx + "," + (-cy) + "," + nx + "," + (-ny) + ");";
       
 16766               x = nx;
       
 16767               y = ny;
       
 16768               break;
       
 16769 
       
 16770             case "Q":
       
 16771               //curContext.quadraticCurveTo(cx,-cy,nx,-ny);
       
 16772               cx = parseFloat(xy[0][0]);
       
 16773               cy = parseFloat(xy[1][0]);
       
 16774               nx = parseFloat(xy[2][0]);
       
 16775               ny = parseFloat(xy[3][0]);
       
 16776               path += "curContext.quadraticCurveTo(" + cx + "," + (-cy) + "," + nx + "," + (-ny) + ");";
       
 16777               x = nx;
       
 16778               y = ny;
       
 16779               break;
       
 16780 
       
 16781             case "Z":
       
 16782               //curContext.closePath();
       
 16783               path += "curContext.closePath();";
       
 16784               break;
       
 16785           }
       
 16786           lastCom = com[0];
       
 16787         }
       
 16788 
       
 16789         path += "afterPathDraw();";
       
 16790         path += "curContext.translate(" + horiz_adv_x + ",0);";
       
 16791         path += "}}";
       
 16792 
       
 16793         return ((new Function("beforePathDraw", "afterPathDraw", path))(beforePathDraw, afterPathDraw));
       
 16794       };
       
 16795 
       
 16796       // Parse SVG font-file into block of Canvas commands
       
 16797       var parseSVGFont = function(svg) {
       
 16798         // Store font attributes
       
 16799         var font = svg.getElementsByTagName("font");
       
 16800         p.glyphTable[url].horiz_adv_x = font[0].getAttribute("horiz-adv-x");
       
 16801 
       
 16802         var font_face = svg.getElementsByTagName("font-face")[0];
       
 16803         p.glyphTable[url].units_per_em = parseFloat(font_face.getAttribute("units-per-em"));
       
 16804         p.glyphTable[url].ascent = parseFloat(font_face.getAttribute("ascent"));
       
 16805         p.glyphTable[url].descent = parseFloat(font_face.getAttribute("descent"));
       
 16806 
       
 16807         var glyph = svg.getElementsByTagName("glyph"),
       
 16808           len = glyph.length;
       
 16809 
       
 16810         // Loop through each glyph in the SVG
       
 16811         for (var i = 0; i < len; i++) {
       
 16812           // Store attributes for this glyph
       
 16813           var unicode = glyph[i].getAttribute("unicode");
       
 16814           var name = glyph[i].getAttribute("glyph-name");
       
 16815           horiz_adv_x = glyph[i].getAttribute("horiz-adv-x");
       
 16816           if (horiz_adv_x === null) {
       
 16817             horiz_adv_x = p.glyphTable[url].horiz_adv_x;
       
 16818           }
       
 16819           d = glyph[i].getAttribute("d");
       
 16820           // Split path commands in glpyh
       
 16821           if (d !== undef) {
       
 16822             path = buildPath(d);
       
 16823             // Store glyph data to table object
       
 16824             p.glyphTable[url][name] = {
       
 16825               name: name,
       
 16826               unicode: unicode,
       
 16827               horiz_adv_x: horiz_adv_x,
       
 16828               draw: path.draw
       
 16829             };
       
 16830           }
       
 16831         } // finished adding glyphs to table
       
 16832       };
       
 16833 
       
 16834       // Load and parse Batik SVG font as XML into a Processing Glyph object
       
 16835       var loadXML = function() {
       
 16836         var xmlDoc;
       
 16837 
       
 16838         try {
       
 16839           xmlDoc = document.implementation.createDocument("", "", null);
       
 16840         }
       
 16841         catch(e_fx_op) {
       
 16842           Processing.debug(e_fx_op.message);
       
 16843           return;
       
 16844         }
       
 16845 
       
 16846         try {
       
 16847           xmlDoc.async = false;
       
 16848           xmlDoc.load(url);
       
 16849           parseSVGFont(xmlDoc.getElementsByTagName("svg")[0]);
       
 16850         }
       
 16851         catch(e_sf_ch) {
       
 16852           // Google Chrome, Safari etc.
       
 16853           Processing.debug(e_sf_ch);
       
 16854           try {
       
 16855             var xmlhttp = new window.XMLHttpRequest();
       
 16856             xmlhttp.open("GET", url, false);
       
 16857             xmlhttp.send(null);
       
 16858             parseSVGFont(xmlhttp.responseXML.documentElement);
       
 16859           }
       
 16860           catch(e) {
       
 16861             Processing.debug(e_sf_ch);
       
 16862           }
       
 16863         }
       
 16864       };
       
 16865 
       
 16866       // Create a new object in glyphTable to store this font
       
 16867       p.glyphTable[url] = {};
       
 16868 
       
 16869       // Begin loading the Batik SVG font...
       
 16870       loadXML(url);
       
 16871 
       
 16872       // Return the loaded font for attribute grabbing
       
 16873       return p.glyphTable[url];
       
 16874     };
       
 16875 
       
 16876     /**
       
 16877      * Gets the sketch parameter value. The parameter can be defined as the canvas attribute with
       
 16878      * the "data-processing-" prefix or provided in the pjs directive (e.g. param-test="52").
       
 16879      * The function tries the canvas attributes, then the pjs directive content.
       
 16880      *
       
 16881      * @param   {String}    name          The name of the param to read.
       
 16882      *
       
 16883      * @returns {String}    The parameter value, or null if parameter is not defined.
       
 16884      */
       
 16885     p.param = function(name) {
       
 16886       // trying attribute that was specified in CANVAS
       
 16887       var attributeName = "data-processing-" + name;
       
 16888       if (curElement.hasAttribute(attributeName)) {
       
 16889         return curElement.getAttribute(attributeName);
       
 16890       }
       
 16891       // trying child PARAM elements of the CANVAS
       
 16892       for (var i = 0, len = curElement.childNodes.length; i < len; ++i) {
       
 16893         var item = curElement.childNodes.item(i);
       
 16894         if (item.nodeType !== 1 || item.tagName.toLowerCase() !== "param") {
       
 16895           continue;
       
 16896         }
       
 16897         if (item.getAttribute("name") === name) {
       
 16898           return item.getAttribute("value");
       
 16899         }
       
 16900       }
       
 16901       // fallback to default params
       
 16902       if (curSketch.params.hasOwnProperty(name)) {
       
 16903         return curSketch.params[name];
       
 16904       }
       
 16905       return null;
       
 16906     };
       
 16907 
       
 16908     ////////////////////////////////////////////////////////////////////////////
       
 16909     // 2D/3D methods wiring utils
       
 16910     ////////////////////////////////////////////////////////////////////////////
       
 16911     function wireDimensionalFunctions(mode) {
       
 16912       // Drawing2D/Drawing3D
       
 16913       if (mode === '3D') {
       
 16914         drawing = new Drawing3D();
       
 16915       } else if (mode === '2D') {
       
 16916         drawing = new Drawing2D();
       
 16917       } else {
       
 16918         drawing = new DrawingPre();
       
 16919       }
       
 16920 
       
 16921       // Wire up functions (Use DrawingPre properties names)
       
 16922       for (var i in DrawingPre.prototype) {
       
 16923         if (DrawingPre.prototype.hasOwnProperty(i) && i.indexOf("$") < 0) {
       
 16924           p[i] = drawing[i];
       
 16925         }
       
 16926       }
       
 16927 
       
 16928       // Run initialization
       
 16929       drawing.$init();
       
 16930     }
       
 16931 
       
 16932     function createDrawingPreFunction(name) {
       
 16933       return function() {
       
 16934         wireDimensionalFunctions("2D");
       
 16935         return drawing[name].apply(this, arguments);
       
 16936       };
       
 16937     }
       
 16938     DrawingPre.prototype.translate = createDrawingPreFunction("translate");
       
 16939     DrawingPre.prototype.scale = createDrawingPreFunction("scale");
       
 16940     DrawingPre.prototype.pushMatrix = createDrawingPreFunction("pushMatrix");
       
 16941     DrawingPre.prototype.popMatrix = createDrawingPreFunction("popMatrix");
       
 16942     DrawingPre.prototype.resetMatrix = createDrawingPreFunction("resetMatrix");
       
 16943     DrawingPre.prototype.applyMatrix = createDrawingPreFunction("applyMatrix");
       
 16944     DrawingPre.prototype.rotate = createDrawingPreFunction("rotate");
       
 16945     DrawingPre.prototype.rotateZ = createDrawingPreFunction("rotateZ");
       
 16946     DrawingPre.prototype.redraw = createDrawingPreFunction("redraw");
       
 16947     DrawingPre.prototype.toImageData = createDrawingPreFunction("toImageData");
       
 16948     DrawingPre.prototype.ambientLight = createDrawingPreFunction("ambientLight");
       
 16949     DrawingPre.prototype.directionalLight = createDrawingPreFunction("directionalLight");
       
 16950     DrawingPre.prototype.lightFalloff = createDrawingPreFunction("lightFalloff");
       
 16951     DrawingPre.prototype.lightSpecular = createDrawingPreFunction("lightSpecular");
       
 16952     DrawingPre.prototype.pointLight = createDrawingPreFunction("pointLight");
       
 16953     DrawingPre.prototype.noLights = createDrawingPreFunction("noLights");
       
 16954     DrawingPre.prototype.spotLight = createDrawingPreFunction("spotLight");
       
 16955     DrawingPre.prototype.beginCamera = createDrawingPreFunction("beginCamera");
       
 16956     DrawingPre.prototype.endCamera = createDrawingPreFunction("endCamera");
       
 16957     DrawingPre.prototype.frustum = createDrawingPreFunction("frustum");
       
 16958     DrawingPre.prototype.box = createDrawingPreFunction("box");
       
 16959     DrawingPre.prototype.sphere = createDrawingPreFunction("sphere");
       
 16960     DrawingPre.prototype.ambient = createDrawingPreFunction("ambient");
       
 16961     DrawingPre.prototype.emissive = createDrawingPreFunction("emissive");
       
 16962     DrawingPre.prototype.shininess = createDrawingPreFunction("shininess");
       
 16963     DrawingPre.prototype.specular = createDrawingPreFunction("specular");
       
 16964     DrawingPre.prototype.fill = createDrawingPreFunction("fill");
       
 16965     DrawingPre.prototype.stroke = createDrawingPreFunction("stroke");
       
 16966     DrawingPre.prototype.strokeWeight = createDrawingPreFunction("strokeWeight");
       
 16967     DrawingPre.prototype.smooth = createDrawingPreFunction("smooth");
       
 16968     DrawingPre.prototype.noSmooth = createDrawingPreFunction("noSmooth");
       
 16969     DrawingPre.prototype.point = createDrawingPreFunction("point");
       
 16970     DrawingPre.prototype.vertex = createDrawingPreFunction("vertex");
       
 16971     DrawingPre.prototype.endShape = createDrawingPreFunction("endShape");
       
 16972     DrawingPre.prototype.bezierVertex = createDrawingPreFunction("bezierVertex");
       
 16973     DrawingPre.prototype.curveVertex = createDrawingPreFunction("curveVertex");
       
 16974     DrawingPre.prototype.curve = createDrawingPreFunction("curve");
       
 16975     DrawingPre.prototype.line = createDrawingPreFunction("line");
       
 16976     DrawingPre.prototype.bezier = createDrawingPreFunction("bezier");
       
 16977     DrawingPre.prototype.rect = createDrawingPreFunction("rect");
       
 16978     DrawingPre.prototype.ellipse = createDrawingPreFunction("ellipse");
       
 16979     DrawingPre.prototype.background = createDrawingPreFunction("background");
       
 16980     DrawingPre.prototype.image = createDrawingPreFunction("image");
       
 16981     DrawingPre.prototype.textWidth = createDrawingPreFunction("textWidth");
       
 16982     DrawingPre.prototype.text$line = createDrawingPreFunction("text$line");
       
 16983     DrawingPre.prototype.$ensureContext = createDrawingPreFunction("$ensureContext");
       
 16984     DrawingPre.prototype.$newPMatrix = createDrawingPreFunction("$newPMatrix");
       
 16985 
       
 16986     DrawingPre.prototype.size = function(aWidth, aHeight, aMode) {
       
 16987       wireDimensionalFunctions(aMode === PConstants.WEBGL ? "3D" : "2D");
       
 16988       p.size(aWidth, aHeight, aMode);
       
 16989     };
       
 16990 
       
 16991     DrawingPre.prototype.$init = nop;
       
 16992 
       
 16993     Drawing2D.prototype.$init = function() {
       
 16994       // Setup default 2d canvas context.
       
 16995       // Moving this here removes the number of times we need to check the 3D variable
       
 16996       p.size(p.width, p.height);
       
 16997 
       
 16998       curContext.lineCap = 'round';
       
 16999 
       
 17000       // Set default stroke and fill color
       
 17001       p.noSmooth();
       
 17002       p.disableContextMenu();
       
 17003     };
       
 17004     Drawing3D.prototype.$init = function() {
       
 17005       // For ref/perf test compatibility until those are fixed
       
 17006       p.use3DContext = true;
       
 17007     };
       
 17008 
       
 17009     DrawingShared.prototype.$ensureContext = function() {
       
 17010       return curContext;
       
 17011     };
       
 17012 
       
 17013     //////////////////////////////////////////////////////////////////////////
       
 17014     // Touch and Mouse event handling
       
 17015     //////////////////////////////////////////////////////////////////////////
       
 17016 
       
 17017     function calculateOffset(curElement, event) {
       
 17018       var element = curElement,
       
 17019         offsetX = 0,
       
 17020         offsetY = 0;
       
 17021 
       
 17022       p.pmouseX = p.mouseX;
       
 17023       p.pmouseY = p.mouseY;
       
 17024 
       
 17025       // Find element offset
       
 17026       if (element.offsetParent) {
       
 17027         do {
       
 17028           offsetX += element.offsetLeft;
       
 17029           offsetY += element.offsetTop;
       
 17030         } while (!!(element = element.offsetParent));
       
 17031       }
       
 17032 
       
 17033       // Find Scroll offset
       
 17034       element = curElement;
       
 17035       do {
       
 17036         offsetX -= element.scrollLeft || 0;
       
 17037         offsetY -= element.scrollTop || 0;
       
 17038       } while (!!(element = element.parentNode));
       
 17039 
       
 17040       // Add padding and border style widths to offset
       
 17041       offsetX += stylePaddingLeft;
       
 17042       offsetY += stylePaddingTop;
       
 17043 
       
 17044       offsetX += styleBorderLeft;
       
 17045       offsetY += styleBorderTop;
       
 17046 
       
 17047       // Take into account any scrolling done
       
 17048       offsetX += window.pageXOffset;
       
 17049       offsetY += window.pageYOffset;
       
 17050 
       
 17051       return {'X':offsetX,'Y':offsetY};
       
 17052     }
       
 17053 
       
 17054     function updateMousePosition(curElement, event) {
       
 17055       var offset = calculateOffset(curElement, event);
       
 17056 
       
 17057       // Dropping support for IE clientX and clientY, switching to pageX and pageY so we don't have to calculate scroll offset.
       
 17058       // Removed in ticket #184. See rev: 2f106d1c7017fed92d045ba918db47d28e5c16f4
       
 17059       p.mouseX = event.pageX - offset.X;
       
 17060       p.mouseY = event.pageY - offset.Y;
       
 17061     }
       
 17062 
       
 17063     // Return a TouchEvent with canvas-specific x/y co-ordinates
       
 17064     function addTouchEventOffset(t) {
       
 17065       var offset = calculateOffset(t.changedTouches[0].target, t.changedTouches[0]),
       
 17066           i;
       
 17067 
       
 17068       for (i = 0; i < t.touches.length; i++) {
       
 17069         var touch = t.touches[i];
       
 17070         touch.offsetX = touch.pageX - offset.X;
       
 17071         touch.offsetY = touch.pageY - offset.Y;
       
 17072       }
       
 17073       for (i = 0; i < t.targetTouches.length; i++) {
       
 17074         var targetTouch = t.targetTouches[i];
       
 17075         targetTouch.offsetX = targetTouch.pageX - offset.X;
       
 17076         targetTouch.offsetY = targetTouch.pageY - offset.Y;
       
 17077       }
       
 17078       for (i = 0; i < t.changedTouches.length; i++) {
       
 17079         var changedTouch = t.changedTouches[i];
       
 17080         changedTouch.offsetX = changedTouch.pageX - offset.X;
       
 17081         changedTouch.offsetY = changedTouch.pageY - offset.Y;
       
 17082       }
       
 17083 
       
 17084       return t;
       
 17085     }
       
 17086 
       
 17087     attachEventHandler(curElement, "touchstart", function (t) {
       
 17088       // Removes unwanted behaviour of the canvas when touching canvas
       
 17089       curElement.setAttribute("style","-webkit-user-select: none");
       
 17090       curElement.setAttribute("onclick","void(0)");
       
 17091       curElement.setAttribute("style","-webkit-tap-highlight-color:rgba(0,0,0,0)");
       
 17092       // Loop though eventHandlers and remove mouse listeners
       
 17093       for (var i=0, ehl=eventHandlers.length; i<ehl; i++) {
       
 17094         var type = eventHandlers[i].type;
       
 17095         // Have this function remove itself from the eventHandlers list too
       
 17096         if (type === "mouseout" ||  type === "mousemove" ||
       
 17097             type === "mousedown" || type === "mouseup" ||
       
 17098             type === "DOMMouseScroll" || type === "mousewheel" || type === "touchstart") {
       
 17099           detachEventHandler(eventHandlers[i]);
       
 17100         }
       
 17101       }
       
 17102 
       
 17103       // If there are any native touch events defined in the sketch, connect all of them
       
 17104       // Otherwise, connect all of the emulated mouse events
       
 17105       if (p.touchStart !== undef || p.touchMove !== undef ||
       
 17106           p.touchEnd !== undef || p.touchCancel !== undef) {
       
 17107         attachEventHandler(curElement, "touchstart", function(t) {
       
 17108           if (p.touchStart !== undef) {
       
 17109             t = addTouchEventOffset(t);
       
 17110             p.touchStart(t);
       
 17111           }
       
 17112         });
       
 17113 
       
 17114         attachEventHandler(curElement, "touchmove", function(t) {
       
 17115           if (p.touchMove !== undef) {
       
 17116             t.preventDefault(); // Stop the viewport from scrolling
       
 17117             t = addTouchEventOffset(t);
       
 17118             p.touchMove(t);
       
 17119           }
       
 17120         });
       
 17121 
       
 17122         attachEventHandler(curElement, "touchend", function(t) {
       
 17123           if (p.touchEnd !== undef) {
       
 17124             t = addTouchEventOffset(t);
       
 17125             p.touchEnd(t);
       
 17126           }
       
 17127         });
       
 17128 
       
 17129         attachEventHandler(curElement, "touchcancel", function(t) {
       
 17130           if (p.touchCancel !== undef) {
       
 17131             t = addTouchEventOffset(t);
       
 17132             p.touchCancel(t);
       
 17133           }
       
 17134         });
       
 17135 
       
 17136       } else {
       
 17137         // Emulated touch start/mouse down event
       
 17138         attachEventHandler(curElement, "touchstart", function(e) {
       
 17139           updateMousePosition(curElement, e.touches[0]);
       
 17140 
       
 17141           p.__mousePressed = true;
       
 17142           p.mouseDragging = false;
       
 17143           p.mouseButton = PConstants.LEFT;
       
 17144 
       
 17145           if (typeof p.mousePressed === "function") {
       
 17146             p.mousePressed();
       
 17147           }
       
 17148         });
       
 17149 
       
 17150         // Emulated touch move/mouse move event
       
 17151         attachEventHandler(curElement, "touchmove", function(e) {
       
 17152           e.preventDefault();
       
 17153           updateMousePosition(curElement, e.touches[0]);
       
 17154 
       
 17155           if (typeof p.mouseMoved === "function" && !p.__mousePressed) {
       
 17156             p.mouseMoved();
       
 17157           }
       
 17158           if (typeof p.mouseDragged === "function" && p.__mousePressed) {
       
 17159             p.mouseDragged();
       
 17160             p.mouseDragging = true;
       
 17161           }
       
 17162         });
       
 17163 
       
 17164         // Emulated touch up/mouse up event
       
 17165         attachEventHandler(curElement, "touchend", function(e) {
       
 17166           p.__mousePressed = false;
       
 17167 
       
 17168           if (typeof p.mouseClicked === "function" && !p.mouseDragging) {
       
 17169             p.mouseClicked();
       
 17170           }
       
 17171 
       
 17172           if (typeof p.mouseReleased === "function") {
       
 17173             p.mouseReleased();
       
 17174           }
       
 17175         });
       
 17176       }
       
 17177 
       
 17178       // Refire the touch start event we consumed in this function
       
 17179       curElement.dispatchEvent(t);
       
 17180     });
       
 17181 
       
 17182     (function() {
       
 17183       var enabled = true,
       
 17184           contextMenu = function(e) {
       
 17185             e.preventDefault();
       
 17186             e.stopPropagation();
       
 17187           };
       
 17188 
       
 17189       p.disableContextMenu = function() {
       
 17190         if (!enabled) {
       
 17191           return;
       
 17192         }
       
 17193         attachEventHandler(curElement, 'contextmenu', contextMenu);
       
 17194         enabled = false;
       
 17195       };
       
 17196 
       
 17197       p.enableContextMenu = function() {
       
 17198         if (enabled) {
       
 17199           return;
       
 17200         }
       
 17201         detachEventHandler({elem: curElement, type: 'contextmenu', fn: contextMenu});
       
 17202         enabled = true;
       
 17203       };
       
 17204     }());
       
 17205 
       
 17206     attachEventHandler(curElement, "mousemove", function(e) {
       
 17207       updateMousePosition(curElement, e);
       
 17208       if (typeof p.mouseMoved === "function" && !p.__mousePressed) {
       
 17209         p.mouseMoved();
       
 17210       }
       
 17211       if (typeof p.mouseDragged === "function" && p.__mousePressed) {
       
 17212         p.mouseDragged();
       
 17213         p.mouseDragging = true;
       
 17214       }
       
 17215     });
       
 17216 
       
 17217     attachEventHandler(curElement, "mouseout", function(e) {
       
 17218       if (typeof p.mouseOut === "function") {
       
 17219         p.mouseOut();
       
 17220       }
       
 17221     });
       
 17222 
       
 17223     attachEventHandler(curElement, "mouseover", function(e) {
       
 17224       updateMousePosition(curElement, e);
       
 17225       if (typeof p.mouseOver === "function") {
       
 17226         p.mouseOver();
       
 17227       }
       
 17228     });
       
 17229 
       
 17230     attachEventHandler(curElement, "mousedown", function(e) {
       
 17231       p.__mousePressed = true;
       
 17232       p.mouseDragging = false;
       
 17233       switch (e.which) {
       
 17234       case 1:
       
 17235         p.mouseButton = PConstants.LEFT;
       
 17236         break;
       
 17237       case 2:
       
 17238         p.mouseButton = PConstants.CENTER;
       
 17239         break;
       
 17240       case 3:
       
 17241         p.mouseButton = PConstants.RIGHT;
       
 17242         break;
       
 17243       }
       
 17244 
       
 17245       if (typeof p.mousePressed === "function") {
       
 17246         p.mousePressed();
       
 17247       }
       
 17248     });
       
 17249 
       
 17250     attachEventHandler(curElement, "mouseup", function(e) {
       
 17251       p.__mousePressed = false;
       
 17252 
       
 17253       if (typeof p.mouseClicked === "function" && !p.mouseDragging) {
       
 17254         p.mouseClicked();
       
 17255       }
       
 17256 
       
 17257       if (typeof p.mouseReleased === "function") {
       
 17258         p.mouseReleased();
       
 17259       }
       
 17260     });
       
 17261 
       
 17262     var mouseWheelHandler = function(e) {
       
 17263       var delta = 0;
       
 17264 
       
 17265       if (e.wheelDelta) {
       
 17266         delta = e.wheelDelta / 120;
       
 17267         if (window.opera) {
       
 17268           delta = -delta;
       
 17269         }
       
 17270       } else if (e.detail) {
       
 17271         delta = -e.detail / 3;
       
 17272       }
       
 17273 
       
 17274       p.mouseScroll = delta;
       
 17275 
       
 17276       if (delta && typeof p.mouseScrolled === 'function') {
       
 17277         p.mouseScrolled();
       
 17278       }
       
 17279     };
       
 17280 
       
 17281     // Support Gecko and non-Gecko scroll events
       
 17282     attachEventHandler(document, 'DOMMouseScroll', mouseWheelHandler);
       
 17283     attachEventHandler(document, 'mousewheel', mouseWheelHandler);
       
 17284 
       
 17285     //////////////////////////////////////////////////////////////////////////
       
 17286     // Keyboard Events
       
 17287     //////////////////////////////////////////////////////////////////////////
       
 17288 
       
 17289     // Get the DOM element if string was passed
       
 17290     if (typeof curElement === "string") {
       
 17291       curElement = document.getElementById(curElement);
       
 17292     }
       
 17293 
       
 17294     // In order to catch key events in a canvas, it needs to be "specially focusable",
       
 17295     // by assigning it a tabindex. If no tabindex is specified on-page, set this to 0.
       
 17296     if (!curElement.getAttribute("tabindex")) {
       
 17297       curElement.setAttribute("tabindex", 0);
       
 17298     }
       
 17299 
       
 17300     function getKeyCode(e) {
       
 17301       var code = e.which || e.keyCode;
       
 17302       switch (code) {
       
 17303         case 13: // ENTER
       
 17304           return 10;
       
 17305         case 91: // META L (Saf/Mac)
       
 17306         case 93: // META R (Saf/Mac)
       
 17307         case 224: // META (FF/Mac)
       
 17308           return 157;
       
 17309         case 57392: // CONTROL (Op/Mac)
       
 17310           return 17;
       
 17311         case 46: // DELETE
       
 17312           return 127;
       
 17313         case 45: // INSERT
       
 17314           return 155;
       
 17315       }
       
 17316       return code;
       
 17317     }
       
 17318 
       
 17319     function getKeyChar(e) {
       
 17320       var c = e.which || e.keyCode;
       
 17321       var anyShiftPressed = e.shiftKey || e.ctrlKey || e.altKey || e.metaKey;
       
 17322       switch (c) {
       
 17323         case 13:
       
 17324           c = anyShiftPressed ? 13 : 10; // RETURN vs ENTER (Mac)
       
 17325           break;
       
 17326         case 8:
       
 17327           c = anyShiftPressed ? 127 : 8; // DELETE vs BACKSPACE (Mac)
       
 17328           break;
       
 17329       }
       
 17330       return new Char(c);
       
 17331     }
       
 17332 
       
 17333     function suppressKeyEvent(e) {
       
 17334       if (typeof e.preventDefault === "function") {
       
 17335         e.preventDefault();
       
 17336       } else if (typeof e.stopPropagation === "function") {
       
 17337         e.stopPropagation();
       
 17338       }
       
 17339       return false;
       
 17340     }
       
 17341 
       
 17342     function updateKeyPressed() {
       
 17343       var ch;
       
 17344       for (ch in pressedKeysMap) {
       
 17345         if (pressedKeysMap.hasOwnProperty(ch)) {
       
 17346           p.__keyPressed = true;
       
 17347           return;
       
 17348         }
       
 17349       }
       
 17350       p.__keyPressed = false;
       
 17351     }
       
 17352 
       
 17353     function resetKeyPressed() {
       
 17354       p.__keyPressed = false;
       
 17355       pressedKeysMap = [];
       
 17356       lastPressedKeyCode = null;
       
 17357     }
       
 17358 
       
 17359     function simulateKeyTyped(code, c) {
       
 17360       pressedKeysMap[code] = c;
       
 17361       lastPressedKeyCode = null;
       
 17362       p.key = c;
       
 17363       p.keyCode = code;
       
 17364       p.keyPressed();
       
 17365       p.keyCode = 0;
       
 17366       p.keyTyped();
       
 17367       updateKeyPressed();
       
 17368     }
       
 17369 
       
 17370     function handleKeydown(e) {
       
 17371       var code = getKeyCode(e);
       
 17372       if (code === PConstants.DELETE) {
       
 17373         simulateKeyTyped(code, new Char(127));
       
 17374         return;
       
 17375       }
       
 17376       if (codedKeys.indexOf(code) < 0) {
       
 17377         lastPressedKeyCode = code;
       
 17378         return;
       
 17379       }
       
 17380       var c = new Char(PConstants.CODED);
       
 17381       p.key = c;
       
 17382       p.keyCode = code;
       
 17383       pressedKeysMap[code] = c;
       
 17384       p.keyPressed();
       
 17385       lastPressedKeyCode = null;
       
 17386       updateKeyPressed();
       
 17387       return suppressKeyEvent(e);
       
 17388     }
       
 17389 
       
 17390     function handleKeypress(e) {
       
 17391       if (lastPressedKeyCode === null) {
       
 17392         return; // processed in handleKeydown
       
 17393       }
       
 17394       var code = lastPressedKeyCode, c = getKeyChar(e);
       
 17395       simulateKeyTyped(code, c);
       
 17396       return suppressKeyEvent(e);
       
 17397     }
       
 17398 
       
 17399     function handleKeyup(e) {
       
 17400       var code = getKeyCode(e), c = pressedKeysMap[code];
       
 17401       if (c === undef) {
       
 17402         return; // no keyPressed event was generated.
       
 17403       }
       
 17404       p.key = c;
       
 17405       p.keyCode = code;
       
 17406       p.keyReleased();
       
 17407       delete pressedKeysMap[code];
       
 17408       updateKeyPressed();
       
 17409     }
       
 17410 
       
 17411     // Send aCode Processing syntax to be converted to JavaScript
       
 17412     if (!pgraphicsMode) {
       
 17413       if (aCode instanceof Processing.Sketch) {
       
 17414         // Use sketch as is
       
 17415         curSketch = aCode;
       
 17416       } else if (typeof aCode === "function") {
       
 17417         // Wrap function with default sketch parameters
       
 17418         curSketch = new Processing.Sketch(aCode);
       
 17419       } else if (!aCode) {
       
 17420         // Empty sketch
       
 17421         curSketch = new Processing.Sketch(function (){});
       
 17422       } else {
       
 17423 //#if PARSER
       
 17424         // Compile the code
       
 17425         curSketch = Processing.compile(aCode);
       
 17426 //#else
       
 17427 //      throw "PJS compile is not supported";
       
 17428 //#endif
       
 17429       }
       
 17430 
       
 17431       // Expose internal field for diagnostics and testing
       
 17432       p.externals.sketch = curSketch;
       
 17433 
       
 17434       wireDimensionalFunctions();
       
 17435 
       
 17436       // the onfocus and onblur events are handled in two parts.
       
 17437       // 1) the p.focused property is handled per sketch
       
 17438       curElement.onfocus = function() {
       
 17439         p.focused = true;
       
 17440       };
       
 17441 
       
 17442       curElement.onblur = function() {
       
 17443         p.focused = false;
       
 17444         if (!curSketch.options.globalKeyEvents) {
       
 17445           resetKeyPressed();
       
 17446         }
       
 17447       };
       
 17448 
       
 17449       // 2) looping status is handled per page, based on the pauseOnBlur @pjs directive
       
 17450       if (curSketch.options.pauseOnBlur) {
       
 17451         attachEventHandler(window, 'focus', function() {
       
 17452           if (doLoop) {
       
 17453             p.loop();
       
 17454           }
       
 17455         });
       
 17456 
       
 17457         attachEventHandler(window, 'blur', function() {
       
 17458           if (doLoop && loopStarted) {
       
 17459             p.noLoop();
       
 17460             doLoop = true; // make sure to keep this true after the noLoop call
       
 17461           }
       
 17462           resetKeyPressed();
       
 17463         });
       
 17464       }
       
 17465 
       
 17466       // if keyboard events should be handled globally, the listeners should
       
 17467       // be bound to the document window, rather than to the current canvas
       
 17468       var keyTrigger = curSketch.options.globalKeyEvents ? window : curElement;
       
 17469       attachEventHandler(keyTrigger, "keydown", handleKeydown);
       
 17470       attachEventHandler(keyTrigger, "keypress", handleKeypress);
       
 17471       attachEventHandler(keyTrigger, "keyup", handleKeyup);
       
 17472 
       
 17473       // Step through the libraries that were attached at doc load...
       
 17474       for (var i in Processing.lib) {
       
 17475         if (Processing.lib.hasOwnProperty(i)) {
       
 17476           if(Processing.lib[i].hasOwnProperty("attach")) {
       
 17477             // use attach function if present
       
 17478             Processing.lib[i].attach(p);
       
 17479           } else if(Processing.lib[i] instanceof Function)  {
       
 17480             // Init the libraries in the context of this p_instance (legacy)
       
 17481             Processing.lib[i].call(this);
       
 17482           }
       
 17483         }
       
 17484       }
       
 17485 
       
 17486       // sketch execute test interval, used to reschedule
       
 17487       // an execute when preloads have not yet finished.
       
 17488       var retryInterval = 100;
       
 17489 
       
 17490       var executeSketch = function(processing) {
       
 17491         // Don't start until all specified images and fonts in the cache are preloaded
       
 17492         if (!(curSketch.imageCache.pending || PFont.preloading.pending(retryInterval))) {
       
 17493           // the opera preload cache can only be cleared once we start
       
 17494           if (window.opera) {
       
 17495             var link,
       
 17496                 element,
       
 17497                 operaCache=curSketch.imageCache.operaCache;
       
 17498             for (link in operaCache) {
       
 17499               if(operaCache.hasOwnProperty(link)) {
       
 17500                 element = operaCache[link];
       
 17501                 if (element !== null) {
       
 17502                   document.body.removeChild(element);
       
 17503                 }
       
 17504                 delete(operaCache[link]);
       
 17505               }
       
 17506             }
       
 17507           }
       
 17508 
       
 17509           curSketch.attach(processing, defaultScope);
       
 17510 
       
 17511           // pass a reference to the p instance for this sketch.
       
 17512           curSketch.onLoad(processing);
       
 17513 
       
 17514           // Run void setup()
       
 17515           if (processing.setup) {
       
 17516             processing.setup();
       
 17517             // if any transforms were performed in setup reset to identity matrix
       
 17518             // so draw loop is unpolluted
       
 17519             processing.resetMatrix();
       
 17520             curSketch.onSetup();
       
 17521           }
       
 17522 
       
 17523           // some pixels can be cached, flushing
       
 17524           resetContext();
       
 17525 
       
 17526           if (processing.draw) {
       
 17527             if (!doLoop) {
       
 17528               processing.redraw();
       
 17529             } else {
       
 17530               processing.loop();
       
 17531             }
       
 17532           }
       
 17533         } else {
       
 17534           window.setTimeout(function() { executeSketch(processing); }, retryInterval);
       
 17535         }
       
 17536       };
       
 17537 
       
 17538       // Only store an instance of non-createGraphics instances.
       
 17539       addInstance(this);
       
 17540 
       
 17541       // The parser adds custom methods to the processing context
       
 17542       // this renames p to processing so these methods will run
       
 17543       executeSketch(p);
       
 17544     } else {
       
 17545       // No executable sketch was specified
       
 17546       // or called via createGraphics
       
 17547       curSketch = new Processing.Sketch();
       
 17548 
       
 17549       wireDimensionalFunctions();
       
 17550 
       
 17551       // Hack to make PGraphics work again after splitting size()
       
 17552       p.size = function(w, h, render) {
       
 17553         if (render && render === PConstants.WEBGL) {
       
 17554           wireDimensionalFunctions('3D');
       
 17555         } else {
       
 17556           wireDimensionalFunctions('2D');
       
 17557         }
       
 17558 
       
 17559         p.size(w, h, render);
       
 17560       };
       
 17561     }
       
 17562   }; // Processing() ends
       
 17563 
       
 17564   // Place-holder for overridable debugging function
       
 17565   Processing.debug = debug;
       
 17566 
       
 17567   Processing.prototype = defaultScope;
       
 17568 
       
 17569 //#if PARSER
       
 17570   // Processing global methods and constants for the parser
       
 17571   function getGlobalMembers() {
       
 17572     // The names array contains the names of everything that is inside "p."
       
 17573     // When something new is added to "p." it must also be added to this list.
       
 17574     var names = [ /* this code is generated by jsglobals.js */
       
 17575       "abs", "acos", "alpha", "ambient", "ambientLight", "append", "applyMatrix",
       
 17576       "arc", "arrayCopy", "asin", "atan", "atan2", "background", "beginCamera",
       
 17577       "beginDraw", "beginShape", "bezier", "bezierDetail", "bezierPoint",
       
 17578       "bezierTangent", "bezierVertex", "binary", "blend", "blendColor",
       
 17579       "blit_resize", "blue", "box", "breakShape", "brightness",
       
 17580       "camera", "ceil", "Character", "color", "colorMode",
       
 17581       "concat", "constrain", "copy", "cos", "createFont",
       
 17582       "createGraphics", "createImage", "cursor", "curve", "curveDetail",
       
 17583       "curvePoint", "curveTangent", "curveTightness", "curveVertex", "day",
       
 17584       "degrees", "directionalLight", "disableContextMenu",
       
 17585       "dist", "draw", "ellipse", "ellipseMode", "emissive", "enableContextMenu",
       
 17586       "endCamera", "endDraw", "endShape", "exit", "exp", "expand", "externals",
       
 17587       "fill", "filter", "floor", "focused", "frameCount", "frameRate", "frustum",
       
 17588       "get", "glyphLook", "glyphTable", "green", "height", "hex", "hint", "hour",
       
 17589       "hue", "image", "imageMode", "intersect", "join", "key",
       
 17590       "keyCode", "keyPressed", "keyReleased", "keyTyped", "lerp", "lerpColor",
       
 17591       "lightFalloff", "lights", "lightSpecular", "line", "link", "loadBytes",
       
 17592       "loadFont", "loadGlyphs", "loadImage", "loadPixels", "loadShape",
       
 17593       "loadStrings", "log", "loop", "mag", "map", "match", "matchAll", "max",
       
 17594       "millis", "min", "minute", "mix", "modelX", "modelY", "modelZ", "modes",
       
 17595       "month", "mouseButton", "mouseClicked", "mouseDragged", "mouseMoved",
       
 17596       "mouseOut", "mouseOver", "mousePressed", "mouseReleased", "mouseScroll",
       
 17597       "mouseScrolled", "mouseX", "mouseY", "name", "nf", "nfc", "nfp", "nfs",
       
 17598       "noCursor", "noFill", "noise", "noiseDetail", "noiseSeed", "noLights",
       
 17599       "noLoop", "norm", "normal", "noSmooth", "noStroke", "noTint", "ortho",
       
 17600       "param", "parseBoolean", "parseByte", "parseChar", "parseFloat",
       
 17601       "parseInt", "peg", "perspective", "PImage", "pixels", "PMatrix2D",
       
 17602       "PMatrix3D", "PMatrixStack", "pmouseX", "pmouseY", "point",
       
 17603       "pointLight", "popMatrix", "popStyle", "pow", "print", "printCamera",
       
 17604       "println", "printMatrix", "printProjection", "PShape", "PShapeSVG",
       
 17605       "pushMatrix", "pushStyle", "quad", "radians", "random", "Random",
       
 17606       "randomSeed", "rect", "rectMode", "red", "redraw", "requestImage",
       
 17607       "resetMatrix", "reverse", "rotate", "rotateX", "rotateY", "rotateZ",
       
 17608       "round", "saturation", "save", "saveFrame", "saveStrings", "scale",
       
 17609       "screenX", "screenY", "screenZ", "second", "set", "setup", "shape",
       
 17610       "shapeMode", "shared", "shininess", "shorten", "sin", "size", "smooth",
       
 17611       "sort", "specular", "sphere", "sphereDetail", "splice", "split",
       
 17612       "splitTokens", "spotLight", "sq", "sqrt", "status", "str", "stroke",
       
 17613       "strokeCap", "strokeJoin", "strokeWeight", "subset", "tan", "text",
       
 17614       "textAlign", "textAscent", "textDescent", "textFont", "textLeading",
       
 17615       "textMode", "textSize", "texture", "textureMode", "textWidth", "tint", "toImageData",
       
 17616       "touchCancel", "touchEnd", "touchMove", "touchStart", "translate",
       
 17617       "triangle", "trim", "unbinary", "unhex", "updatePixels", "use3DContext",
       
 17618       "vertex", "width", "XMLElement", "year", "__contains", "__equals",
       
 17619       "__equalsIgnoreCase", "__frameRate", "__hashCode", "__int_cast",
       
 17620       "__instanceof", "__keyPressed", "__mousePressed", "__printStackTrace",
       
 17621       "__replace", "__replaceAll", "__replaceFirst", "__toCharArray", "__split",
       
 17622       "__codePointAt", "__startsWith", "__endsWith"];
       
 17623 
       
 17624     var members = {};
       
 17625     var i, l;
       
 17626     for (i = 0, l = names.length; i < l ; ++i) {
       
 17627       members[names[i]] = null;
       
 17628     }
       
 17629     for (var lib in Processing.lib) {
       
 17630       if (Processing.lib.hasOwnProperty(lib)) {
       
 17631         if (Processing.lib[lib].exports) {
       
 17632           var exportedNames = Processing.lib[lib].exports;
       
 17633           for (i = 0, l = exportedNames.length; i < l; ++i) {
       
 17634            members[exportedNames[i]] = null;
       
 17635           }
       
 17636         }
       
 17637       }
       
 17638     }
       
 17639     return members;
       
 17640   }
       
 17641 
       
 17642 /*
       
 17643 
       
 17644     Parser converts Java-like syntax into JavaScript.
       
 17645     Creates an Abstract Syntax Tree -- "Light AST" from the Java-like code.
       
 17646 
       
 17647     It is an object tree. The root object is created from the AstRoot class, which contains statements.
       
 17648 
       
 17649     A statement object can be of type: AstForStatement, AstCatchStatement, AstPrefixStatement, AstMethod, AstClass,
       
 17650     AstInterface, AstFunction, AstStatementBlock and AstLabel.
       
 17651 
       
 17652     AstPrefixStatement can be a statement of type: if, switch, while, with, do, else, finally, return, throw, try, break, and continue.
       
 17653 
       
 17654     These object's toString function returns the JavaScript code for the statement.
       
 17655 
       
 17656     Any processing calls need "processing." prepended to them.
       
 17657 
       
 17658     Similarly, calls from inside classes need "$this_1.", prepended to them,
       
 17659     with 1 being the depth level for inner classes.
       
 17660     This includes members passed down from inheritance.
       
 17661 
       
 17662     The resulting code is then eval'd and run.
       
 17663 
       
 17664 */
       
 17665 
       
 17666   function parseProcessing(code) {
       
 17667     var globalMembers = getGlobalMembers();
       
 17668 
       
 17669     // masks parentheses, brackets and braces with '"A5"'
       
 17670     // where A is the bracket type, and 5 is the index in an array containing all brackets split into atoms
       
 17671     // 'while(true){}' -> 'while"B1""A2"'
       
 17672     // parentheses() = B, brackets[] = C and braces{} = A
       
 17673     function splitToAtoms(code) {
       
 17674       var atoms = [];
       
 17675       var items = code.split(/([\{\[\(\)\]\}])/);
       
 17676       var result = items[0];
       
 17677 
       
 17678       var stack = [];
       
 17679       for(var i=1; i < items.length; i += 2) {
       
 17680         var item = items[i];
       
 17681         if(item === '[' || item === '{' || item === '(') {
       
 17682           stack.push(result); result = item;
       
 17683         } else if(item === ']' || item === '}' || item === ')') {
       
 17684           var kind = item === '}' ? 'A' : item === ')' ? 'B' : 'C';
       
 17685           var index = atoms.length; atoms.push(result + item);
       
 17686           result = stack.pop() + '"' + kind + (index + 1) + '"';
       
 17687         }
       
 17688         result += items[i + 1];
       
 17689       }
       
 17690       atoms.unshift(result);
       
 17691       return atoms;
       
 17692     }
       
 17693 
       
 17694     // replaces strings and regexs keyed by index with an array of strings
       
 17695     function injectStrings(code, strings) {
       
 17696       return code.replace(/'(\d+)'/g, function(all, index) {
       
 17697         var val = strings[index];
       
 17698         if(val.charAt(0) === "/") {
       
 17699           return val;
       
 17700         }
       
 17701         return (/^'((?:[^'\\\n])|(?:\\.[0-9A-Fa-f]*))'$/).test(val) ? "(new $p.Character(" + val + "))" : val;
       
 17702       });
       
 17703     }
       
 17704 
       
 17705     // trims off leading and trailing spaces
       
 17706     // returns an object. object.left, object.middle, object.right, object.untrim
       
 17707     function trimSpaces(string) {
       
 17708       var m1 = /^\s*/.exec(string), result;
       
 17709       if(m1[0].length === string.length) {
       
 17710         result = {left: m1[0], middle: "", right: ""};
       
 17711       } else {
       
 17712         var m2 = /\s*$/.exec(string);
       
 17713         result = {left: m1[0], middle: string.substring(m1[0].length, m2.index), right: m2[0]};
       
 17714       }
       
 17715       result.untrim = function(t) { return this.left + t + this.right; };
       
 17716       return result;
       
 17717     }
       
 17718 
       
 17719     // simple trim of leading and trailing spaces
       
 17720     function trim(string) {
       
 17721       return string.replace(/^\s+/,'').replace(/\s+$/,'');
       
 17722     }
       
 17723 
       
 17724     function appendToLookupTable(table, array) {
       
 17725       for(var i=0,l=array.length;i<l;++i) {
       
 17726         table[array[i]] = null;
       
 17727       }
       
 17728       return table;
       
 17729     }
       
 17730 
       
 17731     function isLookupTableEmpty(table) {
       
 17732       for(var i in table) {
       
 17733         if(table.hasOwnProperty(i)) {
       
 17734           return false;
       
 17735         }
       
 17736       }
       
 17737       return true;
       
 17738     }
       
 17739 
       
 17740     function getAtomIndex(templ) { return templ.substring(2, templ.length - 1); }
       
 17741 
       
 17742     // remove carriage returns "\r"
       
 17743     var codeWoExtraCr = code.replace(/\r\n?|\n\r/g, "\n");
       
 17744 
       
 17745     // masks strings and regexs with "'5'", where 5 is the index in an array containing all strings and regexs
       
 17746     // also removes all comments
       
 17747     var strings = [];
       
 17748     var codeWoStrings = codeWoExtraCr.replace(/("(?:[^"\\\n]|\\.)*")|('(?:[^'\\\n]|\\.)*')|(([\[\(=|&!\^:?]\s*)(\/(?![*\/])(?:[^\/\\\n]|\\.)*\/[gim]*)\b)|(\/\/[^\n]*\n)|(\/\*(?:(?!\*\/)(?:.|\n))*\*\/)/g,
       
 17749     function(all, quoted, aposed, regexCtx, prefix, regex, singleComment, comment) {
       
 17750       var index;
       
 17751       if(quoted || aposed) { // replace strings
       
 17752         index = strings.length; strings.push(all);
       
 17753         return "'" + index + "'";
       
 17754       }
       
 17755       if(regexCtx) { // replace RegExps
       
 17756         index = strings.length; strings.push(regex);
       
 17757         return prefix + "'" + index + "'";
       
 17758       }
       
 17759       // kill comments
       
 17760       return comment !== "" ? " " : "\n";
       
 17761     });
       
 17762 
       
 17763     // removes generics
       
 17764     var genericsWereRemoved;
       
 17765     var codeWoGenerics = codeWoStrings;
       
 17766     var replaceFunc = function(all, before, types, after) {
       
 17767       if(!!before || !!after) {
       
 17768         return all;
       
 17769       }
       
 17770       genericsWereRemoved = true;
       
 17771       return "";
       
 17772     };
       
 17773 
       
 17774     do {
       
 17775       genericsWereRemoved = false;
       
 17776       codeWoGenerics = codeWoGenerics.replace(/([<]?)<\s*((?:\?|[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*)(?:\s+(?:extends|super)\s+[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*)?(?:\s*,\s*(?:\?|[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*)(?:\s+(?:extends|super)\s+[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*)?)*)\s*>([=]?)/g, replaceFunc);
       
 17777     } while (genericsWereRemoved);
       
 17778 
       
 17779     var atoms = splitToAtoms(codeWoGenerics);
       
 17780     var replaceContext;
       
 17781     var declaredClasses = {}, currentClassId, classIdSeed = 0;
       
 17782 
       
 17783     function addAtom(text, type) {
       
 17784       var lastIndex = atoms.length;
       
 17785       atoms.push(text);
       
 17786       return '"' + type + lastIndex + '"';
       
 17787     }
       
 17788 
       
 17789     function generateClassId() {
       
 17790       return "class" + (++classIdSeed);
       
 17791     }
       
 17792 
       
 17793     function appendClass(class_, classId, scopeId) {
       
 17794       class_.classId = classId;
       
 17795       class_.scopeId = scopeId;
       
 17796       declaredClasses[classId] = class_;
       
 17797     }
       
 17798 
       
 17799     // functions defined below
       
 17800     var transformClassBody, transformInterfaceBody, transformStatementsBlock, transformStatements, transformMain, transformExpression;
       
 17801 
       
 17802     var classesRegex = /\b((?:(?:public|private|final|protected|static|abstract)\s+)*)(class|interface)\s+([A-Za-z_$][\w$]*\b)(\s+extends\s+[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*(?:\s*,\s*[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*\b)*)?(\s+implements\s+[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*(?:\s*,\s*[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*\b)*)?\s*("A\d+")/g;
       
 17803     var methodsRegex = /\b((?:(?:public|private|final|protected|static|abstract|synchronized)\s+)*)((?!(?:else|new|return|throw|function|public|private|protected)\b)[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*(?:\s*"C\d+")*)\s*([A-Za-z_$][\w$]*\b)\s*("B\d+")(\s*throws\s+[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*(?:\s*,\s*[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*)*)?\s*("A\d+"|;)/g;
       
 17804     var fieldTest = /^((?:(?:public|private|final|protected|static)\s+)*)((?!(?:else|new|return|throw)\b)[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*(?:\s*"C\d+")*)\s*([A-Za-z_$][\w$]*\b)\s*(?:"C\d+"\s*)*([=,]|$)/;
       
 17805     var cstrsRegex = /\b((?:(?:public|private|final|protected|static|abstract)\s+)*)((?!(?:new|return|throw)\b)[A-Za-z_$][\w$]*\b)\s*("B\d+")(\s*throws\s+[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*(?:\s*,\s*[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*)*)?\s*("A\d+")/g;
       
 17806     var attrAndTypeRegex = /^((?:(?:public|private|final|protected|static)\s+)*)((?!(?:new|return|throw)\b)[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*(?:\s*"C\d+")*)\s*/;
       
 17807     var functionsRegex = /\bfunction(?:\s+([A-Za-z_$][\w$]*))?\s*("B\d+")\s*("A\d+")/g;
       
 17808 
       
 17809     // This converts classes, methods and functions into atoms, and adds them to the atoms array.
       
 17810     // classes = E, methods = D and functions = H
       
 17811     function extractClassesAndMethods(code) {
       
 17812       var s = code;
       
 17813       s = s.replace(classesRegex, function(all) {
       
 17814         return addAtom(all, 'E');
       
 17815       });
       
 17816       s = s.replace(methodsRegex, function(all) {
       
 17817         return addAtom(all, 'D');
       
 17818       });
       
 17819       s = s.replace(functionsRegex, function(all) {
       
 17820         return addAtom(all, 'H');
       
 17821       });
       
 17822       return s;
       
 17823     }
       
 17824 
       
 17825     // This converts constructors into atoms, and adds them to the atoms array.
       
 17826     // constructors = G
       
 17827     function extractConstructors(code, className) {
       
 17828       var result = code.replace(cstrsRegex, function(all, attr, name, params, throws_, body) {
       
 17829         if(name !== className) {
       
 17830           return all;
       
 17831         }
       
 17832         return addAtom(all, 'G');
       
 17833       });
       
 17834       return result;
       
 17835     }
       
 17836 
       
 17837     // AstParam contains the name of a parameter inside a function declaration
       
 17838     function AstParam(name) {
       
 17839       this.name = name;
       
 17840     }
       
 17841     AstParam.prototype.toString = function() {
       
 17842       return this.name;
       
 17843     };
       
 17844     // AstParams contains an array of AstParam objects
       
 17845     function AstParams(params) {
       
 17846       this.params = params;
       
 17847     }
       
 17848     AstParams.prototype.getNames = function() {
       
 17849       var names = [];
       
 17850       for(var i=0,l=this.params.length;i<l;++i) {
       
 17851         names.push(this.params[i].name);
       
 17852       }
       
 17853       return names;
       
 17854     };
       
 17855     AstParams.prototype.toString = function() {
       
 17856       if(this.params.length === 0) {
       
 17857         return "()";
       
 17858       }
       
 17859       var result = "(";
       
 17860       for(var i=0,l=this.params.length;i<l;++i) {
       
 17861         result += this.params[i] + ", ";
       
 17862       }
       
 17863       return result.substring(0, result.length - 2) + ")";
       
 17864     };
       
 17865 
       
 17866     function transformParams(params) {
       
 17867       var paramsWoPars = trim(params.substring(1, params.length - 1));
       
 17868       var result = [];
       
 17869       if(paramsWoPars !== "") {
       
 17870         var paramList = paramsWoPars.split(",");
       
 17871         for(var i=0; i < paramList.length; ++i) {
       
 17872           var param = /\b([A-Za-z_$][\w$]*\b)(\s*"[ABC][\d]*")*\s*$/.exec(paramList[i]);
       
 17873           result.push(new AstParam(param[1]));
       
 17874         }
       
 17875       }
       
 17876       return new AstParams(result);
       
 17877     }
       
 17878 
       
 17879     function preExpressionTransform(expr) {
       
 17880       var s = expr;
       
 17881       // new type[] {...} --> {...}
       
 17882       s = s.replace(/\bnew\s+([A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*)(?:\s*"C\d+")+\s*("A\d+")/g, function(all, type, init) {
       
 17883         return init;
       
 17884       });
       
 17885       // new Runnable() {...} --> "F???"
       
 17886       s = s.replace(/\bnew\s+([A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*)(?:\s*"B\d+")\s*("A\d+")/g, function(all, type, init) {
       
 17887         return addAtom(all, 'F');
       
 17888       });
       
 17889       // function(...) { } --> "H???"
       
 17890       s = s.replace(functionsRegex, function(all) {
       
 17891         return addAtom(all, 'H');
       
 17892       });
       
 17893       // new type[?] --> createJavaArray('type', [?])
       
 17894       s = s.replace(/\bnew\s+([A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*)\s*("C\d+"(?:\s*"C\d+")*)/g, function(all, type, index) {
       
 17895         var args = index.replace(/"C(\d+)"/g, function(all, j) { return atoms[j]; })
       
 17896           .replace(/\[\s*\]/g, "[null]").replace(/\s*\]\s*\[\s*/g, ", ");
       
 17897         var arrayInitializer = "{" + args.substring(1, args.length - 1) + "}";
       
 17898         var createArrayArgs = "('" + type + "', " + addAtom(arrayInitializer, 'A') + ")";
       
 17899         return '$p.createJavaArray' + addAtom(createArrayArgs, 'B');
       
 17900       });
       
 17901       // .length() --> .length
       
 17902       s = s.replace(/(\.\s*length)\s*"B\d+"/g, "$1");
       
 17903       // #000000 --> 0x000000
       
 17904       s = s.replace(/#([0-9A-Fa-f]{6})\b/g, function(all, digits) {
       
 17905         return "0xFF" + digits;
       
 17906       });
       
 17907       // delete (type)???, except (int)???
       
 17908       s = s.replace(/"B(\d+)"(\s*(?:[\w$']|"B))/g, function(all, index, next) {
       
 17909         var atom = atoms[index];
       
 17910         if(!/^\(\s*[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*\s*(?:"C\d+"\s*)*\)$/.test(atom)) {
       
 17911           return all;
       
 17912         }
       
 17913         if(/^\(\s*int\s*\)$/.test(atom)) {
       
 17914           return "(int)" + next;
       
 17915         }
       
 17916         var indexParts = atom.split(/"C(\d+)"/g);
       
 17917         if(indexParts.length > 1) {
       
 17918           // even items contains atom numbers, can check only first
       
 17919           if(! /^\[\s*\]$/.test(atoms[indexParts[1]])) {
       
 17920             return all; // fallback - not a cast
       
 17921           }
       
 17922         }
       
 17923         return "" + next;
       
 17924       });
       
 17925       // (int)??? -> __int_cast(???)
       
 17926       s = s.replace(/\(int\)([^,\]\)\}\?\:\*\+\-\/\^\|\%\&\~<\>\=]+)/g, function(all, arg) {
       
 17927         var trimmed = trimSpaces(arg);
       
 17928         return trimmed.untrim("__int_cast(" + trimmed.middle + ")");
       
 17929       });
       
 17930       // super() -> $superCstr(), super. -> $super.;
       
 17931       s = s.replace(/\bsuper(\s*"B\d+")/g, "$$superCstr$1").replace(/\bsuper(\s*\.)/g, "$$super$1");
       
 17932       // 000.43->0.43 and 0010f->10, but not 0010
       
 17933       s = s.replace(/\b0+((\d*)(?:\.[\d*])?(?:[eE][\-\+]?\d+)?[fF]?)\b/, function(all, numberWo0, intPart) {
       
 17934         if( numberWo0 === intPart) {
       
 17935           return all;
       
 17936         }
       
 17937         return intPart === "" ? "0" + numberWo0 : numberWo0;
       
 17938       });
       
 17939       // 3.0f -> 3.0
       
 17940       s = s.replace(/\b(\.?\d+\.?)[fF]\b/g, "$1");
       
 17941       // Weird (?) parsing errors with %
       
 17942       s = s.replace(/([^\s])%([^=\s])/g, "$1 % $2");
       
 17943       // Since frameRate() and frameRate are different things,
       
 17944       // we need to differentiate them somehow. So when we parse
       
 17945       // the Processing.js source, replace frameRate so it isn't
       
 17946       // confused with frameRate(), as well as keyPressed and mousePressed
       
 17947       s = s.replace(/\b(frameRate|keyPressed|mousePressed)\b(?!\s*"B)/g, "__$1");
       
 17948       // "boolean", "byte", "int", etc. => "parseBoolean", "parseByte", "parseInt", etc.
       
 17949       s = s.replace(/\b(boolean|byte|char|float|int)\s*"B/g, function(all, name) {
       
 17950         return "parse" + name.substring(0, 1).toUpperCase() + name.substring(1) + "\"B";
       
 17951       });
       
 17952       // "pixels" replacements:
       
 17953       //   pixels[i] = c => pixels.setPixel(i,c) | pixels[i] => pixels.getPixel(i)
       
 17954       //   pixels.length => pixels.getLength()
       
 17955       //   pixels = ar => pixels.set(ar) | pixels => pixels.toArray()
       
 17956       s = s.replace(/\bpixels\b\s*(("C(\d+)")|\.length)?(\s*=(?!=)([^,\]\)\}]+))?/g,
       
 17957         function(all, indexOrLength, index, atomIndex, equalsPart, rightSide) {
       
 17958           if(index) {
       
 17959             var atom = atoms[atomIndex];
       
 17960             if(equalsPart) {
       
 17961               return "pixels.setPixel" + addAtom("(" +atom.substring(1, atom.length - 1) +
       
 17962                 "," + rightSide + ")", 'B');
       
 17963             }
       
 17964             return "pixels.getPixel" + addAtom("(" + atom.substring(1, atom.length - 1) +
       
 17965               ")", 'B');
       
 17966           }
       
 17967           if(indexOrLength) {
       
 17968             // length
       
 17969             return "pixels.getLength" + addAtom("()", 'B');
       
 17970           }
       
 17971           if(equalsPart) {
       
 17972             return "pixels.set" + addAtom("(" + rightSide + ")", 'B');
       
 17973           }
       
 17974           return "pixels.toArray" + addAtom("()", 'B');
       
 17975         });
       
 17976       // Java method replacements for: replace, replaceAll, replaceFirst, equals, hashCode, etc.
       
 17977       //   xxx.replace(yyy) -> __replace(xxx, yyy)
       
 17978       //   "xx".replace(yyy) -> __replace("xx", yyy)
       
 17979       var repeatJavaReplacement;
       
 17980       function replacePrototypeMethods(all, subject, method, atomIndex) {
       
 17981         var atom = atoms[atomIndex];
       
 17982         repeatJavaReplacement = true;
       
 17983         var trimmed = trimSpaces(atom.substring(1, atom.length - 1));
       
 17984         return "__" + method  + ( trimmed.middle === "" ? addAtom("(" + subject.replace(/\.\s*$/, "") + ")", 'B') :
       
 17985           addAtom("(" + subject.replace(/\.\s*$/, "") + "," + trimmed.middle + ")", 'B') );
       
 17986       }
       
 17987       do {
       
 17988         repeatJavaReplacement = false;
       
 17989         s = s.replace(/((?:'\d+'|\b[A-Za-z_$][\w$]*\s*(?:"[BC]\d+")*)\s*\.\s*(?:[A-Za-z_$][\w$]*\s*(?:"[BC]\d+"\s*)*\.\s*)*)(replace|replaceAll|replaceFirst|contains|equals|equalsIgnoreCase|hashCode|toCharArray|printStackTrace|split|startsWith|endsWith|codePointAt)\s*"B(\d+)"/g,
       
 17990           replacePrototypeMethods);
       
 17991       } while (repeatJavaReplacement);
       
 17992       // xxx instanceof yyy -> __instanceof(xxx, yyy)
       
 17993       function replaceInstanceof(all, subject, type) {
       
 17994         repeatJavaReplacement = true;
       
 17995         return "__instanceof" + addAtom("(" + subject + ", " + type + ")", 'B');
       
 17996       }
       
 17997       do {
       
 17998         repeatJavaReplacement = false;
       
 17999         s = s.replace(/((?:'\d+'|\b[A-Za-z_$][\w$]*\s*(?:"[BC]\d+")*)\s*(?:\.\s*[A-Za-z_$][\w$]*\s*(?:"[BC]\d+"\s*)*)*)instanceof\s+([A-Za-z_$][\w$]*\s*(?:\.\s*[A-Za-z_$][\w$]*)*)/g,
       
 18000           replaceInstanceof);
       
 18001       } while (repeatJavaReplacement);
       
 18002       // this() -> $constr()
       
 18003       s = s.replace(/\bthis(\s*"B\d+")/g, "$$constr$1");
       
 18004 
       
 18005       return s;
       
 18006     }
       
 18007 
       
 18008     function AstInlineClass(baseInterfaceName, body) {
       
 18009       this.baseInterfaceName = baseInterfaceName;
       
 18010       this.body = body;
       
 18011       body.owner = this;
       
 18012     }
       
 18013     AstInlineClass.prototype.toString = function() {
       
 18014       return "new (" + this.body + ")";
       
 18015     };
       
 18016 
       
 18017     function transformInlineClass(class_) {
       
 18018       var m = new RegExp(/\bnew\s*([A-Za-z_$][\w$]*\s*(?:\.\s*[A-Za-z_$][\w$]*)*)\s*"B\d+"\s*"A(\d+)"/).exec(class_);
       
 18019       var oldClassId = currentClassId, newClassId = generateClassId();
       
 18020       currentClassId = newClassId;
       
 18021       var uniqueClassName = m[1] + "$" + newClassId;
       
 18022       var inlineClass = new AstInlineClass(uniqueClassName,
       
 18023         transformClassBody(atoms[m[2]], uniqueClassName, "", "implements " + m[1]));
       
 18024       appendClass(inlineClass, newClassId, oldClassId);
       
 18025       currentClassId = oldClassId;
       
 18026       return inlineClass;
       
 18027     }
       
 18028 
       
 18029     function AstFunction(name, params, body) {
       
 18030       this.name = name;
       
 18031       this.params = params;
       
 18032       this.body = body;
       
 18033     }
       
 18034     AstFunction.prototype.toString = function() {
       
 18035       var oldContext = replaceContext;
       
 18036       // saving "this." and parameters
       
 18037       var names = appendToLookupTable({"this":null}, this.params.getNames());
       
 18038       replaceContext = function (subject) {
       
 18039         return names.hasOwnProperty(subject.name) ? subject.name : oldContext(subject);
       
 18040       };
       
 18041       var result = "function";
       
 18042       if(this.name) {
       
 18043         result += " " + this.name;
       
 18044       }
       
 18045       result += this.params + " " + this.body;
       
 18046       replaceContext = oldContext;
       
 18047       return result;
       
 18048     };
       
 18049 
       
 18050     function transformFunction(class_) {
       
 18051       var m = new RegExp(/\b([A-Za-z_$][\w$]*)\s*"B(\d+)"\s*"A(\d+)"/).exec(class_);
       
 18052       return new AstFunction( m[1] !== "function" ? m[1] : null,
       
 18053         transformParams(atoms[m[2]]), transformStatementsBlock(atoms[m[3]]));
       
 18054     }
       
 18055 
       
 18056     function AstInlineObject(members) {
       
 18057       this.members = members;
       
 18058     }
       
 18059     AstInlineObject.prototype.toString = function() {
       
 18060       var oldContext = replaceContext;
       
 18061       replaceContext = function (subject) {
       
 18062           return subject.name === "this" ? "this" : oldContext(subject); // saving "this."
       
 18063       };
       
 18064       var result = "";
       
 18065       for(var i=0,l=this.members.length;i<l;++i) {
       
 18066         if(this.members[i].label) {
       
 18067           result += this.members[i].label + ": ";
       
 18068         }
       
 18069         result += this.members[i].value.toString() + ", ";
       
 18070       }
       
 18071       replaceContext = oldContext;
       
 18072       return result.substring(0, result.length - 2);
       
 18073     };
       
 18074 
       
 18075     function transformInlineObject(obj) {
       
 18076       var members = obj.split(',');
       
 18077       for(var i=0; i < members.length; ++i) {
       
 18078         var label = members[i].indexOf(':');
       
 18079         if(label < 0) {
       
 18080           members[i] = { value: transformExpression(members[i]) };
       
 18081         } else {
       
 18082           members[i] = { label: trim(members[i].substring(0, label)),
       
 18083             value: transformExpression( trim(members[i].substring(label + 1)) ) };
       
 18084         }
       
 18085       }
       
 18086       return new AstInlineObject(members);
       
 18087     }
       
 18088 
       
 18089     function expandExpression(expr) {
       
 18090       if(expr.charAt(0) === '(' || expr.charAt(0) === '[') {
       
 18091         return expr.charAt(0) + expandExpression(expr.substring(1, expr.length - 1)) + expr.charAt(expr.length - 1);
       
 18092       }
       
 18093       if(expr.charAt(0) === '{') {
       
 18094         if(/^\{\s*(?:[A-Za-z_$][\w$]*|'\d+')\s*:/.test(expr)) {
       
 18095           return "{" + addAtom(expr.substring(1, expr.length - 1), 'I') + "}";
       
 18096         }
       
 18097         return "[" + expandExpression(expr.substring(1, expr.length - 1)) + "]";
       
 18098       }
       
 18099       var trimmed = trimSpaces(expr);
       
 18100       var result = preExpressionTransform(trimmed.middle);
       
 18101       result = result.replace(/"[ABC](\d+)"/g, function(all, index) {
       
 18102         return expandExpression(atoms[index]);
       
 18103       });
       
 18104       return trimmed.untrim(result);
       
 18105     }
       
 18106 
       
 18107     function replaceContextInVars(expr) {
       
 18108       return expr.replace(/(\.\s*)?((?:\b[A-Za-z_]|\$)[\w$]*)(\s*\.\s*([A-Za-z_$][\w$]*)(\s*\()?)?/g,
       
 18109         function(all, memberAccessSign, identifier, suffix, subMember, callSign) {
       
 18110           if(memberAccessSign) {
       
 18111             return all;
       
 18112           }
       
 18113           var subject = { name: identifier, member: subMember, callSign: !!callSign };
       
 18114           return replaceContext(subject) + (suffix === undef ? "" : suffix);
       
 18115         });
       
 18116     }
       
 18117 
       
 18118     function AstExpression(expr, transforms) {
       
 18119       this.expr = expr;
       
 18120       this.transforms = transforms;
       
 18121     }
       
 18122     AstExpression.prototype.toString = function() {
       
 18123       var transforms = this.transforms;
       
 18124       var expr = replaceContextInVars(this.expr);
       
 18125       return expr.replace(/"!(\d+)"/g, function(all, index) {
       
 18126         return transforms[index].toString();
       
 18127       });
       
 18128     };
       
 18129 
       
 18130     transformExpression = function(expr) {
       
 18131       var transforms = [];
       
 18132       var s = expandExpression(expr);
       
 18133       s = s.replace(/"H(\d+)"/g, function(all, index) {
       
 18134         transforms.push(transformFunction(atoms[index]));
       
 18135         return '"!' + (transforms.length - 1) + '"';
       
 18136       });
       
 18137       s = s.replace(/"F(\d+)"/g, function(all, index) {
       
 18138         transforms.push(transformInlineClass(atoms[index]));
       
 18139         return '"!' + (transforms.length - 1) + '"';
       
 18140       });
       
 18141       s = s.replace(/"I(\d+)"/g, function(all, index) {
       
 18142         transforms.push(transformInlineObject(atoms[index]));
       
 18143         return '"!' + (transforms.length - 1) + '"';
       
 18144       });
       
 18145 
       
 18146       return new AstExpression(s, transforms);
       
 18147     };
       
 18148 
       
 18149     function AstVarDefinition(name, value, isDefault) {
       
 18150       this.name = name;
       
 18151       this.value = value;
       
 18152       this.isDefault = isDefault;
       
 18153     }
       
 18154     AstVarDefinition.prototype.toString = function() {
       
 18155       return this.name + ' = ' + this.value;
       
 18156     };
       
 18157 
       
 18158     function transformVarDefinition(def, defaultTypeValue) {
       
 18159       var eqIndex = def.indexOf("=");
       
 18160       var name, value, isDefault;
       
 18161       if(eqIndex < 0) {
       
 18162         name = def;
       
 18163         value = defaultTypeValue;
       
 18164         isDefault = true;
       
 18165       } else {
       
 18166         name = def.substring(0, eqIndex);
       
 18167         value = transformExpression(def.substring(eqIndex + 1));
       
 18168         isDefault = false;
       
 18169       }
       
 18170       return new AstVarDefinition( trim(name.replace(/(\s*"C\d+")+/g, "")),
       
 18171         value, isDefault);
       
 18172     }
       
 18173 
       
 18174     function getDefaultValueForType(type) {
       
 18175         if(type === "int" || type === "float") {
       
 18176           return "0";
       
 18177         }
       
 18178         if(type === "boolean") {
       
 18179           return "false";
       
 18180         }
       
 18181         if(type === "color") {
       
 18182           return "0x00000000";
       
 18183         }
       
 18184         return "null";
       
 18185     }
       
 18186 
       
 18187     function AstVar(definitions, varType) {
       
 18188       this.definitions = definitions;
       
 18189       this.varType = varType;
       
 18190     }
       
 18191     AstVar.prototype.getNames = function() {
       
 18192       var names = [];
       
 18193       for(var i=0,l=this.definitions.length;i<l;++i) {
       
 18194         names.push(this.definitions[i].name);
       
 18195       }
       
 18196       return names;
       
 18197     };
       
 18198     AstVar.prototype.toString = function() {
       
 18199       return "var " + this.definitions.join(",");
       
 18200     };
       
 18201     function AstStatement(expression) {
       
 18202       this.expression = expression;
       
 18203     }
       
 18204     AstStatement.prototype.toString = function() {
       
 18205       return this.expression.toString();
       
 18206     };
       
 18207 
       
 18208     function transformStatement(statement) {
       
 18209       if(fieldTest.test(statement)) {
       
 18210         var attrAndType = attrAndTypeRegex.exec(statement);
       
 18211         var definitions = statement.substring(attrAndType[0].length).split(",");
       
 18212         var defaultTypeValue = getDefaultValueForType(attrAndType[2]);
       
 18213         for(var i=0; i < definitions.length; ++i) {
       
 18214           definitions[i] = transformVarDefinition(definitions[i], defaultTypeValue);
       
 18215         }
       
 18216         return new AstVar(definitions, attrAndType[2]);
       
 18217       }
       
 18218       return new AstStatement(transformExpression(statement));
       
 18219     }
       
 18220 
       
 18221     function AstForExpression(initStatement, condition, step) {
       
 18222       this.initStatement = initStatement;
       
 18223       this.condition = condition;
       
 18224       this.step = step;
       
 18225     }
       
 18226     AstForExpression.prototype.toString = function() {
       
 18227       return "(" + this.initStatement + "; " + this.condition + "; " + this.step + ")";
       
 18228     };
       
 18229 
       
 18230     function AstForInExpression(initStatement, container) {
       
 18231       this.initStatement = initStatement;
       
 18232       this.container = container;
       
 18233     }
       
 18234     AstForInExpression.prototype.toString = function() {
       
 18235       var init = this.initStatement.toString();
       
 18236       if(init.indexOf("=") >= 0) { // can be without var declaration
       
 18237         init = init.substring(0, init.indexOf("="));
       
 18238       }
       
 18239       return "(" + init + " in " + this.container + ")";
       
 18240     };
       
 18241 
       
 18242     function AstForEachExpression(initStatement, container) {
       
 18243       this.initStatement = initStatement;
       
 18244       this.container = container;
       
 18245     }
       
 18246     AstForEachExpression.iteratorId = 0;
       
 18247     AstForEachExpression.prototype.toString = function() {
       
 18248       var init = this.initStatement.toString();
       
 18249       var iterator = "$it" + (AstForEachExpression.iteratorId++);
       
 18250       var variableName = init.replace(/^\s*var\s*/, "").split("=")[0];
       
 18251       var initIteratorAndVariable = "var " + iterator + " = new $p.ObjectIterator(" + this.container + "), " +
       
 18252          variableName + " = void(0)";
       
 18253       var nextIterationCondition = iterator + ".hasNext() && ((" +
       
 18254          variableName + " = " + iterator + ".next()) || true)";
       
 18255       return "(" + initIteratorAndVariable + "; " + nextIterationCondition + ";)";
       
 18256     };
       
 18257 
       
 18258     function transformForExpression(expr) {
       
 18259       var content;
       
 18260       if (/\bin\b/.test(expr)) {
       
 18261         content = expr.substring(1, expr.length - 1).split(/\bin\b/g);
       
 18262         return new AstForInExpression( transformStatement(trim(content[0])),
       
 18263           transformExpression(content[1]));
       
 18264       }
       
 18265       if (expr.indexOf(":") >= 0 && expr.indexOf(";") < 0) {
       
 18266         content = expr.substring(1, expr.length - 1).split(":");
       
 18267         return new AstForEachExpression( transformStatement(trim(content[0])),
       
 18268           transformExpression(content[1]));
       
 18269       }
       
 18270       content = expr.substring(1, expr.length - 1).split(";");
       
 18271       return new AstForExpression( transformStatement(trim(content[0])),
       
 18272         transformExpression(content[1]), transformExpression(content[2]));
       
 18273     }
       
 18274 
       
 18275     function sortByWeight(array) {
       
 18276       array.sort(function (a,b) {
       
 18277         return b.weight - a.weight;
       
 18278       });
       
 18279     }
       
 18280 
       
 18281     function AstInnerInterface(name, body, isStatic) {
       
 18282       this.name = name;
       
 18283       this.body = body;
       
 18284       this.isStatic = isStatic;
       
 18285       body.owner = this;
       
 18286     }
       
 18287     AstInnerInterface.prototype.toString = function() {
       
 18288       return "" + this.body;
       
 18289     };
       
 18290     function AstInnerClass(name, body, isStatic) {
       
 18291       this.name = name;
       
 18292       this.body = body;
       
 18293       this.isStatic = isStatic;
       
 18294       body.owner = this;
       
 18295     }
       
 18296     AstInnerClass.prototype.toString = function() {
       
 18297       return "" + this.body;
       
 18298     };
       
 18299 
       
 18300     function transformInnerClass(class_) {
       
 18301       var m = classesRegex.exec(class_); // 1 - attr, 2 - class|int, 3 - name, 4 - extends, 5 - implements, 6 - body
       
 18302       classesRegex.lastIndex = 0;
       
 18303       var isStatic = m[1].indexOf("static") >= 0;
       
 18304       var body = atoms[getAtomIndex(m[6])], innerClass;
       
 18305       var oldClassId = currentClassId, newClassId = generateClassId();
       
 18306       currentClassId = newClassId;
       
 18307       if(m[2] === "interface") {
       
 18308         innerClass = new AstInnerInterface(m[3], transformInterfaceBody(body, m[3], m[4]), isStatic);
       
 18309       } else {
       
 18310         innerClass = new AstInnerClass(m[3], transformClassBody(body, m[3], m[4], m[5]), isStatic);
       
 18311       }
       
 18312       appendClass(innerClass, newClassId, oldClassId);
       
 18313       currentClassId = oldClassId;
       
 18314       return innerClass;
       
 18315     }
       
 18316 
       
 18317     function AstClassMethod(name, params, body, isStatic) {
       
 18318       this.name = name;
       
 18319       this.params = params;
       
 18320       this.body = body;
       
 18321       this.isStatic = isStatic;
       
 18322     }
       
 18323     AstClassMethod.prototype.toString = function(){
       
 18324       var paramNames = appendToLookupTable({}, this.params.getNames());
       
 18325       var oldContext = replaceContext;
       
 18326       replaceContext = function (subject) {
       
 18327         return paramNames.hasOwnProperty(subject.name) ? subject.name : oldContext(subject);
       
 18328       };
       
 18329       var result = "function " + this.methodId + this.params + " " + this.body +"\n";
       
 18330       replaceContext = oldContext;
       
 18331       return result;
       
 18332     };
       
 18333 
       
 18334     function transformClassMethod(method) {
       
 18335       var m = methodsRegex.exec(method);
       
 18336       methodsRegex.lastIndex = 0;
       
 18337       var isStatic = m[1].indexOf("static") >= 0;
       
 18338       var body = m[6] !== ';' ? atoms[getAtomIndex(m[6])] : "{}";
       
 18339       return new AstClassMethod(m[3], transformParams(atoms[getAtomIndex(m[4])]),
       
 18340         transformStatementsBlock(body), isStatic );
       
 18341     }
       
 18342 
       
 18343     function AstClassField(definitions, fieldType, isStatic) {
       
 18344       this.definitions = definitions;
       
 18345       this.fieldType = fieldType;
       
 18346       this.isStatic = isStatic;
       
 18347     }
       
 18348     AstClassField.prototype.getNames = function() {
       
 18349       var names = [];
       
 18350       for(var i=0,l=this.definitions.length;i<l;++i) {
       
 18351         names.push(this.definitions[i].name);
       
 18352       }
       
 18353       return names;
       
 18354     };
       
 18355     AstClassField.prototype.toString = function() {
       
 18356       var thisPrefix = replaceContext({ name: "[this]" });
       
 18357       if(this.isStatic) {
       
 18358         var className = this.owner.name;
       
 18359         var staticDeclarations = [];
       
 18360         for(var i=0,l=this.definitions.length;i<l;++i) {
       
 18361           var definition = this.definitions[i];
       
 18362           var name = definition.name, staticName = className + "." + name;
       
 18363           var declaration = "if(" + staticName + " === void(0)) {\n" +
       
 18364             " " + staticName + " = " + definition.value + "; }\n" +
       
 18365             "$p.defineProperty(" + thisPrefix + ", " +
       
 18366             "'" + name + "', { get: function(){return " + staticName + ";}, " +
       
 18367             "set: function(val){" + staticName + " = val;} });\n";
       
 18368           staticDeclarations.push(declaration);
       
 18369         }
       
 18370         return staticDeclarations.join("");
       
 18371       }
       
 18372       return thisPrefix + "." + this.definitions.join("; " + thisPrefix + ".");
       
 18373     };
       
 18374 
       
 18375     function transformClassField(statement) {
       
 18376       var attrAndType = attrAndTypeRegex.exec(statement);
       
 18377       var isStatic = attrAndType[1].indexOf("static") >= 0;
       
 18378       var definitions = statement.substring(attrAndType[0].length).split(/,\s*/g);
       
 18379       var defaultTypeValue = getDefaultValueForType(attrAndType[2]);
       
 18380       for(var i=0; i < definitions.length; ++i) {
       
 18381         definitions[i] = transformVarDefinition(definitions[i], defaultTypeValue);
       
 18382       }
       
 18383       return new AstClassField(definitions, attrAndType[2], isStatic);
       
 18384     }
       
 18385 
       
 18386     function AstConstructor(params, body) {
       
 18387       this.params = params;
       
 18388       this.body = body;
       
 18389     }
       
 18390     AstConstructor.prototype.toString = function() {
       
 18391       var paramNames = appendToLookupTable({}, this.params.getNames());
       
 18392       var oldContext = replaceContext;
       
 18393       replaceContext = function (subject) {
       
 18394         return paramNames.hasOwnProperty(subject.name) ? subject.name : oldContext(subject);
       
 18395       };
       
 18396       var prefix = "function $constr_" + this.params.params.length + this.params.toString();
       
 18397       var body = this.body.toString();
       
 18398       if(!/\$(superCstr|constr)\b/.test(body)) {
       
 18399         body = "{\n$superCstr();\n" + body.substring(1);
       
 18400       }
       
 18401       replaceContext = oldContext;
       
 18402       return prefix + body + "\n";
       
 18403     };
       
 18404 
       
 18405     function transformConstructor(cstr) {
       
 18406       var m = new RegExp(/"B(\d+)"\s*"A(\d+)"/).exec(cstr);
       
 18407       var params = transformParams(atoms[m[1]]);
       
 18408 
       
 18409       return new AstConstructor(params, transformStatementsBlock(atoms[m[2]]));
       
 18410     }
       
 18411 
       
 18412     function AstInterfaceBody(name, interfacesNames, methodsNames, fields, innerClasses, misc) {
       
 18413       var i,l;
       
 18414       this.name = name;
       
 18415       this.interfacesNames = interfacesNames;
       
 18416       this.methodsNames = methodsNames;
       
 18417       this.fields = fields;
       
 18418       this.innerClasses = innerClasses;
       
 18419       this.misc = misc;
       
 18420       for(i=0,l=fields.length; i<l; ++i) {
       
 18421         fields[i].owner = this;
       
 18422       }
       
 18423     }
       
 18424     AstInterfaceBody.prototype.getMembers = function(classFields, classMethods, classInners) {
       
 18425       if(this.owner.base) {
       
 18426         this.owner.base.body.getMembers(classFields, classMethods, classInners);
       
 18427       }
       
 18428       var i, j, l, m;
       
 18429       for(i=0,l=this.fields.length;i<l;++i) {
       
 18430         var fieldNames = this.fields[i].getNames();
       
 18431         for(j=0,m=fieldNames.length;j<m;++j) {
       
 18432           classFields[fieldNames[j]] = this.fields[i];
       
 18433         }
       
 18434       }
       
 18435       for(i=0,l=this.methodsNames.length;i<l;++i) {
       
 18436         var methodName = this.methodsNames[i];
       
 18437         classMethods[methodName] = true;
       
 18438       }
       
 18439       for(i=0,l=this.innerClasses.length;i<l;++i) {
       
 18440         var innerClass = this.innerClasses[i];
       
 18441         classInners[innerClass.name] = innerClass;
       
 18442       }
       
 18443     };
       
 18444     AstInterfaceBody.prototype.toString = function() {
       
 18445       function getScopeLevel(p) {
       
 18446         var i = 0;
       
 18447         while(p) {
       
 18448           ++i;
       
 18449           p=p.scope;
       
 18450         }
       
 18451         return i;
       
 18452       }
       
 18453 
       
 18454       var scopeLevel = getScopeLevel(this.owner);
       
 18455 
       
 18456       var className = this.name;
       
 18457       var staticDefinitions = "";
       
 18458       var metadata = "";
       
 18459 
       
 18460       var thisClassFields = {}, thisClassMethods = {}, thisClassInners = {};
       
 18461       this.getMembers(thisClassFields, thisClassMethods, thisClassInners);
       
 18462 
       
 18463       var i, l, j, m;
       
 18464 
       
 18465       if (this.owner.interfaces) {
       
 18466         // interface name can be present, but interface is not
       
 18467         var resolvedInterfaces = [], resolvedInterface;
       
 18468         for (i = 0, l = this.interfacesNames.length; i < l; ++i) {
       
 18469           if (!this.owner.interfaces[i]) {
       
 18470             continue;
       
 18471           }
       
 18472           resolvedInterface = replaceContext({name: this.interfacesNames[i]});
       
 18473           resolvedInterfaces.push(resolvedInterface);
       
 18474           staticDefinitions += "$p.extendInterfaceMembers(" + className + ", " + resolvedInterface + ");\n";
       
 18475         }
       
 18476         metadata += className + ".$interfaces = [" + resolvedInterfaces.join(", ") + "];\n";
       
 18477       }
       
 18478       metadata += className + ".$isInterface = true;\n";
       
 18479       metadata += className + ".$methods = [\'" + this.methodsNames.join("\', \'") + "\'];\n";
       
 18480 
       
 18481       sortByWeight(this.innerClasses);
       
 18482       for (i = 0, l = this.innerClasses.length; i < l; ++i) {
       
 18483         var innerClass = this.innerClasses[i];
       
 18484         if (innerClass.isStatic) {
       
 18485           staticDefinitions += className + "." + innerClass.name + " = " + innerClass + ";\n";
       
 18486         }
       
 18487       }
       
 18488 
       
 18489       for (i = 0, l = this.fields.length; i < l; ++i) {
       
 18490         var field = this.fields[i];
       
 18491         if (field.isStatic) {
       
 18492           staticDefinitions += className + "." + field.definitions.join(";\n" + className + ".") + ";\n";
       
 18493         }
       
 18494       }
       
 18495 
       
 18496       return "(function() {\n" +
       
 18497         "function " + className + "() { throw \'Unable to create the interface\'; }\n" +
       
 18498         staticDefinitions +
       
 18499         metadata +
       
 18500         "return " + className + ";\n" +
       
 18501         "})()";
       
 18502     };
       
 18503 
       
 18504     transformInterfaceBody = function(body, name, baseInterfaces) {
       
 18505       var declarations = body.substring(1, body.length - 1);
       
 18506       declarations = extractClassesAndMethods(declarations);
       
 18507       declarations = extractConstructors(declarations, name);
       
 18508       var methodsNames = [], classes = [];
       
 18509       declarations = declarations.replace(/"([DE])(\d+)"/g, function(all, type, index) {
       
 18510         if(type === 'D') { methodsNames.push(index); }
       
 18511         else if(type === 'E') { classes.push(index); }
       
 18512         return "";
       
 18513       });
       
 18514       var fields = declarations.split(/;(?:\s*;)*/g);
       
 18515       var baseInterfaceNames;
       
 18516       var i, l;
       
 18517 
       
 18518       if(baseInterfaces !== undef) {
       
 18519         baseInterfaceNames = baseInterfaces.replace(/^\s*extends\s+(.+?)\s*$/g, "$1").split(/\s*,\s*/g);
       
 18520       }
       
 18521 
       
 18522       for(i = 0, l = methodsNames.length; i < l; ++i) {
       
 18523         var method = transformClassMethod(atoms[methodsNames[i]]);
       
 18524         methodsNames[i] = method.name;
       
 18525       }
       
 18526       for(i = 0, l = fields.length - 1; i < l; ++i) {
       
 18527         var field = trimSpaces(fields[i]);
       
 18528         fields[i] = transformClassField(field.middle);
       
 18529       }
       
 18530       var tail = fields.pop();
       
 18531       for(i = 0, l = classes.length; i < l; ++i) {
       
 18532         classes[i] = transformInnerClass(atoms[classes[i]]);
       
 18533       }
       
 18534 
       
 18535       return new AstInterfaceBody(name, baseInterfaceNames, methodsNames, fields, classes, { tail: tail });
       
 18536     };
       
 18537 
       
 18538     function AstClassBody(name, baseClassName, interfacesNames, functions, methods, fields, cstrs, innerClasses, misc) {
       
 18539       var i,l;
       
 18540       this.name = name;
       
 18541       this.baseClassName = baseClassName;
       
 18542       this.interfacesNames = interfacesNames;
       
 18543       this.functions = functions;
       
 18544       this.methods = methods;
       
 18545       this.fields = fields;
       
 18546       this.cstrs = cstrs;
       
 18547       this.innerClasses = innerClasses;
       
 18548       this.misc = misc;
       
 18549       for(i=0,l=fields.length; i<l; ++i) {
       
 18550         fields[i].owner = this;
       
 18551       }
       
 18552     }
       
 18553     AstClassBody.prototype.getMembers = function(classFields, classMethods, classInners) {
       
 18554       if(this.owner.base) {
       
 18555         this.owner.base.body.getMembers(classFields, classMethods, classInners);
       
 18556       }
       
 18557       var i, j, l, m;
       
 18558       for(i=0,l=this.fields.length;i<l;++i) {
       
 18559         var fieldNames = this.fields[i].getNames();
       
 18560         for(j=0,m=fieldNames.length;j<m;++j) {
       
 18561           classFields[fieldNames[j]] = this.fields[i];
       
 18562         }
       
 18563       }
       
 18564       for(i=0,l=this.methods.length;i<l;++i) {
       
 18565         var method = this.methods[i];
       
 18566         classMethods[method.name] = method;
       
 18567       }
       
 18568       for(i=0,l=this.innerClasses.length;i<l;++i) {
       
 18569         var innerClass = this.innerClasses[i];
       
 18570         classInners[innerClass.name] = innerClass;
       
 18571       }
       
 18572     };
       
 18573     AstClassBody.prototype.toString = function() {
       
 18574       function getScopeLevel(p) {
       
 18575         var i = 0;
       
 18576         while(p) {
       
 18577           ++i;
       
 18578           p=p.scope;
       
 18579         }
       
 18580         return i;
       
 18581       }
       
 18582 
       
 18583       var scopeLevel = getScopeLevel(this.owner);
       
 18584 
       
 18585       var selfId = "$this_" + scopeLevel;
       
 18586       var className = this.name;
       
 18587       var result = "var " + selfId + " = this;\n";
       
 18588       var staticDefinitions = "";
       
 18589       var metadata = "";
       
 18590 
       
 18591       var thisClassFields = {}, thisClassMethods = {}, thisClassInners = {};
       
 18592       this.getMembers(thisClassFields, thisClassMethods, thisClassInners);
       
 18593 
       
 18594       var oldContext = replaceContext;
       
 18595       replaceContext = function (subject) {
       
 18596         var name = subject.name;
       
 18597         if(name === "this") {
       
 18598           // returns "$this_N.$self" pointer instead of "this" in cases:
       
 18599           // "this()", "this.XXX()", "this", but not for "this.XXX"
       
 18600           return subject.callSign || !subject.member ? selfId + ".$self" : selfId;
       
 18601         }
       
 18602         if(thisClassFields.hasOwnProperty(name)) {
       
 18603           return thisClassFields[name].isStatic ? className + "." + name : selfId + "." + name;
       
 18604         }
       
 18605         if(thisClassInners.hasOwnProperty(name)) {
       
 18606           return selfId + "." + name;
       
 18607         }
       
 18608         if(thisClassMethods.hasOwnProperty(name)) {
       
 18609           return thisClassMethods[name].isStatic ? className + "." + name : selfId + ".$self." + name;
       
 18610         }
       
 18611         return oldContext(subject);
       
 18612       };
       
 18613 
       
 18614       var resolvedBaseClassName;
       
 18615       if (this.baseClassName) {
       
 18616         resolvedBaseClassName = oldContext({name: this.baseClassName});
       
 18617         result += "var $super = { $upcast: " + selfId + " };\n";
       
 18618         result += "function $superCstr(){" + resolvedBaseClassName +
       
 18619           ".apply($super,arguments);if(!('$self' in $super)) $p.extendClassChain($super)}\n";
       
 18620         metadata += className + ".$base = " + resolvedBaseClassName + ";\n";
       
 18621       } else {
       
 18622         result += "function $superCstr(){$p.extendClassChain("+ selfId +")}\n";
       
 18623       }
       
 18624 
       
 18625       if (this.owner.base) {
       
 18626         // base class name can be present, but class is not
       
 18627         staticDefinitions += "$p.extendStaticMembers(" + className + ", " + resolvedBaseClassName + ");\n";
       
 18628       }
       
 18629 
       
 18630       var i, l, j, m;
       
 18631 
       
 18632       if (this.owner.interfaces) {
       
 18633         // interface name can be present, but interface is not
       
 18634         var resolvedInterfaces = [], resolvedInterface;
       
 18635         for (i = 0, l = this.interfacesNames.length; i < l; ++i) {
       
 18636           if (!this.owner.interfaces[i]) {
       
 18637             continue;
       
 18638           }
       
 18639           resolvedInterface = oldContext({name: this.interfacesNames[i]});
       
 18640           resolvedInterfaces.push(resolvedInterface);
       
 18641           staticDefinitions += "$p.extendInterfaceMembers(" + className + ", " + resolvedInterface + ");\n";
       
 18642         }
       
 18643         metadata += className + ".$interfaces = [" + resolvedInterfaces.join(", ") + "];\n";
       
 18644       }
       
 18645 
       
 18646       if (this.functions.length > 0) {
       
 18647         result += this.functions.join('\n') + '\n';
       
 18648       }
       
 18649 
       
 18650       sortByWeight(this.innerClasses);
       
 18651       for (i = 0, l = this.innerClasses.length; i < l; ++i) {
       
 18652         var innerClass = this.innerClasses[i];
       
 18653         if (innerClass.isStatic) {
       
 18654           staticDefinitions += className + "." + innerClass.name + " = " + innerClass + ";\n";
       
 18655           result += selfId + "." + innerClass.name + " = " + className + "." + innerClass.name + ";\n";
       
 18656         } else {
       
 18657           result += selfId + "." + innerClass.name + " = " + innerClass + ";\n";
       
 18658         }
       
 18659       }
       
 18660 
       
 18661       for (i = 0, l = this.fields.length; i < l; ++i) {
       
 18662         var field = this.fields[i];
       
 18663         if (field.isStatic) {
       
 18664           staticDefinitions += className + "." + field.definitions.join(";\n" + className + ".") + ";\n";
       
 18665           for (j = 0, m = field.definitions.length; j < m; ++j) {
       
 18666             var fieldName = field.definitions[j].name, staticName = className + "." + fieldName;
       
 18667             result += "$p.defineProperty(" + selfId + ", '" + fieldName + "', {" +
       
 18668               "get: function(){return " + staticName + "}, " +
       
 18669               "set: function(val){" + staticName + " = val}});\n";
       
 18670           }
       
 18671         } else {
       
 18672           result += selfId + "." + field.definitions.join(";\n" + selfId + ".") + ";\n";
       
 18673         }
       
 18674       }
       
 18675       var methodOverloads = {};
       
 18676       for (i = 0, l = this.methods.length; i < l; ++i) {
       
 18677         var method = this.methods[i];
       
 18678         var overload = methodOverloads[method.name];
       
 18679         var methodId = method.name + "$" + method.params.params.length;
       
 18680         if (overload) {
       
 18681           ++overload;
       
 18682           methodId += "_" + overload;
       
 18683         } else {
       
 18684           overload = 1;
       
 18685         }
       
 18686         method.methodId = methodId;
       
 18687         methodOverloads[method.name] = overload;
       
 18688         if (method.isStatic) {
       
 18689           staticDefinitions += method;
       
 18690           staticDefinitions += "$p.addMethod(" + className + ", '" + method.name + "', " + methodId + ");\n";
       
 18691           result += "$p.addMethod(" + selfId + ", '" + method.name + "', " + methodId + ");\n";
       
 18692         } else {
       
 18693           result += method;
       
 18694           result += "$p.addMethod(" + selfId + ", '" + method.name + "', " + methodId + ");\n";
       
 18695         }
       
 18696       }
       
 18697       result += trim(this.misc.tail);
       
 18698 
       
 18699       if (this.cstrs.length > 0) {
       
 18700         result += this.cstrs.join('\n') + '\n';
       
 18701       }
       
 18702 
       
 18703       result += "function $constr() {\n";
       
 18704       var cstrsIfs = [];
       
 18705       for (i = 0, l = this.cstrs.length; i < l; ++i) {
       
 18706         var paramsLength = this.cstrs[i].params.params.length;
       
 18707         cstrsIfs.push("if(arguments.length === " + paramsLength + ") { " +
       
 18708           "$constr_" + paramsLength + ".apply(" + selfId + ", arguments); }");
       
 18709       }
       
 18710       if(cstrsIfs.length > 0) {
       
 18711         result += cstrsIfs.join(" else ") + " else ";
       
 18712       }
       
 18713       // ??? add check if length is 0, otherwise fail
       
 18714       result += "$superCstr();\n}\n";
       
 18715       result += "$constr.apply(null, arguments);\n";
       
 18716 
       
 18717       replaceContext = oldContext;
       
 18718       return "(function() {\n" +
       
 18719         "function " + className + "() {\n" + result + "}\n" +
       
 18720         staticDefinitions +
       
 18721         metadata +
       
 18722         "return " + className + ";\n" +
       
 18723         "})()";
       
 18724     };
       
 18725 
       
 18726     transformClassBody = function(body, name, baseName, interfaces) {
       
 18727       var declarations = body.substring(1, body.length - 1);
       
 18728       declarations = extractClassesAndMethods(declarations);
       
 18729       declarations = extractConstructors(declarations, name);
       
 18730       var methods = [], classes = [], cstrs = [], functions = [];
       
 18731       declarations = declarations.replace(/"([DEGH])(\d+)"/g, function(all, type, index) {
       
 18732         if(type === 'D') { methods.push(index); }
       
 18733         else if(type === 'E') { classes.push(index); }
       
 18734         else if(type === 'H') { functions.push(index); }
       
 18735         else { cstrs.push(index); }
       
 18736         return "";
       
 18737       });
       
 18738       var fields = declarations.replace(/^(?:\s*;)+/, "").split(/;(?:\s*;)*/g);
       
 18739       var baseClassName, interfacesNames;
       
 18740       var i;
       
 18741 
       
 18742       if(baseName !== undef) {
       
 18743         baseClassName = baseName.replace(/^\s*extends\s+([A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*)\s*$/g, "$1");
       
 18744       }
       
 18745 
       
 18746       if(interfaces !== undef) {
       
 18747         interfacesNames = interfaces.replace(/^\s*implements\s+(.+?)\s*$/g, "$1").split(/\s*,\s*/g);
       
 18748       }
       
 18749 
       
 18750       for(i = 0; i < functions.length; ++i) {
       
 18751         functions[i] = transformFunction(atoms[functions[i]]);
       
 18752       }
       
 18753       for(i = 0; i < methods.length; ++i) {
       
 18754         methods[i] = transformClassMethod(atoms[methods[i]]);
       
 18755       }
       
 18756       for(i = 0; i < fields.length - 1; ++i) {
       
 18757         var field = trimSpaces(fields[i]);
       
 18758         fields[i] = transformClassField(field.middle);
       
 18759       }
       
 18760       var tail = fields.pop();
       
 18761       for(i = 0; i < cstrs.length; ++i) {
       
 18762         cstrs[i] = transformConstructor(atoms[cstrs[i]]);
       
 18763       }
       
 18764       for(i = 0; i < classes.length; ++i) {
       
 18765         classes[i] = transformInnerClass(atoms[classes[i]]);
       
 18766       }
       
 18767 
       
 18768       return new AstClassBody(name, baseClassName, interfacesNames, functions, methods, fields, cstrs,
       
 18769         classes, { tail: tail });
       
 18770     };
       
 18771 
       
 18772     function AstInterface(name, body) {
       
 18773       this.name = name;
       
 18774       this.body = body;
       
 18775       body.owner = this;
       
 18776     }
       
 18777     AstInterface.prototype.toString = function() {
       
 18778       return "var " + this.name + " = " + this.body + ";\n" +
       
 18779         "$p." + this.name + " = " + this.name + ";\n";
       
 18780     };
       
 18781     function AstClass(name, body) {
       
 18782       this.name = name;
       
 18783       this.body = body;
       
 18784       body.owner = this;
       
 18785     }
       
 18786     AstClass.prototype.toString = function() {
       
 18787       return "var " + this.name + " = " + this.body + ";\n" +
       
 18788         "$p." + this.name + " = " + this.name + ";\n";
       
 18789     };
       
 18790 
       
 18791     function transformGlobalClass(class_) {
       
 18792       var m = classesRegex.exec(class_); // 1 - attr, 2 - class|int, 3 - name, 4 - extends, 5 - implements, 6 - body
       
 18793       classesRegex.lastIndex = 0;
       
 18794       var body = atoms[getAtomIndex(m[6])];
       
 18795       var oldClassId = currentClassId, newClassId = generateClassId();
       
 18796       currentClassId = newClassId;
       
 18797       var globalClass;
       
 18798       if(m[2] === "interface") {
       
 18799         globalClass = new AstInterface(m[3], transformInterfaceBody(body, m[3], m[4]) );
       
 18800       } else {
       
 18801         globalClass = new AstClass(m[3], transformClassBody(body, m[3], m[4], m[5]) );
       
 18802       }
       
 18803       appendClass(globalClass, newClassId, oldClassId);
       
 18804       currentClassId = oldClassId;
       
 18805       return globalClass;
       
 18806     }
       
 18807 
       
 18808     function AstMethod(name, params, body) {
       
 18809       this.name = name;
       
 18810       this.params = params;
       
 18811       this.body = body;
       
 18812     }
       
 18813     AstMethod.prototype.toString = function(){
       
 18814       var paramNames = appendToLookupTable({}, this.params.getNames());
       
 18815       var oldContext = replaceContext;
       
 18816       replaceContext = function (subject) {
       
 18817         return paramNames.hasOwnProperty(subject.name) ? subject.name : oldContext(subject);
       
 18818       };
       
 18819       var result = "function " + this.name + this.params + " " + this.body + "\n" +
       
 18820         "$p." + this.name + " = " + this.name + ";";
       
 18821       replaceContext = oldContext;
       
 18822       return result;
       
 18823     };
       
 18824 
       
 18825     function transformGlobalMethod(method) {
       
 18826       var m = methodsRegex.exec(method);
       
 18827       var result =
       
 18828       methodsRegex.lastIndex = 0;
       
 18829       return new AstMethod(m[3], transformParams(atoms[getAtomIndex(m[4])]),
       
 18830         transformStatementsBlock(atoms[getAtomIndex(m[6])]));
       
 18831     }
       
 18832 
       
 18833     function preStatementsTransform(statements) {
       
 18834       var s = statements;
       
 18835       // turns multiple catch blocks into one, because we have no way to properly get into them anyway.
       
 18836       s = s.replace(/\b(catch\s*"B\d+"\s*"A\d+")(\s*catch\s*"B\d+"\s*"A\d+")+/g, "$1");
       
 18837       return s;
       
 18838     }
       
 18839 
       
 18840     function AstForStatement(argument, misc) {
       
 18841       this.argument = argument;
       
 18842       this.misc = misc;
       
 18843     }
       
 18844     AstForStatement.prototype.toString = function() {
       
 18845       return this.misc.prefix + this.argument.toString();
       
 18846     };
       
 18847     function AstCatchStatement(argument, misc) {
       
 18848       this.argument = argument;
       
 18849       this.misc = misc;
       
 18850     }
       
 18851     AstCatchStatement.prototype.toString = function() {
       
 18852       return this.misc.prefix + this.argument.toString();
       
 18853     };
       
 18854     function AstPrefixStatement(name, argument, misc) {
       
 18855       this.name = name;
       
 18856       this.argument = argument;
       
 18857       this.misc = misc;
       
 18858     }
       
 18859     AstPrefixStatement.prototype.toString = function() {
       
 18860       var result = this.misc.prefix;
       
 18861       if(this.argument !== undef) {
       
 18862         result += this.argument.toString();
       
 18863       }
       
 18864       return result;
       
 18865     };
       
 18866     function AstSwitchCase(expr) {
       
 18867       this.expr = expr;
       
 18868     }
       
 18869     AstSwitchCase.prototype.toString = function() {
       
 18870       return "case " + this.expr + ":";
       
 18871     };
       
 18872     function AstLabel(label) {
       
 18873       this.label = label;
       
 18874     }
       
 18875     AstLabel.prototype.toString = function() {
       
 18876       return this.label;
       
 18877     };
       
 18878 
       
 18879     transformStatements = function(statements, transformMethod, transformClass) {
       
 18880       var nextStatement = new RegExp(/\b(catch|for|if|switch|while|with)\s*"B(\d+)"|\b(do|else|finally|return|throw|try|break|continue)\b|("[ADEH](\d+)")|\b(case)\s+([^:]+):|\b([A-Za-z_$][\w$]*\s*:)|(;)/g);
       
 18881       var res = [];
       
 18882       statements = preStatementsTransform(statements);
       
 18883       var lastIndex = 0, m, space;
       
 18884       // m contains the matches from the nextStatement regexp, null if there are no matches.
       
 18885       // nextStatement.exec starts searching at nextStatement.lastIndex.
       
 18886       while((m = nextStatement.exec(statements)) !== null) {
       
 18887         if(m[1] !== undef) { // catch, for ...
       
 18888           var i = statements.lastIndexOf('"B', nextStatement.lastIndex);
       
 18889           var statementsPrefix = statements.substring(lastIndex, i);
       
 18890           if(m[1] === "for") {
       
 18891             res.push(new AstForStatement(transformForExpression(atoms[m[2]]),
       
 18892               { prefix: statementsPrefix }) );
       
 18893           } else if(m[1] === "catch") {
       
 18894             res.push(new AstCatchStatement(transformParams(atoms[m[2]]),
       
 18895               { prefix: statementsPrefix }) );
       
 18896           } else {
       
 18897             res.push(new AstPrefixStatement(m[1], transformExpression(atoms[m[2]]),
       
 18898               { prefix: statementsPrefix }) );
       
 18899           }
       
 18900         } else if(m[3] !== undef) { // do, else, ...
       
 18901             res.push(new AstPrefixStatement(m[3], undef,
       
 18902               { prefix: statements.substring(lastIndex, nextStatement.lastIndex) }) );
       
 18903         } else if(m[4] !== undef) { // block, class and methods
       
 18904           space = statements.substring(lastIndex, nextStatement.lastIndex - m[4].length);
       
 18905           if(trim(space).length !== 0) { continue; } // avoiding new type[] {} construct
       
 18906           res.push(space);
       
 18907           var kind = m[4].charAt(1), atomIndex = m[5];
       
 18908           if(kind === 'D') {
       
 18909             res.push(transformMethod(atoms[atomIndex]));
       
 18910           } else if(kind === 'E') {
       
 18911             res.push(transformClass(atoms[atomIndex]));
       
 18912           } else if(kind === 'H') {
       
 18913             res.push(transformFunction(atoms[atomIndex]));
       
 18914           } else {
       
 18915             res.push(transformStatementsBlock(atoms[atomIndex]));
       
 18916           }
       
 18917         } else if(m[6] !== undef) { // switch case
       
 18918           res.push(new AstSwitchCase(transformExpression(trim(m[7]))));
       
 18919         } else if(m[8] !== undef) { // label
       
 18920           space = statements.substring(lastIndex, nextStatement.lastIndex - m[8].length);
       
 18921           if(trim(space).length !== 0) { continue; } // avoiding ?: construct
       
 18922           res.push(new AstLabel(statements.substring(lastIndex, nextStatement.lastIndex)) );
       
 18923         } else { // semicolon
       
 18924           var statement = trimSpaces(statements.substring(lastIndex, nextStatement.lastIndex - 1));
       
 18925           res.push(statement.left);
       
 18926           res.push(transformStatement(statement.middle));
       
 18927           res.push(statement.right + ";");
       
 18928         }
       
 18929         lastIndex = nextStatement.lastIndex;
       
 18930       }
       
 18931       var statementsTail = trimSpaces(statements.substring(lastIndex));
       
 18932       res.push(statementsTail.left);
       
 18933       if(statementsTail.middle !== "") {
       
 18934         res.push(transformStatement(statementsTail.middle));
       
 18935         res.push(";" + statementsTail.right);
       
 18936       }
       
 18937       return res;
       
 18938     };
       
 18939 
       
 18940     function getLocalNames(statements) {
       
 18941       var localNames = [];
       
 18942       for(var i=0,l=statements.length;i<l;++i) {
       
 18943         var statement = statements[i];
       
 18944         if(statement instanceof AstVar) {
       
 18945           localNames = localNames.concat(statement.getNames());
       
 18946         } else if(statement instanceof AstForStatement &&
       
 18947           statement.argument.initStatement instanceof AstVar) {
       
 18948           localNames = localNames.concat(statement.argument.initStatement.getNames());
       
 18949         } else if(statement instanceof AstInnerInterface || statement instanceof AstInnerClass ||
       
 18950           statement instanceof AstInterface || statement instanceof AstClass ||
       
 18951           statement instanceof AstMethod || statement instanceof AstFunction) {
       
 18952           localNames.push(statement.name);
       
 18953         }
       
 18954       }
       
 18955       return appendToLookupTable({}, localNames);
       
 18956     }
       
 18957 
       
 18958     function AstStatementsBlock(statements) {
       
 18959       this.statements = statements;
       
 18960     }
       
 18961     AstStatementsBlock.prototype.toString = function() {
       
 18962       var localNames = getLocalNames(this.statements);
       
 18963       var oldContext = replaceContext;
       
 18964 
       
 18965       // replacing context only when necessary
       
 18966       if(!isLookupTableEmpty(localNames)) {
       
 18967         replaceContext = function (subject) {
       
 18968           return localNames.hasOwnProperty(subject.name) ? subject.name : oldContext(subject);
       
 18969         };
       
 18970       }
       
 18971 
       
 18972       var result = "{\n" + this.statements.join('') + "\n}";
       
 18973       replaceContext = oldContext;
       
 18974       return result;
       
 18975     };
       
 18976 
       
 18977     transformStatementsBlock = function(block) {
       
 18978       var content = trimSpaces(block.substring(1, block.length - 1));
       
 18979       return new AstStatementsBlock(transformStatements(content.middle));
       
 18980     };
       
 18981 
       
 18982     function AstRoot(statements) {
       
 18983       this.statements = statements;
       
 18984     }
       
 18985     AstRoot.prototype.toString = function() {
       
 18986       var classes = [], otherStatements = [], statement;
       
 18987       for (var i = 0, len = this.statements.length; i < len; ++i) {
       
 18988         statement = this.statements[i];
       
 18989         if (statement instanceof AstClass || statement instanceof AstInterface) {
       
 18990           classes.push(statement);
       
 18991         } else {
       
 18992           otherStatements.push(statement);
       
 18993         }
       
 18994       }
       
 18995       sortByWeight(classes);
       
 18996 
       
 18997       var localNames = getLocalNames(this.statements);
       
 18998       replaceContext = function (subject) {
       
 18999         var name = subject.name;
       
 19000         if(localNames.hasOwnProperty(name)) {
       
 19001           return name;
       
 19002         }
       
 19003         if(globalMembers.hasOwnProperty(name) ||
       
 19004            PConstants.hasOwnProperty(name) ||
       
 19005            defaultScope.hasOwnProperty(name)) {
       
 19006           return "$p." + name;
       
 19007         }
       
 19008         return name;
       
 19009       };
       
 19010       var result = "// this code was autogenerated from PJS\n" +
       
 19011         "(function($p) {\n" +
       
 19012         classes.join('') + "\n" +
       
 19013         otherStatements.join('') + "\n})";
       
 19014       replaceContext = null;
       
 19015       return result;
       
 19016     };
       
 19017 
       
 19018     transformMain = function() {
       
 19019       var statements = extractClassesAndMethods(atoms[0]);
       
 19020       statements = statements.replace(/\bimport\s+[^;]+;/g, "");
       
 19021       return new AstRoot( transformStatements(statements,
       
 19022         transformGlobalMethod, transformGlobalClass) );
       
 19023     };
       
 19024 
       
 19025     function generateMetadata(ast) {
       
 19026       var globalScope = {};
       
 19027       var id, class_;
       
 19028       for(id in declaredClasses) {
       
 19029         if(declaredClasses.hasOwnProperty(id)) {
       
 19030           class_ = declaredClasses[id];
       
 19031           var scopeId = class_.scopeId, name = class_.name;
       
 19032           if(scopeId) {
       
 19033             var scope = declaredClasses[scopeId];
       
 19034             class_.scope = scope;
       
 19035             if(scope.inScope === undef) {
       
 19036               scope.inScope = {};
       
 19037             }
       
 19038             scope.inScope[name] = class_;
       
 19039           } else {
       
 19040             globalScope[name] = class_;
       
 19041           }
       
 19042         }
       
 19043       }
       
 19044 
       
 19045       function findInScopes(class_, name) {
       
 19046         var parts = name.split('.');
       
 19047         var currentScope = class_.scope, found;
       
 19048         while(currentScope) {
       
 19049           if(currentScope.hasOwnProperty(parts[0])) {
       
 19050             found = currentScope[parts[0]]; break;
       
 19051           }
       
 19052           currentScope = currentScope.scope;
       
 19053         }
       
 19054         if(found === undef) {
       
 19055           found = globalScope[parts[0]];
       
 19056         }
       
 19057         for(var i=1,l=parts.length;i<l && found;++i) {
       
 19058           found = found.inScope[parts[i]];
       
 19059         }
       
 19060         return found;
       
 19061       }
       
 19062 
       
 19063       for(id in declaredClasses) {
       
 19064         if(declaredClasses.hasOwnProperty(id)) {
       
 19065           class_ = declaredClasses[id];
       
 19066           var baseClassName = class_.body.baseClassName;
       
 19067           if(baseClassName) {
       
 19068             var parent = findInScopes(class_, baseClassName);
       
 19069             if (parent) {
       
 19070               class_.base = parent;
       
 19071               if (!parent.derived) {
       
 19072                 parent.derived = [];
       
 19073               }
       
 19074               parent.derived.push(class_);
       
 19075             }
       
 19076           }
       
 19077           var interfacesNames = class_.body.interfacesNames,
       
 19078             interfaces = [], i, l;
       
 19079           if (interfacesNames && interfacesNames.length > 0) {
       
 19080             for (i = 0, l = interfacesNames.length; i < l; ++i) {
       
 19081               var interface_ = findInScopes(class_, interfacesNames[i]);
       
 19082               interfaces.push(interface_);
       
 19083               if (!interface_) {
       
 19084                 continue;
       
 19085               }
       
 19086               if (!interface_.derived) {
       
 19087                 interface_.derived = [];
       
 19088               }
       
 19089               interface_.derived.push(class_);
       
 19090             }
       
 19091             if (interfaces.length > 0) {
       
 19092               class_.interfaces = interfaces;
       
 19093             }
       
 19094           }
       
 19095         }
       
 19096       }
       
 19097     }
       
 19098 
       
 19099     function setWeight(ast) {
       
 19100       var queue = [], tocheck = {};
       
 19101       var id, scopeId, class_;
       
 19102       // queue most inner and non-inherited
       
 19103       for (id in declaredClasses) {
       
 19104         if (declaredClasses.hasOwnProperty(id)) {
       
 19105           class_ = declaredClasses[id];
       
 19106           if (!class_.inScope && !class_.derived) {
       
 19107             queue.push(id);
       
 19108             class_.weight = 0;
       
 19109           } else {
       
 19110             var dependsOn = [];
       
 19111             if (class_.inScope) {
       
 19112               for (scopeId in class_.inScope) {
       
 19113                 if (class_.inScope.hasOwnProperty(scopeId)) {
       
 19114                   dependsOn.push(class_.inScope[scopeId]);
       
 19115                 }
       
 19116               }
       
 19117             }
       
 19118             if (class_.derived) {
       
 19119               dependsOn = dependsOn.concat(class_.derived);
       
 19120             }
       
 19121             tocheck[id] = dependsOn;
       
 19122           }
       
 19123         }
       
 19124       }
       
 19125       function removeDependentAndCheck(targetId, from) {
       
 19126         var dependsOn = tocheck[targetId];
       
 19127         if (!dependsOn) {
       
 19128           return false; // no need to process
       
 19129         }
       
 19130         var i = dependsOn.indexOf(from);
       
 19131         if (i < 0) {
       
 19132           return false;
       
 19133         }
       
 19134         dependsOn.splice(i, 1);
       
 19135         if (dependsOn.length > 0) {
       
 19136           return false;
       
 19137         }
       
 19138         delete tocheck[targetId];
       
 19139         return true;
       
 19140       }
       
 19141       while (queue.length > 0) {
       
 19142         id = queue.shift();
       
 19143         class_ = declaredClasses[id];
       
 19144         if (class_.scopeId && removeDependentAndCheck(class_.scopeId, class_)) {
       
 19145           queue.push(class_.scopeId);
       
 19146           declaredClasses[class_.scopeId].weight = class_.weight + 1;
       
 19147         }
       
 19148         if (class_.base && removeDependentAndCheck(class_.base.classId, class_)) {
       
 19149           queue.push(class_.base.classId);
       
 19150           class_.base.weight = class_.weight + 1;
       
 19151         }
       
 19152         if (class_.interfaces) {
       
 19153           var i, l;
       
 19154           for (i = 0, l = class_.interfaces.length; i < l; ++i) {
       
 19155             if (!class_.interfaces[i] ||
       
 19156                 !removeDependentAndCheck(class_.interfaces[i].classId, class_)) {
       
 19157               continue;
       
 19158             }
       
 19159             queue.push(class_.interfaces[i].classId);
       
 19160             class_.interfaces[i].weight = class_.weight + 1;
       
 19161           }
       
 19162         }
       
 19163       }
       
 19164     }
       
 19165 
       
 19166     var transformed = transformMain();
       
 19167     generateMetadata(transformed);
       
 19168     setWeight(transformed);
       
 19169 
       
 19170     var redendered = transformed.toString();
       
 19171 
       
 19172     // remove empty extra lines with space
       
 19173     redendered = redendered.replace(/\s*\n(?:[\t ]*\n)+/g, "\n\n");
       
 19174 
       
 19175     return injectStrings(redendered, strings);
       
 19176   }// Parser ends
       
 19177 
       
 19178   function preprocessCode(aCode, sketch) {
       
 19179     // Parse out @pjs directive, if any.
       
 19180     var dm = new RegExp(/\/\*\s*@pjs\s+((?:[^\*]|\*+[^\*\/])*)\*\//g).exec(aCode);
       
 19181     if (dm && dm.length === 2) {
       
 19182       // masks contents of a JSON to be replaced later
       
 19183       // to protect the contents from further parsing
       
 19184       var jsonItems = [],
       
 19185           directives = dm.splice(1, 2)[0].replace(/\{([\s\S]*?)\}/g, (function() {
       
 19186             return function(all, item) {
       
 19187               jsonItems.push(item);
       
 19188               return "{" + (jsonItems.length-1) + "}";
       
 19189             };
       
 19190           }())).replace('\n', '').replace('\r', '').split(";");
       
 19191 
       
 19192       // We'll L/RTrim, and also remove any surrounding double quotes (e.g., just take string contents)
       
 19193       var clean = function(s) {
       
 19194         return s.replace(/^\s*["']?/, '').replace(/["']?\s*$/, '');
       
 19195       };
       
 19196 
       
 19197       for (var i = 0, dl = directives.length; i < dl; i++) {
       
 19198         var pair = directives[i].split('=');
       
 19199         if (pair && pair.length === 2) {
       
 19200           var key = clean(pair[0]),
       
 19201               value = clean(pair[1]),
       
 19202               list = [];
       
 19203           // A few directives require work beyond storying key/value pairings
       
 19204           if (key === "preload") {
       
 19205             list = value.split(',');
       
 19206             // All pre-loaded images will get put in imageCache, keyed on filename
       
 19207             for (var j = 0, jl = list.length; j < jl; j++) {
       
 19208               var imageName = clean(list[j]);
       
 19209               sketch.imageCache.add(imageName);
       
 19210             }
       
 19211           // fonts can be declared as a string containing a url,
       
 19212           // or a JSON object, containing a font name, and a url
       
 19213           } else if (key === "font") {
       
 19214             list = value.split(",");
       
 19215             for (var x = 0, xl = list.length; x < xl; x++) {
       
 19216               var fontName = clean(list[x]),
       
 19217                   index = /^\{(\d*?)\}$/.exec(fontName);
       
 19218               // if index is not null, send JSON, otherwise, send string
       
 19219               PFont.preloading.add(index ? JSON.parse("{" + jsonItems[index[1]] + "}") : fontName);
       
 19220             }
       
 19221           } else if (key === "pauseOnBlur") {
       
 19222             sketch.options.pauseOnBlur = value === "true";
       
 19223           } else if (key === "globalKeyEvents") {
       
 19224             sketch.options.globalKeyEvents = value === "true";
       
 19225           } else if (key.substring(0, 6) === "param-") {
       
 19226             sketch.params[key.substring(6)] = value;
       
 19227           } else {
       
 19228             sketch.options[key] = value;
       
 19229           }
       
 19230         }
       
 19231       }
       
 19232     }
       
 19233     return aCode;
       
 19234   }
       
 19235 
       
 19236   // Parse/compiles Processing (Java-like) syntax to JavaScript syntax
       
 19237   Processing.compile = function(pdeCode) {
       
 19238     var sketch = new Processing.Sketch();
       
 19239     var code = preprocessCode(pdeCode, sketch);
       
 19240     var compiledPde = parseProcessing(code);
       
 19241     sketch.sourceCode = compiledPde;
       
 19242     return sketch;
       
 19243   };
       
 19244 //#endif
       
 19245 
       
 19246   // tinylog lite JavaScript library
       
 19247   // http://purl.eligrey.com/tinylog/lite
       
 19248   /*global tinylog,print*/
       
 19249   var tinylogLite = (function() {
       
 19250     "use strict";
       
 19251 
       
 19252     var tinylogLite = {},
       
 19253       undef = "undefined",
       
 19254       func = "function",
       
 19255       False = !1,
       
 19256       True = !0,
       
 19257       logLimit = 512,
       
 19258       log = "log";
       
 19259 
       
 19260     if (typeof tinylog !== undef && typeof tinylog[log] === func) {
       
 19261       // pre-existing tinylog present
       
 19262       tinylogLite[log] = tinylog[log];
       
 19263     } else if (typeof document !== undef && !document.fake) {
       
 19264       (function() {
       
 19265         // DOM document
       
 19266         var doc = document,
       
 19267 
       
 19268         $div = "div",
       
 19269         $style = "style",
       
 19270         $title = "title",
       
 19271 
       
 19272         containerStyles = {
       
 19273           zIndex: 10000,
       
 19274           position: "fixed",
       
 19275           bottom: "0px",
       
 19276           width: "100%",
       
 19277           height: "15%",
       
 19278           fontFamily: "sans-serif",
       
 19279           color: "#ccc",
       
 19280           backgroundColor: "black"
       
 19281         },
       
 19282         outputStyles = {
       
 19283           position: "relative",
       
 19284           fontFamily: "monospace",
       
 19285           overflow: "auto",
       
 19286           height: "100%",
       
 19287           paddingTop: "5px"
       
 19288         },
       
 19289         resizerStyles = {
       
 19290           height: "5px",
       
 19291           marginTop: "-5px",
       
 19292           cursor: "n-resize",
       
 19293           backgroundColor: "darkgrey"
       
 19294         },
       
 19295         closeButtonStyles = {
       
 19296           position: "absolute",
       
 19297           top: "5px",
       
 19298           right: "20px",
       
 19299           color: "#111",
       
 19300           MozBorderRadius: "4px",
       
 19301           webkitBorderRadius: "4px",
       
 19302           borderRadius: "4px",
       
 19303           cursor: "pointer",
       
 19304           fontWeight: "normal",
       
 19305           textAlign: "center",
       
 19306           padding: "3px 5px",
       
 19307           backgroundColor: "#333",
       
 19308           fontSize: "12px"
       
 19309         },
       
 19310         entryStyles = {
       
 19311           //borderBottom: "1px solid #d3d3d3",
       
 19312           minHeight: "16px"
       
 19313         },
       
 19314         entryTextStyles = {
       
 19315           fontSize: "12px",
       
 19316           margin: "0 8px 0 8px",
       
 19317           maxWidth: "100%",
       
 19318           whiteSpace: "pre-wrap",
       
 19319           overflow: "auto"
       
 19320         },
       
 19321 
       
 19322         view = doc.defaultView,
       
 19323           docElem = doc.documentElement,
       
 19324           docElemStyle = docElem[$style],
       
 19325 
       
 19326         setStyles = function() {
       
 19327           var i = arguments.length,
       
 19328             elemStyle, styles, style;
       
 19329 
       
 19330           while (i--) {
       
 19331             styles = arguments[i--];
       
 19332             elemStyle = arguments[i][$style];
       
 19333 
       
 19334             for (style in styles) {
       
 19335               if (styles.hasOwnProperty(style)) {
       
 19336                 elemStyle[style] = styles[style];
       
 19337               }
       
 19338             }
       
 19339           }
       
 19340         },
       
 19341 
       
 19342         observer = function(obj, event, handler) {
       
 19343           if (obj.addEventListener) {
       
 19344             obj.addEventListener(event, handler, False);
       
 19345           } else if (obj.attachEvent) {
       
 19346             obj.attachEvent("on" + event, handler);
       
 19347           }
       
 19348           return [obj, event, handler];
       
 19349         },
       
 19350         unobserve = function(obj, event, handler) {
       
 19351           if (obj.removeEventListener) {
       
 19352             obj.removeEventListener(event, handler, False);
       
 19353           } else if (obj.detachEvent) {
       
 19354             obj.detachEvent("on" + event, handler);
       
 19355           }
       
 19356         },
       
 19357         clearChildren = function(node) {
       
 19358           var children = node.childNodes,
       
 19359             child = children.length;
       
 19360 
       
 19361           while (child--) {
       
 19362             node.removeChild(children.item(0));
       
 19363           }
       
 19364         },
       
 19365         append = function(to, elem) {
       
 19366           return to.appendChild(elem);
       
 19367         },
       
 19368         createElement = function(localName) {
       
 19369           return doc.createElement(localName);
       
 19370         },
       
 19371         createTextNode = function(text) {
       
 19372           return doc.createTextNode(text);
       
 19373         },
       
 19374 
       
 19375         createLog = tinylogLite[log] = function(message) {
       
 19376           // don't show output log until called once
       
 19377           var uninit,
       
 19378             originalPadding = docElemStyle.paddingBottom,
       
 19379             container = createElement($div),
       
 19380             containerStyle = container[$style],
       
 19381             resizer = append(container, createElement($div)),
       
 19382             output = append(container, createElement($div)),
       
 19383             closeButton = append(container, createElement($div)),
       
 19384             resizingLog = False,
       
 19385             previousHeight = False,
       
 19386             previousScrollTop = False,
       
 19387             messages = 0,
       
 19388 
       
 19389             updateSafetyMargin = function() {
       
 19390               // have a blank space large enough to fit the output box at the page bottom
       
 19391               docElemStyle.paddingBottom = container.clientHeight + "px";
       
 19392             },
       
 19393             setContainerHeight = function(height) {
       
 19394               var viewHeight = view.innerHeight,
       
 19395                 resizerHeight = resizer.clientHeight;
       
 19396 
       
 19397               // constrain the container inside the viewport's dimensions
       
 19398               if (height < 0) {
       
 19399                 height = 0;
       
 19400               } else if (height + resizerHeight > viewHeight) {
       
 19401                 height = viewHeight - resizerHeight;
       
 19402               }
       
 19403 
       
 19404               containerStyle.height = height / viewHeight * 100 + "%";
       
 19405 
       
 19406               updateSafetyMargin();
       
 19407             },
       
 19408             observers = [
       
 19409               observer(doc, "mousemove", function(evt) {
       
 19410                 if (resizingLog) {
       
 19411                   setContainerHeight(view.innerHeight - evt.clientY);
       
 19412                   output.scrollTop = previousScrollTop;
       
 19413                 }
       
 19414               }),
       
 19415 
       
 19416               observer(doc, "mouseup", function() {
       
 19417                 if (resizingLog) {
       
 19418                   resizingLog = previousScrollTop = False;
       
 19419                 }
       
 19420               }),
       
 19421 
       
 19422               observer(resizer, "dblclick", function(evt) {
       
 19423                 evt.preventDefault();
       
 19424 
       
 19425                 if (previousHeight) {
       
 19426                   setContainerHeight(previousHeight);
       
 19427                   previousHeight = False;
       
 19428                 } else {
       
 19429                   previousHeight = container.clientHeight;
       
 19430                   containerStyle.height = "0px";
       
 19431                 }
       
 19432               }),
       
 19433 
       
 19434               observer(resizer, "mousedown", function(evt) {
       
 19435                 evt.preventDefault();
       
 19436                 resizingLog = True;
       
 19437                 previousScrollTop = output.scrollTop;
       
 19438               }),
       
 19439 
       
 19440               observer(resizer, "contextmenu", function() {
       
 19441                 resizingLog = False;
       
 19442               }),
       
 19443 
       
 19444               observer(closeButton, "click", function() {
       
 19445                 uninit();
       
 19446               })
       
 19447             ];
       
 19448 
       
 19449           uninit = function() {
       
 19450             // remove observers
       
 19451             var i = observers.length;
       
 19452 
       
 19453             while (i--) {
       
 19454               unobserve.apply(tinylogLite, observers[i]);
       
 19455             }
       
 19456 
       
 19457             // remove tinylog lite from the DOM
       
 19458             docElem.removeChild(container);
       
 19459             docElemStyle.paddingBottom = originalPadding;
       
 19460 
       
 19461             clearChildren(output);
       
 19462             clearChildren(container);
       
 19463 
       
 19464             tinylogLite[log] = createLog;
       
 19465           };
       
 19466 
       
 19467           setStyles(
       
 19468           container, containerStyles, output, outputStyles, resizer, resizerStyles, closeButton, closeButtonStyles);
       
 19469 
       
 19470           closeButton[$title] = "Close Log";
       
 19471           append(closeButton, createTextNode("\u2716"));
       
 19472 
       
 19473           resizer[$title] = "Double-click to toggle log minimization";
       
 19474 
       
 19475           docElem.insertBefore(container, docElem.firstChild);
       
 19476 
       
 19477           tinylogLite[log] = function(message) {
       
 19478             if (messages === logLimit) {
       
 19479               output.removeChild(output.firstChild);
       
 19480             } else {
       
 19481               messages++;
       
 19482             }
       
 19483 
       
 19484             var entry = append(output, createElement($div)),
       
 19485               entryText = append(entry, createElement($div));
       
 19486 
       
 19487             entry[$title] = (new Date()).toLocaleTimeString();
       
 19488 
       
 19489             setStyles(
       
 19490             entry, entryStyles, entryText, entryTextStyles);
       
 19491 
       
 19492             append(entryText, createTextNode(message));
       
 19493             output.scrollTop = output.scrollHeight;
       
 19494           };
       
 19495 
       
 19496           tinylogLite[log](message);
       
 19497           updateSafetyMargin();
       
 19498         };
       
 19499       }());
       
 19500     } else if (typeof print === func) { // JS shell
       
 19501       tinylogLite[log] = print;
       
 19502     }
       
 19503 
       
 19504     return tinylogLite;
       
 19505   }());
       
 19506   // end of tinylog lite JavaScript library
       
 19507 
       
 19508   Processing.logger = tinylogLite;
       
 19509 
       
 19510   Processing.version = "@VERSION@";
       
 19511 
       
 19512   // Share lib space
       
 19513   Processing.lib = {};
       
 19514 
       
 19515   Processing.registerLibrary = function(name, desc) {
       
 19516     Processing.lib[name] = desc;
       
 19517 
       
 19518     if(desc.hasOwnProperty("init")) {
       
 19519       desc.init(defaultScope);
       
 19520     }
       
 19521   };
       
 19522 
       
 19523   // Store Processing instances. Only Processing.instances,
       
 19524   // Processing.getInstanceById are exposed.
       
 19525   Processing.instances = processingInstances;
       
 19526 
       
 19527   Processing.getInstanceById = function(name) {
       
 19528     return processingInstances[processingInstanceIds[name]];
       
 19529   };
       
 19530 
       
 19531   Processing.Sketch = function(attachFunction) {
       
 19532     this.attachFunction = attachFunction; // can be optional
       
 19533     this.options = {
       
 19534       pauseOnBlur: false,
       
 19535       globalKeyEvents: false
       
 19536     };
       
 19537 
       
 19538     /* Optional Sketch event hooks:
       
 19539      *   onLoad - parsing/preloading is done, before sketch starts
       
 19540      *   onSetup - setup() has been called, before first draw()
       
 19541      *   onPause - noLoop() has been called, pausing draw loop
       
 19542      *   onLoop - loop() has been called, resuming draw loop
       
 19543      *   onFrameStart - draw() loop about to begin
       
 19544      *   onFrameEnd - draw() loop finished
       
 19545      *   onExit - exit() done being called
       
 19546      */
       
 19547     this.onLoad = nop;
       
 19548     this.onSetup = nop;
       
 19549     this.onPause = nop;
       
 19550     this.onLoop = nop;
       
 19551     this.onFrameStart = nop;
       
 19552     this.onFrameEnd = nop;
       
 19553     this.onExit = nop;
       
 19554 
       
 19555     this.params = {};
       
 19556     this.imageCache = {
       
 19557       pending: 0,
       
 19558       images: {},
       
 19559       // Opera requires special administration for preloading
       
 19560       operaCache: {},
       
 19561       // Specify an optional img arg if the image is already loaded in the DOM,
       
 19562       // otherwise href will get loaded.
       
 19563       add: function(href, img) {
       
 19564         // Prevent muliple loads for an image, in case it gets
       
 19565         // preloaded more than once, or is added via JS and then preloaded.
       
 19566         if (this.images[href]) {
       
 19567           return;
       
 19568         }
       
 19569 
       
 19570         if (!isDOMPresent) {
       
 19571           this.images[href] = null;
       
 19572         }
       
 19573 
       
 19574         // No image in the DOM, kick-off a background load
       
 19575         if (!img) {
       
 19576           img = new Image();
       
 19577           img.onload = (function(owner) {
       
 19578             return function() {
       
 19579               owner.pending--;
       
 19580             };
       
 19581           }(this));
       
 19582           this.pending++;
       
 19583           img.src = href;
       
 19584         }
       
 19585 
       
 19586         this.images[href] = img;
       
 19587 
       
 19588         // Opera will not load images until they are inserted into the DOM.
       
 19589         if (window.opera) {
       
 19590           var div = document.createElement("div");
       
 19591           div.appendChild(img);
       
 19592           // we can't use "display: none", since that makes it invisible, and thus not load
       
 19593           div.style.position = "absolute";
       
 19594           div.style.opacity = 0;
       
 19595           div.style.width = "1px";
       
 19596           div.style.height= "1px";
       
 19597           if (!this.operaCache[href]) {
       
 19598             document.body.appendChild(div);
       
 19599             this.operaCache[href] = div;
       
 19600           }
       
 19601         }
       
 19602       }
       
 19603     };
       
 19604     this.sourceCode = undefined;
       
 19605     this.attach = function(processing) {
       
 19606       // either attachFunction or sourceCode must be present on attach
       
 19607       if(typeof this.attachFunction === "function") {
       
 19608         this.attachFunction(processing);
       
 19609       } else if(this.sourceCode) {
       
 19610         var func = ((new Function("return (" + this.sourceCode + ");"))());
       
 19611         func(processing);
       
 19612         this.attachFunction = func;
       
 19613       } else {
       
 19614         throw "Unable to attach sketch to the processing instance";
       
 19615       }
       
 19616     };
       
 19617 //#if PARSER
       
 19618     this.toString = function() {
       
 19619       var i;
       
 19620       var code = "((function(Sketch) {\n";
       
 19621       code += "var sketch = new Sketch(\n" + this.sourceCode + ");\n";
       
 19622       for(i in this.options) {
       
 19623         if(this.options.hasOwnProperty(i)) {
       
 19624           var value = this.options[i];
       
 19625           code += "sketch.options." + i + " = " +
       
 19626             (typeof value === 'string' ? '\"' + value + '\"' : "" + value) + ";\n";
       
 19627         }
       
 19628       }
       
 19629       for(i in this.imageCache) {
       
 19630         if(this.options.hasOwnProperty(i)) {
       
 19631           code += "sketch.imageCache.add(\"" + i + "\");\n";
       
 19632         }
       
 19633       }
       
 19634       // TODO serialize fonts
       
 19635       code += "return sketch;\n})(Processing.Sketch))";
       
 19636       return code;
       
 19637     };
       
 19638 //#endif
       
 19639   };
       
 19640 
       
 19641 //#if PARSER
       
 19642   /**
       
 19643    * aggregate all source code into a single file, then rewrite that
       
 19644    * source and bind to canvas via new Processing(canvas, sourcestring).
       
 19645    * @param {CANVAS} canvas The html canvas element to bind to
       
 19646    * @param {String[]} source The array of files that must be loaded
       
 19647    */
       
 19648   var loadSketchFromSources = function(canvas, sources) {
       
 19649     var code = [], errors = [], sourcesCount = sources.length, loaded = 0;
       
 19650 
       
 19651     function ajaxAsync(url, callback) {
       
 19652       var xhr = new XMLHttpRequest();
       
 19653       xhr.onreadystatechange = function() {
       
 19654         if (xhr.readyState === 4) {
       
 19655           var error;
       
 19656           if (xhr.status !== 200 && xhr.status !== 0) {
       
 19657             error = "Invalid XHR status " + xhr.status;
       
 19658           } else if (xhr.responseText === "") {
       
 19659             // Give a hint when loading fails due to same-origin issues on file:/// urls
       
 19660             if ( ("withCredentials" in new XMLHttpRequest()) &&
       
 19661                  (new XMLHttpRequest()).withCredentials === false &&
       
 19662                  window.location.protocol === "file:" ) {
       
 19663               error = "XMLHttpRequest failure, possibly due to a same-origin policy violation. You can try loading this page in another browser, or load it from http://localhost using a local webserver. See the Processing.js README for a more detailed explanation of this problem and solutions.";
       
 19664             } else {
       
 19665               error = "File is empty.";
       
 19666             }
       
 19667           }
       
 19668 
       
 19669           callback(xhr.responseText, error);
       
 19670         }
       
 19671       };
       
 19672       xhr.open("GET", url, true);
       
 19673       if (xhr.overrideMimeType) {
       
 19674         xhr.overrideMimeType("application/json");
       
 19675       }
       
 19676       xhr.setRequestHeader("If-Modified-Since", "Fri, 01 Jan 1960 00:00:00 GMT"); // no cache
       
 19677       xhr.send(null);
       
 19678     }
       
 19679 
       
 19680     function loadBlock(index, filename) {
       
 19681       function callback(block, error) {
       
 19682         code[index] = block;
       
 19683         ++loaded;
       
 19684         if (error) {
       
 19685           errors.push(filename + " ==> " + error);
       
 19686         }
       
 19687         if (loaded === sourcesCount) {
       
 19688           if (errors.length === 0) {
       
 19689             try {
       
 19690               return new Processing(canvas, code.join("\n"));
       
 19691             } catch(e) {
       
 19692               throw "Processing.js: Unable to execute pjs sketch: " + e;
       
 19693             }
       
 19694           } else {
       
 19695             throw "Processing.js: Unable to load pjs sketch files: " + errors.join("\n");
       
 19696           }
       
 19697         }
       
 19698       }
       
 19699       if (filename.charAt(0) === '#') {
       
 19700         // trying to get script from the element
       
 19701         var scriptElement = document.getElementById(filename.substring(1));
       
 19702         if (scriptElement) {
       
 19703           callback(scriptElement.text || scriptElement.textContent);
       
 19704         } else {
       
 19705           callback("", "Unable to load pjs sketch: element with id \'" + filename.substring(1) + "\' was not found");
       
 19706         }
       
 19707         return;
       
 19708       }
       
 19709 
       
 19710       ajaxAsync(filename, callback);
       
 19711     }
       
 19712 
       
 19713     for (var i = 0; i < sourcesCount; ++i) {
       
 19714       loadBlock(i, sources[i]);
       
 19715     }
       
 19716   };
       
 19717 
       
 19718   /**
       
 19719    * Automatic initialization function.
       
 19720    */
       
 19721   var init = function() {
       
 19722     document.removeEventListener('DOMContentLoaded', init, false);
       
 19723 
       
 19724     var canvas = document.getElementsByTagName('canvas'),
       
 19725       filenames;
       
 19726 
       
 19727     for (var i = 0, l = canvas.length; i < l; i++) {
       
 19728       // datasrc and data-src are deprecated.
       
 19729       var processingSources = canvas[i].getAttribute('data-processing-sources');
       
 19730       if (processingSources === null) {
       
 19731         // Temporary fallback for datasrc and data-src
       
 19732         processingSources = canvas[i].getAttribute('data-src');
       
 19733         if (processingSources === null) {
       
 19734           processingSources = canvas[i].getAttribute('datasrc');
       
 19735         }
       
 19736       }
       
 19737       if (processingSources) {
       
 19738         filenames = processingSources.split(' ');
       
 19739         for (var j = 0; j < filenames.length;) {
       
 19740           if (filenames[j]) {
       
 19741             j++;
       
 19742           } else {
       
 19743             filenames.splice(j, 1);
       
 19744           }
       
 19745         }
       
 19746         loadSketchFromSources(canvas[i], filenames);
       
 19747       }
       
 19748     }
       
 19749 
       
 19750     // also process all <script>-indicated sketches, if there are any
       
 19751     var scripts = document.getElementsByTagName('script');
       
 19752     var s, source, instance;
       
 19753     for (s = 0; s < scripts.length; s++) {
       
 19754       var script = scripts[s];
       
 19755       if (!script.getAttribute) {
       
 19756         continue;
       
 19757       }
       
 19758 
       
 19759       var type = script.getAttribute("type");
       
 19760       if (type && (type.toLowerCase() === "text/processing" || type.toLowerCase() === "application/processing")) {
       
 19761         var target = script.getAttribute("data-processing-target");
       
 19762         canvas = undef;
       
 19763         if (target) {
       
 19764           canvas = document.getElementById(target);
       
 19765         } else {
       
 19766           var nextSibling = script.nextSibling;
       
 19767           while (nextSibling && nextSibling.nodeType !== 1) {
       
 19768             nextSibling = nextSibling.nextSibling;
       
 19769           }
       
 19770           if (nextSibling.nodeName.toLowerCase() === "canvas") {
       
 19771             canvas = nextSibling;
       
 19772           }
       
 19773         }
       
 19774 
       
 19775         if (canvas) {
       
 19776           if (script.getAttribute("src")) {
       
 19777             filenames = script.getAttribute("src").split(/\s+/);
       
 19778             loadSketchFromSources(canvas, filenames);
       
 19779             continue;
       
 19780           }
       
 19781           source =  script.textContent || script.text;
       
 19782           instance = new Processing(canvas, source);
       
 19783         }
       
 19784       }
       
 19785     }
       
 19786   };
       
 19787 
       
 19788   /**
       
 19789    * Make loadSketchFromSources publically visible
       
 19790    */
       
 19791   Processing.loadSketchFromSources = loadSketchFromSources;
       
 19792 
       
 19793   /**
       
 19794    * Disable the automatic loading of all sketches on the page
       
 19795    */
       
 19796   Processing.disableInit = function() {
       
 19797     if(isDOMPresent) {
       
 19798       document.removeEventListener('DOMContentLoaded', init, false);
       
 19799     }
       
 19800   };
       
 19801 //#endif
       
 19802 
       
 19803   if(isDOMPresent) {
       
 19804     window['Processing'] = Processing;
       
 19805 //#if PARSER
       
 19806     document.addEventListener('DOMContentLoaded', init, false);
       
 19807 //#endif
       
 19808   } else {
       
 19809     // DOM is not found
       
 19810     this.Processing = Processing;
       
 19811   }
       
 19812 }(window, window.document, Math));