Source: processing.js

var nop = function () { };

/**
 * 
 */
var debug = (function () {
    if ("console" in window) {
        return function (msg) {
            window.console.log('Processing.js: ' + msg);
        };
    }
    return nop();
}());

/** Browsers fixes end */

/**
 * NOTE: in releases we replace symbolic PConstants.* names with their values.
 * Using PConstants.* in code below is fine.  See tools/rewrite-pconstants.js.
 */
var PConstants = {
    // NOTE(jeresig): Disable some constants as they were confusing users.
    //X: 0,
    //Y: 1,
    //Z: 2,

    //R: 3,
    //G: 4,
    //B: 5,
    //A: 6,

    //U: 7,
    //V: 8,

    NX: 9,
    NY: 10,
    NZ: 11,

    EDGE: 12,

    // Stroke
    SR: 13,
    SG: 14,
    SB: 15,
    SA: 16,

    SW: 17,

    // Transformations (2D and 3D)
    TX: 18,
    TY: 19,
    TZ: 20,

    VX: 21,
    VY: 22,
    VZ: 23,
    VW: 24,

    // Material properties
    AR: 25,
    AG: 26,
    AB: 27,

    DR: 3,
    DG: 4,
    DB: 5,
    DA: 6,

    SPR: 28,
    SPG: 29,
    SPB: 30,

    SHINE: 31,

    ER: 32,
    EG: 33,
    EB: 34,

    BEEN_LIT: 35,

    VERTEX_FIELD_COUNT: 36,

    // Renderers
    P2D: 1,
    JAVA2D: 1,
    WEBGL: 2,
    P3D: 2,
    OPENGL: 2,
    PDF: 0,
    DXF: 0,

    // Platform IDs
    OTHER: 0,
    WINDOWS: 1,
    MAXOSX: 2,
    LINUX: 3,

    EPSILON: 0.0001,

    MAX_FLOAT: 3.4028235e+38,
    MIN_FLOAT: -3.4028235e+38,
    MAX_INT: 2147483647,
    MIN_INT: -2147483648,

    PI: Math.PI,
    TWO_PI: 2 * Math.PI,
    HALF_PI: Math.PI / 2,
    THIRD_PI: Math.PI / 3,
    QUARTER_PI: Math.PI / 4,
    TAU: 2 * Math.PI,

    DEG_TO_RAD: Math.PI / 180,
    RAD_TO_DEG: 180 / Math.PI,

    WHITESPACE: " \t\n\r\f\u00A0",

    // Color modes
    RGB: 1,
    ARGB: 2,
    HSB: 3,
    ALPHA: 4,
    CMYK: 5,

    // Image file types
    TIFF: 0,
    TARGA: 1,
    JPEG: 2,
    GIF: 3,

    // Filter/convert types
    BLUR: 11,
    GRAY: 12,
    INVERT: 13,
    OPAQUE: 14,
    POSTERIZE: 15,
    THRESHOLD: 16,
    ERODE: 17,
    DILATE: 18,

    // Blend modes
    REPLACE: 0,
    BLEND: 1 << 0,
    ADD: 1 << 1,
    SUBTRACT: 1 << 2,
    LIGHTEST: 1 << 3,
    DARKEST: 1 << 4,
    DIFFERENCE: 1 << 5,
    EXCLUSION: 1 << 6,
    MULTIPLY: 1 << 7,
    SCREEN: 1 << 8,
    OVERLAY: 1 << 9,
    HARD_LIGHT: 1 << 10,
    SOFT_LIGHT: 1 << 11,
    DODGE: 1 << 12,
    BURN: 1 << 13,

    // Color component bit masks
    ALPHA_MASK: 0xff000000,
    RED_MASK: 0x00ff0000,
    GREEN_MASK: 0x0000ff00,
    BLUE_MASK: 0x000000ff,

    // Projection matrices
    CUSTOM: 0,
    ORTHOGRAPHIC: 2,
    PERSPECTIVE: 3,

    // Shapes
    POINT: 2,
    POINTS: 2,
    LINE: 4,
    LINES: 4,
    TRIANGLE: 8,
    TRIANGLES: 9,
    TRIANGLE_STRIP: 10,
    TRIANGLE_FAN: 11,
    QUAD: 16,
    QUADS: 16,
    QUAD_STRIP: 17,
    POLYGON: 20,
    PATH: 21,
    RECT: 30,
    ELLIPSE: 31,
    ARC: 32,
    SPHERE: 40,
    BOX: 41,

    GROUP: 0,
    PRIMITIVE: 1,
    //PATH:         21, // shared with Shape PATH
    GEOMETRY: 3,

    // Shape Vertex
    VERTEX: 0,
    BEZIER_VERTEX: 1,
    CURVE_VERTEX: 2,
    BREAK: 3,
    CLOSESHAPE: 4,

    // Shape closing modes
    OPEN: 1,
    CLOSE: 2,

    // Shape drawing modes
    CORNER: 0, // Draw mode convention to use (x, y) to (width, height)
    CORNERS: 1, // Draw mode convention to use (x1, y1) to (x2, y2) coordinates
    RADIUS: 2, // Draw mode from the center, and using the radius
    CENTER_RADIUS: 2, // Deprecated! Use RADIUS instead
    CENTER: 3, // Draw from the center, using second pair of values as the diameter
    DIAMETER: 3, // Synonym for the CENTER constant. Draw from the center
    CENTER_DIAMETER: 3, // Deprecated! Use DIAMETER instead

    // Text vertical alignment modes
    BASELINE: 0,   // Default vertical alignment for text placement
    TOP: 101, // Align text to the top
    BOTTOM: 102, // Align text from the bottom, using the baseline

    // UV Texture coordinate modes
    NORMAL: 1,
    NORMALIZED: 1,
    IMAGE: 2,

    // Text placement modes
    MODEL: 4,
    SHAPE: 5,

    // Stroke modes
    SQUARE: 'butt',
    ROUND: 'round',
    PROJECT: 'square',
    MITER: 'miter',
    BEVEL: 'bevel',

    // Lighting modes
    AMBIENT: 0,
    DIRECTIONAL: 1,
    //POINT:     2, Shared with Shape constant
    SPOT: 3,

    // Key constants

    // Both key and keyCode will be equal to these values
    BACKSPACE: 8,
    TAB: 9,
    ENTER: 10,
    RETURN: 13,
    ESC: 27,
    DELETE: 127,
    CODED: 0xffff,

    // key will be CODED and keyCode will be this value
    SHIFT: 16,
    CONTROL: 17,
    ALT: 18,
    CAPSLK: 20,
    PGUP: 33,
    PGDN: 34,
    END: 35,
    HOME: 36,
    LEFT: 37,
    UP: 38,
    RIGHT: 39,
    DOWN: 40,
    F1: 112,
    F2: 113,
    F3: 114,
    F4: 115,
    F5: 116,
    F6: 117,
    F7: 118,
    F8: 119,
    F9: 120,
    F10: 121,
    F11: 122,
    F12: 123,
    NUMLK: 144,
    META: 157,
    INSERT: 155,

    // Cursor types
    ARROW: 'default',
    CROSS: 'crosshair',
    HAND: 'pointer',
    MOVE: 'move',
    TEXT: 'text',
    WAIT: 'wait',
    NOCURSOR: "url('data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=='), auto",

    // Hints
    DISABLE_OPENGL_2X_SMOOTH: 1,
    ENABLE_OPENGL_2X_SMOOTH: -1,
    ENABLE_OPENGL_4X_SMOOTH: 2,
    ENABLE_NATIVE_FONTS: 3,
    DISABLE_DEPTH_TEST: 4,
    ENABLE_DEPTH_TEST: -4,
    ENABLE_DEPTH_SORT: 5,
    DISABLE_DEPTH_SORT: -5,
    DISABLE_OPENGL_ERROR_REPORT: 6,
    ENABLE_OPENGL_ERROR_REPORT: -6,
    ENABLE_ACCURATE_TEXTURES: 7,
    DISABLE_ACCURATE_TEXTURES: -7,
    HINT_COUNT: 10,

    // PJS defined constants
    SINCOS_LENGTH: 720, // every half degree
    PRECISIONB: 15, // fixed point precision is limited to 15 bits!!
    PRECISIONF: 1 << 15,
    PREC_MAXVAL: (1 << 15) - 1,
    PREC_ALPHA_SHIFT: 24 - 15,
    PREC_RED_SHIFT: 16 - 15,
    NORMAL_MODE_AUTO: 0,
    NORMAL_MODE_SHAPE: 1,
    NORMAL_MODE_VERTEX: 2,
    MAX_LIGHTS: 8
};

/**
 * A ObjectIterator is an iterator wrapper for objects. If passed object contains
 * the iterator method, the object instance will be replaced by the result returned by
 * this method call. If passed object is an array, the ObjectIterator instance iterates
 * through its items.
 *
 * @param {Object} obj          The object to be iterated.
 */
var ObjectIterator = function (obj) {
    if (obj.iterator instanceof Function) {
        return obj.iterator();
    }
    if (obj instanceof Array) {
        // iterate through array items
        var index = -1;
        this.hasNext = function () {
            return ++index < obj.length;
        };
        this.next = function () {
            return obj[index];
        };
    } else {
        throw "Unable to iterate: " + obj;
    }
};

/**
 * An ArrayList stores a variable number of objects.
 *
 * @param {int} initialCapacity optional defines the initial capacity of the list, it's empty by default
 *
 * @returns {ArrayList} new ArrayList object
 */
var ArrayList = (function () {
    function Iterator(array) {
        var index = 0;
        this.hasNext = function () {
            return index < array.length;
        };

        this.next = function () {
            return array[index++];
        };

        this.remove = function () {
            array.splice(index, 1);
        };
    }

    function ArrayList() {
        var array;
        if (arguments.length === 0) {
            array = [];
        } else if (arguments.length > 0 && typeof arguments[0] !== 'number') {
            array = arguments[0].toArray();
        } else {
            array = [];
            array.length = 0 | arguments[0];
        }

        /**
         * ArrayList.get() Returns the element at the specified position in this list.
         *
         * @param {int} i index of element to return
         *
         * @returns {Object} the element at the specified position in this list.
         * 
         * @method ArrayList
         */
        this.get = function (i) {
            return array[i];
        };
        /**
         * ArrayList.contains() Returns true if this list contains the specified element.
         *
         * @param {Object} item element whose presence in this List is to be tested.
         *
         * @returns {boolean} true if the specified element is present; false otherwise.
         * 
         * @method ArrayList
         */
        this.contains = function (item) {
            return this.indexOf(item) > -1;
        };
        /**
        * ArrayList.indexOf() Returns the position this element takes in the list, or -1 if the element is not found.
        *
        * @param {Object} item element whose position in this List is to be tested.
        *
        * @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.
        * 
        * @method ArrayList
        */
        this.indexOf = function (item) {
            for (var i = 0, len = array.length; i < len; ++i) {
                if (virtEquals(item, array[i])) {
                    return i;
                }
            }
            return -1;
        };
        /**
          * ArrayList.add() Adds the specified element to this list.
          *
          * @param {int}    index  optional index at which the specified element is to be inserted
          * @param {Object} object element to be added to the list
          * 
          * @method ArrayList
          */
        this.add = function () {
            if (arguments.length === 1) {
                array.push(arguments[0]); // for add(Object)
            } else if (arguments.length === 2) {
                var arg0 = arguments[0];
                if (typeof arg0 === 'number') {
                    if (arg0 >= 0 && arg0 <= array.length) {
                        array.splice(arg0, 0, arguments[1]); // for add(i, Object)
                    } else {
                        throw (arg0 + " is not a valid index");
                    }
                } else {
                    throw (typeof arg0 + " is not a number");
                }
            } else {
                throw ("Please use the proper number of parameters.");
            }
        };
        /**
         * ArrayList.addAll(collection) appends all of the elements in the specified
         * Collection to the end of this list, in the order that they are returned by
         * the specified Collection's Iterator.
         *
         * When called as addAll(index, collection) the elements are inserted into
         * this list at the position indicated by index.
         *
         * @param {index} Optional; specifies the position the colletion should be inserted at
         * @param {collection} Any iterable object (ArrayList, HashMakeySet(), etc.)
         * @throws out of bounds error for negative index, or index greater than list size.
         * 
         * @method ArrayList
         */
        this.addAll = function (arg1, arg2) {
            // addAll(int, Collection)
            var it;
            if (typeof arg1 === "number") {
                if (arg1 < 0 || arg1 > array.length) {
                    throw ("Index out of bounds for addAll: " + arg1 + " greater or equal than " + array.length);
                }
                it = new ObjectIterator(arg2);
                while (it.hasNext()) {
                    array.splice(arg1++, 0, it.next());
                }
            }
            // addAll(Collection)
            else {
                it = new ObjectIterator(arg1);
                while (it.hasNext()) {
                    array.push(it.next());
                }
            }
        };
        /**
         * ArrayList.set() Replaces the element at the specified position in this list with the specified element.
         *
         * @param {int}    index  index of element to replace
         * @param {Object} object element to be stored at the specified position
         * 
         * @method ArrayList
         */
        this.set = function () {
            if (arguments.length === 2) {
                var arg0 = arguments[0];
                if (typeof arg0 === 'number') {
                    if (arg0 >= 0 && arg0 < array.length) {
                        array.splice(arg0, 1, arguments[1]);
                    } else {
                        throw (arg0 + " is not a valid index.");
                    }
                } else {
                    throw (typeof arg0 + " is not a number");
                }
            } else {
                throw ("Please use the proper number of parameters.");
            }
        };

        /**
         * ArrayList.size() Returns the number of elements in this list.
         *
         * @returns {int} the number of elements in this list
         * 
         * @method ArrayList
         */
        this.size = function () {
            return array.length;
        };

        /**
         * ArrayList.clear() Removes all of the elements from this list. The list will be empty after this call returns.
         * 
         * @method ArrayList
         */
        this.clear = function () {
            array.length = 0;
        };

        /**
         * ArrayList.remove() Removes an element either based on index, if the argument is a number, or
         * by equality check, if the argument is an object.
         *
         * @param {int|Object} item either the index of the element to be removed, or the element itself.
         *
         * @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.
         * 
         * @method ArrayList
         */
        this.remove = function (item) {
            if (typeof item === 'number') {
                return array.splice(item, 1)[0];
            }
            item = this.indexOf(item);
            if (item > -1) {
                array.splice(item, 1);
                return true;
            }
            return false;
        };

        /**
         * ArrayList.isEmpty() Tests if this list has no elements.
         *
         * @returns {boolean} true if this list has no elements; false otherwise
         * 
         * @method ArrayList
         */
        this.isEmpty = function () {
            return !array.length;
        };

        /**
         * ArrayList.clone() Returns a shallow copy of this ArrayList instance. (The elements themselves are not copied.)
         *
         * @returns {ArrayList} a clone of this ArrayList instance
         * @method ArrayList
         */
        this.clone = function () {
            return new ArrayList(this);
        };

        /**
         * ArrayList.toArray() Returns an array containing all of the elements in this list in the correct order.
         *
         * @returns {Object[]} Returns an array containing all of the elements in this list in the correct order
         * 
         * @method ArrayList
         */
        this.toArray = function () {
            return array.slice(0);
        };

        this.iterator = function () {
            return new Iterator(array);
        };
    }

    return ArrayList;
}());

/**
 * A HashMap stores a collection of objects, each referenced by a key. This is similar to an Array, only
 * instead of accessing elements with a numeric index, a String  is used. (If you are familiar with
 * associative arrays from other languages, this is the same idea.)
 *
 * @param {int} initialCapacity          defines the initial capacity of the map, it's 16 by default
 * @param {float} loadFactor             the load factor for the map, the default is 0.75
 * @param {Map} m                        gives the new HashMap the same mappings as this Map
 */
var HashMap = (function () {
    /**
    * A HashMap stores a collection of objects, each referenced by a key. This is similar to an Array, only
    * instead of accessing elements with a numeric index, a String  is used. (If you are familiar with
    * associative arrays from other languages, this is the same idea.)
    *
    * @param {int} initialCapacity          defines the initial capacity of the map, it's 16 by default
    * @param {float} loadFactor             the load factor for the map, the default is 0.75
    * @param {Map} m                        gives the new HashMap the same mappings as this Map
    * 
    * @method HashMap
    */
    function HashMap() {
        if (arguments.length === 1 && arguments[0] instanceof HashMap) {
            return arguments[0].clone();
        }

        var initialCapacity = arguments.length > 0 ? arguments[0] : 16;
        var loadFactor = arguments.length > 1 ? arguments[1] : 0.75;
        var buckets = [];
        buckets.length = initialCapacity;
        var count = 0;
        var hashMap = this;

        function getBucketIndex(key) {
            var index = virtHashCode(key) % buckets.length;
            return index < 0 ? buckets.length + index : index;
        }
        function ensureLoad() {
            if (count <= loadFactor * buckets.length) {
                return;
            }
            var allEntries = [];
            for (var i = 0; i < buckets.length; ++i) {
                if (buckets[i] !== undef) {
                    allEntries = allEntries.concat(buckets[i]);
                }
            }
            var newBucketsLength = buckets.length * 2;
            buckets = [];
            buckets.length = newBucketsLength;
            for (var j = 0; j < allEntries.length; ++j) {
                var index = getBucketIndex(allEntries[j].key);
                var bucket = buckets[index];
                if (bucket === undef) {
                    buckets[index] = bucket = [];
                }
                bucket.push(allEntries[j]);
            }
        }

        function Iterator(conversion, removeItem) {
            var bucketIndex = 0;
            var itemIndex = -1;
            var endOfBuckets = false;

            function findNext() {
                while (!endOfBuckets) {
                    ++itemIndex;
                    if (bucketIndex >= buckets.length) {
                        endOfBuckets = true;
                    } else if (buckets[bucketIndex] === undef || itemIndex >= buckets[bucketIndex].length) {
                        itemIndex = -1;
                        ++bucketIndex;
                    } else {
                        return;
                    }
                }
            }

            /**
            * Checks if the Iterator has more items
            * @method Iterator
            */
            this.hasNext = function () {
                return !endOfBuckets;
            };

            /**
            * Return the next Item
            * @method Iterator
            */
            this.next = function () {
                var result = conversion(buckets[bucketIndex][itemIndex]);
                findNext();
                return result;
            };

            /**
            * Remove the current item
            * @method Iterator
            */
            this.remove = function () {
                removeItem(this.next());
                --itemIndex;
            };

            findNext();
        }

        function Set(conversion, isIn, removeItem) {
            this.clear = function () {
                hashMaclear();
            };

            this.contains = function (o) {
                return isIn(o);
            };

            this.containsAll = function (o) {
                var it = o.iterator();
                while (it.hasNext()) {
                    if (!this.contains(it.next())) {
                        return false;
                    }
                }
                return true;
            };

            this.isEmpty = function () {
                return hashMaisEmpty();
            };

            this.iterator = function () {
                return new Iterator(conversion, removeItem);
            };

            this.remove = function (o) {
                if (this.contains(o)) {
                    removeItem(o);
                    return true;
                }
                return false;
            };

            this.removeAll = function (c) {
                var it = c.iterator();
                var changed = false;
                while (it.hasNext()) {
                    var item = it.next();
                    if (this.contains(item)) {
                        removeItem(item);
                        changed = true;
                    }
                }
                return true;
            };

            this.retainAll = function (c) {
                var it = this.iterator();
                var toRemove = [];
                while (it.hasNext()) {
                    var entry = it.next();
                    if (!c.contains(entry)) {
                        toRemove.push(entry);
                    }
                }
                for (var i = 0; i < toRemove.length; ++i) {
                    removeItem(toRemove[i]);
                }
                return toRemove.length > 0;
            };

            this.size = function () {
                return hashMasize();
            };

            this.toArray = function () {
                var result = [];
                var it = this.iterator();
                while (it.hasNext()) {
                    result.push(it.next());
                }
                return result;
            };
        }

        function Entry(pair) {
            this._isIn = function (map) {
                return map === hashMap && (pair.removed === undef);
            };

            this.equals = function (o) {
                return virtEquals(pair.key, o.getKey());
            };

            this.getKey = function () {
                return pair.key;
            };

            this.getValue = function () {
                return pair.value;
            };

            this.hashCode = function (o) {
                return virtHashCode(pair.key);
            };

            this.setValue = function (value) {
                var old = pair.value;
                pair.value = value;
                return old;
            };
        }

        this.clear = function () {
            count = 0;
            buckets = [];
            buckets.length = initialCapacity;
        };

        this.clone = function () {
            var map = new HashMap();
            maputAll(this);
            return map;
        };

        this.containsKey = function (key) {
            var index = getBucketIndex(key);
            var bucket = buckets[index];
            if (bucket === undef) {
                return false;
            }
            for (var i = 0; i < bucket.length; ++i) {
                if (virtEquals(bucket[i].key, key)) {
                    return true;
                }
            }
            return false;
        };

        this.containsValue = function (value) {
            for (var i = 0; i < buckets.length; ++i) {
                var bucket = buckets[i];
                if (bucket === undef) {
                    continue;
                }
                for (var j = 0; j < bucket.length; ++j) {
                    if (virtEquals(bucket[j].value, value)) {
                        return true;
                    }
                }
            }
            return false;
        };

        this.entrySet = function () {
            return new Set(

                function (pair) {
                    return new Entry(pair);
                },

                function (pair) {
                    return (pair instanceof Entry) && pair._isIn(hashMap);
                },

                function (pair) {
                    return hashMaremove(pair.getKey());
                });
        };

        this.get = function (key) {
            var index = getBucketIndex(key);
            var bucket = buckets[index];
            if (bucket === undef) {
                return null;
            }
            for (var i = 0; i < bucket.length; ++i) {
                if (virtEquals(bucket[i].key, key)) {
                    return bucket[i].value;
                }
            }
            return null;
        };

        this.isEmpty = function () {
            return count === 0;
        };

        this.keySet = function () {
            return new Set(
                // get key from pair
                function (pair) {
                    return pair.key;
                },
                // is-in test
                function (key) {
                    return hashMacontainsKey(key);
                },
                // remove from hashmap by key
                function (key) {
                    return hashMaremove(key);
                }
            );
        };

        this.values = function () {
            return new Set(
                // get value from pair
                function (pair) {
                    return pair.value;
                },
                // is-in test
                function (value) {
                    return hashMacontainsValue(value);
                },
                // remove from hashmap by value
                function (value) {
                    return hashMaremoveByValue(value);
                }
            );
        };

        this.put = function (key, value) {
            var index = getBucketIndex(key);
            var bucket = buckets[index];
            if (bucket === undef) {
                ++count;
                buckets[index] = [{
                    key: key,
                    value: value
                }];
                ensureLoad();
                return null;
            }
            for (var i = 0; i < bucket.length; ++i) {
                if (virtEquals(bucket[i].key, key)) {
                    var previous = bucket[i].value;
                    bucket[i].value = value;
                    return previous;
                }
            }
            ++count;
            bucket.push({
                key: key,
                value: value
            });
            ensureLoad();
            return null;
        };

        this.putAll = function (m) {
            var it = m.entrySet().iterator();
            while (it.hasNext()) {
                var entry = it.next();
                this.put(entry.getKey(), entry.getValue());
            }
        };

        this.remove = function (key) {
            var index = getBucketIndex(key);
            var bucket = buckets[index];
            if (bucket === undef) {
                return null;
            }
            for (var i = 0; i < bucket.length; ++i) {
                if (virtEquals(bucket[i].key, key)) {
                    --count;
                    var previous = bucket[i].value;
                    bucket[i].removed = true;
                    if (bucket.length > 1) {
                        bucket.splice(i, 1);
                    } else {
                        buckets[index] = undef;
                    }
                    return previous;
                }
            }
            return null;
        };

        this.removeByValue = function (value) {
            var bucket, i, ilen, pair;
            for (bucket in buckets) {
                if (buckets.hasOwnProperty(bucket)) {
                    for (i = 0, ilen = buckets[bucket].length; i < ilen; i++) {
                        pair = buckets[bucket][i];
                        // removal on values is based on identity, not equality
                        if (pair.value === value) {
                            buckets[bucket].splice(i, 1);
                            return true;
                        }
                    }
                }
            }
            return false;
        };

        this.size = function () {
            return count;
        };
    }

    return HashMap;
}());

////////////////////////////////////////////////////////////////////////////
// PFONT.JS START
////////////////////////////////////////////////////////////////////////////

/**
 * Defines system (non-SVG) font.
 * 
 * @param {*} name 
 * @param {*} size 
 */
function PFont(name, size) {
    // according to the P5 API, new PFont() is legal (albeit completely useless)
    if (name === undef) {
        name = "";
    }
    this.name = name;
    if (size === undef) {
        size = 0;
    }
    this.size = size;
    this.glyph = false;
    this.ascent = 0;
    this.descent = 0;
    // For leading, the "safe" value uses the standard TEX ratio
    this.leading = 1.2 * size;

    // Note that an italic, bold font must used "... Bold Italic"
    // in P5. "... Italic Bold" is treated as normal/normal.
    var illegalIndicator = name.indexOf(" Italic Bold");
    if (illegalIndicator !== -1) {
        name = name.substring(0, illegalIndicator);
    }

    // determine font style
    this.style = "normal";
    var italicsIndicator = name.indexOf(" Italic");
    if (italicsIndicator !== -1) {
        name = name.substring(0, italicsIndicator);
        this.style = "italic";
    }

    // determine font weight
    this.weight = "normal";
    var boldIndicator = name.indexOf(" Bold");
    if (boldIndicator !== -1) {
        name = name.substring(0, boldIndicator);
        this.weight = "bold";
    }

    // determine font-family name
    this.family = "sans-serif";
    if (name !== undef) {
        switch (name) {
            case "sans-serif":
            case "serif":
            case "monospace":
            case "fantasy":
            case "cursive":
                this.family = name;
                break;
            default:
                this.family = '"' + name + '", sans-serif';
                break;
        }
    }
    // Calculate the ascent/descent/leading value based on
    // how the browser renders this font.
    this.context2d = null;
    computeFontMetrics(this);
    this.css = this.getCSSDefinition();
    this.context2d.font = this.css;
}

/**
 * This function generates the CSS "font" string for this PFont
 * 
 * @param {*} fontSize 
 * @param {*} lineHeight 
 * @returns 
 */
PFont.prototype.getCSSDefinition = function (fontSize, lineHeight) {
    if (fontSize === undef) {
        fontSize = this.size + "px";
    }
    if (lineHeight === undef) {
        lineHeight = this.leading + "px";
    }
    // CSS "font" definition: font-style font-variant font-weight font-size/line-height font-family
    var components = [this.style, "normal", this.weight, fontSize + "/" + lineHeight, this.family];
    return components.join(" ");
};

/**
 * We cannot rely on there being a 2d context available,
 * because we support OPENGL sketches, and canvas3d has
 * no "measureText" function in the API.
 * 
 * @param {*} string 
 * @returns 
 */
PFont.prototype.measureTextWidth = function (string) {
    return this.context2d.measureText(string).width;
};

/**
 * Global "loaded fonts" list, internal to PFont
 */
PFont.PFontCache = new LRUCache(100);

/**
 * This function acts as single access point for getting and caching
 * fonts across all sketches handled by an instance of Processing.js
 * 
 * @param {*} fontName 
 * @param {*} fontSize 
 * @returns 
 */
PFont.get = function (fontName, fontSize) {
    var cache = PFont.PFontCache;
    var idx = fontName + "/" + fontSize;
    var val = cache.get(idx);
    if (!val) {
        val = new PFont(fontName, fontSize);
        cache.set(idx, val);
    }
    return val;
};

/**
 * Lists all standard fonts. Due to browser limitations, this list is
 * not the system font list, like in P5, but the CSS "genre" list.
 */
PFont.list = function () {
    return ["sans-serif", "serif", "monospace", "fantasy", "cursive"];
};

/**
 * Loading external fonts through @font-face rules is handled by PFont,
 * to ensure fonts loaded in this way are globally available.
 */
PFont.preloading = {
    // template element used to compare font sizes
    template: {},
    // indicates whether or not the reference tiny font has been loaded
    initialized: false,
    // load the reference tiny font via a css @font-face rule
    initialize: function () {
        var generateTinyFont = function () {
            var encoded = "#E3KAI2wAgT1MvMg7Eo3VmNtYX7ABi3CxnbHlm" +
                "7Abw3kaGVhZ7ACs3OGhoZWE7A53CRobXR47AY3" +
                "AGbG9jYQ7G03Bm1heH7ABC3CBuYW1l7Ae3AgcG" +
                "9zd7AI3AE#B3AQ2kgTY18PPPUACwAg3ALSRoo3" +
                "#yld0xg32QAB77#E777773B#E3C#I#Q77773E#" +
                "Q7777777772CMAIw7AB77732B#M#Q3wAB#g3B#" +
                "E#E2BB//82BB////w#B7#gAEg3E77x2B32B#E#" +
                "Q#MTcBAQ32gAe#M#QQJ#E32M#QQJ#I#g32Q77#";
            var expand = function (input) {
                return "AAAAAAAA".substr(~~input ? 7 - input : 6);
            };
            return encoded.replace(/[#237]/g, expand);
        };
        var fontface = document.createElement("style");
        fontface.setAttribute("type", "text/css");
        fontface.innerHTML = "@font-face {\n" +
            '  font-family: "PjsEmptyFont";' + "\n" +
            "  src: url('data:application/x-font-ttf;base64," + generateTinyFont() + "')\n" +
            "       format('truetype');\n" +
            "}";
        document.head.appendChild(fontface);

        // set up the template element
        var element = document.createElement("span");
        element.style.cssText = 'position: absolute; top: 0; left: 0; opacity: 0; font-family: "PjsEmptyFont", fantasy; pointer-events: none;';
        element.innerHTML = "AAAAAAAA";
        document.body.appendChild(element);
        this.template = element;

        this.initialized = true;
    },
    // Shorthand function to get the computed width for an element.
    getElementWidth: function (element) {
        return document.defaultView.getComputedStyle(element, "").getPropertyValue("width");
    },
    // time taken so far in attempting to load a font
    timeAttempted: 0,
    // returns false if no fonts are pending load, or true otherwise.
    pending: function (intervallength) {
        if (!this.initialized) {
            this.initialize();
        }
        var element,
            computedWidthFont,
            computedWidthRef = this.getElementWidth(this.template);
        for (var i = 0; i < this.fontList.length; i++) {
            // compares size of text in pixels. if equal, custom font is not yet loaded
            element = this.fontList[i];
            computedWidthFont = this.getElementWidth(element);
            if (this.timeAttempted < 4000 && computedWidthFont === computedWidthRef) {
                this.timeAttempted += intervallength;
                return true;
            } else {
                document.body.removeChild(element);
                this.fontList.splice(i--, 1);
                this.timeAttempted = 0;
            }
        }
        // if there are no more fonts to load, pending is false
        if (this.fontList.length === 0) {
            return false;
        }
        // We should have already returned before getting here.
        // But, if we do get here, length!=0 so fonts are pending.
        return true;
    },
    // fontList contains elements to compare font sizes against a template
    fontList: [],
    // addedList contains the fontnames of all the fonts loaded via @font-face
    addedList: {},
    // adds a font to the font cache
    // creates an element using the font, to start loading the font,
    // and compare against a default font to see if the custom font is loaded
    add: function (fontSrc) {
        if (!this.initialized) {
            this.initialize();
        }
        // fontSrc can be a string or a javascript object
        // acceptable fonts are .ttf, .otf, and data uri
        var fontName = (typeof fontSrc === 'object' ? fontSrc.fontFace : fontSrc),
            fontUrl = (typeof fontSrc === 'object' ? fontSrc.url : fontSrc);

        // check whether we already created the @font-face rule for this font
        if (this.addedList[fontName]) {
            return;
        }

        // if we didn't, create the @font-face rule
        var style = document.createElement("style");
        style.setAttribute("type", "text/css");
        style.innerHTML = "@font-face{\n  font-family: '" + fontName + "';\n  src:  url('" + fontUrl + "');\n}\n";
        document.head.appendChild(style);
        this.addedList[fontName] = true;

        // also create the element to load and compare the new font
        var element = document.createElement("span");
        element.style.cssText = "position: absolute; top: 0; left: 0; opacity: 0; pointer-events: none;";
        element.style.fontFamily = '"' + fontName + '", "PjsEmptyFont", fantasy';
        element.innerHTML = "AAAAAAAA";
        document.body.appendChild(element);
        this.fontList.push(element);
    }
};

////////////////////////////////////////////////////////////////////////////
// PFONT.JS END
////////////////////////////////////////////////////////////////////////////

/**
 * PJS specific (non-p5) methods and properties to externalize
 */
externals = {
    canvas: curElement,
    context: undef,
    sketch: undef
};

/**
 * Set Processing defaults / environment variables
 */
name = 'Processing.js Instance';
/**
 * Default '2d' canvas context
 */
use3DContext = false;

/**
 * Confirms if a Processing program is "focused", meaning that it is
 * active and will accept input from mouse or keyboard. This variable
 * is "true" if it is focused and "false" if not. This variable is
 * often used when you want to warn people they need to click on the
 * browser before it will work.
 */
focused = false;
breakShape = false;

/**
 * Glyph path storage for textFonts
 */
glyphTable = {};

// Global vars for tracking mouse position
/**
 * Previous mouseX
 */
pmouseX = 0;

/**
 * Previous mouseY
 */
pmouseY = 0;

/**
 * Mouse coordinate
 */
mouseX = 0;

/**
 * Mouse coordinate
 */
mouseY = 0;

/**
 * Mouse button pressed
 */
mouseButton = 0;

/**
 * Mouse scrolled
 */
mouseScroll = 0;

// Undefined event handlers to be replaced by user when needed
/**
 * Event handler.
 */
mouseClicked = undef;
/**
 * Event handler.
 */
mouseDragged = undef;
/**
 * Event handler.
 */
mouseMoved = undef;
/**
 * Event handler.
 */
mousePressed = undef;
/**
 * Event handler.
 */
mouseReleased = undef;
/**
 * Event handler.
 */
mouseScrolled = undef;
/**
 * Event handler.
 */
mouseOver = undef;
/**
 * Event handler.
 */
mouseOut = undef;
/**
 * Event handler.
 */
touchStart = undef;
/**
 * Event handler.
 */
touchEnd = undef;
/**
 * Event handler.
 */
touchMove = undef;
/**
 * Event handler.
 */
touchCancel = undef;
/**
 * Event handler.
 */
key = undef;
/**
 * Event handler.
 */
keyCode = undef;
/**
 * Event handler.
 */
keyPressed = nop; // needed to remove function checks
/**
 * Event handler.
 */
keyReleased = nop;
/**
 * Event handler.
 */
keyTyped = nop;
/**
 * Draw loop.
 */
draw = undef;
/**
 * Setup.
 */
setup = undef;

/**
 * Remapped variables.
 */
__mousePressed = false;

/**
 * Remapped variables.
 */
__keyPressed = false;

/**
 * Remapped variables.
 */
__frameRate = 60;

// XXX(jeresig): Added mouseIsPressed/keyIsPressed
/**
 * Mouse is pressed.
 */
mouseIsPressed = false;

/**
 * Key is pressed.
 */
keyIsPressed = false;

// The current animation frame
frameCount = 0;

// The height/width of the canvas
/**
 * The width of the canvas.
 */
width = 100;
/**
 * The height of the canvas.
 */
height = 100;

// XXX(jeresig)
angleMode = "radians";

/**
 * PVector
 */
PVector = (function () {
    /**
     * @param {*} x 
     * @param {*} y 
     * @param {*} z 
     * 
     * @method PVector
     */
    function PVector(x, y, z) {
        this.x = x || 0;
        this.y = y || 0;
        this.z = z || 0;
    }

    /**
     * @param {*} angle 
     * @param {*} v 
     * @returns 
     * 
     * @method PVector
     */
    PVector.fromAngle = function (angle, v) {
        if (v === undef || v === null) {
            v = new PVector();
        }
        // XXX(jeresig)
        v.x = cos(angle);
        v.y = sin(angle);
        return v;
    };

    /**
     * @param {*} v 
     * @returns 
     * 
     * @method PVector
     */
    PVector.random2D = function (v) {
        return PVector.fromAngle(Math.random() * 360, v);
    };

    /**
     * @param {*} v 
     * @returns 
     * 
     * @method PVector
     */
    PVector.random3D = function (v) {
        var angle = Math.random() * 360;
        var vz = Math.random() * 2 - 1;
        var mult = Math.sqrt(1 - vz * vz);
        // XXX(jeresig)
        var vx = mult * cos(angle);
        var vy = mult * sin(angle);
        if (v === undef || v === null) {
            v = new PVector(vx, vy, vz);
        } else {
            v.set(vx, vy, vz);
        }
        return v;
    };

    /**
     * @param {*} v1 
     * @param {*} v2 
     * @returns 
     * 
     * @method PVector
     */
    PVector.dist = function (v1, v2) {
        return v1.dist(v2);
    };

    /**
     * @param {*} v1 
     * @param {*} v2 
     * @returns 
     * 
     * @method PVector
     */
    PVector.dot = function (v1, v2) {
        return v1.dot(v2);
    };

    /**
     * @param {*} v1 
     * @param {*} v2 
     * @returns 
     * 
     * @method PVector
     */
    PVector.cross = function (v1, v2) {
        return v1.cross(v2);
    };

    /**
     * @param {*} v1 
     * @param {*} v2 
     * @returns 
     * 
     * @method PVector
     */
    PVector.sub = function (v1, v2) {
        return new PVector(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z);
    };

    /**
     * @param {*} v1 
     * @param {*} v2 
     * @returns 
     * 
     * @method PVector
     */
    PVector.angleBetween = function (v1, v2) {
        // XXX(jeresig)
        return acos(v1.dot(v2) / (v1.mag() * v2.mag()));
    };

    /**
     * @param {*} v1 
     * @param {*} v2 
     * @param {*} amt 
     * @returns 
     * 
     * @method PVector
     */
    PVector.lerp = function (v1, v2, amt) {
        // non-static lerp mutates object, but this version returns a new vector
        var retval = new PVector(v1.x, v1.y, v1.z);
        retval.lerp(v2, amt);
        return retval;
    };

    // Common vector operations for PVector
    PVector.prototype = {
        /**
         * @param {*} v 
         * @param {*} y 
         * @param {*} z 
         * 
         * @method PVector
         */
        set: function (v, y, z) {
            if (arguments.length === 1) {
                this.set(v.x || v[0] || 0,
                    v.y || v[1] || 0,
                    v.z || v[2] || 0);
            } else {
                this.x = v || 0;
                this.y = y || 0;
                this.z = z || 0;
            }
        },
        /**
         * @method PVector
         */
        get: function () {
            return new PVector(this.x, this.y, this.z);
        },
        /**
         * @method PVector
         */
        mag: function () {
            var x = this.x,
                y = this.y,
                z = this.z;
            return Math.sqrt(x * x + y * y + z * z);
        },
        /**
         * @method PVector
         */
        magSq: function () {
            var x = this.x,
                y = this.y,
                z = this.z;
            return (x * x + y * y + z * z);
        },
        /**
         * @param {*} v_or_len 
         * @param {*} len 
         * @returns 
         * 
         * @method PVector
         */
        setMag: function (v_or_len, len) {
            if (len === undef) {
                len = v_or_len;
                this.normalize();
                this.mult(len);
            } else {
                var v = v_or_len;
                v.normalize();
                v.mult(len);
                return v;
            }
        },
        /**
         * @param {*} v 
         * @param {*} y 
         * @param {*} z 
         * 
         * @method PVector
         */
        add: function (v, y, z) {
            if (arguments.length === 1) {
                this.x += v.x;
                this.y += v.y;
                this.z += v.z;
            } else {
                this.x += v;
                this.y += y;
                this.z += z;
            }
        },
        /**
         * @param {*} v 
         * @param {*} y 
         * @param {*} z 
         * 
         * @method PVector
         */
        sub: function (v, y, z) {
            if (arguments.length === 1) {
                this.x -= v.x;
                this.y -= v.y;
                this.z -= v.z;
            } else {
                this.x -= v;
                this.y -= y;
                this.z -= z;
            }
        },
        /**
         * @param {*} v 
         * 
         * @method PVector
         */
        mult: function (v) {
            if (typeof v === 'number') {
                this.x *= v;
                this.y *= v;
                this.z *= v;
            } else {
                this.x *= v.x;
                this.y *= v.y;
                this.z *= v.z;
            }
        },
        /**
         * @param {*} v 
         * 
         * @method PVector
         */
        div: function (v) {
            if (typeof v === 'number') {
                this.x /= v;
                this.y /= v;
                this.z /= v;
            } else {
                this.x /= v.x;
                this.y /= v.y;
                this.z /= v.z;
            }
        },
        /**
         * @param {*} angle 
         * 
         * @method PVector
         */
        rotate: function (angle) {
            var prev_x = this.x;
            var c = cos(angle);
            var s = sin(angle);
            this.x = c * this.x - s * this.y;
            this.y = s * prev_x + c * this.y;
        },
        /**
         * @param {*} v 
         * 
         * @method PVector
         */
        dist: function (v) {
            var dx = this.x - v.x,
                dy = this.y - v.y,
                dz = this.z - v.z;
            return Math.sqrt(dx * dx + dy * dy + dz * dz);
        },
        /**
         * @param {*} v 
         * @param {*} y 
         * @param {*} z 
         * @returns 
         * 
         * @method PVector
         */
        dot: function (v, y, z) {
            if (arguments.length === 1) {
                return (this.x * v.x + this.y * v.y + this.z * v.z);
            }
            return (this.x * v + this.y * y + this.z * z);
        },
        /**
         * @param {*} v 
         * 
         * @method PVector
         */
        cross: function (v) {
            var x = this.x,
                y = this.y,
                z = this.z;
            return new PVector(y * v.z - v.y * z,
                z * v.x - v.z * x,
                x * v.y - v.x * y);
        },
        /**
         * @param {*} v_or_x 
         * @param {*} amt_or_y 
         * @param {*} z 
         * @param {*} amt 
         * 
         * @method PVector
         */
        lerp: function (v_or_x, amt_or_y, z, amt) {
            var lerp_val = function (start, stop, amt) {
                return start + (stop - start) * amt;
            };
            var x, y;
            if (arguments.length === 2) {
                // given vector and amt
                amt = amt_or_y;
                x = v_or_x.x;
                y = v_or_x.y;
                z = v_or_x.z;
            } else {
                // given x, y, z and amt
                x = v_or_x;
                y = amt_or_y;
            }
            this.x = lerp_val(this.x, x, amt);
            this.y = lerp_val(this.y, y, amt);
            this.z = lerp_val(this.z, z, amt);
        },
        /**
         * @method PVector
         */
        normalize: function () {
            var m = this.mag();
            if (m > 0) {
                this.div(m);
            }
        },
        /**
         * @param {*} high 
         * 
         * @method PVector
         */
        limit: function (high) {
            if (this.mag() > high) {
                this.normalize();
                this.mult(high);
            }
        },
        /**
         * @method PVector
         */
        heading: function () {
            // XXX(jeresig)
            return -atan2(-this.y, this.x);
        },
        /**
         * @method PVector
         */
        heading2D: function () {
            return this.heading();
        },
        /**
         * @method PVector
         */
        toString: function () {
            return "[" + this.x + ", " + this.y + ", " + this.z + "]";
        },
        /**
         * @method PVector
         */
        array: function () {
            return [this.x, this.y, this.z];
        }
    };

    function createPVectorMethod(method) {
        return function (v1, v2) {
            var v = v1.get();
            v[method](v2);
            return v;
        };
    }

    // Create the static methods of PVector automatically
    // We don't do toString because it causes a TypeError
    //  when attempting to stringify PVector
    for (var method in PVector.prototype) {
        if (PVector.prototype.hasOwnProperty(method) && !PVector.hasOwnProperty(method) &&
            method !== "toString") {
            PVector[method] = createPVectorMethod(method);
        }
    }

    return PVector;
}());

////////////////////////////////////////////////////////////////////////////
// Char handling
////////////////////////////////////////////////////////////////////////////

/**
 * @param {*} chr 
 * @returns 
 */
Character = function (chr) {
    if (typeof chr === 'string' && chr.length === 1) {
        this.code = chr.charCodeAt(0);
    } else if (typeof chr === 'number') {
        this.code = chr;
    } else if (chr instanceof Char) {
        this.code = chr;
    } else {
        this.code = NaN;
    }

    return (charMap[this.code] === undef) ? charMap[this.code] = this : charMap[this.code];
};

/**
 * @method Character
 */
Character.prototype.toString = function () {
    return String.fromCharCode(this.code);
};

/**
 * @method Character
 */
Character.prototype.valueOf = function () {
    return this.code;
};

/**
 * Datatype for storing shapes. Processing can currently load and display SVG (Scalable Vector Graphics) shapes.
 * 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.
 * The <b>PShape</b> object contain a group of methods, linked below, that can operate on the shape data.
 * <br><br>The <b>loadShape()</b> method supports SVG files created with Inkscape and Adobe Illustrator.
 * It is not a full SVG implementation, but offers some straightforward support for handling vector data.
 *
 * @param {int} family the shape type, one of GROUP, PRIMITIVE, PATH, or GEOMETRY
 *
 * @see #shape()
 * @see #loadShape()
 * @see #shapeMode()
 */
PShape = function (family) {
    this.family = family || PConstants.GROUP;
    this.visible = true;
    this.style = true;
    this.children = [];
    this.nameTable = [];
    this.params = [];
    this.name = "";
    this.image = null;  //type PImage
    this.matrix = null;
    this.kind = null;
    this.close = null;
    this.width = null;
    this.height = null;
    this.parent = null;
};
/**
  * PShape methods
  * missing: findChild(), apply(), contains(), findChild(), getPrimitive(), getParams(), getVertex() , getVertexCount(),
  * getVertexCode() , getVertexCodes() , getVertexCodeCount(), getVertexX(), getVertexY(), getVertexZ()
  */
PShape.prototype = {
    /**
     * 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.
     * <br><br>The visibility of a shape is usually controlled by whatever program created the SVG file.
     * For instance, this parameter is controlled by showing or hiding the shape in the layers palette in Adobe Illustrator.
     *
     * @return {boolean}  returns "true" if the image is set to be visible, "false" if not
     * 
     * @method PShape
     */
    isVisible: function () {
        return this.visible;
    },
    /**
     * The setVisible() function sets the shape to be visible or invisible. This is determined by the value of the <b>visible</b> parameter.
     * <br><br>The visibility of a shape is usually controlled by whatever program created the SVG file.
     * For instance, this parameter is controlled by showing or hiding the shape in the layers palette in Adobe Illustrator.
     *
     * @param {boolean} visible "false" makes the shape invisible and "true" makes it visible
     * 
     * @method PShape
     */
    setVisible: function (visible) {
        this.visible = visible;
    },
    /**
     * 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.
     * Overrides this shape's style information and uses PGraphics styles and colors. Identical to ignoreStyles(true). Also disables styles for all child shapes.
     * 
     * @method PShape
     */
    disableStyle: function () {
        this.style = false;
        for (var i = 0, j = this.children.length; i < j; i++) {
            this.children[i].disableStyle();
        }
    },
    /**
     * 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.
     * 
     * @method PShape
     */
    enableStyle: function () {
        this.style = true;
        for (var i = 0, j = this.children.length; i < j; i++) {
            this.children[i].enableStyle();
        }
    },
    /**
     * The getFamily function returns the shape type
     *
     * @return {int} the shape type, one of GROUP, PRIMITIVE, PATH, or GEOMETRY
     * 
     * @method PShape
     */
    getFamily: function () {
        return this.family;
    },
    /**
     * The getWidth() function gets the width of the drawing area (not necessarily the shape boundary).
     * 
     * @method PShape
     */
    getWidth: function () {
        return this.width;
    },
    /**
     * The getHeight() function gets the height of the drawing area (not necessarily the shape boundary).
     * 
     * @method PShape
     */
    getHeight: function () {
        return this.height;
    },
    /**
     * The setName() function sets the name of the shape
     * 
     * 
     * @param {String} name the name of the shape
     * 
     * @method PShape
     */
    setName: function (name) {
        this.name = name;
    },
    /**
     * The getName() function returns the name of the shape
     *
     * @return {String} the name of the shape
     * 
     * @method PShape
     */
    getName: function () {
        return this.name;
    },
    /**
     * Called by the following (the shape() command adds the g)
     * PShape s = loadShapes("blah.svg");
     * shape(s);
     * 
     * @method PShape
     */
    draw: function () {
        if (this.visible) {
            this.pre();
            this.drawImpl();
            this.post();
        }
    },
    /**
     * the drawImpl() function draws the SVG document.
     * 
     * @method PShape
     */
    drawImpl: function () {
        if (this.family === PConstants.GROUP) {
            this.drawGroup();
        } else if (this.family === PConstants.PRIMITIVE) {
            this.drawPrimitive();
        } else if (this.family === PConstants.GEOMETRY) {
            this.drawGeometry();
        } else if (this.family === PConstants.PATH) {
            this.drawPath();
        }
    },
    /**
     * The drawPath() function draws the <path> part of the SVG document.
     * 
     * @method PShape
     */
    drawPath: function () {
        var i, j;
        if (this.vertices.length === 0) { return; }
        beginShape();
        if (this.vertexCodes.length === 0) {  // each point is a simple vertex
            if (this.vertices[0].length === 2) {  // drawing 2D vertices
                for (i = 0, j = this.vertices.length; i < j; i++) {
                    vertex(this.vertices[i][0], this.vertices[i][1]);
                }
            } else {  // drawing 3D vertices
                for (i = 0, j = this.vertices.length; i < j; i++) {
                    vertex(this.vertices[i][0],
                        this.vertices[i][1],
                        this.vertices[i][2]);
                }
            }
        } else {  // coded set of vertices
            var index = 0;
            if (this.vertices[0].length === 2) {  // drawing a 2D path
                for (i = 0, j = this.vertexCodes.length; i < j; i++) {
                    if (this.vertexCodes[i] === PConstants.VERTEX) {
                        vertex(this.vertices[index][0], this.vertices[index][1]);
                        if (this.vertices[index]["moveTo"] === true) {
                            vertArray[vertArray.length - 1]["moveTo"] = true;
                        } else if (this.vertices[index]["moveTo"] === false) {
                            vertArray[vertArray.length - 1]["moveTo"] = false;
                        }
                        breakShape = false;
                        index++;
                    } else if (this.vertexCodes[i] === PConstants.BEZIER_VERTEX) {
                        bezierVertex(this.vertices[index + 0][0],
                            this.vertices[index + 0][1],
                            this.vertices[index + 1][0],
                            this.vertices[index + 1][1],
                            this.vertices[index + 2][0],
                            this.vertices[index + 2][1]);
                        index += 3;
                    } else if (this.vertexCodes[i] === PConstants.CURVE_VERTEX) {
                        curveVertex(this.vertices[index][0],
                            this.vertices[index][1]);
                        index++;
                    } else if (this.vertexCodes[i] === PConstants.BREAK) {
                        breakShape = true;
                    }
                }
            } else {  // drawing a 3D path
                for (i = 0, j = this.vertexCodes.length; i < j; i++) {
                    if (this.vertexCodes[i] === PConstants.VERTEX) {
                        vertex(this.vertices[index][0],
                            this.vertices[index][1],
                            this.vertices[index][2]);
                        if (this.vertices[index]["moveTo"] === true) {
                            vertArray[vertArray.length - 1]["moveTo"] = true;
                        } else if (this.vertices[index]["moveTo"] === false) {
                            vertArray[vertArray.length - 1]["moveTo"] = false;
                        }
                        breakShape = false;
                    } else if (this.vertexCodes[i] === PConstants.BEZIER_VERTEX) {
                        bezierVertex(this.vertices[index + 0][0],
                            this.vertices[index + 0][1],
                            this.vertices[index + 0][2],
                            this.vertices[index + 1][0],
                            this.vertices[index + 1][1],
                            this.vertices[index + 1][2],
                            this.vertices[index + 2][0],
                            this.vertices[index + 2][1],
                            this.vertices[index + 2][2]);
                        index += 3;
                    } else if (this.vertexCodes[i] === PConstants.CURVE_VERTEX) {
                        curveVertex(this.vertices[index][0],
                            this.vertices[index][1],
                            this.vertices[index][2]);
                        index++;
                    } else if (this.vertexCodes[i] === PConstants.BREAK) {
                        breakShape = true;
                    }
                }
            }
        }
        endShape(this.close ? PConstants.CLOSE : PConstants.OPEN);
    },
    /**
     * The drawGeometry() function draws the geometry part of the SVG document.
     * 
     * @method PShape
     */
    drawGeometry: function () {
        var i, j;
        beginShape(this.kind);
        if (this.style) {
            for (i = 0, j = this.vertices.length; i < j; i++) {
                vertex(this.vertices[i]);
            }
        } else {
            for (i = 0, j = this.vertices.length; i < j; i++) {
                var vert = this.vertices[i];
                if (vert[2] === 0) {
                    vertex(vert[0], vert[1]);
                } else {
                    vertex(vert[0], vert[1], vert[2]);
                }
            }
        }
        endShape();
    },
    /**
     * The drawGroup() function draws the <g> part of the SVG document.
     * 
     * @method PShape
     */
    drawGroup: function () {
        for (var i = 0, j = this.children.length; i < j; i++) {
            this.children[i].draw();
        }
    },
    /**
     * The drawPrimitive() function draws SVG document shape elements. These can be point, line, triangle, quad, rect, ellipse, arc, box, or sphere.
     * 
     * @method PShape
     */
    drawPrimitive: function () {
        if (this.kind === PConstants.POINT) {
            point(this.params[0], this.params[1]);
        } else if (this.kind === PConstants.LINE) {
            if (this.params.length === 4) {  // 2D
                line(this.params[0], this.params[1],
                    this.params[2], this.params[3]);
            } else {  // 3D
                line(this.params[0], this.params[1], this.params[2],
                    this.params[3], this.params[4], this.params[5]);
            }
        } else if (this.kind === PConstants.TRIANGLE) {
            triangle(this.params[0], this.params[1],
                this.params[2], this.params[3],
                this.params[4], this.params[5]);
        } else if (this.kind === PConstants.QUAD) {
            quad(this.params[0], this.params[1],
                this.params[2], this.params[3],
                this.params[4], this.params[5],
                this.params[6], this.params[7]);
        } else if (this.kind === PConstants.RECT) {
            if (this.image !== null) {
                imageMode(PConstants.CORNER);
                image(this.image,
                    this.params[0],
                    this.params[1],
                    this.params[2],
                    this.params[3]);
            } else {
                rectMode(PConstants.CORNER);
                rect(this.params[0],
                    this.params[1],
                    this.params[2],
                    this.params[3]);
            }
        } else if (this.kind === PConstants.ELLIPSE) {
            ellipseMode(PConstants.CORNER);
            ellipse(this.params[0],
                this.params[1],
                this.params[2],
                this.params[3]);
        } else if (this.kind === PConstants.ARC) {
            ellipseMode(PConstants.CORNER);
            arc(this.params[0],
                this.params[1],
                this.params[2],
                this.params[3],
                this.params[4],
                this.params[5]);
        } else if (this.kind === PConstants.BOX) {
            if (this.params.length === 1) {
                box(this.params[0]);
            } else {
                box(this.params[0], this.params[1], this.params[2]);
            }
        } else if (this.kind === PConstants.SPHERE) {
            sphere(this.params[0]);
        }
    },
    /**
     * The pre() function performs the preparations before the SVG is drawn. This includes doing transformations and storing previous styles.
     * 
     * @method PShape
     */
    pre: function () {
        if (this.matrix) {
            pushMatrix();
            curContext.transform(this.matrix.elements[0],
                this.matrix.elements[3],
                this.matrix.elements[1],
                this.matrix.elements[4],
                this.matrix.elements[2],
                this.matrix.elements[5]);
            //applyMatrix(this.matrix.elements[0],this.matrix.elements[0]);
        }
        if (this.style) {
            pushStyle();
            this.styles();
        }
    },
    /**
     * The post() function performs the necessary actions after the SVG is drawn. This includes removing transformations and removing added styles.
     * 
     * @method PShape
     */
    post: function () {
        if (this.matrix) {
            popMatrix();
        }
        if (this.style) {
            popStyle();
        }
    },
    /**
     * The styles() function changes the Processing's current styles
     * 
     * @method PShape
     */
    styles: function () {
        if (this.stroke) {
            stroke(this.strokeColor);
            strokeWeight(this.strokeWeight);
            strokeCap(this.strokeCap);
            strokeJoin(this.strokeJoin);
        } else {
            noStroke();
        }

        if (this.fill) {
            fill(this.fillColor);

        } else {
            noFill();
        }
    },
    /**
     * 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
     * layer position of the shape to get with the <b>index</b> parameter.
     * The shape is returned as a <b>PShape</b> object, or <b>null</b> is returned if there is an error.
     *
     * @param {String} target   the name of the shape to get
     * @param {int} index   the layer position of the shape to get
     *
     * @return {PShape} returns a child element of a shape as a PShape object or null if there is an error
     * 
     * @method PShape
     */
    getChild: function (child) {
        var i, j;
        if (typeof child === 'number') {
            return this.children[child];
        }
        var found;
        if (child === "" || this.name === child) {
            return this;
        }
        if (this.nameTable.length > 0) {
            for (i = 0, j = this.nameTable.length; i < j || found; i++) {
                if (this.nameTable[i].getName === child) {
                    found = this.nameTable[i];
                    break;
                }
            }
            if (found) { return found; }
        }
        for (i = 0, j = this.children.length; i < j; i++) {
            found = this.children[i].getChild(child);
            if (found) { return found; }
        }
        return null;
    },
    /**
     * The getChildCount() returns the number of children
     *
     * @return {int} returns a count of children
     * 
     * @method PShape
     */
    getChildCount: function () {
        return this.children.length;
    },
    /**
     * The addChild() adds a child to the PShape.
     *
     * @param {PShape} child the child to add     * 
     * 
     * @method PShape
     */
    addChild: function (child) {
        this.children.push(child);
        child.parent = this;
        if (child.getName() !== null) {
            this.addName(child.getName(), child);
        }
    },
    /**
     * The addName() functions adds a shape to the name lookup table.
     *
     * @param {String} name   the name to be added
     * @param {PShape} shape  the shape
     * 
     * @method PShape
     */
    addName: function (name, shape) {
        if (this.parent !== null) {
            this.parent.addName(name, shape);
        } else {
            this.nameTable.push([name, shape]);
        }
    },
    /**
     * 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.
     * 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>.
     * This transformation is applied directly to the shape, it's not refreshed each time <b>draw()</b> is run.
     * <br><br>Using this method with the <b>z</b> parameter requires using the P3D or OPENGL parameter in combination with size.
     *
     * @param {int|float} x left/right translation
     * @param {int|float} y up/down translation
     * @param {int|float} z forward/back translation
     *
     * @see PMatrix2D#translate
     * @see PMatrix3D#translate
     * 
     * @method PShape
     */
    translate: function () {
        if (arguments.length === 2) {
            this.checkMatrix(2);
            this.matrix.translate(arguments[0], arguments[1]);
        } else {
            this.checkMatrix(3);
            this.matrix.translate(arguments[0], arguments[1], 0);
        }
    },
    /**
     * The checkMatrix() function makes sure that the shape's matrix is 1) not null, and 2) has a matrix
     * that can handle <em>at least</em> the specified number of dimensions.
     *
     * @param {int} dimensions the specified number of dimensions
     * 
     * @method PShape
     */
    checkMatrix: function (dimensions) {
        if (this.matrix === null) {
            if (dimensions === 2) {
                this.matrix = new PMatrix2D();
            } else {
                this.matrix = new PMatrix3D();
            }
        } else if (dimensions === 3 && this.matrix instanceof PMatrix2D) {
            this.matrix = new PMatrix3D();
        }
    },
    /**
     * 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.
     * <br><br>Shapes are always rotated around the upper-left corner of their bounding box. Positive numbers rotate objects in a clockwise direction.
     * 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>.
     * This transformation is applied directly to the shape, it's not refreshed each time <b>draw()</b> is run.
     * <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.
     *
     * @param {float}angle angle of rotation specified in radians
     *
     * @see PMatrix3D#rotateX
     * 
     * @method PShape
     */
    rotateX: function (angle) {
        this.rotate(angle, 1, 0, 0);
    },
    /**
     * 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.
     * <br><br>Shapes are always rotated around the upper-left corner of their bounding box. Positive numbers rotate objects in a clockwise direction.
     * 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>.
     * This transformation is applied directly to the shape, it's not refreshed each time <b>draw()</b> is run.
     * <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.
     *
     * @param {float}angle angle of rotation specified in radians
     *
     * @see PMatrix3D#rotateY
     * 
     * @method PShape
     */
    rotateY: function (angle) {
        this.rotate(angle, 0, 1, 0);
    },
    /**
     * 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.
     * <br><br>Shapes are always rotated around the upper-left corner of their bounding box. Positive numbers rotate objects in a clockwise direction.
     * 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>.
     * This transformation is applied directly to the shape, it's not refreshed each time <b>draw()</b> is run.
     * <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.
     *
     * @param {float}angle angle of rotation specified in radians
     *
     * @see PMatrix3D#rotateZ
     * 
     * @method PShape
     */
    rotateZ: function (angle) {
        this.rotate(angle, 0, 0, 1);
    },
    /**
     * 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.
     * <br><br>Shapes are always rotated around the upper-left corner of their bounding box. Positive numbers rotate objects in a clockwise direction.
     * Transformations apply to everything that happens after and subsequent calls to the method accumulates the effect.
     * For example, calling <b>rotate(HALF_PI)</b> and then <b>rotate(HALF_PI)</b> is the same as <b>rotate(PI)</b>.
     * This transformation is applied directly to the shape, it's not refreshed each time <b>draw()</b> is run.
     * If optional parameters x,y,z are supplied, the rotate is about the point (x, y, z).
     *
     * @param {float}angle  angle of rotation specified in radians
     * @param {float}x      x-coordinate of the point
     * @param {float}y      y-coordinate of the point
     * @param {float}z      z-coordinate of the point
     * @see PMatrix2D#rotate
     * @see PMatrix3D#rotate
     * 
     * @method PShape
     */
    rotate: function () {
        if (arguments.length === 1) {
            this.checkMatrix(2);
            this.matrix.rotate(arguments[0]);
        } else {
            this.checkMatrix(3);
            this.matrix.rotate(arguments[0],
                arguments[1],
                arguments[2],
                arguments[3]);
        }
    },
    /**
     * 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.
     * 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%.
     * 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>.
     * This transformation is applied directly to the shape, it's not refreshed each time <b>draw()</b> is run.
     * <br><br>Using this fuction with the <b>z</b> parameter requires passing P3D or OPENGL into the size() parameter.
     *
     * @param {float}s      percentage to scale the object
     * @param {float}x      percentage to scale the object in the x-axis
     * @param {float}y      percentage to scale the object in the y-axis
     * @param {float}z      percentage to scale the object in the z-axis
     *
     * @see PMatrix2D#scale
     * @see PMatrix3D#scale
     * 
     * @method PShape
     */
    scale: function () {
        if (arguments.length === 2) {
            this.checkMatrix(2);
            this.matrix.scale(arguments[0], arguments[1]);
        } else if (arguments.length === 3) {
            this.checkMatrix(2);
            this.matrix.scale(arguments[0], arguments[1], arguments[2]);
        } else {
            this.checkMatrix(2);
            this.matrix.scale(arguments[0]);
        }
    },
    /**
     * The resetMatrix() function resets the matrix
     *
     * @see PMatrix2D#reset
     * @see PMatrix3D#reset
     * 
     * @method PShape
     */
    resetMatrix: function () {
        this.checkMatrix(2);
        this.matrix.reset();
    },
    /**
     * The applyMatrix() function multiplies this matrix by another matrix of type PMatrix3D or PMatrix2D.
     * Individual elements can also be provided
     *
     * @param {PMatrix3D|PMatrix2D} matrix   the matrix to multiply by
     *
     * @see PMatrix2D#apply
     * @see PMatrix3D#apply
     * 
     * @method PShape
     */
    applyMatrix: function (matrix) {
        if (arguments.length === 1) {
            this.applyMatrix(matrix.elements[0],
                matrix.elements[1], 0,
                matrix.elements[2],
                matrix.elements[3],
                matrix.elements[4], 0,
                matrix.elements[5],
                0, 0, 1, 0,
                0, 0, 0, 1);
        } else if (arguments.length === 6) {
            this.checkMatrix(2);
            this.matrix.apply(arguments[0], arguments[1], arguments[2], 0,
                arguments[3], arguments[4], arguments[5], 0,
                0, 0, 1, 0,
                0, 0, 0, 1);

        } else if (arguments.length === 16) {
            this.checkMatrix(3);
            this.matrix.apply(arguments[0],
                arguments[1],
                arguments[2],
                arguments[3],
                arguments[4],
                arguments[5],
                arguments[6],
                arguments[7],
                arguments[8],
                arguments[9],
                arguments[10],
                arguments[11],
                arguments[12],
                arguments[13],
                arguments[14],
                arguments[15]);
        }
    }
};

/**
 * SVG stands for Scalable Vector Graphics, a portable graphics format. It is
 * a vector format so it allows for infinite resolution and relatively small
 * file sizes. Most modern media software can view SVG files, including Adobe
 * products, Firefox, etc. Illustrator and Inkscape can edit SVG files.
 *
 * @param {PApplet} parent     typically use "this"
 * @param {String} filename    name of the SVG file to load
 * @param {XMLElement} xml     an XMLElement element
 * @param {PShapeSVG} parent   the parent PShapeSVG
 *
 * @see PShape
 */
PShapeSVG = function () {
    PShape.call(this); // PShape is the base class.
    if (arguments.length === 1) { //xml element coming in
        this.element = arguments[0];//new XMLElement(null, arguments[0]);
        // set values to their defaults according to the SVG spec
        this.vertexCodes = [];
        this.vertices = [];
        this.opacity = 1;

        this.stroke = false;
        this.strokeColor = PConstants.ALPHA_MASK;
        this.strokeWeight = 1;
        this.strokeCap = PConstants.SQUARE;  // BUTT in svg spec
        this.strokeJoin = PConstants.MITER;
        this.strokeGradient = null;
        this.strokeGradientPaint = null;
        this.strokeName = null;
        this.strokeOpacity = 1;

        this.fill = true;
        this.fillColor = PConstants.ALPHA_MASK;
        this.fillGradient = null;
        this.fillGradientPaint = null;
        this.fillName = null;
        this.fillOpacity = 1;

        if (this.element.getName() !== "svg") {
            throw ("root is not <svg>, it's <" + this.element.getName() + ">");
        }
    }
    else if (arguments.length === 2) {
        if (typeof arguments[1] === 'string') {
            if (arguments[1].indexOf(".svg") > -1) { //its a filename
                this.element = new XMLElement(null, arguments[1]);
                // set values to their defaults according to the SVG spec
                this.vertexCodes = [];
                this.vertices = [];
                this.opacity = 1;

                this.stroke = false;
                this.strokeColor = PConstants.ALPHA_MASK;
                this.strokeWeight = 1;
                this.strokeCap = PConstants.SQUARE;  // BUTT in svg spec
                this.strokeJoin = PConstants.MITER;
                this.strokeGradient = "";
                this.strokeGradientPaint = "";
                this.strokeName = "";
                this.strokeOpacity = 1;

                this.fill = true;
                this.fillColor = PConstants.ALPHA_MASK;
                this.fillGradient = null;
                this.fillGradientPaint = null;
                this.fillOpacity = 1;

            }
        } else { // XMLElement
            if (arguments[0]) { // PShapeSVG
                this.element = arguments[1];
                this.vertexCodes = arguments[0].vertexCodes.slice();
                this.vertices = arguments[0].vertices.slice();

                this.stroke = arguments[0].stroke;
                this.strokeColor = arguments[0].strokeColor;
                this.strokeWeight = arguments[0].strokeWeight;
                this.strokeCap = arguments[0].strokeCap;
                this.strokeJoin = arguments[0].strokeJoin;
                this.strokeGradient = arguments[0].strokeGradient;
                this.strokeGradientPaint = arguments[0].strokeGradientPaint;
                this.strokeName = arguments[0].strokeName;

                this.fill = arguments[0].fill;
                this.fillColor = arguments[0].fillColor;
                this.fillGradient = arguments[0].fillGradient;
                this.fillGradientPaint = arguments[0].fillGradientPaint;
                this.fillName = arguments[0].fillName;
                this.strokeOpacity = arguments[0].strokeOpacity;
                this.fillOpacity = arguments[0].fillOpacity;
                this.opacity = arguments[0].opacity;
            }
        }
    }

    this.name = this.element.getStringAttribute("id");
    var displayStr = this.element.getStringAttribute("display", "inline");
    this.visible = displayStr !== "none";
    var str = this.element.getAttribute("transform");
    if (str) {
        this.matrix = this.parseMatrix(str);
    }
    // not proper parsing of the viewBox, but will cover us for cases where
    // the width and height of the object is not specified
    var viewBoxStr = this.element.getStringAttribute("viewBox");
    if (viewBoxStr !== null) {
        var viewBox = viewBoxStr.split(" ");
        this.width = viewBox[2];
        this.height = viewBox[3];
    }

    // TODO if viewbox is not same as width/height, then use it to scale
    // the original objects. for now, viewbox only used when width/height
    // are empty values (which by the spec means w/h of "100%"
    var unitWidth = this.element.getStringAttribute("width");
    var unitHeight = this.element.getStringAttribute("height");
    if (unitWidth !== null) {
        this.width = this.parseUnitSize(unitWidth);
        this.height = this.parseUnitSize(unitHeight);
    } else {
        if ((this.width === 0) || (this.height === 0)) {
            // For the spec, the default is 100% and 100%. For purposes
            // here, insert a dummy value because this is prolly just a
            // font or something for which the w/h doesn't matter.
            this.width = 1;
            this.height = 1;

            //show warning
            throw ("The width and/or height is not " +
                "readable in the <svg> tag of this file.");
        }
    }
    this.parseColors(this.element);
    this.parseChildren(this.element);

};
/**
 * PShapeSVG methods
 * missing: getChild(), print(), parseStyleAttributes(), styles() - deals with strokeGradient and fillGradient
 */
PShapeSVG.prototype = new PShape();
/**
 * The parseMatrix() function parses the specified SVG matrix into a PMatrix2D. Note that PMatrix2D
 * is rotated relative to the SVG definition, so parameters are rearranged
 * here. More about the transformation matrices in
 * <a href="http://www.w3.org/TR/SVG/coords.html#TransformAttribute">this section</a>
 * of the SVG documentation.
 *
 * @param {String} str text of the matrix param.
 *
 * @return {PMatrix2D} a PMatrix2D
 * 
 * @method PShapeSVG
 */
PShapeSVG.prototype.parseMatrix = (function () {
    function getCoords(s) {
        var m = [];
        s.replace(/\((.*?)\)/, (function () {
            return function (all, params) {
                // get the coordinates that can be separated by spaces or a comma
                m = params.replace(/,+/g, " ").split(/\s+/);
            };
        }()));
        return m;
    }

    return function (str) {
        this.checkMatrix(2);
        var pieces = [];
        str.replace(/\s*(\w+)\((.*?)\)/g, function (all) {
            // get a list of transform definitions
            pieces.push(trim(all));
        });
        if (pieces.length === 0) {
            return null;
        }

        for (var i = 0, j = pieces.length; i < j; i++) {
            var m = getCoords(pieces[i]);

            if (pieces[i].indexOf("matrix") !== -1) {
                this.matrix.set(m[0], m[2], m[4], m[1], m[3], m[5]);
            } else if (pieces[i].indexOf("translate") !== -1) {
                var tx = m[0];
                var ty = (m.length === 2) ? m[1] : 0;
                this.matrix.translate(tx, ty);
            } else if (pieces[i].indexOf("scale") !== -1) {
                var sx = m[0];
                var sy = (m.length === 2) ? m[1] : m[0];
                this.matrix.scale(sx, sy);
            } else if (pieces[i].indexOf("rotate") !== -1) {
                var angle = m[0];
                if (m.length === 1) {
                    // XXX(jeresig)
                    this.matrix.rotate(angleMode === "degrees" ? angle : radians(angle));
                } else if (m.length === 3) {
                    this.matrix.translate(m[1], m[2]);
                    // XXX(jeresig)
                    this.matrix.rotate(angleMode === "degrees" ? m[0] : radians(m[0]));
                    this.matrix.translate(-m[1], -m[2]);
                }
            } else if (pieces[i].indexOf("skewX") !== -1) {
                this.matrix.skewX(parseFloat(m[0]));
            } else if (pieces[i].indexOf("skewY") !== -1) {
                this.matrix.skewY(m[0]);
            }
        }
        return this.matrix;
    };
}());

/**
 * The parseChildren() function parses the specified XMLElement
 *
 * @param {XMLElement}element the XMLElement to parse
 * 
 * @method PShapeSVG
 */
PShapeSVG.prototype.parseChildren = function (element) {
    var newelement = element.getChildren();
    var children = new PShape();
    for (var i = 0, j = newelement.length; i < j; i++) {
        var kid = this.parseChild(newelement[i]);
        if (kid) {
            children.addChild(kid);
        }
    }
    this.children.push(children);
};
/**
 * The getName() function returns the name
 *
 * @return {String} the name
 * 
 * @method PShapeSVG
 */
PShapeSVG.prototype.getName = function () {
    return this.name;
};
/**
 * The parseChild() function parses a child XML element.
 *
 * @param {XMLElement} elem the element to parse
 *
 * @return {PShape} the newly created PShape
 * 
 * @method PShapeSVG
 */
PShapeSVG.prototype.parseChild = function (elem) {
    var name = elem.getName();
    var shape;
    if (name === "g") {
        shape = new PShapeSVG(this, elem);
    } else if (name === "defs") {
        // generally this will contain gradient info, so may
        // as well just throw it into a group element for parsing
        shape = new PShapeSVG(this, elem);
    } else if (name === "line") {
        shape = new PShapeSVG(this, elem);
        shape.parseLine();
    } else if (name === "circle") {
        shape = new PShapeSVG(this, elem);
        shape.parseEllipse(true);
    } else if (name === "ellipse") {
        shape = new PShapeSVG(this, elem);
        shape.parseEllipse(false);
    } else if (name === "rect") {
        shape = new PShapeSVG(this, elem);
        shape.parseRect();
    } else if (name === "polygon") {
        shape = new PShapeSVG(this, elem);
        shape.parsePoly(true);
    } else if (name === "polyline") {
        shape = new PShapeSVG(this, elem);
        shape.parsePoly(false);
    } else if (name === "path") {
        shape = new PShapeSVG(this, elem);
        shape.parsePath();
    } else if (name === "radialGradient") {
        //return new RadialGradient(this, elem);
        unimplemented('PShapeSVG.prototype.parseChild, name = radialGradient');
    } else if (name === "linearGradient") {
        //return new LinearGradient(this, elem);
        unimplemented('PShapeSVG.prototype.parseChild, name = linearGradient');
    } else if (name === "text") {
        unimplemented('PShapeSVG.prototype.parseChild, name = text');
    } else if (name === "filter") {
        unimplemented('PShapeSVG.prototype.parseChild, name = filter');
    } else if (name === "mask") {
        unimplemented('PShapeSVG.prototype.parseChild, name = mask');
    } else {
        // ignoring
        nop();
    }
    return shape;
};
/**
 * The parsePath() function parses the <path> element of the svg file
 * A path is defined by including a path element which contains a d="(path data)" attribute, where the d attribute contains
 * the moveto, line, curve (both cubic and quadratic Beziers), arc and closepath instructions.
 ** 
 * @method PShapeSVG
 */
PShapeSVG.prototype.parsePath = function () {
    this.family = PConstants.PATH;
    this.kind = 0;
    var pathDataChars = [];
    var c;
    //change multiple spaces and commas to single space
    var pathData = trim(this.element.getStringAttribute("d")
        .replace(/[\s,]+/g, ' '));
    if (pathData === null) {
        return;
    }
    pathData = __toCharArray(pathData);
    var cx = 0,
        cy = 0,
        ctrlX = 0,
        ctrlY = 0,
        ctrlX1 = 0,
        ctrlX2 = 0,
        ctrlY1 = 0,
        ctrlY2 = 0,
        endX = 0,
        endY = 0,
        ppx = 0,
        ppy = 0,
        px = 0,
        py = 0,
        i = 0,
        valOf = 0;
    var str = "";
    var tmpArray = [];
    var flag = false;
    var lastInstruction;
    var command;
    var j, k;
    while (i < pathData.length) {
        valOf = pathData[i].valueOf();
        if ((valOf >= 65 && valOf <= 90) || (valOf >= 97 && valOf <= 122)) {
            // if it's a letter
            // populate the tmpArray with coordinates
            j = i;
            i++;
            if (i < pathData.length) { // don't go over boundary of array
                tmpArray = [];
                valOf = pathData[i].valueOf();
                while (!((valOf >= 65 && valOf <= 90) ||
                    (valOf >= 97 && valOf <= 100) ||
                    (valOf >= 102 && valOf <= 122))
                    && flag === false) { // if its NOT a letter
                    if (valOf === 32) { //if its a space and the str isn't empty
                        // sometimes you get a space after the letter
                        if (str !== "") {
                            tmpArray.push(parseFloat(str));
                            str = "";
                        }
                        i++;
                    } else if (valOf === 45) { //if it's a -
                        // allow for 'e' notation in numbers, e.g. 2.10e-9
                        if (pathData[i - 1].valueOf() === 101) {
                            str += pathData[i].toString();
                            i++;
                        } else {
                            // sometimes no space separator after (ex: 104.535-16.322)
                            if (str !== "") {
                                tmpArray.push(parseFloat(str));
                            }
                            str = pathData[i].toString();
                            i++;
                        }
                    } else {
                        str += pathData[i].toString();
                        i++;
                    }
                    if (i === pathData.length) { // don't go over boundary of array
                        flag = true;
                    } else {
                        valOf = pathData[i].valueOf();
                    }
                }
            }
            if (str !== "") {
                tmpArray.push(parseFloat(str));
                str = "";
            }
            command = pathData[j];
            valOf = command.valueOf();
            if (valOf === 77) {  // M - move to (absolute)
                if (tmpArray.length >= 2 && tmpArray.length % 2 === 0) {
                    // need one+ pairs of co-ordinates
                    cx = tmpArray[0];
                    cy = tmpArray[1];
                    this.parsePathMoveto(cx, cy);
                    if (tmpArray.length > 2) {
                        for (j = 2, k = tmpArray.length; j < k; j += 2) {
                            // absolute line to
                            cx = tmpArray[j];
                            cy = tmpArray[j + 1];
                            this.parsePathLineto(cx, cy);
                        }
                    }
                }
            } else if (valOf === 109) {  // m - move to (relative)
                if (tmpArray.length >= 2 && tmpArray.length % 2 === 0) {
                    // need one+ pairs of co-ordinates
                    cx += tmpArray[0];
                    cy += tmpArray[1];
                    this.parsePathMoveto(cx, cy);
                    if (tmpArray.length > 2) {
                        for (j = 2, k = tmpArray.length; j < k; j += 2) {
                            // relative line to
                            cx += tmpArray[j];
                            cy += tmpArray[j + 1];
                            this.parsePathLineto(cx, cy);
                        }
                    }
                }
            } else if (valOf === 76) { // L - lineto (absolute)
                if (tmpArray.length >= 2 && tmpArray.length % 2 === 0) {
                    // need one+ pairs of co-ordinates
                    for (j = 0, k = tmpArray.length; j < k; j += 2) {
                        cx = tmpArray[j];
                        cy = tmpArray[j + 1];
                        this.parsePathLineto(cx, cy);
                    }
                }
            } else if (valOf === 108) { // l - lineto (relative)
                if (tmpArray.length >= 2 && tmpArray.length % 2 === 0) {
                    // need one+ pairs of co-ordinates
                    for (j = 0, k = tmpArray.length; j < k; j += 2) {
                        cx += tmpArray[j];
                        cy += tmpArray[j + 1];
                        this.parsePathLineto(cx, cy);
                    }
                }
            } else if (valOf === 72) { // H - horizontal lineto (absolute)
                for (j = 0, k = tmpArray.length; j < k; j++) {
                    // multiple x co-ordinates can be provided
                    cx = tmpArray[j];
                    this.parsePathLineto(cx, cy);
                }
            } else if (valOf === 104) { // h - horizontal lineto (relative)
                for (j = 0, k = tmpArray.length; j < k; j++) {
                    // multiple x co-ordinates can be provided
                    cx += tmpArray[j];
                    this.parsePathLineto(cx, cy);
                }
            } else if (valOf === 86) { // V - vertical lineto (absolute)
                for (j = 0, k = tmpArray.length; j < k; j++) {
                    // multiple y co-ordinates can be provided
                    cy = tmpArray[j];
                    this.parsePathLineto(cx, cy);
                }
            } else if (valOf === 118) { // v - vertical lineto (relative)
                for (j = 0, k = tmpArray.length; j < k; j++) {
                    // multiple y co-ordinates can be provided
                    cy += tmpArray[j];
                    this.parsePathLineto(cx, cy);
                }
            } else if (valOf === 67) { // C - curve to (absolute)
                if (tmpArray.length >= 6 && tmpArray.length % 6 === 0) {
                    // need one+ multiples of 6 co-ordinates
                    for (j = 0, k = tmpArray.length; j < k; j += 6) {
                        ctrlX1 = tmpArray[j];
                        ctrlY1 = tmpArray[j + 1];
                        ctrlX2 = tmpArray[j + 2];
                        ctrlY2 = tmpArray[j + 3];
                        endX = tmpArray[j + 4];
                        endY = tmpArray[j + 5];
                        this.parsePathCurveto(ctrlX1,
                            ctrlY1,
                            ctrlX2,
                            ctrlY2,
                            endX,
                            endY);
                        cx = endX;
                        cy = endY;
                    }
                }
            } else if (valOf === 99) { // c - curve to (relative)
                if (tmpArray.length >= 6 && tmpArray.length % 6 === 0) {
                    // need one+ multiples of 6 co-ordinates
                    for (j = 0, k = tmpArray.length; j < k; j += 6) {
                        ctrlX1 = cx + tmpArray[j];
                        ctrlY1 = cy + tmpArray[j + 1];
                        ctrlX2 = cx + tmpArray[j + 2];
                        ctrlY2 = cy + tmpArray[j + 3];
                        endX = cx + tmpArray[j + 4];
                        endY = cy + tmpArray[j + 5];
                        this.parsePathCurveto(ctrlX1,
                            ctrlY1,
                            ctrlX2,
                            ctrlY2,
                            endX,
                            endY);
                        cx = endX;
                        cy = endY;
                    }
                }
            } else if (valOf === 83) { // S - curve to shorthand (absolute)
                if (tmpArray.length >= 4 && tmpArray.length % 4 === 0) {
                    // need one+ multiples of 4 co-ordinates
                    for (j = 0, k = tmpArray.length; j < k; j += 4) {
                        if (lastInstruction.toLowerCase() === "c" ||
                            lastInstruction.toLowerCase() === "s") {
                            ppx = this.vertices[this.vertices.length - 2][0];
                            ppy = this.vertices[this.vertices.length - 2][1];
                            px = this.vertices[this.vertices.length - 1][0];
                            py = this.vertices[this.vertices.length - 1][1];
                            ctrlX1 = px + (px - ppx);
                            ctrlY1 = py + (py - ppy);
                        } else {
                            //If there is no previous curve,
                            //the current point will be used as the first control point.
                            ctrlX1 = this.vertices[this.vertices.length - 1][0];
                            ctrlY1 = this.vertices[this.vertices.length - 1][1];
                        }
                        ctrlX2 = tmpArray[j];
                        ctrlY2 = tmpArray[j + 1];
                        endX = tmpArray[j + 2];
                        endY = tmpArray[j + 3];
                        this.parsePathCurveto(ctrlX1,
                            ctrlY1,
                            ctrlX2,
                            ctrlY2,
                            endX,
                            endY);
                        cx = endX;
                        cy = endY;
                    }
                }
            } else if (valOf === 115) { // s - curve to shorthand (relative)
                if (tmpArray.length >= 4 && tmpArray.length % 4 === 0) {
                    // need one+ multiples of 4 co-ordinates
                    for (j = 0, k = tmpArray.length; j < k; j += 4) {
                        if (lastInstruction.toLowerCase() === "c" ||
                            lastInstruction.toLowerCase() === "s") {
                            ppx = this.vertices[this.vertices.length - 2][0];
                            ppy = this.vertices[this.vertices.length - 2][1];
                            px = this.vertices[this.vertices.length - 1][0];
                            py = this.vertices[this.vertices.length - 1][1];
                            ctrlX1 = px + (px - ppx);
                            ctrlY1 = py + (py - ppy);
                        } else {
                            //If there is no previous curve,
                            //the current point will be used as the first control point.
                            ctrlX1 = this.vertices[this.vertices.length - 1][0];
                            ctrlY1 = this.vertices[this.vertices.length - 1][1];
                        }
                        ctrlX2 = cx + tmpArray[j];
                        ctrlY2 = cy + tmpArray[j + 1];
                        endX = cx + tmpArray[j + 2];
                        endY = cy + tmpArray[j + 3];
                        this.parsePathCurveto(ctrlX1,
                            ctrlY1,
                            ctrlX2,
                            ctrlY2,
                            endX,
                            endY);
                        cx = endX;
                        cy = endY;
                    }
                }
            } else if (valOf === 81) { // Q - quadratic curve to (absolute)
                if (tmpArray.length >= 4 && tmpArray.length % 4 === 0) {
                    // need one+ multiples of 4 co-ordinates
                    for (j = 0, k = tmpArray.length; j < k; j += 4) {
                        ctrlX = tmpArray[j];
                        ctrlY = tmpArray[j + 1];
                        endX = tmpArray[j + 2];
                        endY = tmpArray[j + 3];
                        this.parsePathQuadto(cx, cy, ctrlX, ctrlY, endX, endY);
                        cx = endX;
                        cy = endY;
                    }
                }
            } else if (valOf === 113) { // q - quadratic curve to (relative)
                if (tmpArray.length >= 4 && tmpArray.length % 4 === 0) {
                    // need one+ multiples of 4 co-ordinates
                    for (j = 0, k = tmpArray.length; j < k; j += 4) {
                        ctrlX = cx + tmpArray[j];
                        ctrlY = cy + tmpArray[j + 1];
                        endX = cx + tmpArray[j + 2];
                        endY = cy + tmpArray[j + 3];
                        this.parsePathQuadto(cx, cy, ctrlX, ctrlY, endX, endY);
                        cx = endX;
                        cy = endY;
                    }
                }
            } else if (valOf === 84) {
                // T - quadratic curve to shorthand (absolute)
                if (tmpArray.length >= 2 && tmpArray.length % 2 === 0) {
                    // need one+ pairs of co-ordinates
                    for (j = 0, k = tmpArray.length; j < k; j += 2) {
                        if (lastInstruction.toLowerCase() === "q" ||
                            lastInstruction.toLowerCase() === "t") {
                            ppx = this.vertices[this.vertices.length - 2][0];
                            ppy = this.vertices[this.vertices.length - 2][1];
                            px = this.vertices[this.vertices.length - 1][0];
                            py = this.vertices[this.vertices.length - 1][1];
                            ctrlX = px + (px - ppx);
                            ctrlY = py + (py - ppy);
                        } else {
                            // If there is no previous command or if the previous command
                            // was not a Q, q, T or t, assume the control point is
                            // coincident with the current point.
                            ctrlX = cx;
                            ctrlY = cy;
                        }
                        endX = tmpArray[j];
                        endY = tmpArray[j + 1];
                        this.parsePathQuadto(cx, cy, ctrlX, ctrlY, endX, endY);
                        cx = endX;
                        cy = endY;
                    }
                }
            } else if (valOf === 116) {
                // t - quadratic curve to shorthand (relative)
                if (tmpArray.length >= 2 && tmpArray.length % 2 === 0) {
                    // need one+ pairs of co-ordinates
                    for (j = 0, k = tmpArray.length; j < k; j += 2) {
                        if (lastInstruction.toLowerCase() === "q" ||
                            lastInstruction.toLowerCase() === "t") {
                            ppx = this.vertices[this.vertices.length - 2][0];
                            ppy = this.vertices[this.vertices.length - 2][1];
                            px = this.vertices[this.vertices.length - 1][0];
                            py = this.vertices[this.vertices.length - 1][1];
                            ctrlX = px + (px - ppx);
                            ctrlY = py + (py - ppy);
                        } else {
                            // If there is no previous command or if the previous command
                            // was not a Q, q, T or t, assume the control point is
                            // coincident with the current point.
                            ctrlX = cx;
                            ctrlY = cy;
                        }
                        endX = cx + tmpArray[j];
                        endY = cy + tmpArray[j + 1];
                        this.parsePathQuadto(cx, cy, ctrlX, ctrlY, endX, endY);
                        cx = endX;
                        cy = endY;
                    }
                }
            } else if (valOf === 90 || valOf === 122) { // Z or z (these do the same thing)
                this.close = true;
            }
            lastInstruction = command.toString();
        } else { i++; }
    }
};
/**
 * PShapeSVG.parsePath() helper function
 *
 * @see PShapeSVG#parsePath
 * 
 * @method PShapeSVG
 */
PShapeSVG.prototype.parsePathQuadto = function (x1, y1, cx, cy, x2, y2) {
    if (this.vertices.length > 0) {
        this.parsePathCode(PConstants.BEZIER_VERTEX);
        // x1/y1 already covered by last moveto, lineto, or curveto
        this.parsePathVertex(x1 + ((cx - x1) * 2 / 3), y1 + ((cy - y1) * 2 / 3));
        this.parsePathVertex(x2 + ((cx - x2) * 2 / 3), y2 + ((cy - y2) * 2 / 3));
        this.parsePathVertex(x2, y2);
    } else {
        throw ("Path must start with M/m");
    }
};
/**
 * PShapeSVG.parsePath() helper function
 *
 * @see PShapeSVG#parsePath
 * 
 * @method PShapeSVG
 */
PShapeSVG.prototype.parsePathCurveto = function (x1, y1, x2, y2, x3, y3) {
    if (this.vertices.length > 0) {
        this.parsePathCode(PConstants.BEZIER_VERTEX);
        this.parsePathVertex(x1, y1);
        this.parsePathVertex(x2, y2);
        this.parsePathVertex(x3, y3);
    } else {
        throw ("Path must start with M/m");
    }
};
/**
 * PShapeSVG.parsePath() helper function
 *
 * @see PShapeSVG#parsePath
 * 
 * @method PShapeSVG
 */
PShapeSVG.prototype.parsePathLineto = function (px, py) {
    if (this.vertices.length > 0) {
        this.parsePathCode(PConstants.VERTEX);
        this.parsePathVertex(px, py);
        // add property to distinguish between curContext.moveTo
        // or curContext.lineTo
        this.vertices[this.vertices.length - 1]["moveTo"] = false;
    } else {
        throw ("Path must start with M/m");
    }
};

PShapeSVG.prototype.parsePathMoveto = function (px, py) {
    if (this.vertices.length > 0) {
        this.parsePathCode(PConstants.BREAK);
    }
    this.parsePathCode(PConstants.VERTEX);
    this.parsePathVertex(px, py);
    // add property to distinguish between curContext.moveTo
    // or curContext.lineTo
    this.vertices[this.vertices.length - 1]["moveTo"] = true;
};
/**
 * PShapeSVG.parsePath() helper function
 *
 * @see PShapeSVG#parsePath
 * 
 * @method PShapeSVG
 */
PShapeSVG.prototype.parsePathVertex = function (x, y) {
    var verts = [];
    verts[0] = x;
    verts[1] = y;
    this.vertices.push(verts);
};
/**
 * PShapeSVG.parsePath() helper function
 *
 * @see PShapeSVG#parsePath
 * 
 * @method PShapeSVG
 */
PShapeSVG.prototype.parsePathCode = function (what) {
    this.vertexCodes.push(what);
};
/**
 * The parsePoly() function parses a polyline or polygon from an SVG file.
 *
 * @param {boolean}val true if shape is closed (polygon), false if not (polyline)
 * 
 * @method PShapeSVG
 */
PShapeSVG.prototype.parsePoly = function (val) {
    this.family = PConstants.PATH;
    this.close = val;
    var pointsAttr = trim(this.element.getStringAttribute("points")
        .replace(/[,\s]+/g, ' '));
    if (pointsAttr !== null) {
        //split into array
        var pointsBuffer = pointsAttr.split(" ");
        if (pointsBuffer.length % 2 === 0) {
            for (var i = 0, j = pointsBuffer.length; i < j; i++) {
                var verts = [];
                verts[0] = pointsBuffer[i];
                verts[1] = pointsBuffer[++i];
                this.vertices.push(verts);
            }
        } else {
            throw ("Error parsing polygon points: odd number of coordinates provided");
        }
    }
};
/**
 * The parseRect() function parses a rect from an SVG file.
 * 
 * @method PShapeSVG
 */
PShapeSVG.prototype.parseRect = function () {
    this.kind = PConstants.RECT;
    this.family = PConstants.PRIMITIVE;
    this.params = [];
    this.params[0] = this.element.getFloatAttribute("x");
    this.params[1] = this.element.getFloatAttribute("y");
    this.params[2] = this.element.getFloatAttribute("width");
    this.params[3] = this.element.getFloatAttribute("height");
    if (this.params[2] < 0 || this.params[3] < 0) {
        throw ("svg error: negative width or height found while parsing <rect>");
    }
};
/**
 * The parseEllipse() function handles parsing ellipse and circle tags.
 *
 * @param {boolean}val true if this is a circle and not an ellipse
 * 
 * @method PShapeSVG
 */
PShapeSVG.prototype.parseEllipse = function (val) {
    this.kind = PConstants.ELLIPSE;
    this.family = PConstants.PRIMITIVE;
    this.params = [];

    this.params[0] = this.element.getFloatAttribute("cx") | 0;
    this.params[1] = this.element.getFloatAttribute("cy") | 0;

    var rx, ry;
    if (val) {
        rx = ry = this.element.getFloatAttribute("r");
        if (rx < 0) {
            throw ("svg error: negative radius found while parsing <circle>");
        }
    } else {
        rx = this.element.getFloatAttribute("rx");
        ry = this.element.getFloatAttribute("ry");
        if (rx < 0 || ry < 0) {
            throw ("svg error: negative x-axis radius or y-axis radius found while parsing <ellipse>");
        }
    }
    this.params[0] -= rx;
    this.params[1] -= ry;

    this.params[2] = rx * 2;
    this.params[3] = ry * 2;
};
/**
 * The parseLine() function handles parsing line tags.
 *
 * @param {boolean}val true if this is a circle and not an ellipse
 * 
 * @method PShapeSVG
 */
PShapeSVG.prototype.parseLine = function () {
    this.kind = PConstants.LINE;
    this.family = PConstants.PRIMITIVE;
    this.params = [];
    this.params[0] = this.element.getFloatAttribute("x1");
    this.params[1] = this.element.getFloatAttribute("y1");
    this.params[2] = this.element.getFloatAttribute("x2");
    this.params[3] = this.element.getFloatAttribute("y2");
};
/**
 * The parseColors() function handles parsing the opacity, strijem stroke-width, stroke-linejoin,stroke-linecap, fill, and style attributes
 *
 * @param {XMLElement}element the element of which attributes to parse
 * 
 * @method PShapeSVG
 */
PShapeSVG.prototype.parseColors = function (element) {
    if (element.hasAttribute("opacity")) {
        this.setOpacity(element.getAttribute("opacity"));
    }
    if (element.hasAttribute("stroke")) {
        this.setStroke(element.getAttribute("stroke"));
    }
    if (element.hasAttribute("stroke-width")) {
        // if NaN (i.e. if it's 'inherit') then default
        // back to the inherit setting
        this.setStrokeWeight(element.getAttribute("stroke-width"));
    }
    if (element.hasAttribute("stroke-linejoin")) {
        this.setStrokeJoin(element.getAttribute("stroke-linejoin"));
    }
    if (element.hasAttribute("stroke-linecap")) {
        this.setStrokeCap(element.getStringAttribute("stroke-linecap"));
    }
    // fill defaults to black (though stroke defaults to "none")
    // http://www.w3.org/TR/SVG/painting.html#FillProperties
    if (element.hasAttribute("fill")) {
        this.setFill(element.getStringAttribute("fill"));
    }
    if (element.hasAttribute("style")) {
        var styleText = element.getStringAttribute("style");
        var styleTokens = styleText.toString().split(";");

        for (var i = 0, j = styleTokens.length; i < j; i++) {
            var tokens = trim(styleTokens[i].split(":"));
            if (tokens[0] === "fill") {
                this.setFill(tokens[1]);
            } else if (tokens[0] === "fill-opacity") {
                this.setFillOpacity(tokens[1]);
            } else if (tokens[0] === "stroke") {
                this.setStroke(tokens[1]);
            } else if (tokens[0] === "stroke-width") {
                this.setStrokeWeight(tokens[1]);
            } else if (tokens[0] === "stroke-linecap") {
                this.setStrokeCap(tokens[1]);
            } else if (tokens[0] === "stroke-linejoin") {
                this.setStrokeJoin(tokens[1]);
            } else if (tokens[0] === "stroke-opacity") {
                this.setStrokeOpacity(tokens[1]);
            } else if (tokens[0] === "opacity") {
                this.setOpacity(tokens[1]);
            } // Other attributes are not yet implemented
        }
    }
};
/**
 * PShapeSVG.parseColors() helper function
 *
 * @param {String} opacityText the value of fillOpacity
 *
 * @see PShapeSVG#parseColors
 * 
 * @method PShapeSVG
 */
PShapeSVG.prototype.setFillOpacity = function (opacityText) {
    this.fillOpacity = parseFloat(opacityText);
    this.fillColor = this.fillOpacity * 255 << 24 |
        this.fillColor & 0xFFFFFF;
};
/**
 * PShapeSVG.parseColors() helper function
 *
 * @param {String} fillText the value of fill
 *
 * @see PShapeSVG#parseColors
 * 
 * @method PShapeSVG
 */
PShapeSVG.prototype.setFill = function (fillText) {
    var opacityMask = this.fillColor & 0xFF000000;
    if (fillText === "none") {
        this.fill = false;
    } else if (fillText.indexOf("#") === 0) {
        this.fill = true;
        if (fillText.length === 4) {
            // convert #00F to #0000FF
            fillText = fillText.replace(/#(.)(.)(.)/, "#$1$1$2$2$3$3");
        }
        this.fillColor = opacityMask |
            (parseInt(fillText.substring(1), 16)) &
            0xFFFFFF;
    } else if (fillText.indexOf("rgb") === 0) {
        this.fill = true;
        this.fillColor = opacityMask | this.parseRGB(fillText);
    } else if (fillText.indexOf("url(#") === 0) {
        this.fillName = fillText.substring(5, fillText.length - 1);
    } else if (colors[fillText]) {
        this.fill = true;
        this.fillColor = opacityMask |
            (parseInt(colors[fillText].substring(1), 16)) &
            0xFFFFFF;
    }
};
/**
 * PShapeSVG.parseColors() helper function
 *
 * @param {String} opacity the value of opacity
 *
 * @see PShapeSVG#parseColors
 * 
 * @method PShapeSVG
 */
PShapeSVG.prototype.setOpacity = function (opacity) {
    this.strokeColor = parseFloat(opacity) * 255 << 24 |
        this.strokeColor & 0xFFFFFF;
    this.fillColor = parseFloat(opacity) * 255 << 24 |
        this.fillColor & 0xFFFFFF;
};
/**
 * PShapeSVG.parseColors() helper function
 *
 * @param {String} strokeText the value to set stroke to
 *
 * @see PShapeSVG#parseColors
 * 
 * @method PShapeSVG
 */
PShapeSVG.prototype.setStroke = function (strokeText) {
    var opacityMask = this.strokeColor & 0xFF000000;
    if (strokeText === "none") {
        this.stroke = false;
    } else if (strokeText.charAt(0) === "#") {
        this.stroke = true;
        if (strokeText.length === 4) {
            // convert #00F to #0000FF
            strokeText = strokeText.replace(/#(.)(.)(.)/, "#$1$1$2$2$3$3");
        }
        this.strokeColor = opacityMask |
            (parseInt(strokeText.substring(1), 16)) &
            0xFFFFFF;
    } else if (strokeText.indexOf("rgb") === 0) {
        this.stroke = true;
        this.strokeColor = opacityMask | this.parseRGB(strokeText);
    } else if (strokeText.indexOf("url(#") === 0) {
        this.strokeName = strokeText.substring(5, strokeText.length - 1);
    } else if (colors[strokeText]) {
        this.stroke = true;
        this.strokeColor = opacityMask |
            (parseInt(colors[strokeText].substring(1), 16)) &
            0xFFFFFF;
    }
};
/**
 * PShapeSVG.parseColors() helper function
 *
 * @param {String} weight the value to set strokeWeight to
 *
 * @see PShapeSVG#parseColors
 * 
 * @method PShapeSVG
 */
PShapeSVG.prototype.setStrokeWeight = function (weight) {
    this.strokeWeight = this.parseUnitSize(weight);
};
/**
 * PShapeSVG.parseColors() helper function
 *
 * @param {String} linejoin the value to set strokeJoin to
 *
 * @see PShapeSVG#parseColors
 * 
 * @method PShapeSVG
 */
PShapeSVG.prototype.setStrokeJoin = function (linejoin) {
    if (linejoin === "miter") {
        this.strokeJoin = PConstants.MITER;

    } else if (linejoin === "round") {
        this.strokeJoin = PConstants.ROUND;

    } else if (linejoin === "bevel") {
        this.strokeJoin = PConstants.BEVEL;
    }
};
/**
 * PShapeSVG.parseColors() helper function
 *
 * @param {String} linecap the value to set strokeCap to
 *
 * @see PShapeSVG#parseColors
 * 
 * @method PShapeSVG
 */
PShapeSVG.prototype.setStrokeCap = function (linecap) {
    if (linecap === "butt") {
        this.strokeCap = PConstants.SQUARE;

    } else if (linecap === "round") {
        this.strokeCap = PConstants.ROUND;

    } else if (linecap === "square") {
        this.strokeCap = PConstants.PROJECT;
    }
};
/**
 * PShapeSVG.parseColors() helper function
 *
 * @param {String} opacityText the value to set stroke opacity to
 *
 * @see PShapeSVG#parseColors
 * 
 * @method PShapeSVG
 */
PShapeSVG.prototype.setStrokeOpacity = function (opacityText) {
    this.strokeOpacity = parseFloat(opacityText);
    this.strokeColor = this.strokeOpacity * 255 << 24 |
        this.strokeColor &
        0xFFFFFF;
};
/**
 * The parseRGB() function parses an rbg() color string and returns a color int
 *
 * @param {String} color the color to parse in rbg() format
 *
 * @return {int} the equivalent color int
 * 
 * @method PShapeSVG
 */
PShapeSVG.prototype.parseRGB = function (color) {
    var sub = color.substring(color.indexOf('(') + 1, color.indexOf(')'));
    var values = sub.split(", ");
    return (values[0] << 16) | (values[1] << 8) | (values[2]);
};
/**
 * The parseUnitSize() function parse a size that may have a suffix for its units.
 * Ignoring cases where this could also be a percentage.
 * The <A HREF="http://www.w3.org/TR/SVG/coords.html#Units">units</A> spec:
 * <UL>
 * <LI>"1pt" equals "1.25px" (and therefore 1.25 user units)
 * <LI>"1pc" equals "15px" (and therefore 15 user units)
 * <LI>"1mm" would be "3.543307px" (3.543307 user units)
 * <LI>"1cm" equals "35.43307px" (and therefore 35.43307 user units)
 * <LI>"1in" equals "90px" (and therefore 90 user units)
 * </UL>
 * 
 * @method PShapeSVG
 */
PShapeSVG.prototype.parseUnitSize = function (text) {
    var len = text.length - 2;
    if (len < 0) { return text; }
    if (text.indexOf("pt") === len) {
        return parseFloat(text.substring(0, len)) * 1.25;
    }
    if (text.indexOf("pc") === len) {
        return parseFloat(text.substring(0, len)) * 15;
    }
    if (text.indexOf("mm") === len) {
        return parseFloat(text.substring(0, len)) * 3.543307;
    }
    if (text.indexOf("cm") === len) {
        return parseFloat(text.substring(0, len)) * 35.43307;
    }
    if (text.indexOf("in") === len) {
        return parseFloat(text.substring(0, len)) * 90;
    }
    if (text.indexOf("px") === len) {
        return parseFloat(text.substring(0, len));
    }
    return parseFloat(text);
};
/**
 * The shape() function displays shapes to the screen.
 * Processing currently works with SVG shapes only.
 * The <b>shape</b> parameter specifies the shape to display and the <b>x</b>
 * and <b>y</b> parameters define the location of the shape from its
 * upper-left corner.
 * The shape is displayed at its original size unless the <b>width</b>
 * and <b>height</b> parameters specify a different size.
 * The <b>shapeMode()</b> function changes the way the parameters work.
 * A call to <b>shapeMode(CORNERS)</b>, for example, will change the width
 * and height parameters to define the x and y values of the opposite corner
 * of the shape.
 * <br><br>
 * Note complex shapes may draw awkwardly with P2D, P3D, and OPENGL. Those
 * renderers do not yet support shapes that have holes or complicated breaks.
 *
 * @param {PShape} shape       the shape to display
 * @param {int|float} x        x-coordinate of the shape
 * @param {int|float} y        y-coordinate of the shape
 * @param {int|float} width    width to display the shape
 * @param {int|float} height   height to display the shape
 *
 * @see PShape
 * @see loadShape()
 * @see shapeMode()
 */
shape = function (shape, x, y, width, height) {
    if (arguments.length >= 1 && arguments[0] !== null) {
        if (shape.isVisible()) {
            pushMatrix();
            if (curShapeMode === PConstants.CENTER) {
                if (arguments.length === 5) {
                    translate(x - width / 2, y - height / 2);
                    scale(width / shape.getWidth(), height / shape.getHeight());
                } else if (arguments.length === 3) {
                    translate(x - shape.getWidth() / 2, - shape.getHeight() / 2);
                } else {
                    translate(-shape.getWidth() / 2, -shape.getHeight() / 2);
                }
            } else if (curShapeMode === PConstants.CORNER) {
                if (arguments.length === 5) {
                    translate(x, y);
                    scale(width / shape.getWidth(), height / shape.getHeight());
                } else if (arguments.length === 3) {
                    translate(x, y);
                }
            } else if (curShapeMode === PConstants.CORNERS) {
                if (arguments.length === 5) {
                    width -= x;
                    height -= y;
                    translate(x, y);
                    scale(width / shape.getWidth(), height / shape.getHeight());
                } else if (arguments.length === 3) {
                    translate(x, y);
                }
            }
            shape.draw();
            if ((arguments.length === 1 && curShapeMode === PConstants.CENTER) || arguments.length > 1) {
                popMatrix();
            }
        }
    }
};

/**
 * The shapeMode() function modifies the location from which shapes draw.
 * The default mode is <b>shapeMode(CORNER)</b>, which specifies the
 * location to be the upper left corner of the shape and uses the third
 * and fourth parameters of <b>shape()</b> to specify the width and height.
 * The syntax <b>shapeMode(CORNERS)</b> uses the first and second parameters
 * of <b>shape()</b> to set the location of one corner and uses the third
 * and fourth parameters to set the opposite corner.
 * The syntax <b>shapeMode(CENTER)</b> draws the shape from its center point
 * and uses the third and forth parameters of <b>shape()</b> to specify the
 * width and height.
 * The parameter must be written in "ALL CAPS" because Processing syntax
 * is case sensitive.
 *
 * @param {int} mode One of CORNER, CORNERS, CENTER
 *
 * @see shape()
 * @see rectMode()
 */
shapeMode = function (mode) {
    curShapeMode = mode;
};

/**
 * The loadShape() function loads vector shapes into a variable of type PShape. Currently, only SVG files may be loaded.
 * 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.
 *
 * @param {String} filename     an SVG file
 *
 * @return {PShape} a object of type PShape or null
 * @see PShape
 * @see PApplet#shape()
 * @see PApplet#shapeMode()
 */
loadShape = function (filename) {
    if (arguments.length === 1) {
        if (filename.indexOf(".svg") > -1) {
            return new PShapeSVG(null, filename);
        }
    }
    return null;
};

/**
 * XMLElement is a representation of an XML object. The object is able to parse XML code
 *
 * @param {PApplet} parent   typically use "this"
 * @param {String} filename  name of the XML/SVG file to load
 * @param {String} xml       the xml/svg string
 * @param {String} fullname  the full name of the element
 * @param {String} namespace the namespace  of the URI
 * @param {String} systemID  the system ID of the XML data where the element starts
 * @param {Integer }lineNr   the line in the XML data where the element starts
 */
XMLElement = function () {
    this.attributes = [];
    this.children = [];
    this.fullName = null;
    this.name = null;
    this.namespace = "";
    this.content = null;
    this.parent = null;
    this.lineNr = "";
    this.systemID = "";
    this.type = "ELEMENT";

    if (arguments.length === 4) {
        this.fullName = arguments[0] || "";
        if (arguments[1]) {
            this.name = arguments[1];
        } else {
            var index = this.fullName.indexOf(':');
            if (index >= 0) {
                this.name = this.fullName.substring(index + 1);
            } else {
                this.name = this.fullName;
            }
        }
        this.namespace = arguments[1];
        this.lineNr = arguments[3];
        this.systemID = arguments[2];
    }
    else if ((arguments.length === 2 && arguments[1].indexOf(".") > -1)) {
        // filename or svg xml element
        this.parse(arguments[arguments.length - 1]);
    } else if (arguments.length === 1 && typeof arguments[0] === "string") {
        this.parse(arguments[0]);
    }
};
/**
 * XMLElement methods
 * missing: enumerateAttributeNames(), enumerateChildren(),
 * NOTE: parse does not work when a url is passed in
 */
XMLElement.prototype = {
    /**
     * The parse() function retrieves the file via ajax() and uses DOMParser()
     * parseFromString method to make an XML document
     * @addon
     *
     * @param {String} filename name of the XML/SVG file to load
     *
     * @throws ExceptionType Error loading document
     *
     * @see XMLElement#parseChildrenRecursive
     * 
     * @method XMLElement
     */
    parse: function (textstring) {
        var xmlDoc;
        try {
            var extension = textstring.substring(textstring.length - 4);
            if (extension === ".xml" || extension === ".svg") {
                textstring = ajax(textstring);
            }
            xmlDoc = new DOMParser().parseFromString(textstring, "text/xml");
            var elements = xmlDoc.documentElement;
            if (elements) {
                this.parseChildrenRecursive(null, elements);
            } else {
                throw ("Error loading document");
            }
            return this;
        } catch (e) {
            throw (e);
        }
    },
    /**
     * Internal helper function for parse().
     * Loops through the
     * @addon
     *
     * @param {XMLElement} parent                      the parent node
     * @param {XML document childNodes} elementpath    the remaining nodes that need parsing
     *
     * @return {XMLElement} the new element and its children elements
     * 
     * @method XMLElement
     */
    parseChildrenRecursive: function (parent, elementpath) {
        var xmlelement,
            xmlattribute,
            tmpattrib,
            l, m,
            child;
        if (!parent) { // this element is the root element
            this.fullName = elementpath.localName;
            this.name = elementpath.nodeName;
            xmlelement = this;
        } else { // this element has a parent
            xmlelement = new XMLElement(elementpath.localName, elementpath.nodeName, "", "");
            xmlelement.parent = parent;
        }

        // if this is a text node, return a PCData element, instead of an XML element.
        if (elementpath.nodeType === 3 && elementpath.textContent !== "") {
            return this.createPCDataElement(elementpath.textContent);
        }

        // bind all attributes
        for (l = 0, m = elementpath.attributes.length; l < m; l++) {
            tmpattrib = elementpath.attributes[l];
            xmlattribute = new XMLAttribute(tmpattrib.getname,
                tmpattrib.nodeName,
                tmpattrib.namespaceURI,
                tmpattrib.nodeValue,
                tmpattrib.nodeType);
            xmlelement.attributes.push(xmlattribute);
        }

        // bind all children
        for (l = 0, m = elementpath.childNodes.length; l < m; l++) {
            var node = elementpath.childNodes[l];
            if (node.nodeType === 1 || node.nodeType === 3) { // ELEMENT_NODE or TEXT_NODE
                child = xmlelement.parseChildrenRecursive(xmlelement, node);
                if (child !== null) {
                    xmlelement.children.push(child);
                }
            }
        }

        return xmlelement;
    },
    /**
     * The createElement() function Creates an empty element
     *
     * @param {String} fullName   the full name of the element
     * @param {String} namespace  the namespace URI
     * @param {String} systemID   the system ID of the XML data where the element starts
     * @param {int} lineNr    the line in the XML data where the element starts
     * 
     * @method XMLElement
     */
    createElement: function () {
        if (arguments.length === 2) {
            return new XMLElement(arguments[0], arguments[1], null, null);
        }
        return new XMLElement(arguments[0], arguments[1], arguments[2], arguments[3]);
    },
    /**
     * The createPCDataElement() function creates an element to be used for #PCDATA content.
     * Because Processing discards whitespace TEXT nodes, this method will not build an element
     * if the passed content is empty after trimming for whitespace.
     *
     * @return {XMLElement} new "test" XMLElement, or null if content consists only of whitespace
     * 
     * @method XMLElement
     */
    createPCDataElement: function (content) {
        if (content.replace(/^\s+$/g, "") === "") {
            return null;
        }
        var pcdata = new XMLElement();
        pcdata.content = content;
        pcdata.type = "TEXT";
        return pcdata;
    },
    /**
     * The hasAttribute() function returns whether an attribute exists
     *
     * @param {String} name      name of the attribute
     * @param {String} namespace the namespace URI of the attribute
     *
     * @return {boolean} true if the attribute exists
     * 
     * @method XMLElement
     */
    hasAttribute: function () {
        if (arguments.length === 1) {
            return this.getAttribute(arguments[0]) !== null;
        }
        if (arguments.length === 2) {
            return this.getAttribute(arguments[0], arguments[1]) !== null;
        }
    },
    /**
     * The equals() function checks to see if the XMLElement being passed in equals another XMLElement
     *
     * @param {XMLElement} rawElement the element to compare to
     *
     * @return {boolean} true if the element equals another element
     * 
     * @method XMLElement
     */
    equals: function (other) {
        if (!(other instanceof XMLElement)) {
            return false;
        }
        var i, j;
        if (this.name !== other.getLocalName()) { return false; }
        if (this.attributes.length !== other.getAttributeCount()) { return false; }
        // attributes may be ordered differently
        if (this.attributes.length !== other.attributes.length) { return false; }
        var attr_name, attr_ns, attr_value, attr_type, attr_other;
        for (i = 0, j = this.attributes.length; i < j; i++) {
            attr_name = this.attributes[i].getName();
            attr_ns = this.attributes[i].getNamespace();
            attr_other = other.findAttribute(attr_name, attr_ns);
            if (attr_other === null) { return false; }
            if (this.attributes[i].getValue() !== attr_other.getValue()) { return false; }
            if (this.attributes[i].getType() !== attr_other.getType()) { return false; }
        }
        // children must be ordered identically
        if (this.children.length !== other.getChildCount()) { return false; }
        if (this.children.length > 0) {
            var child1, child2;
            for (i = 0, j = this.children.length; i < j; i++) {
                child1 = this.getChild(i);
                child2 = other.getChild(i);
                if (!child1.equals(child2)) { return false; }
            }
            return true;
        }
        return (this.content === other.content);
    },
    /**
     * The getContent() function returns the content of an element. If there is no such content, null is returned
     *
     * @return {String} the (possibly null) content
     * 
     * @method XMLElement
     */
    getContent: function () {
        if (this.type === "TEXT") {
            return this.content;
        }
        var children = this.children;
        if (children.length === 1 && children[0].type === "TEXT") {
            return children[0].content;
        }
        return null;
    },
    /**
     * The getAttribute() function returns the value of an attribute
     *
     * @param {String} name         the non-null full name of the attribute
     * @param {String} namespace    the namespace URI, which may be null
     * @param {String} defaultValue the default value of the attribute
     *
     * @return {String} the value, or defaultValue if the attribute does not exist
     * 
     * @method XMLElement
     */
    getAttribute: function () {
        var attribute;
        if (arguments.length === 2) {
            attribute = this.findAttribute(arguments[0]);
            if (attribute) {
                return attribute.getValue();
            }
            return arguments[1];
        } else if (arguments.length === 1) {
            attribute = this.findAttribute(arguments[0]);
            if (attribute) {
                return attribute.getValue();
            }
            return null;
        } else if (arguments.length === 3) {
            attribute = this.findAttribute(arguments[0], arguments[1]);
            if (attribute) {
                return attribute.getValue();
            }
            return arguments[2];
        }
    },
    /**
     * The getStringAttribute() function returns the string attribute of the element
     * If the <b>defaultValue</b> parameter is used and the attribute doesn't exist, the <b>defaultValue</b> value is returned.
     * When calling the function without the <b>defaultValue</b> parameter, if the attribute doesn't exist, the value 0 is returned.
     *
     * @param name         the name of the attribute
     * @param defaultValue value returned if the attribute is not found
     *
     * @return {String} the value, or defaultValue if the attribute does not exist
     * 
     * @method XMLElement
     */
    getStringAttribute: function () {
        if (arguments.length === 1) {
            return this.getAttribute(arguments[0]);
        }
        if (arguments.length === 2) {
            return this.getAttribute(arguments[0], arguments[1]);
        }
        return this.getAttribute(arguments[0], arguments[1], arguments[2]);
    },
    /**
     * Processing 1.5 XML API wrapper for the generic String
     * attribute getter. This may only take one argument.
     */
    getString: function (attributeName) {
        return this.getStringAttribute(attributeName);
    },
    /**
     * The getFloatAttribute() function returns the float attribute of the element.
     * If the <b>defaultValue</b> parameter is used and the attribute doesn't exist, the <b>defaultValue</b> value is returned.
     * When calling the function without the <b>defaultValue</b> parameter, if the attribute doesn't exist, the value 0 is returned.
     *
     * @param name         the name of the attribute
     * @param defaultValue value returned if the attribute is not found
     *
     * @return {float} the value, or defaultValue if the attribute does not exist
     * 
     * @method XMLElement
     */
    getFloatAttribute: function () {
        if (arguments.length === 1) {
            return parseFloat(this.getAttribute(arguments[0], 0));
        }
        if (arguments.length === 2) {
            return this.getAttribute(arguments[0], arguments[1]);
        }
        return this.getAttribute(arguments[0], arguments[1], arguments[2]);
    },
    /**
     * Processing 1.5 XML API wrapper for the generic float
     * attribute getter. This may only take one argument.
     */
    getFloat: function (attributeName) {
        return this.getFloatAttribute(attributeName);
    },
    /**
     * The getIntAttribute() function returns the integer attribute of the element.
     * If the <b>defaultValue</b> parameter is used and the attribute doesn't exist, the <b>defaultValue</b> value is returned.
     * When calling the function without the <b>defaultValue</b> parameter, if the attribute doesn't exist, the value 0 is returned.
     *
     * @param name         the name of the attribute
     * @param defaultValue value returned if the attribute is not found
     *
     * @return {int} the value, or defaultValue if the attribute does not exist
     * 
     * @method XMLElement
     */
    getIntAttribute: function () {
        if (arguments.length === 1) {
            return this.getAttribute(arguments[0], 0);
        }
        if (arguments.length === 2) {
            return this.getAttribute(arguments[0], arguments[1]);
        }
        return this.getAttribute(arguments[0], arguments[1], arguments[2]);
    },
    /**
     * Processing 1.5 XML API wrapper for the generic int
     * attribute getter. This may only take one argument.
     */
    getInt: function (attributeName) {
        return this.getIntAttribute(attributeName);
    },
    /**
     * The hasChildren() function returns whether the element has children.
     *
     * @return {boolean} true if the element has children.
     * 
     * @method XMLElement
     */
    hasChildren: function () {
        return this.children.length > 0;
    },
    /**
     * The addChild() function adds a child element
     *
     * @param {XMLElement} child the non-null child to add.
     * 
     * @method XMLElement
     */
    addChild: function (child) {
        if (child !== null) {
            child.parent = this;
            this.children.push(child);
        }
    },
    /**
     * The insertChild() function inserts a child element at the index provided
     *
     * @param {XMLElement} child  the non-null child to add.
     * @param {int} index     where to put the child.
     * 
     * @method XMLElement
     */
    insertChild: function (child, index) {
        if (child) {
            if ((child.getLocalName() === null) && (!this.hasChildren())) {
                var lastChild = this.children[this.children.length - 1];
                if (lastChild.getLocalName() === null) {
                    lastChild.setContent(lastChild.getContent() + child.getContent());
                    return;
                }
            }
            child.parent = this;
            this.children.splice(index, 0, child);
        }
    },
    /**
     * The getChild() returns the child XMLElement as specified by the <b>index</b> parameter.
     * 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.
     * 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.
     *
     * @param {int} index     where to put the child.
     * @param {String} path       path to a particular element
     *
     * @return {XMLElement} the element
     * 
     * @method XMLElement
     */
    getChild: function () {
        if (typeof arguments[0] === "number") {
            return this.children[arguments[0]];
        }
        if (arguments[0].indexOf('/') !== -1) { // path was given
            this.getChildRecursive(arguments[0].split("/"), 0);
            return null;
        }
        var kid, kidName;
        for (var i = 0, j = this.getChildCount(); i < j; i++) {
            kid = this.getChild(i);
            kidName = kid.getName();
            if (kidName !== null && kidName === arguments[0]) {
                return kid;
            }
        }
        return null;
    },
    /**
     * The getChildren() returns all of the children as an XMLElement array.
     * 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.
     *
     * @param {String} path       element name or path/to/element
     *
     * @return {XMLElement} array of child elements that match
     *
     * @see XMLElement#getChildCount()
     * @see XMLElement#getChild()
     * 
     * @method XMLElement
     */
    getChildren: function () {
        if (arguments.length === 1) {
            if (typeof arguments[0] === "number") {
                return this.getChild(arguments[0]);
            }
            if (arguments[0].indexOf('/') !== -1) { // path was given
                return this.getChildrenRecursive(arguments[0].split("/"), 0);
            }
            var matches = [];
            var kid, kidName;
            for (var i = 0, j = this.getChildCount(); i < j; i++) {
                kid = this.getChild(i);
                kidName = kid.getName();
                if (kidName !== null && kidName === arguments[0]) {
                    matches.push(kid);
                }
            }
            return matches;
        }
        return this.children;
    },
    /**
     * The getChildCount() returns the number of children for the element.
     *
     * @return {int} the count
     *
     * @see XMLElement#getChild()
     * @see XMLElement#getChildren()
     * 
     * @method XMLElement
     */
    getChildCount: function () {
        return this.children.length;
    },
    /**
     * Internal helper function for getChild().
     *
     * @param {String[]} items   result of splitting the query on slashes
     * @param {int} offset   where in the items[] array we're currently looking
     *
     * @return {XMLElement} matching element or null if no match
     * 
     * @method XMLElement
     */
    getChildRecursive: function (items, offset) {
        var kid, kidName;
        for (var i = 0, j = this.getChildCount(); i < j; i++) {
            kid = this.getChild(i);
            kidName = kid.getName();
            if (kidName !== null && kidName === items[offset]) {
                if (offset === items.length - 1) {
                    return kid;
                }
                offset += 1;
                return kid.getChildRecursive(items, offset);
            }
        }
        return null;
    },
    /**
     * Internal helper function for getChildren().
     *
     * @param {String[]} items   result of splitting the query on slashes
     * @param {int} offset   where in the items[] array we're currently looking
     *
     * @return {XMLElement[]} matching elements or empty array if no match
     * 
     * @method XMLElement
     */
    getChildrenRecursive: function (items, offset) {
        if (offset === items.length - 1) {
            return this.getChildren(items[offset]);
        }
        var matches = this.getChildren(items[offset]);
        var kidMatches = [];
        for (var i = 0; i < matches.length; i++) {
            kidMatches = kidMatches.concat(matches[i].getChildrenRecursive(items, offset + 1));
        }
        return kidMatches;
    },
    /**
     * The isLeaf() function returns whether the element is a leaf element.
     *
     * @return {boolean} true if the element has no children.
     * 
     * @method XMLElement
     */
    isLeaf: function () {
        return !this.hasChildren();
    },
    /**
     * The listChildren() function put the names of all children into an array. Same as looping through
     * each child and calling getName() on each XMLElement.
     *
     * @return {String[]} a list of element names.
     * 
     * @method XMLElement
     */
    listChildren: function () {
        var arr = [];
        for (var i = 0, j = this.children.length; i < j; i++) {
            arr.push(this.getChild(i).getName());
        }
        return arr;
    },
    /**
     * The removeAttribute() function removes an attribute
     *
     * @param {String} name        the non-null name of the attribute.
     * @param {String} namespace   the namespace URI of the attribute, which may be null.
     * 
     * @method XMLElement
     */
    removeAttribute: function (name, namespace) {
        this.namespace = namespace || "";
        for (var i = 0, j = this.attributes.length; i < j; i++) {
            if (this.attributes[i].getName() === name && this.attributes[i].getNamespace() === this.namespace) {
                this.attributes.splice(i, 1);
                break;
            }
        }
    },
    /**
     * The removeChild() removes a child element.
     *
     * @param {XMLElement} child      the the non-null child to be renoved
     * 
     * @method XMLElement
     */
    removeChild: function (child) {
        if (child) {
            for (var i = 0, j = this.children.length; i < j; i++) {
                if (this.children[i].equals(child)) {
                    this.children.splice(i, 1);
                    break;
                }
            }
        }
    },
    /**
     * The removeChildAtIndex() removes the child located at a certain index
     *
     * @param {int} index      the index of the child, where the first child has index 0
     * 
     * @method XMLElement
     */
    removeChildAtIndex: function (index) {
        if (this.children.length > index) { //make sure its not outofbounds
            this.children.splice(index, 1);
        }
    },
    /**
     * The findAttribute() function searches an attribute
     *
     * @param {String} name        fullName the non-null full name of the attribute
     * @param {String} namespace   the name space, which may be null
     *
     * @return {XMLAttribute} the attribute, or null if the attribute does not exist.
     * 
     * @method XMLElement
     */
    findAttribute: function (name, namespace) {
        this.namespace = namespace || "";
        for (var i = 0, j = this.attributes.length; i < j; i++) {
            if (this.attributes[i].getName() === name && this.attributes[i].getNamespace() === this.namespace) {
                return this.attributes[i];
            }
        }
        return null;
    },
    /**
     * The setAttribute() function sets an attribute.
     *
     * @param {String} name        the non-null full name of the attribute
     * @param {String} namespace   the non-null value of the attribute
     * 
     * @method XMLElement
     */
    setAttribute: function () {
        var attr;
        if (arguments.length === 3) {
            var index = arguments[0].indexOf(':');
            var name = arguments[0].substring(index + 1);
            attr = this.findAttribute(name, arguments[1]);
            if (attr) {
                attr.setValue(arguments[2]);
            } else {
                attr = new XMLAttribute(arguments[0], name, arguments[1], arguments[2], "CDATA");
                this.attributes.push(attr);
            }
        } else {
            attr = this.findAttribute(arguments[0]);
            if (attr) {
                attr.setValue(arguments[1]);
            } else {
                attr = new XMLAttribute(arguments[0], arguments[0], null, arguments[1], "CDATA");
                this.attributes.push(attr);
            }
        }
    },
    /**
     * Processing 1.5 XML API wrapper for the generic String
     * attribute setter. This must take two arguments.
     */
    setString: function (attribute, value) {
        this.setAttribute(attribute, value);
    },
    /**
     * Processing 1.5 XML API wrapper for the generic int
     * attribute setter. This must take two arguments.
     */
    setInt: function (attribute, value) {
        this.setAttribute(attribute, value);
    },
    /**
     * Processing 1.5 XML API wrapper for the generic float
     * attribute setter. This must take two arguments.
     */
    setFloat: function (attribute, value) {
        this.setAttribute(attribute, value);
    },
    /**
     * The setContent() function sets the #PCDATA content. It is an error to call this method with a
     * non-null value if there are child objects.
     *
     * @param {String} content     the (possibly null) content
     * 
     * @method XMLElement
     */
    setContent: function (content) {
        if (this.children.length > 0) {
            Processing.debug("Tried to set content for XMLElement with children");
        }
        this.content = content;
    },
    /**
     * The setName() function sets the full name. This method also sets the short name and clears the
     * namespace URI.
     *
     * @param {String} name        the non-null name
     * @param {String} namespace   the namespace URI, which may be null.
     * 
     * @method XMLElement
     */
    setName: function () {
        if (arguments.length === 1) {
            this.name = arguments[0];
            this.fullName = arguments[0];
            this.namespace = null;
        } else {
            var index = arguments[0].indexOf(':');
            if ((arguments[1] === null) || (index < 0)) {
                this.name = arguments[0];
            } else {
                this.name = arguments[0].substring(index + 1);
            }
            this.fullName = arguments[0];
            this.namespace = arguments[1];
        }
    },
    /**
     * The getName() function returns the full name (i.e. the name including an eventual namespace
     * prefix) of the element.
     *
     * @return {String} the name, or null if the element only contains #PCDATA.
     * 
     * @method XMLElement
     */
    getName: function () {
        return this.fullName;
    },
    /**
     * The getLocalName() function returns the local name (i.e. the name excluding an eventual namespace
     * prefix) of the element.
     *
     * @return {String} the name, or null if the element only contains #PCDATA.
     * 
     * @method XMLElement
     */
    getLocalName: function () {
        return this.name;
    },
    /**
     * The getAttributeCount() function returns the number of attributes for the node
     * that this XMLElement represents.
     *
     * @return {int} the number of attributes in this XMLElement
     * 
     * @method XMLElement
     */
    getAttributeCount: function () {
        return this.attributes.length;
    },
    /**
     * The toString() function returns the XML definition of an XMLElement.
     *
     * @return {String} the XML definition of this XMLElement
     * 
     * @method XMLElement
     */
    toString: function () {
        // shortcut for text nodes
        if (this.type === "TEXT") { return this.content; }

        // real XMLElements
        var tagstring = (this.namespace !== "" && this.namespace !== this.name ? this.namespace + ":" : "") + this.name;
        var xmlstring = "<" + tagstring;
        var a, c;

        // serialize the attributes to XML string
        for (a = 0; a < this.attributes.length; a++) {
            var attr = this.attributes[a];
            xmlstring += " " + attr.getName() + "=" + '"' + attr.getValue() + '"';
        }

        // serialize all children to XML string
        if (this.children.length === 0) {
            if (this.content === "") {
                xmlstring += "/>";
            } else {
                xmlstring += ">" + this.content + "</" + tagstring + ">";
            }
        } else {
            xmlstring += ">";
            for (c = 0; c < this.children.length; c++) {
                xmlstring += this.children[c].toString();
            }
            xmlstring += "</" + tagstring + ">";
        }
        return xmlstring;
    }
};

/**
 * static Processing 1.5 XML API wrapper for the
 * parse method. This may only take one argument.
 */
XMLElement.parse = function (xmlstring) {
    var element = new XMLElement();
    element.parse(xmlstring);
    return element;
};

////////////////////////////////////////////////////////////////////////////
// 2D Matrix
////////////////////////////////////////////////////////////////////////////

/**
 * PMatrix2D is a 3x2 affine matrix implementation. The constructor accepts another PMatrix2D or a list of six float elements.
 * If no parameters are provided the matrix is set to the identity matrix.
 *
 * @param {PMatrix2D} matrix  the initial matrix to set to
 * @param {float} m00         the first element of the matrix
 * @param {float} m01         the second element of the matrix
 * @param {float} m02         the third element of the matrix
 * @param {float} m10         the fourth element of the matrix
 * @param {float} m11         the fifth element of the matrix
 * @param {float} m12         the sixth element of the matrix
 */
PMatrix2D = function () {
    if (arguments.length === 0) {
        this.reset();
    } else if (arguments.length === 1 && arguments[0] instanceof PMatrix2D) {
        this.set(arguments[0].array());
    } else if (arguments.length === 6) {
        this.set(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5]);
    }
};
/**
 * PMatrix2D methods
 */
PMatrix2D.prototype = {
    /**
     * The set() function sets the matrix elements. The function accepts either another PMatrix2D, an array of elements, or a list of six floats.
     *
     * @param {PMatrix2D} matrix    the matrix to set this matrix to
     * @param {float[]} elements    an array of elements to set this matrix to
     * @param {float} m00           the first element of the matrix
     * @param {float} m01           the third element of the matrix
     * @param {float} m10           the fourth element of the matrix
     * @param {float} m11           the fith element of the matrix
     * @param {float} m12           the sixth element of the matrix
     * 
     * @method PMatrix2D
     */
    set: function () {
        if (arguments.length === 6) {
            var a = arguments;
            this.set([a[0], a[1], a[2],
            a[3], a[4], a[5]]);
        } else if (arguments.length === 1 && arguments[0] instanceof PMatrix2D) {
            this.elements = arguments[0].array();
        } else if (arguments.length === 1 && arguments[0] instanceof Array) {
            this.elements = arguments[0].slice();
        }
    },
    /**
     * The get() function returns a copy of this PMatrix2D.
     *
     * @return {PMatrix2D} a copy of this PMatrix2D
     * 
     * @method PMatrix2D
     */
    get: function () {
        var outgoing = new PMatrix2D();
        outgoing.set(this.elements);
        return outgoing;
    },
    /**
     * The reset() function sets this PMatrix2D to the identity matrix.
     * 
     * @method PMatrix2D
     */
    reset: function () {
        this.set([1, 0, 0, 0, 1, 0]);
    },
    /**
     * The array() function returns a copy of the element values.
     * @addon
     *
     * @return {float[]} returns a copy of the element values
     * 
     * @method PMatrix2D
     */
    array: function array() {
        return this.elements.slice();
    },
    /**
     * The translate() function translates this matrix by moving the current coordinates to the location specified by tx and ty.
     *
     * @param {float} tx  the x-axis coordinate to move to
     * @param {float} ty  the y-axis coordinate to move to
     * 
     * @method PMatrix2D
     */
    translate: function (tx, ty) {
        this.elements[2] = tx * this.elements[0] + ty * this.elements[1] + this.elements[2];
        this.elements[5] = tx * this.elements[3] + ty * this.elements[4] + this.elements[5];
    },
    /**
     * The invTranslate() function translates this matrix by moving the current coordinates to the negative location specified by tx and ty.
     *
     * @param {float} tx  the x-axis coordinate to move to
     * @param {float} ty  the y-axis coordinate to move to
     * 
     * @method PMatrix2D
     */
    invTranslate: function (tx, ty) {
        this.translate(-tx, -ty);
    },
    /**
    * The transpose() function is not used in processingjs.
    * 
    * @method PMatrix2D
    */
    transpose: function () {
        // Does nothing in Processing.
    },
    /**
     * The mult() function multiplied this matrix.
     * If two array elements are passed in the function will multiply a two element vector against this matrix.
     * If target is null or not length four, a new float array will be returned.
     * The values for vec and target can be the same (though that's less efficient).
     * If two PVectors are passed in the function multiply the x and y coordinates of a PVector against this matrix.
     *
     * @param {PVector} source, target  the PVectors used to multiply this matrix
     * @param {float[]} source, target  the arrays used to multiply this matrix
     *
     * @return {PVector|float[]} returns a PVector or an array representing the new matrix
     * 
     * @method PMatrix2D
     */
    mult: function (source, target) {
        var x, y;
        if (source instanceof PVector) {
            x = source.x;
            y = source.y;
            if (!target) {
                target = new PVector();
            }
        } else if (source instanceof Array) {
            x = source[0];
            y = source[1];
            if (!target) {
                target = [];
            }
        }
        if (target instanceof Array) {
            target[0] = this.elements[0] * x + this.elements[1] * y + this.elements[2];
            target[1] = this.elements[3] * x + this.elements[4] * y + this.elements[5];
        } else if (target instanceof PVector) {
            target.x = this.elements[0] * x + this.elements[1] * y + this.elements[2];
            target.y = this.elements[3] * x + this.elements[4] * y + this.elements[5];
            target.z = 0;
        }
        return target;
    },
    /**
     * The multX() function calculates the x component of a vector from a transformation.
     *
     * @param {float} x the x component of the vector being transformed
     * @param {float} y the y component of the vector being transformed
     *
     * @return {float} returnes the result of the calculation
     * 
     * @method PMatrix2D
     */
    multX: function (x, y) {
        return (x * this.elements[0] + y * this.elements[1] + this.elements[2]);
    },
    /**
     * The multY() function calculates the y component of a vector from a transformation.
     *
     * @param {float} x the x component of the vector being transformed
     * @param {float} y the y component of the vector being transformed
     *
     * @return {float} returnes the result of the calculation
     * 
     * @method PMatrix2D
     */
    multY: function (x, y) {
        return (x * this.elements[3] + y * this.elements[4] + this.elements[5]);
    },
    /**
     * The skewX() function skews the matrix along the x-axis the amount specified by the angle parameter.
     * Angles should be specified in radians (values from 0 to PI*2) or converted to radians with the <b>radians()</b> function.
     *
     * @param {float} angle  angle of skew specified in radians
     * 
     * @method PMatrix2D
     */
    skewX: function (angle) {
        this.apply(1, 0, 1, angle, 0, 0);
    },
    /**
     * The skewY() function skews the matrix along the y-axis the amount specified by the angle parameter.
     * Angles should be specified in radians (values from 0 to PI*2) or converted to radians with the <b>radians()</b> function.
     *
     * @param {float} angle  angle of skew specified in radians
     * 
     * @method PMatrix2D
     */
    skewY: function (angle) {
        this.apply(1, 0, 1, 0, angle, 0);
    },
    /**
     * The determinant() function calvculates the determinant of this matrix.
     *
     * @return {float} the determinant of the matrix
     * 
     * @method PMatrix2D
     */
    determinant: function () {
        return (this.elements[0] * this.elements[4] - this.elements[1] * this.elements[3]);
    },
    /**
     * The invert() function inverts this matrix
     *
     * @return {boolean} true if successful
     * 
     * @method PMatrix2D
     */
    invert: function () {
        var d = this.determinant();
        if (Math.abs(d) > PConstants.MIN_INT) {
            var old00 = this.elements[0];
            var old01 = this.elements[1];
            var old02 = this.elements[2];
            var old10 = this.elements[3];
            var old11 = this.elements[4];
            var old12 = this.elements[5];
            this.elements[0] = old11 / d;
            this.elements[3] = -old10 / d;
            this.elements[1] = -old01 / d;
            this.elements[4] = old00 / d;
            this.elements[2] = (old01 * old12 - old11 * old02) / d;
            this.elements[5] = (old10 * old02 - old00 * old12) / d;
            return true;
        }
        return false;
    },
    /**
     * 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.
     * This is equivalent to a two parameter call.
     *
     * @param {float} sx  the amount to scale on the x-axis
     * @param {float} sy  the amount to scale on the y-axis
     * 
     * @method PMatrix2D
     */
    scale: function (sx, sy) {
        if (sx && !sy) {
            sy = sx;
        }
        if (sx && sy) {
            this.elements[0] *= sx;
            this.elements[1] *= sy;
            this.elements[3] *= sx;
            this.elements[4] *= sy;
        }
    },
    /**
     * 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.
     * This is equivalent to a two parameter call.
     *
     * @param {float} sx  the amount to scale on the x-axis
     * @param {float} sy  the amount to scale on the y-axis
     * 
     * @method PMatrix2D
     */
    invScale: function (sx, sy) {
        if (sx && !sy) {
            sy = sx;
        }
        this.scale(1 / sx, 1 / sy);
    },
    /**
     * 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.
     *
     * @param {PMatrix2D} matrix    the matrix to apply this matrix to
     * @param {float} m00           the first element of the matrix
     * @param {float} m01           the third element of the matrix
     * @param {float} m10           the fourth element of the matrix
     * @param {float} m11           the fith element of the matrix
     * @param {float} m12           the sixth element of the matrix
     * 
     * @method PMatrix2D
     */
    apply: function () {
        var source;
        if (arguments.length === 1 && arguments[0] instanceof PMatrix2D) {
            source = arguments[0].array();
        } else if (arguments.length === 6) {
            source = Array.prototype.slice.call(arguments);
        } else if (arguments.length === 1 && arguments[0] instanceof Array) {
            source = arguments[0];
        }

        var result = [0, 0, this.elements[2],
            0, 0, this.elements[5]];
        var e = 0;
        for (var row = 0; row < 2; row++) {
            for (var col = 0; col < 3; col++, e++) {
                result[e] += this.elements[row * 3 + 0] * source[col + 0] +
                    this.elements[row * 3 + 1] * source[col + 3];
            }
        }
        this.elements = result.slice();
    },
    /**
     * 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.
     *
     * @param {PMatrix2D} matrix    the matrix to apply this matrix to
     * @param {float} m00           the first element of the matrix
     * @param {float} m01           the third element of the matrix
     * @param {float} m10           the fourth element of the matrix
     * @param {float} m11           the fith element of the matrix
     * @param {float} m12           the sixth element of the matrix
     * 
     * @method PMatrix2D
     */
    preApply: function () {
        var source;
        if (arguments.length === 1 && arguments[0] instanceof PMatrix2D) {
            source = arguments[0].array();
        } else if (arguments.length === 6) {
            source = Array.prototype.slice.call(arguments);
        } else if (arguments.length === 1 && arguments[0] instanceof Array) {
            source = arguments[0];
        }
        var result = [0, 0, source[2],
            0, 0, source[5]];
        result[2] = source[2] + this.elements[2] * source[0] + this.elements[5] * source[1];
        result[5] = source[5] + this.elements[2] * source[3] + this.elements[5] * source[4];
        result[0] = this.elements[0] * source[0] + this.elements[3] * source[1];
        result[3] = this.elements[0] * source[3] + this.elements[3] * source[4];
        result[1] = this.elements[1] * source[0] + this.elements[4] * source[1];
        result[4] = this.elements[1] * source[3] + this.elements[4] * source[4];
        this.elements = result.slice();
    },
    /**
     * The rotate() function rotates the matrix.
     *
     * @param {float} angle         the angle of rotation in radiants
     * 
     * @method PMatrix2D
     */
    rotate: function (angle) {
        // XXX(jeresig)
        var c = cos(angle);
        var s = sin(angle);
        var temp1 = this.elements[0];
        var temp2 = this.elements[1];
        this.elements[0] = c * temp1 + s * temp2;
        this.elements[1] = -s * temp1 + c * temp2;
        temp1 = this.elements[3];
        temp2 = this.elements[4];
        this.elements[3] = c * temp1 + s * temp2;
        this.elements[4] = -s * temp1 + c * temp2;
    },
    /**
     * The rotateZ() function rotates the matrix.
     *
     * @param {float} angle         the angle of rotation in radiants
     * 
     * @method PMatrix2D
     */
    rotateZ: function (angle) {
        this.rotate(angle);
    },
    /**
     * The invRotateZ() function rotates the matrix in opposite direction.
     *
     * @param {float} angle         the angle of rotation in radiants
     * 
     * @method PMatrix2D
     */
    invRotateZ: function (angle) {
        // XXX(jeresig)
        this.rotateZ(angle - (angleMode === "degrees" ? 180 : Math.PI));
    },
    /**
     * The print() function prints out the elements of this matrix
     * 
     * @method PMatrix2D
     */
    print: function () {
        var digits = printMatrixHelper(this.elements);
        var output = "" + nfs(this.elements[0], digits, 4) + " " +
            nfs(this.elements[1], digits, 4) + " " +
            nfs(this.elements[2], digits, 4) + "\n" +
            nfs(this.elements[3], digits, 4) + " " +
            nfs(this.elements[4], digits, 4) + " " +
            nfs(this.elements[5], digits, 4) + "\n\n";
        println(output);
    }
};

/**
 * PMatrix3D is a 4x4  matrix implementation. The constructor accepts another PMatrix3D or a list of six or sixteen float elements.
 * If no parameters are provided the matrix is set to the identity matrix.
 */
PMatrix3D = function () {
    // When a matrix is created, it is set to an identity matrix
    this.reset();
};
/**
 * PMatrix3D methods
 */
PMatrix3D.prototype = {
    /**
     * 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.
     *
     * @param {PMatrix3D} matrix    the initial matrix to set to
     * @param {float[]} elements    an array of elements to set this matrix to
     * @param {float} m00           the first element of the matrix
     * @param {float} m01           the second element of the matrix
     * @param {float} m02           the third element of the matrix
     * @param {float} m03           the fourth element of the matrix
     * @param {float} m10           the fifth element of the matrix
     * @param {float} m11           the sixth element of the matrix
     * @param {float} m12           the seventh element of the matrix
     * @param {float} m13           the eight element of the matrix
     * @param {float} m20           the nineth element of the matrix
     * @param {float} m21           the tenth element of the matrix
     * @param {float} m22           the eleventh element of the matrix
     * @param {float} m23           the twelveth element of the matrix
     * @param {float} m30           the thirteenth element of the matrix
     * @param {float} m31           the fourtheenth element of the matrix
     * @param {float} m32           the fivetheenth element of the matrix
     * @param {float} m33           the sixteenth element of the matrix
     * 
     * @method PMatrix2D
     */
    set: function () {
        if (arguments.length === 16) {
            this.elements = Array.prototype.slice.call(arguments);
        } else if (arguments.length === 1 && arguments[0] instanceof PMatrix3D) {
            this.elements = arguments[0].array();
        } else if (arguments.length === 1 && arguments[0] instanceof Array) {
            this.elements = arguments[0].slice();
        }
    },
    /**
     * The get() function returns a copy of this PMatrix3D.
     *
     * @return {PMatrix3D} a copy of this PMatrix3D
     * 
     * @method PMatrix3D
     */
    get: function () {
        var outgoing = new PMatrix3D();
        outgoing.set(this.elements);
        return outgoing;
    },
    /**
     * The reset() function sets this PMatrix3D to the identity matrix.
     * 
     * @method PMatrix3D
     */
    reset: function () {
        this.elements = [1, 0, 0, 0,
            0, 1, 0, 0,
            0, 0, 1, 0,
            0, 0, 0, 1];
    },
    /**
     * The array() function returns a copy of the element values.
     * @addon
     *
     * @return {float[]} returns a copy of the element values
     * 
     * @method PMatrix3D
     */
    array: function array() {
        return this.elements.slice();
    },
    /**
     * The translate() function translates this matrix by moving the current coordinates to the location specified by tx, ty, and tz.
     *
     * @param {float} tx  the x-axis coordinate to move to
     * @param {float} ty  the y-axis coordinate to move to
     * @param {float} tz  the z-axis coordinate to move to
     * 
     * @method PMatrix3D
     */
    translate: function (tx, ty, tz) {
        if (tz === undef) {
            tz = 0;
        }

        this.elements[3] += tx * this.elements[0] + ty * this.elements[1] + tz * this.elements[2];
        this.elements[7] += tx * this.elements[4] + ty * this.elements[5] + tz * this.elements[6];
        this.elements[11] += tx * this.elements[8] + ty * this.elements[9] + tz * this.elements[10];
        this.elements[15] += tx * this.elements[12] + ty * this.elements[13] + tz * this.elements[14];
    },
    /**
     * The transpose() function transpose this matrix.
     * 
     * @method PMatrix3D
     */
    transpose: function () {
        var temp = this.elements[4];
        this.elements[4] = this.elements[1];
        this.elements[1] = temp;

        temp = this.elements[8];
        this.elements[8] = this.elements[2];
        this.elements[2] = temp;

        temp = this.elements[6];
        this.elements[6] = this.elements[9];
        this.elements[9] = temp;

        temp = this.elements[3];
        this.elements[3] = this.elements[12];
        this.elements[12] = temp;

        temp = this.elements[7];
        this.elements[7] = this.elements[13];
        this.elements[13] = temp;

        temp = this.elements[11];
        this.elements[11] = this.elements[14];
        this.elements[14] = temp;
    },
    /**
     * The mult() function multiplied this matrix.
     * If two array elements are passed in the function will multiply a two element vector against this matrix.
     * If target is null or not length four, a new float array will be returned.
     * The values for vec and target can be the same (though that's less efficient).
     * If two PVectors are passed in the function multiply the x and y coordinates of a PVector against this matrix.
     *
     * @param {PVector} source, target  the PVectors used to multiply this matrix
     * @param {float[]} source, target  the arrays used to multiply this matrix
     *
     * @return {PVector|float[]} returns a PVector or an array representing the new matrix
     * 
     * @method PMatrix3D
     */
    mult: function (source, target) {
        var x, y, z, w;
        if (source instanceof PVector) {
            x = source.x;
            y = source.y;
            z = source.z;
            w = 1;
            if (!target) {
                target = new PVector();
            }
        } else if (source instanceof Array) {
            x = source[0];
            y = source[1];
            z = source[2];
            w = source[3] || 1;

            if (!target || (target.length !== 3 && target.length !== 4)) {
                target = [0, 0, 0];
            }
        }

        if (target instanceof Array) {
            if (target.length === 3) {
                target[0] = this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3];
                target[1] = this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7];
                target[2] = this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11];
            } else if (target.length === 4) {
                target[0] = this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3] * w;
                target[1] = this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7] * w;
                target[2] = this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11] * w;
                target[3] = this.elements[12] * x + this.elements[13] * y + this.elements[14] * z + this.elements[15] * w;
            }
        }
        if (target instanceof PVector) {
            target.x = this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3];
            target.y = this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7];
            target.z = this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11];
        }
        return target;
    },
    /**
     * 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.
     *
     * @param {PMatrix3D} matrix    the matrix to apply this matrix to
     * @param {float} m00           the first element of the matrix
     * @param {float} m01           the second element of the matrix
     * @param {float} m02           the third element of the matrix
     * @param {float} m03           the fourth element of the matrix
     * @param {float} m10           the fifth element of the matrix
     * @param {float} m11           the sixth element of the matrix
     * @param {float} m12           the seventh element of the matrix
     * @param {float} m13           the eight element of the matrix
     * @param {float} m20           the nineth element of the matrix
     * @param {float} m21           the tenth element of the matrix
     * @param {float} m22           the eleventh element of the matrix
     * @param {float} m23           the twelveth element of the matrix
     * @param {float} m30           the thirteenth element of the matrix
     * @param {float} m31           the fourtheenth element of the matrix
     * @param {float} m32           the fivetheenth element of the matrix
     * @param {float} m33           the sixteenth element of the matrix
     * 
     * @method PMatrix3D
     */
    preApply: function () {
        var source;
        if (arguments.length === 1 && arguments[0] instanceof PMatrix3D) {
            source = arguments[0].array();
        } else if (arguments.length === 16) {
            source = Array.prototype.slice.call(arguments);
        } else if (arguments.length === 1 && arguments[0] instanceof Array) {
            source = arguments[0];
        }

        var result = [0, 0, 0, 0,
            0, 0, 0, 0,
            0, 0, 0, 0,
            0, 0, 0, 0];
        var e = 0;
        for (var row = 0; row < 4; row++) {
            for (var col = 0; col < 4; col++, e++) {
                result[e] += this.elements[col + 0] * source[row * 4 + 0] + this.elements[col + 4] *
                    source[row * 4 + 1] + this.elements[col + 8] * source[row * 4 + 2] +
                    this.elements[col + 12] * source[row * 4 + 3];
            }
        }
        this.elements = result.slice();
    },
    /**
     * 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.
     *
     * @param {PMatrix3D} matrix    the matrix to apply this matrix to
     * @param {float} m00           the first element of the matrix
     * @param {float} m01           the second element of the matrix
     * @param {float} m02           the third element of the matrix
     * @param {float} m03           the fourth element of the matrix
     * @param {float} m10           the fifth element of the matrix
     * @param {float} m11           the sixth element of the matrix
     * @param {float} m12           the seventh element of the matrix
     * @param {float} m13           the eight element of the matrix
     * @param {float} m20           the nineth element of the matrix
     * @param {float} m21           the tenth element of the matrix
     * @param {float} m22           the eleventh element of the matrix
     * @param {float} m23           the twelveth element of the matrix
     * @param {float} m30           the thirteenth element of the matrix
     * @param {float} m31           the fourtheenth element of the matrix
     * @param {float} m32           the fivetheenth element of the matrix
     * @param {float} m33           the sixteenth element of the matrix
     * 
     * @method PMatrix3D
     */
    apply: function () {
        var source;
        if (arguments.length === 1 && arguments[0] instanceof PMatrix3D) {
            source = arguments[0].array();
        } else if (arguments.length === 16) {
            source = Array.prototype.slice.call(arguments);
        } else if (arguments.length === 1 && arguments[0] instanceof Array) {
            source = arguments[0];
        }

        var result = [0, 0, 0, 0,
            0, 0, 0, 0,
            0, 0, 0, 0,
            0, 0, 0, 0];
        var e = 0;
        for (var row = 0; row < 4; row++) {
            for (var col = 0; col < 4; col++, e++) {
                result[e] += this.elements[row * 4 + 0] * source[col + 0] + this.elements[row * 4 + 1] *
                    source[col + 4] + this.elements[row * 4 + 2] * source[col + 8] +
                    this.elements[row * 4 + 3] * source[col + 12];
            }
        }
        this.elements = result.slice();
    },
    /**
     * The rotate() function rotates the matrix.
     *
     * @param {float} angle         the angle of rotation in radiants
     * 
     * @method PMatrix3D
     */
    rotate: function (angle, v0, v1, v2) {
        if (!v1) {
            this.rotateZ(angle);
        } else {
            // TODO should make sure this vector is normalized
            var c = cos(angle);
            var s = sin(angle);
            var t = 1.0 - c;

            this.apply((t * v0 * v0) + c,
                (t * v0 * v1) - (s * v2),
                (t * v0 * v2) + (s * v1),
                0,
                (t * v0 * v1) + (s * v2),
                (t * v1 * v1) + c,
                (t * v1 * v2) - (s * v0),
                0,
                (t * v0 * v2) - (s * v1),
                (t * v1 * v2) + (s * v0),
                (t * v2 * v2) + c,
                0,
                0, 0, 0, 1);
        }
    },
    /**
     * The invApply() function applies the inverted matrix to this matrix.
     *
     * @param {float} m00           the first element of the matrix
     * @param {float} m01           the second element of the matrix
     * @param {float} m02           the third element of the matrix
     * @param {float} m03           the fourth element of the matrix
     * @param {float} m10           the fifth element of the matrix
     * @param {float} m11           the sixth element of the matrix
     * @param {float} m12           the seventh element of the matrix
     * @param {float} m13           the eight element of the matrix
     * @param {float} m20           the nineth element of the matrix
     * @param {float} m21           the tenth element of the matrix
     * @param {float} m22           the eleventh element of the matrix
     * @param {float} m23           the twelveth element of the matrix
     * @param {float} m30           the thirteenth element of the matrix
     * @param {float} m31           the fourtheenth element of the matrix
     * @param {float} m32           the fivetheenth element of the matrix
     * @param {float} m33           the sixteenth element of the matrix
     *
     * @return {boolean} returns true if the operation was successful.
     * 
     * @method PMatrix3D
     */
    invApply: function () {
        if (inverseCopy === undef) {
            inverseCopy = new PMatrix3D();
        }
        var a = arguments;
        inverseCopy.set(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]);

        if (!inverseCopy.invert()) {
            return false;
        }
        this.preApply(inverseCopy);
        return true;
    },
    /**
     * The rotateZ() function rotates the matrix.
     *
     * @param {float} angle         the angle of rotation in radiants
     * 
     * @method PMatrix3D
     */
    rotateX: function (angle) {
        var c = cos(angle);
        var s = sin(angle);
        this.apply([1, 0, 0, 0, 0, c, -s, 0, 0, s, c, 0, 0, 0, 0, 1]);
    },
    /**
     * The rotateY() function rotates the matrix.
     *
     * @param {float} angle         the angle of rotation in radiants
     * 
     * @method PMatrix3D
     */
    rotateY: function (angle) {
        var c = cos(angle);
        var s = sin(angle);
        this.apply([c, 0, s, 0, 0, 1, 0, 0, -s, 0, c, 0, 0, 0, 0, 1]);
    },
    /**
     * The rotateZ() function rotates the matrix.
     *
     * @param {float} angle         the angle of rotation in radiants
     * 
     * @method PMatrix3D
     */
    rotateZ: function (angle) {
        // XXX(jeresig)
        var c = cos(angle);
        var s = sin(angle);
        this.apply([c, -s, 0, 0, s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
    },
    /**
     * 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.
     * This is equivalent to a three parameter call.
     *
     * @param {float} sx  the amount to scale on the x-axis
     * @param {float} sy  the amount to scale on the y-axis
     * @param {float} sz  the amount to scale on the z-axis
     * 
     * @method PMatrix3D
     */
    scale: function (sx, sy, sz) {
        if (sx && !sy && !sz) {
            sy = sz = sx;
        } else if (sx && sy && !sz) {
            sz = 1;
        }

        if (sx && sy && sz) {
            this.elements[0] *= sx;
            this.elements[1] *= sy;
            this.elements[2] *= sz;
            this.elements[4] *= sx;
            this.elements[5] *= sy;
            this.elements[6] *= sz;
            this.elements[8] *= sx;
            this.elements[9] *= sy;
            this.elements[10] *= sz;
            this.elements[12] *= sx;
            this.elements[13] *= sy;
            this.elements[14] *= sz;
        }
    },
    /**
     * The skewX() function skews the matrix along the x-axis the amount specified by the angle parameter.
     * Angles should be specified in radians (values from 0 to PI*2) or converted to radians with the <b>radians()</b> function.
     *
     * @param {float} angle  angle of skew specified in radians
     * 
     * @method PMatrix3D
     */
    skewX: function (angle) {
        // XXX(jeresig)
        var t = tan(angle);
        this.apply(1, t, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
    },
    /**
     * The skewY() function skews the matrix along the y-axis the amount specified by the angle parameter.
     * Angles should be specified in radians (values from 0 to PI*2) or converted to radians with the <b>radians()</b> function.
     *
     * @param {float} angle  angle of skew specified in radians
     * 
     * @method PMatrix3D
     */
    skewY: function (angle) {
        // XXX(jeresig)
        var t = tan(angle);
        this.apply(1, 0, 0, 0, t, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
    },
    multX: function (x, y, z, w) {
        if (!z) {
            return this.elements[0] * x + this.elements[1] * y + this.elements[3];
        }
        if (!w) {
            return this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3];
        }
        return this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3] * w;
    },
    multY: function (x, y, z, w) {
        if (!z) {
            return this.elements[4] * x + this.elements[5] * y + this.elements[7];
        }
        if (!w) {
            return this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7];
        }
        return this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7] * w;
    },
    multZ: function (x, y, z, w) {
        if (!w) {
            return this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11];
        }
        return this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11] * w;
    },
    multW: function (x, y, z, w) {
        if (!w) {
            return this.elements[12] * x + this.elements[13] * y + this.elements[14] * z + this.elements[15];
        }
        return this.elements[12] * x + this.elements[13] * y + this.elements[14] * z + this.elements[15] * w;
    },
    /**
     * The invert() function inverts this matrix
     *
     * @return {boolean} true if successful
     * 
     * @method PMatrix3D
     */
    invert: function () {
        var fA0 = this.elements[0] * this.elements[5] - this.elements[1] * this.elements[4];
        var fA1 = this.elements[0] * this.elements[6] - this.elements[2] * this.elements[4];
        var fA2 = this.elements[0] * this.elements[7] - this.elements[3] * this.elements[4];
        var fA3 = this.elements[1] * this.elements[6] - this.elements[2] * this.elements[5];
        var fA4 = this.elements[1] * this.elements[7] - this.elements[3] * this.elements[5];
        var fA5 = this.elements[2] * this.elements[7] - this.elements[3] * this.elements[6];
        var fB0 = this.elements[8] * this.elements[13] - this.elements[9] * this.elements[12];
        var fB1 = this.elements[8] * this.elements[14] - this.elements[10] * this.elements[12];
        var fB2 = this.elements[8] * this.elements[15] - this.elements[11] * this.elements[12];
        var fB3 = this.elements[9] * this.elements[14] - this.elements[10] * this.elements[13];
        var fB4 = this.elements[9] * this.elements[15] - this.elements[11] * this.elements[13];
        var fB5 = this.elements[10] * this.elements[15] - this.elements[11] * this.elements[14];

        // Determinant
        var fDet = fA0 * fB5 - fA1 * fB4 + fA2 * fB3 + fA3 * fB2 - fA4 * fB1 + fA5 * fB0;

        // Account for a very small value
        // return false if not successful.
        if (Math.abs(fDet) <= 1e-9) {
            return false;
        }

        var kInv = [];
        kInv[0] = +this.elements[5] * fB5 - this.elements[6] * fB4 + this.elements[7] * fB3;
        kInv[4] = -this.elements[4] * fB5 + this.elements[6] * fB2 - this.elements[7] * fB1;
        kInv[8] = +this.elements[4] * fB4 - this.elements[5] * fB2 + this.elements[7] * fB0;
        kInv[12] = -this.elements[4] * fB3 + this.elements[5] * fB1 - this.elements[6] * fB0;
        kInv[1] = -this.elements[1] * fB5 + this.elements[2] * fB4 - this.elements[3] * fB3;
        kInv[5] = +this.elements[0] * fB5 - this.elements[2] * fB2 + this.elements[3] * fB1;
        kInv[9] = -this.elements[0] * fB4 + this.elements[1] * fB2 - this.elements[3] * fB0;
        kInv[13] = +this.elements[0] * fB3 - this.elements[1] * fB1 + this.elements[2] * fB0;
        kInv[2] = +this.elements[13] * fA5 - this.elements[14] * fA4 + this.elements[15] * fA3;
        kInv[6] = -this.elements[12] * fA5 + this.elements[14] * fA2 - this.elements[15] * fA1;
        kInv[10] = +this.elements[12] * fA4 - this.elements[13] * fA2 + this.elements[15] * fA0;
        kInv[14] = -this.elements[12] * fA3 + this.elements[13] * fA1 - this.elements[14] * fA0;
        kInv[3] = -this.elements[9] * fA5 + this.elements[10] * fA4 - this.elements[11] * fA3;
        kInv[7] = +this.elements[8] * fA5 - this.elements[10] * fA2 + this.elements[11] * fA1;
        kInv[11] = -this.elements[8] * fA4 + this.elements[9] * fA2 - this.elements[11] * fA0;
        kInv[15] = +this.elements[8] * fA3 - this.elements[9] * fA1 + this.elements[10] * fA0;

        // Inverse using Determinant
        var fInvDet = 1.0 / fDet;
        kInv[0] *= fInvDet;
        kInv[1] *= fInvDet;
        kInv[2] *= fInvDet;
        kInv[3] *= fInvDet;
        kInv[4] *= fInvDet;
        kInv[5] *= fInvDet;
        kInv[6] *= fInvDet;
        kInv[7] *= fInvDet;
        kInv[8] *= fInvDet;
        kInv[9] *= fInvDet;
        kInv[10] *= fInvDet;
        kInv[11] *= fInvDet;
        kInv[12] *= fInvDet;
        kInv[13] *= fInvDet;
        kInv[14] *= fInvDet;
        kInv[15] *= fInvDet;

        this.elements = kInv.slice();
        return true;
    },
    toString: function () {
        var str = "";
        for (var i = 0; i < 15; i++) {
            str += this.elements[i] + ", ";
        }
        str += this.elements[15];
        return str;
    },
    /**
     * The print() function prints out the elements of this matrix
     * 
     * @method PMatrix3D
     */
    print: function () {
        var digits = printMatrixHelper(this.elements);

        var output = "" + nfs(this.elements[0], digits, 4) + " " + nfs(this.elements[1], digits, 4) +
            " " + nfs(this.elements[2], digits, 4) + " " + nfs(this.elements[3], digits, 4) +
            "\n" + nfs(this.elements[4], digits, 4) + " " + nfs(this.elements[5], digits, 4) +
            " " + nfs(this.elements[6], digits, 4) + " " + nfs(this.elements[7], digits, 4) +
            "\n" + nfs(this.elements[8], digits, 4) + " " + nfs(this.elements[9], digits, 4) +
            " " + nfs(this.elements[10], digits, 4) + " " + nfs(this.elements[11], digits, 4) +
            "\n" + nfs(this.elements[12], digits, 4) + " " + nfs(this.elements[13], digits, 4) +
            " " + nfs(this.elements[14], digits, 4) + " " + nfs(this.elements[15], digits, 4) + "\n\n";
        println(output);
    },
    invTranslate: function (tx, ty, tz) {
        this.preApply(1, 0, 0, -tx, 0, 1, 0, -ty, 0, 0, 1, -tz, 0, 0, 0, 1);
    },
    invRotateX: function (angle) {
        // XXX(jeresig)
        var c = cos(-angle);
        var s = sin(-angle);
        this.preApply([1, 0, 0, 0, 0, c, -s, 0, 0, s, c, 0, 0, 0, 0, 1]);
    },
    invRotateY: function (angle) {
        // XXX(jeresig)
        var c = cos(-angle);
        var s = sin(-angle);
        this.preApply([c, 0, s, 0, 0, 1, 0, 0, -s, 0, c, 0, 0, 0, 0, 1]);
    },
    invRotateZ: function (angle) {
        // XXX(jeresig)
        var c = cos(-angle);
        var s = sin(-angle);
        this.preApply([c, -s, 0, 0, s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
    },
    invScale: function (x, y, z) {
        this.preApply([1 / x, 0, 0, 0, 0, 1 / y, 0, 0, 0, 0, 1 / z, 0, 0, 0, 0, 1]);
    }
};

/**
 * @private
 * The matrix stack stores the transformations and translations that occur within the space.
 */
PMatrixStack = function () {
    this.matrixStack = [];
};

/**
 * load pushes the matrix given in the function into the stack
 *
 * @param {Object | Array} matrix the matrix to be pushed into the stack
 * 
 * @method PMatrixStack
 */
PMatrixStack.prototype.load = function () {
    var tmpMatrix = drawing.$newPMatrix();

    if (arguments.length === 1) {
        tmpMatrix.set(arguments[0]);
    } else {
        tmpMatrix.set(arguments);
    }
    this.matrixStack.push(tmpMatrix);
};

$newPMatrix = function () {
    return new PMatrix2D();
};

$newPMatrix = function () {
    return new PMatrix3D();
};

/**
 * push adds a duplicate of the top of the stack onto the stack - uses the peek function
 * 
 * @method PMatrixStack
 */
PMatrixStack.prototype.push = function () {
    this.matrixStack.push(this.peek());
};

/**
 * pop removes returns the matrix at the top of the stack
 *
 * @returns {Object} the matrix at the top of the stack
 * 
 * @method PMatrixStack
 */
PMatrixStack.prototype.pop = function () {
    return this.matrixStack.pop();
};

/**
 * peek returns but doesn't remove the matrix at the top of the stack
 *
 * @returns {Object} the matrix at the top of the stack
 * 
 * @method PMatrixStack
 */
PMatrixStack.prototype.peek = function () {
    var tmpMatrix = drawing.$newPMatrix();

    tmpMatrix.set(this.matrixStack[this.matrixStack.length - 1]);
    return tmpMatrix;
};

/**
 * this function multiplies the matrix at the top of the stack with the matrix given as a parameter
 *
 * @param {Object | Array} matrix the matrix to be multiplied into the stack
 * 
 * @method PMatrixStack
 */
PMatrixStack.prototype.mult = function (matrix) {
    this.matrixStack[this.matrixStack.length - 1].apply(matrix);
};

////////////////////////////////////////////////////////////////////////////
// Array handling
////////////////////////////////////////////////////////////////////////////

/**
 * The split() function breaks a string into pieces using a character or string
 * as the divider. The delim  parameter specifies the character or characters that
 * mark the boundaries between each piece. A String[] array is returned that contains
 * each of the pieces.
 * If the result is a set of numbers, you can convert the String[] array to to a float[]
 * or int[] array using the datatype conversion functions int() and float() (see example above).
 * The splitTokens() function works in a similar fashion, except that it splits using a range
 * of characters instead of a specific character or sequence.
 *
 * @param {String} str       the String to be split
 * @param {String} delim     the character or String used to separate the data
 *
 * @returns {string[]} The new string array
 *
 * @see splitTokens
 * @see join
 * @see trim
 */
split = function (str, delim) {
    return str.split(delim);
};

/**
 * The splitTokens() function splits a String at one or many character "tokens." The tokens
 * parameter specifies the character or characters to be used as a boundary.
 * If no tokens character is specified, any whitespace character is used to split.
 * Whitespace characters include tab (\t), line feed (\n), carriage return (\r), form
 * feed (\f), and space. To convert a String to an array of integers or floats, use the
 * datatype conversion functions int() and float() to convert the array of Strings.
 *
 * @param {String} str       the String to be split
 * @param {Char[]} tokens    list of individual characters that will be used as separators
 *
 * @returns {string[]} The new string array
 *
 * @see split
 * @see join
 * @see trim
 */
splitTokens = function (str, tokens) {
    if (arguments.length === 1) {
        tokens = "\n\t\r\f ";
    }

    tokens = "[" + tokens + "]";

    var ary = [];
    var index = 0;
    var pos = str.search(tokens);

    while (pos >= 0) {
        if (pos === 0) {
            str = str.substring(1);
        } else {
            ary[index] = str.substring(0, pos);
            index++;
            str = str.substring(pos);
        }
        pos = str.search(tokens);
    }

    if (str.length > 0) {
        ary[index] = str;
    }

    if (ary.length === 0) {
        ary = undef;
    }

    return ary;
};

/**
 * Expands an array by one element and adds data to the new position. The datatype of
 * the element parameter must be the same as the datatype of the array.
 * When using an array of objects, the data returned from the function must be cast to
 * the object array's data type. For example: SomeClass[] items = (SomeClass[])
 * append(originalArray, element).
 *
 * @param {boolean[]|byte[]|char[]|int[]|float[]|String[]|array of objects} array boolean[],
 * byte[], char[], int[], float[], or String[], or an array of objects
 * @param {boolean[]|byte[]|char[]|int[]|float[]|String[]|array of objects} element new data for the array
 *
 * @returns Array (the same datatype as the input)
 *
 * @see shorten
 * @see expand
 */
append = function (array, element) {
    array[array.length] = element;
    return array;
};

/**
 * Concatenates two arrays. For example, concatenating the array { 1, 2, 3 } and the
 * array { 4, 5, 6 } yields { 1, 2, 3, 4, 5, 6 }. Both parameters must be arrays of the
 * same datatype.
 * When using an array of objects, the data returned from the function must be cast to the
 * object array's data type. For example: SomeClass[] items = (SomeClass[]) concat(array1, array2).
 *
 * @param {boolean[]|byte[]|char[]|int[]|float[]|String[]|array of objects} array1 boolean[],
 * byte[], char[], int[], float[], String[], or an array of objects
 * @param {boolean[]|byte[]|char[]|int[]|float[]|String[]|array of objects} array2 boolean[],
 * byte[], char[], int[], float[], String[], or an array of objects
 *
 * @returns Array (the same datatype as the input)
 *
 * @see splice
 */
concat = function (array1, array2) {
    return array1.concat(array2);
};

/**
 * Sorts an array of numbers from smallest to largest and puts an array of
 * words in alphabetical order. The original array is not modified, a
 * re-ordered array is returned. The count parameter states the number of
 * elements to sort. For example if there are 12 elements in an array and
 * if count is the value 5, only the first five elements on the array will
 * be sorted. Alphabetical ordering is case insensitive.
 *
 * @param {String[] | int[] | float[]}  array Array of elements to sort
 * @param {int}                         numElem Number of elements to sort
 *
 * @returns {String[] | int[] | float[]} Array (same datatype as the input)
 *
 * @see reverse
 */
sort = function (array, numElem) {
    var ret = [];

    // depending on the type used (int, float) or string
    // we'll need to use a different compare function
    if (array.length > 0) {
        // copy since we need to return another array
        var elemsToCopy = numElem > 0 ? numElem : array.length;
        for (var i = 0; i < elemsToCopy; i++) {
            ret.push(array[i]);
        }
        if (typeof array[0] === "string") {
            ret.sort();
        }
        // int or float
        else {
            ret.sort(function (a, b) {
                return a - b;
            });
        }

        // copy on the rest of the elements that were not sorted in case the user
        // only wanted a subset of an array to be sorted.
        if (numElem > 0) {
            for (var j = ret.length; j < array.length; j++) {
                ret.push(array[j]);
            }
        }
    }
    return ret;
};

/**
 * Inserts a value or array of values into an existing array. The first two parameters must
 * be of the same datatype. The array parameter defines the array which will be modified
 * and the second parameter defines the data which will be inserted. When using an array
 * of objects, the data returned from the function must be cast to the object array's data
 * type. For example: SomeClass[] items = (SomeClass[]) splice(array1, array2, index).
 *
 * @param {boolean[]|byte[]|char[]|int[]|float[]|String[]|array of objects} array boolean[],
 * byte[], char[], int[], float[], String[], or an array of objects
 * @param {boolean|byte|char|int|float|String|boolean[]|byte[]|char[]|int[]|float[]|String[]|array of objects}
 * value boolean, byte, char, int, float, String, boolean[], byte[], char[], int[],
 * float[], String[], or other Object: value or an array of objects to be spliced in
 * @param {int} index                position in the array from which to insert data
 *
 * @returns Array (the same datatype as the input)
 *
 * @see contract
 * @see subset
 */
splice = function (array, value, index) {

    // Trying to splice an empty array into "array" in P5 won't do
    // anything, just return the original.
    if (value.length === 0) {
        return array;
    }

    // If the second argument was an array, we'll need to iterate over all
    // the "value" elements and add one by one because
    // array.splice(index, 0, value);
    // would create a multi-dimensional array which isn't what we want.
    if (value instanceof Array) {
        for (var i = 0, j = index; i < value.length; j++, i++) {
            array.splice(j, 0, value[i]);
        }
    } else {
        array.splice(index, 0, value);
    }

    return array;
};

/**
 * Extracts an array of elements from an existing array. The array parameter defines the
 * array from which the elements will be copied and the offset and length parameters determine
 * which elements to extract. If no length is given, elements will be extracted from the offset
 * to the end of the array. When specifying the offset remember the first array element is 0.
 * This function does not change the source array.
 * When using an array of objects, the data returned from the function must be cast to the
 * object array's data type.
 *
 * @param {boolean[]|byte[]|char[]|int[]|float[]|String[]|array of objects} array boolean[],
 * byte[], char[], int[], float[], String[], or an array of objects
 * @param {int} offset         position to begin
 * @param {int} length         number of values to extract
 *
 * @returns Array (the same datatype as the input)
 *
 * @see splice
 */
subset = function (array, offset, length) {
    var end = (length !== undef) ? offset + length : array.length;
    return array.slice(offset, end);
};

/**
 * Combines an array of Strings into one String, each separated by the character(s) used for
 * the separator parameter. To join arrays of ints or floats, it's necessary to first convert
 * them to strings using nf() or nfs().
 *
 * @param {Array} array              array of Strings
 * @param {char|String} separator    char or String to be placed between each item
 *
 * @returns {String} The combined string
 *
 * @see split
 * @see trim
 * @see nf
 * @see nfs
 */
join = function (array, seperator) {
    return array.join(seperator);
};

/**
 * Decreases an array by one element and returns the shortened array. When using an
 * array of objects, the data returned from the function must be cast to the object array's
 * data type. For example: SomeClass[] items = (SomeClass[]) shorten(originalArray).
 *
 * @param {boolean[]|byte[]|char[]|int[]|float[]|String[]|array of objects} array
 * boolean[], byte[], char[], int[], float[], or String[], or an array of objects
 *
 * @returns Array (the same datatype as the input)
 *
 * @see append
 * @see expand
 */
shorten = function (ary) {
    var newary = [];

    // copy array into new array
    var len = ary.length;
    for (var i = 0; i < len; i++) {
        newary[i] = ary[i];
    }
    newary.pop();

    return newary;
};

/**
 * Increases the size of an array. By default, this function doubles the size of the array,
 * but the optional newSize parameter provides precise control over the increase in size.
 * When using an array of objects, the data returned from the function must be cast to the
 * object array's data type. For example: SomeClass[] items = (SomeClass[]) expand(originalArray).
 *
 * @param {boolean[]|byte[]|char[]|int[]|float[]|String[]|array of objects} ary
 * boolean[], byte[], char[], int[], float[], String[], or an array of objects
 * @param {int} newSize              positive int: new size for the array
 *
 * @returns Array (the same datatype as the input)
 *
 * @see contract
 */
expand = function (ary, targetSize) {
    var temp = ary.slice(0),
        newSize = targetSize || ary.length * 2;
    temlength = newSize;
    return temp;
};

/**
 * Copies an array (or part of an array) to another array. The src array is copied to the
 * dst array, beginning at the position specified by srcPos and into the position specified
 * by dstPos. The number of elements to copy is determined by length. The simplified version
 * with two arguments copies an entire array to another of the same size. It is equivalent
 * to "arrayCopy(src, 0, dst, 0, src.length)". This function is far more efficient for copying
 * array data than iterating through a for and copying each element.
 *
 * @param {Array} src an array of any data type: the source array
 * @param {Array} dest an array of any data type (as long as it's the same as src): the destination array
 * @param {int} srcPos     starting position in the source array
 * @param {int} destPos    starting position in the destination array
 * @param {int} length     number of array elements to be copied
 *
 * @returns none
 */
arrayCopy = function () { // src, srcPos, dest, destPos, length) {
    var src, srcPos = 0, dest, destPos = 0, length;

    if (arguments.length === 2) {
        // recall itself and copy src to dest from start index 0 to 0 of src.length
        src = arguments[0];
        dest = arguments[1];
        length = src.length;
    } else if (arguments.length === 3) {
        // recall itself and copy src to dest from start index 0 to 0 of length
        src = arguments[0];
        dest = arguments[1];
        length = arguments[2];
    } else if (arguments.length === 5) {
        src = arguments[0];
        srcPos = arguments[1];
        dest = arguments[2];
        destPos = arguments[3];
        length = arguments[4];
    }

    // copy src to dest from index srcPos to index destPos of length recursivly on objects
    for (var i = srcPos, j = destPos; i < length + srcPos; i++, j++) {
        if (dest[j] !== undef) {
            dest[j] = src[i];
        } else {
            throw "array index out of bounds exception";
        }
    }
};

/**
 * Reverses the order of an array.
 *
 * @param {boolean[]|byte[]|char[]|int[]|float[]|String[]} array
 * boolean[], byte[], char[], int[], float[], or String[]
 *
 * @returns Array (the same datatype as the input)
 *
 * @see sort
 */
reverse = function (array) {
    return array.reverse();
};

////////////////////////////////////////////////////////////////////////////
// Color functions
////////////////////////////////////////////////////////////////////////////

/**
 * Helper function for internal blending modes
 * 
 * @param {*} a
 * @param {*} b
 * @param {*} f
 */
mix = function (a, b, f) {
    return a + (((b - a) * f) >> 8);
};

/**
 * Helper function for internal blending modes
 * 
 * @param {*} n
 */
peg = function (n) {
    return (n < 0) ? 0 : ((n > 255) ? 255 : n);
};

// blending modes
/**
 * These are internal blending modes used for BlendColor()
 *
 * @param {Color} c1       First Color to blend
 * @param {Color} c2       Second Color to blend
 *
 * @returns {Color}        The blended Color
 *
 * @see BlendColor
 * @see Blend
 */
modes = (function () {
    var ALPHA_MASK = PConstants.ALPHA_MASK,
        RED_MASK = PConstants.RED_MASK,
        GREEN_MASK = PConstants.GREEN_MASK,
        BLUE_MASK = PConstants.BLUE_MASK,
        min = Math.min,
        max = Math.max;

    function applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb) {
        var a = min(((c1 & 0xff000000) >>> 24) + f, 0xff) << 24;

        var r = (ar + (((cr - ar) * f) >> 8));
        r = ((r < 0) ? 0 : ((r > 255) ? 255 : r)) << 16;

        var g = (ag + (((cg - ag) * f) >> 8));
        g = ((g < 0) ? 0 : ((g > 255) ? 255 : g)) << 8;

        var b = ab + (((cb - ab) * f) >> 8);
        b = (b < 0) ? 0 : ((b > 255) ? 255 : b);

        return (a | r | g | b);
    }

    return {
        replace: function (c1, c2) {
            return c2;
        },
        blend: function (c1, c2) {
            var f = (c2 & ALPHA_MASK) >>> 24,
                ar = (c1 & RED_MASK),
                ag = (c1 & GREEN_MASK),
                ab = (c1 & BLUE_MASK),
                br = (c2 & RED_MASK),
                bg = (c2 & GREEN_MASK),
                bb = (c2 & BLUE_MASK);

            return (min(((c1 & ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
                (ar + (((br - ar) * f) >> 8)) & RED_MASK |
                (ag + (((bg - ag) * f) >> 8)) & GREEN_MASK |
                (ab + (((bb - ab) * f) >> 8)) & BLUE_MASK);
        },
        add: function (c1, c2) {
            var f = (c2 & ALPHA_MASK) >>> 24;
            return (min(((c1 & ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
                min(((c1 & RED_MASK) + ((c2 & RED_MASK) >> 8) * f), RED_MASK) & RED_MASK |
                min(((c1 & GREEN_MASK) + ((c2 & GREEN_MASK) >> 8) * f), GREEN_MASK) & GREEN_MASK |
                min((c1 & BLUE_MASK) + (((c2 & BLUE_MASK) * f) >> 8), BLUE_MASK));
        },
        subtract: function (c1, c2) {
            var f = (c2 & ALPHA_MASK) >>> 24;
            return (min(((c1 & ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
                max(((c1 & RED_MASK) - ((c2 & RED_MASK) >> 8) * f), GREEN_MASK) & RED_MASK |
                max(((c1 & GREEN_MASK) - ((c2 & GREEN_MASK) >> 8) * f), BLUE_MASK) & GREEN_MASK |
                max((c1 & BLUE_MASK) - (((c2 & BLUE_MASK) * f) >> 8), 0));
        },
        lightest: function (c1, c2) {
            var f = (c2 & ALPHA_MASK) >>> 24;
            return (min(((c1 & ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
                max(c1 & RED_MASK, ((c2 & RED_MASK) >> 8) * f) & RED_MASK |
                max(c1 & GREEN_MASK, ((c2 & GREEN_MASK) >> 8) * f) & GREEN_MASK |
                max(c1 & BLUE_MASK, ((c2 & BLUE_MASK) * f) >> 8));
        },
        darkest: function (c1, c2) {
            var f = (c2 & ALPHA_MASK) >>> 24,
                ar = (c1 & RED_MASK),
                ag = (c1 & GREEN_MASK),
                ab = (c1 & BLUE_MASK),
                br = min(c1 & RED_MASK, ((c2 & RED_MASK) >> 8) * f),
                bg = min(c1 & GREEN_MASK, ((c2 & GREEN_MASK) >> 8) * f),
                bb = min(c1 & BLUE_MASK, ((c2 & BLUE_MASK) * f) >> 8);

            return (min(((c1 & ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
                (ar + (((br - ar) * f) >> 8)) & RED_MASK |
                (ag + (((bg - ag) * f) >> 8)) & GREEN_MASK |
                (ab + (((bb - ab) * f) >> 8)) & BLUE_MASK);
        },
        difference: function (c1, c2) {
            var f = (c2 & ALPHA_MASK) >>> 24,
                ar = (c1 & RED_MASK) >> 16,
                ag = (c1 & GREEN_MASK) >> 8,
                ab = (c1 & BLUE_MASK),
                br = (c2 & RED_MASK) >> 16,
                bg = (c2 & GREEN_MASK) >> 8,
                bb = (c2 & BLUE_MASK),
                cr = (ar > br) ? (ar - br) : (br - ar),
                cg = (ag > bg) ? (ag - bg) : (bg - ag),
                cb = (ab > bb) ? (ab - bb) : (bb - ab);

            return applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb);
        },
        exclusion: function (c1, c2) {
            var f = (c2 & ALPHA_MASK) >>> 24,
                ar = (c1 & RED_MASK) >> 16,
                ag = (c1 & GREEN_MASK) >> 8,
                ab = (c1 & BLUE_MASK),
                br = (c2 & RED_MASK) >> 16,
                bg = (c2 & GREEN_MASK) >> 8,
                bb = (c2 & BLUE_MASK),
                cr = ar + br - ((ar * br) >> 7),
                cg = ag + bg - ((ag * bg) >> 7),
                cb = ab + bb - ((ab * bb) >> 7);

            return applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb);
        },
        multiply: function (c1, c2) {
            var f = (c2 & ALPHA_MASK) >>> 24,
                ar = (c1 & RED_MASK) >> 16,
                ag = (c1 & GREEN_MASK) >> 8,
                ab = (c1 & BLUE_MASK),
                br = (c2 & RED_MASK) >> 16,
                bg = (c2 & GREEN_MASK) >> 8,
                bb = (c2 & BLUE_MASK),
                cr = (ar * br) >> 8,
                cg = (ag * bg) >> 8,
                cb = (ab * bb) >> 8;

            return applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb);
        },
        screen: function (c1, c2) {
            var f = (c2 & ALPHA_MASK) >>> 24,
                ar = (c1 & RED_MASK) >> 16,
                ag = (c1 & GREEN_MASK) >> 8,
                ab = (c1 & BLUE_MASK),
                br = (c2 & RED_MASK) >> 16,
                bg = (c2 & GREEN_MASK) >> 8,
                bb = (c2 & BLUE_MASK),
                cr = 255 - (((255 - ar) * (255 - br)) >> 8),
                cg = 255 - (((255 - ag) * (255 - bg)) >> 8),
                cb = 255 - (((255 - ab) * (255 - bb)) >> 8);

            return applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb);
        },
        hard_light: function (c1, c2) {
            var f = (c2 & ALPHA_MASK) >>> 24,
                ar = (c1 & RED_MASK) >> 16,
                ag = (c1 & GREEN_MASK) >> 8,
                ab = (c1 & BLUE_MASK),
                br = (c2 & RED_MASK) >> 16,
                bg = (c2 & GREEN_MASK) >> 8,
                bb = (c2 & BLUE_MASK),
                cr = (br < 128) ? ((ar * br) >> 7) : (255 - (((255 - ar) * (255 - br)) >> 7)),
                cg = (bg < 128) ? ((ag * bg) >> 7) : (255 - (((255 - ag) * (255 - bg)) >> 7)),
                cb = (bb < 128) ? ((ab * bb) >> 7) : (255 - (((255 - ab) * (255 - bb)) >> 7));

            return applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb);
        },
        soft_light: function (c1, c2) {
            var f = (c2 & ALPHA_MASK) >>> 24,
                ar = (c1 & RED_MASK) >> 16,
                ag = (c1 & GREEN_MASK) >> 8,
                ab = (c1 & BLUE_MASK),
                br = (c2 & RED_MASK) >> 16,
                bg = (c2 & GREEN_MASK) >> 8,
                bb = (c2 & BLUE_MASK),
                cr = ((ar * br) >> 7) + ((ar * ar) >> 8) - ((ar * ar * br) >> 15),
                cg = ((ag * bg) >> 7) + ((ag * ag) >> 8) - ((ag * ag * bg) >> 15),
                cb = ((ab * bb) >> 7) + ((ab * ab) >> 8) - ((ab * ab * bb) >> 15);

            return applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb);
        },
        overlay: function (c1, c2) {
            var f = (c2 & ALPHA_MASK) >>> 24,
                ar = (c1 & RED_MASK) >> 16,
                ag = (c1 & GREEN_MASK) >> 8,
                ab = (c1 & BLUE_MASK),
                br = (c2 & RED_MASK) >> 16,
                bg = (c2 & GREEN_MASK) >> 8,
                bb = (c2 & BLUE_MASK),
                cr = (ar < 128) ? ((ar * br) >> 7) : (255 - (((255 - ar) * (255 - br)) >> 7)),
                cg = (ag < 128) ? ((ag * bg) >> 7) : (255 - (((255 - ag) * (255 - bg)) >> 7)),
                cb = (ab < 128) ? ((ab * bb) >> 7) : (255 - (((255 - ab) * (255 - bb)) >> 7));

            return applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb);
        },
        dodge: function (c1, c2) {
            var f = (c2 & ALPHA_MASK) >>> 24,
                ar = (c1 & RED_MASK) >> 16,
                ag = (c1 & GREEN_MASK) >> 8,
                ab = (c1 & BLUE_MASK),
                br = (c2 & RED_MASK) >> 16,
                bg = (c2 & GREEN_MASK) >> 8,
                bb = (c2 & BLUE_MASK);

            var cr = 255;
            if (br !== 255) {
                cr = (ar << 8) / (255 - br);
                cr = (cr < 0) ? 0 : ((cr > 255) ? 255 : cr);
            }

            var cg = 255;
            if (bg !== 255) {
                cg = (ag << 8) / (255 - bg);
                cg = (cg < 0) ? 0 : ((cg > 255) ? 255 : cg);
            }

            var cb = 255;
            if (bb !== 255) {
                cb = (ab << 8) / (255 - bb);
                cb = (cb < 0) ? 0 : ((cb > 255) ? 255 : cb);
            }

            return applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb);
        },
        burn: function (c1, c2) {
            var f = (c2 & ALPHA_MASK) >>> 24,
                ar = (c1 & RED_MASK) >> 16,
                ag = (c1 & GREEN_MASK) >> 8,
                ab = (c1 & BLUE_MASK),
                br = (c2 & RED_MASK) >> 16,
                bg = (c2 & GREEN_MASK) >> 8,
                bb = (c2 & BLUE_MASK);

            var cr = 0;
            if (br !== 0) {
                cr = ((255 - ar) << 8) / br;
                cr = 255 - ((cr < 0) ? 0 : ((cr > 255) ? 255 : cr));
            }

            var cg = 0;
            if (bg !== 0) {
                cg = ((255 - ag) << 8) / bg;
                cg = 255 - ((cg < 0) ? 0 : ((cg > 255) ? 255 : cg));
            }

            var cb = 0;
            if (bb !== 0) {
                cb = ((255 - ab) << 8) / bb;
                cb = 255 - ((cb < 0) ? 0 : ((cb > 255) ? 255 : cb));
            }

            return applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb);
        }
    };
}());

/**
 * Creates colors for storing in variables of the color datatype. The parameters are
 * interpreted as RGB or HSB values depending on the current colorMode(). The default
 * mode is RGB values from 0 to 255 and therefore, the function call color(255, 204, 0)
 * will return a bright yellow color. More about how colors are stored can be found in
 * the reference for the color datatype.
 *
 * @param {int|float} aValue1        red or hue or grey values relative to the current color range.
 * Also can be color value in hexadecimal notation (i.e. #FFCC00 or 0xFFFFCC00)
 * @param {int|float} aValue2        green or saturation values relative to the current color range
 * @param {int|float} aValue3        blue or brightness values relative to the current color range
 * @param {int|float} aValue4        relative to current color range. Represents alpha
 *
 * @returns {color} the color
 *
 * @see colorMode
 */
color = function (aValue1, aValue2, aValue3, aValue4) {

    // 4 arguments: (R, G, B, A) or (H, S, B, A)
    if (aValue1 !== undef && aValue2 !== undef && aValue3 !== undef && aValue4 !== undef) {
        return color$4(aValue1, aValue2, aValue3, aValue4);
    }

    // 3 arguments: (R, G, B) or (H, S, B)
    if (aValue1 !== undef && aValue2 !== undef && aValue3 !== undef) {
        return color$4(aValue1, aValue2, aValue3, colorModeA);
    }

    // 2 arguments: (Color, A) or (Grayscale, A)
    if (aValue1 !== undef && aValue2 !== undef) {
        return color$2(aValue1, aValue2);
    }

    // 1 argument: (Grayscale) or (Color)
    if (typeof aValue1 === "number") {
        return color$1(aValue1);
    }

    // Default
    return color$4(colorModeX, colorModeY, colorModeZ, colorModeA);
};

// Ease of use function to extract the colour bits into a string
color.toString = function (colorInt) {
    return "rgba(" + ((colorInt & PConstants.RED_MASK) >>> 16) + "," + ((colorInt & PConstants.GREEN_MASK) >>> 8) +
        "," + ((colorInt & PConstants.BLUE_MASK)) + "," + ((colorInt & PConstants.ALPHA_MASK) >>> 24) / 255 + ")";
};

// Easy of use function to pack rgba values into a single bit-shifted color int.
color.toInt = function (r, g, b, a) {
    return (a << 24) & PConstants.ALPHA_MASK | (r << 16) & PConstants.RED_MASK | (g << 8) & PConstants.GREEN_MASK | b & PConstants.BLUE_MASK;
};

// Creates a simple array in [R, G, B, A] format, [255, 255, 255, 255]
color.toArray = function (colorInt) {
    return [(colorInt & PConstants.RED_MASK) >>> 16, (colorInt & PConstants.GREEN_MASK) >>> 8,
    colorInt & PConstants.BLUE_MASK, (colorInt & PConstants.ALPHA_MASK) >>> 24];
};

// Creates a WebGL color array in [R, G, B, A] format. WebGL wants the color ranges between 0 and 1, [1, 1, 1, 1]
color.toGLArray = function (colorInt) {
    return [((colorInt & PConstants.RED_MASK) >>> 16) / 255, ((colorInt & PConstants.GREEN_MASK) >>> 8) / 255,
    (colorInt & PConstants.BLUE_MASK) / 255, ((colorInt & PConstants.ALPHA_MASK) >>> 24) / 255];
};

// HSB conversion function from Mootools, MIT Licensed
color.toRGB = function (h, s, b) {
    // Limit values greater than range
    h = (h > colorModeX) ? colorModeX : h;
    s = (s > colorModeY) ? colorModeY : s;
    b = (b > colorModeZ) ? colorModeZ : b;

    h = (h / colorModeX) * 360;
    s = (s / colorModeY) * 100;
    b = (b / colorModeZ) * 100;

    var br = Math.round(b / 100 * 255);

    if (s === 0) { // Grayscale
        return [br, br, br];
    }
    var hue = h % 360;
    var f = hue % 60;
    var p = Math.round((b * (100 - s)) / 10000 * 255);
    var q = Math.round((b * (6000 - s * f)) / 600000 * 255);
    var t = Math.round((b * (6000 - s * (60 - f))) / 600000 * 255);
    switch (Math.floor(hue / 60)) {
        case 0:
            return [br, t, p];
        case 1:
            return [q, br, p];
        case 2:
            return [p, br, t];
        case 3:
            return [p, q, br];
        case 4:
            return [t, p, br];
        case 5:
            return [br, p, q];
    }
};

/**
 * Extracts the brightness value from a color.
 *
 * @param {color} colInt any value of the color datatype
 *
 * @returns {float} The brightness color value.
 *
 * @see red
 * @see green
 * @see blue
 * @see hue
 * @see saturation
 */
brightness = function (colInt) {
    return colorToHSB(colInt)[2];
};

/**
 * Extracts the saturation value from a color.
 *
 * @param {color} colInt any value of the color datatype
 *
 * @returns {float} The saturation color value.
 *
 * @see red
 * @see green
 * @see blue
 * @see hue
 * @see brightness
 */
saturation = function (colInt) {
    return colorToHSB(colInt)[1];
};

/**
 * Extracts the hue value from a color.
 *
 * @param {color} colInt any value of the color datatype
 *
 * @returns {float} The hue color value.
 *
 * @see red
 * @see green
 * @see blue
 * @see saturation
 * @see brightness
 */
hue = function (colInt) {
    return colorToHSB(colInt)[0];
};

/**
 * Extracts the red value from a color, scaled to match current colorMode().
 * This value is always returned as a float so be careful not to assign it to an int value.
 *
 * @param {color} aColor any value of the color datatype
 *
 * @returns {float} The red color value.
 *
 * @see green
 * @see blue
 * @see alpha
 * @see >> right shift
 * @see hue
 * @see saturation
 * @see brightness
 */
red = function (aColor) {
    return ((aColor & PConstants.RED_MASK) >>> 16) / 255 * colorModeX;
};

/**
 * Extracts the green value from a color, scaled to match current colorMode().
 * This value is always returned as a float so be careful not to assign it to an int value.
 *
 * @param {color} aColor any value of the color datatype
 *
 * @returns {float} The green color value.
 *
 * @see red
 * @see blue
 * @see alpha
 * @see >> right shift
 * @see hue
 * @see saturation
 * @see brightness
 */
green = function (aColor) {
    return ((aColor & PConstants.GREEN_MASK) >>> 8) / 255 * colorModeY;
};

/**
 * Extracts the blue value from a color, scaled to match current colorMode().
 * This value is always returned as a float so be careful not to assign it to an int value.
 *
 * @param {color} aColor any value of the color datatype
 *
 * @returns {float} The blue color value.
 *
 * @see red
 * @see green
 * @see alpha
 * @see >> right shift
 * @see hue
 * @see saturation
 * @see brightness
 */
blue = function (aColor) {
    return (aColor & PConstants.BLUE_MASK) / 255 * colorModeZ;
};

/**
 * Extracts the alpha value from a color, scaled to match current colorMode().
 * This value is always returned as a float so be careful not to assign it to an int value.
 *
 * @param {color} aColor any value of the color datatype
 *
 * @returns {float} The alpha color value.
 *
 * @see red
 * @see green
 * @see blue
 * @see >> right shift
 * @see hue
 * @see saturation
 * @see brightness
 */
alpha = function (aColor) {
    return ((aColor & PConstants.ALPHA_MASK) >>> 24) / 255 * colorModeA;
};

/**
 * Calculates a color or colors between two colors at a specific increment.
 * The amt parameter is the amount to interpolate between the two values where 0.0
 * equal to the first point, 0.1 is very near the first point, 0.5 is half-way in between, etc.
 *
 * @param {color} c1     interpolate from this color
 * @param {color} c2     interpolate to this color
 * @param {float} amt    between 0.0 and 1.0
 *
 * @returns {float} The blended color.
 *
 * @see blendColor
 * @see color
 */
lerpColor = function (c1, c2, amt) {
    var r, g, b, a, r1, g1, b1, a1, r2, g2, b2, a2;
    var hsb1, hsb2, rgb, h, s;
    var colorBits1 = color(c1);
    var colorBits2 = color(c2);

    if (curColorMode === PConstants.HSB) {
        // Special processing for HSB mode.
        // Get HSB and Alpha values for Color 1 and 2
        hsb1 = colorToHSB(colorBits1);
        a1 = ((colorBits1 & PConstants.ALPHA_MASK) >>> 24) / colorModeA;
        hsb2 = colorToHSB(colorBits2);
        a2 = ((colorBits2 & PConstants.ALPHA_MASK) >>> 24) / colorModeA;

        // Return lerp value for each channel, for HSB components
        h = lerp(hsb1[0], hsb2[0], amt);
        s = lerp(hsb1[1], hsb2[1], amt);
        b = lerp(hsb1[2], hsb2[2], amt);
        rgb = color.toRGB(h, s, b);
        // ... and for Alpha-range
        a = lerp(a1, a2, amt) * colorModeA;

        return (a << 24) & PConstants.ALPHA_MASK |
            (rgb[0] << 16) & PConstants.RED_MASK |
            (rgb[1] << 8) & PConstants.GREEN_MASK |
            rgb[2] & PConstants.BLUE_MASK;
    }

    // Get RGBA values for Color 1 to floats
    r1 = (colorBits1 & PConstants.RED_MASK) >>> 16;
    g1 = (colorBits1 & PConstants.GREEN_MASK) >>> 8;
    b1 = (colorBits1 & PConstants.BLUE_MASK);
    a1 = ((colorBits1 & PConstants.ALPHA_MASK) >>> 24) / colorModeA;

    // Get RGBA values for Color 2 to floats
    r2 = (colorBits2 & PConstants.RED_MASK) >>> 16;
    g2 = (colorBits2 & PConstants.GREEN_MASK) >>> 8;
    b2 = (colorBits2 & PConstants.BLUE_MASK);
    a2 = ((colorBits2 & PConstants.ALPHA_MASK) >>> 24) / colorModeA;

    // Return lerp value for each channel, INT for color, Float for Alpha-range
    r = lerp(r1, r2, amt) | 0;
    g = lerp(g1, g2, amt) | 0;
    b = lerp(b1, b2, amt) | 0;
    a = lerp(a1, a2, amt) * colorModeA;

    return (a << 24) & PConstants.ALPHA_MASK |
        (r << 16) & PConstants.RED_MASK |
        (g << 8) & PConstants.GREEN_MASK |
        b & PConstants.BLUE_MASK;
};

/**
 * Changes the way Processing interprets color data. By default, fill(), stroke(), and background()
 * colors are set by values between 0 and 255 using the RGB color model. It is possible to change the
 * numerical range used for specifying colors and to switch color systems. For example, calling colorMode(RGB, 1.0)
 * will specify that values are specified between 0 and 1. The limits for defining colors are altered by setting the
 * parameters range1, range2, range3, and range 4.
 *
 * @param {MODE} mode Either RGB or HSB, corresponding to Red/Green/Blue and Hue/Saturation/Brightness
 * @param {int|float} range              range for all color elements
 * @param {int|float} range1             range for the red or hue depending on the current color mode
 * @param {int|float} range2             range for the green or saturation depending on the current color mode
 * @param {int|float} range3             range for the blue or brightness depending on the current color mode
 * @param {int|float} range4             range for the alpha
 *
 * @returns none
 *
 * @see background
 * @see fill
 * @see stroke
 */
colorMode = function () { // mode, range1, range2, range3, range4
    curColorMode = arguments[0];
    if (arguments.length > 1) {
        colorModeX = arguments[1];
        colorModeY = arguments[2] || arguments[1];
        colorModeZ = arguments[3] || arguments[1];
        colorModeA = arguments[4] || arguments[1];
    }
};

/**
 * Blends two color values together based on the blending mode given as the MODE parameter.
 * The possible modes are described in the reference for the blend() function.
 *
 * @param {color} c1 color: the first color to blend
 * @param {color} c2 color: the second color to blend
 * @param {MODE} MODE Either BLEND, ADD, SUBTRACT, DARKEST, LIGHTEST, DIFFERENCE, EXCLUSION, MULTIPLY,
 * SCREEN, OVERLAY, HARD_LIGHT, SOFT_LIGHT, DODGE, or BURN
 *
 * @returns {float} The blended color.
 *
 * @see blend
 * @see color
 */
blendColor = function (c1, c2, mode) {
    if (mode === PConstants.REPLACE) {
        return modes.replace(c1, c2);
    } else if (mode === PConstants.BLEND) {
        return modes.blend(c1, c2);
    } else if (mode === PConstants.ADD) {
        return modes.add(c1, c2);
    } else if (mode === PConstants.SUBTRACT) {
        return modes.subtract(c1, c2);
    } else if (mode === PConstants.LIGHTEST) {
        return modes.lightest(c1, c2);
    } else if (mode === PConstants.DARKEST) {
        return modes.darkest(c1, c2);
    } else if (mode === PConstants.DIFFERENCE) {
        return modes.difference(c1, c2);
    } else if (mode === PConstants.EXCLUSION) {
        return modes.exclusion(c1, c2);
    } else if (mode === PConstants.MULTIPLY) {
        return modes.multiply(c1, c2);
    } else if (mode === PConstants.SCREEN) {
        return modes.screen(c1, c2);
    } else if (mode === PConstants.HARD_LIGHT) {
        return modes.hard_light(c1, c2);
    } else if (mode === PConstants.SOFT_LIGHT) {
        return modes.soft_light(c1, c2);
    } else if (mode === PConstants.OVERLAY) {
        return modes.overlay(c1, c2);
    } else if (mode === PConstants.DODGE) {
        return modes.dodge(c1, c2);
    } else if (mode === PConstants.BURN) {
        return modes.burn(c1, c2);
    }
};

////////////////////////////////////////////////////////////////////////////
// Canvas-Matrix manipulation
////////////////////////////////////////////////////////////////////////////

/**
 * Prints the current matrix to the text window.
 *
 * @returns none
 *
 * @see pushMatrix
 * @see popMatrix
 * @see resetMatrix
 * @see applyMatrix
 */
printMatrix = function () {
    modelView.print();
};

/**
 * Specifies an amount to displace objects within the display window. The x parameter specifies left/right translation,
 * the y parameter specifies up/down translation, and the z parameter specifies translations toward/away from the screen.
 * Using this function with the z  parameter requires using the P3D or OPENGL parameter in combination with size as shown
 * in the above example. Transformations apply to everything that happens after and subsequent calls to the function
 * accumulates the effect. For example, calling translate(50, 0) and then translate(20, 0) is the same as translate(70, 0).
 * If translate() is called within draw(), the transformation is reset when the loop begins again.
 * This function can be further controlled by the pushMatrix() and popMatrix().
 *
 * @param {int|float} x        left/right translation
 * @param {int|float} y        up/down translation
 * @param {int|float} z        forward/back translation
 *
 * @returns none
 *
 * @see pushMatrix
 * @see popMatrix
 * @see scale
 * @see rotate
 * @see rotateX
 * @see rotateY
 * @see rotateZ
 */
translate = function (x, y) {
    modelView.translate(x, y);
    modelViewInv.invTranslate(x, y);
    curContext.translate(x, y);
};

translate = function (x, y, z) {
    modelView.translate(x, y, z);
    modelViewInv.invTranslate(x, y, z);
};

/**
 * Increases or decreases the size of a shape by expanding and contracting vertices. Objects always scale from their
 * relative origin to the coordinate system. Scale values are specified as decimal percentages. For example, the
 * function call scale(2.0) increases the dimension of a shape by 200%. Transformations apply to everything that
 * happens after and subsequent calls to the function multiply the effect. For example, calling scale(2.0) and
 * then scale(1.5) is the same as scale(3.0). If scale() is called within draw(), the transformation is reset when
 * the loop begins again. Using this fuction with the z  parameter requires passing P3D or OPENGL into the size()
 * parameter as shown in the example above. This function can be further controlled by pushMatrix() and popMatrix().
 *
 * @param {int|float} size     percentage to scale the object
 * @param {int|float} x        percentage to scale the object in the x-axis
 * @param {int|float} y        percentage to scale the object in the y-axis
 * @param {int|float} z        percentage to scale the object in the z-axis
 *
 * @returns none
 *
 * @see pushMatrix
 * @see popMatrix
 * @see translate
 * @see rotate
 * @see rotateX
 * @see rotateY
 * @see rotateZ
 */
scale = function (x, y) {
    modelView.scale(x, y);
    modelViewInv.invScale(x, y);
    curContext.scale(x, y || x);
};

scale = function (x, y, z) {
    modelView.scale(x, y, z);
    modelViewInv.invScale(x, y, z);
};

/**
 * Pushes the current transformation matrix onto the matrix stack. Understanding pushMatrix() and popMatrix()
 * requires understanding the concept of a matrix stack. The pushMatrix() function saves the current coordinate
 * system to the stack and popMatrix() restores the prior coordinate system. pushMatrix() and popMatrix() are
 * used in conjuction with the other transformation methods and may be embedded to control the scope of
 * the transformations.
 *
 * @returns none
 *
 * @see popMatrix
 * @see translate
 * @see rotate
 * @see rotateX
 * @see rotateY
 * @see rotateZ
 */
pushMatrix = function () {
    userMatrixStack.load(modelView);
    userReverseMatrixStack.load(modelViewInv);
    saveContext();
};

pushMatrix = function () {
    userMatrixStack.load(modelView);
    userReverseMatrixStack.load(modelViewInv);
};

/**
 * Pops the current transformation matrix off the matrix stack. Understanding pushing and popping requires
 * understanding the concept of a matrix stack. The pushMatrix() function saves the current coordinate system to
 * the stack and popMatrix() restores the prior coordinate system. pushMatrix() and popMatrix() are used in
 * conjuction with the other transformation methods and may be embedded to control the scope of the transformations.
 *
 * @returns none
 *
 * @see popMatrix
 * @see pushMatrix
 */
popMatrix = function () {
    modelView.set(userMatrixStack.pop());
    modelViewInv.set(userReverseMatrixStack.pop());
    restoreContext();
};

popMatrix = function () {
    modelView.set(userMatrixStack.pop());
    modelViewInv.set(userReverseMatrixStack.pop());
};

/**
 * Replaces the current matrix with the identity matrix. The equivalent function in OpenGL is glLoadIdentity().
 *
 * @returns none
 *
 * @see popMatrix
 * @see pushMatrix
 * @see applyMatrix
 * @see printMatrix
 */
resetMatrix = function () {
    modelView.reset();
    modelViewInv.reset();
    curContext.setTransform(1, 0, 0, 1, 0, 0);
};

resetMatrix = function () {
    modelView.reset();
    modelViewInv.reset();
};

/**
 * Multiplies the current matrix by the one specified through the parameters. This is very slow because it will
 * try to calculate the inverse of the transform, so avoid it whenever possible. The equivalent function
 * in OpenGL is glMultMatrix().
 *
 * @param {int|float} n00-n15      numbers which define the 4x4 matrix to be multiplied
 *
 * @returns none
 *
 * @see popMatrix
 * @see pushMatrix
 * @see resetMatrix
 * @see printMatrix
 */
applyMatrix = function () {
    var a = arguments;
    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]);
    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]);
};

applyMatrix = function () {
    var a = arguments;
    for (var cnt = a.length; cnt < 16; cnt++) {
        a[cnt] = 0;
    }
    a[10] = a[15] = 1;
    applyMatrix.apply(this, a);
};

/**
 * Rotates a shape around the x-axis the amount specified by the angle parameter. Angles should be
 * specified in radians (values from 0 to PI*2) or converted to radians with the radians()  function.
 * Objects are always rotated around their relative position to the origin and positive numbers
 * rotate objects in a counterclockwise direction. Transformations apply to everything that happens
 * after and subsequent calls to the function accumulates the effect. For example, calling rotateX(PI/2)
 * and then rotateX(PI/2) is the same as rotateX(PI). If rotateX() is called within the draw(), the
 * transformation is reset when the loop begins again. This function requires passing P3D or OPENGL
 * into the size() parameter as shown in the example above.
 *
 * @param {int|float} angleInRadians     angle of rotation specified in radians
 *
 * @returns none
 *
 * @see rotateY
 * @see rotateZ
 * @see rotate
 * @see translate
 * @see scale
 * @see popMatrix
 * @see pushMatrix
 */
rotateX = function (angleInRadians) {
    modelView.rotateX(angleInRadians);
    modelViewInv.invRotateX(angleInRadians);
};

/**
 * Rotates a shape around the z-axis the amount specified by the angle parameter. Angles should be
 * specified in radians (values from 0 to PI*2) or converted to radians with the radians()  function.
 * Objects are always rotated around their relative position to the origin and positive numbers
 * rotate objects in a counterclockwise direction. Transformations apply to everything that happens
 * after and subsequent calls to the function accumulates the effect. For example, calling rotateZ(PI/2)
 * and then rotateZ(PI/2) is the same as rotateZ(PI). If rotateZ() is called within the draw(), the
 * transformation is reset when the loop begins again. This function requires passing P3D or OPENGL
 * into the size() parameter as shown in the example above.
 *
 * @param {int|float} angleInRadians     angle of rotation specified in radians
 *
 * @returns none
 *
 * @see rotateX
 * @see rotateY
 * @see rotate
 * @see translate
 * @see scale
 * @see popMatrix
 * @see pushMatrix
 */
rotateZ = function () {
    throw "rotateZ() is not supported in 2D mode. Use rotate(float) instead.";
};

rotateZ = function (angleInRadians) {
    modelView.rotateZ(angleInRadians);
    modelViewInv.invRotateZ(angleInRadians);
};

/**
 * Rotates a shape around the y-axis the amount specified by the angle parameter. Angles should be
 * specified in radians (values from 0 to PI*2) or converted to radians with the radians()  function.
 * Objects are always rotated around their relative position to the origin and positive numbers
 * rotate objects in a counterclockwise direction. Transformations apply to everything that happens
 * after and subsequent calls to the function accumulates the effect. For example, calling rotateY(PI/2)
 * and then rotateY(PI/2) is the same as rotateY(PI). If rotateY() is called within the draw(), the
 * transformation is reset when the loop begins again. This function requires passing P3D or OPENGL
 * into the size() parameter as shown in the example above.
 *
 * @param {int|float} angleInRadians     angle of rotation specified in radians
 *
 * @returns none
 *
 * @see rotateX
 * @see rotateZ
 * @see rotate
 * @see translate
 * @see scale
 * @see popMatrix
 * @see pushMatrix
 */
rotateY = function (angleInRadians) {
    modelView.rotateY(angleInRadians);
    modelViewInv.invRotateY(angleInRadians);
};

/**
 * Rotates a shape the amount specified by the angle parameter. Angles should be specified in radians
 * (values from 0 to TWO_PI) or converted to radians with the radians() function. Objects are always
 * rotated around their relative position to the origin and positive numbers rotate objects in a
 * clockwise direction. Transformations apply to everything that happens after and subsequent calls
 * to the function accumulates the effect. For example, calling rotate(HALF_PI) and then rotate(HALF_PI)
 * is the same as rotate(PI). All tranformations are reset when draw() begins again. Technically,
 * rotate() multiplies the current transformation matrix by a rotation matrix. This function can be
 * further controlled by the pushMatrix() and popMatrix().
 *
 * @param {int|float} angleInRadians     angle of rotation specified in radians
 *
 * @returns none
 *
 * @see rotateX
 * @see rotateY
 * @see rotateZ
 * @see rotate
 * @see translate
 * @see scale
 * @see popMatrix
 * @see pushMatrix
 */
rotate = function (angleInRadians) {
    modelView.rotateZ(angleInRadians);
    modelViewInv.invRotateZ(angleInRadians);
    // XXX(jeresig): Note, angleInRadians may be in degrees
    // depending upon the angleMode
    curContext.rotate(convertToRadians(angleInRadians));
};

rotate = function (angleInRadians) {
    rotateZ(angleInRadians);
};

/**
 * The pushStyle() function saves the current style settings and popStyle()  restores the prior settings.
 * Note that these functions are always used together. They allow you to change the style settings and later
 * return to what you had. When a new style is started with pushStyle(), it builds on the current style information.
 * The pushStyle() and popStyle() functions can be embedded to provide more control (see the second example
 * above for a demonstration.)
 * The style information controlled by the following functions are included in the style: fill(), stroke(), tint(),
 * strokeWeight(), strokeCap(), strokeJoin(), imageMode(), rectMode(), ellipseMode(), shapeMode(), colorMode(),
 * textAlign(), textFont(), textMode(), textSize(), textLeading(), emissive(), specular(), shininess(), ambient()
 *
 * @returns none
 *
 * @see popStyle
 */
pushStyle = function () {
    // Save the canvas state.
    saveContext();

    pushMatrix();

    var newState = {
        'doFill': doFill,
        'currentFillColor': currentFillColor,
        'doStroke': doStroke,
        'currentStrokeColor': currentStrokeColor,
        'curTint': curTint,
        'curRectMode': curRectMode,
        'curColorMode': curColorMode,
        'colorModeX': colorModeX,
        'colorModeZ': colorModeZ,
        'colorModeY': colorModeY,
        'colorModeA': colorModeA,
        'curTextFont': curTextFont,
        'horizontalTextAlignment': horizontalTextAlignment,
        'verticalTextAlignment': verticalTextAlignment,
        'textMode': textMode,
        'curFontName': curFontName,
        'curTextSize': curTextSize,
        'curTextAscent': curTextAscent,
        'curTextDescent': curTextDescent,
        'curTextLeading': curTextLeading
    };

    styleArray.push(newState);
};

/**
 * The pushStyle() function saves the current style settings and popStyle()  restores the prior settings; these
 * functions are always used together. They allow you to change the style settings and later return to what you had.
 * When a new style is started with pushStyle(), it builds on the current style information. The pushStyle() and
 * popStyle() functions can be embedded to provide more control (see the second example above for a demonstration.)
 *
 * @returns none
 *
 * @see pushStyle
 */
popStyle = function () {
    var oldState = styleArray.pop();

    if (oldState) {
        restoreContext();

        popMatrix();

        doFill = oldState.doFill;
        currentFillColor = oldState.currentFillColor;
        doStroke = oldState.doStroke;
        currentStrokeColor = oldState.currentStrokeColor;
        curTint = oldState.curTint;
        curRectMode = oldState.curRectmode;
        curColorMode = oldState.curColorMode;
        colorModeX = oldState.colorModeX;
        colorModeZ = oldState.colorModeZ;
        colorModeY = oldState.colorModeY;
        colorModeA = oldState.colorModeA;
        curTextFont = oldState.curTextFont;
        curFontName = oldState.curFontName;
        curTextSize = oldState.curTextSize;
        horizontalTextAlignment = oldState.horizontalTextAlignment;
        verticalTextAlignment = oldState.verticalTextAlignment;
        textMode = oldState.textMode;
        curTextAscent = oldState.curTextAscent;
        curTextDescent = oldState.curTextDescent;
        curTextLeading = oldState.curTextLeading;
    } else {
        throw "Too many popStyle() without enough pushStyle()";
    }
};

////////////////////////////////////////////////////////////////////////////
// Time based functions
////////////////////////////////////////////////////////////////////////////

/**
 * Processing communicates with the clock on your computer.
 * The year() function returns the current year as an integer (2003, 2004, 2005, etc).
 *
 * @returns {float} The current year.
 *
 * @see millis
 * @see second
 * @see minute
 * @see hour
 * @see day
 * @see month
 */
year = function () {
    return new Date().getFullYear();
};
/**
 * Processing communicates with the clock on your computer.
 * The month() function returns the current month as a value from 1 - 12.
 *
 * @returns {float} The current month.
 *
 * @see millis
 * @see second
 * @see minute
 * @see hour
 * @see day
 * @see year
 */
month = function () {
    return new Date().getMonth() + 1;
};
/**
 * Processing communicates with the clock on your computer.
 * The day() function returns the current day as a value from 1 - 31.
 *
 * @returns {float} The current day.
 *
 * @see millis
 * @see second
 * @see minute
 * @see hour
 * @see month
 * @see year
 */
day = function () {
    return new Date().getDate();
};
/**
 * Processing communicates with the clock on your computer.
 * The hour() function returns the current hour as a value from 0 - 23.
 *
 * @returns {float} The current hour.
 *
 * @see millis
 * @see second
 * @see minute
 * @see month
 * @see day
 * @see year
 */
hour = function () {
    return new Date().getHours();
};
/**
 * Processing communicates with the clock on your computer.
 * The minute() function returns the current minute as a value from 0 - 59.
 *
 * @returns {float} The current minute.
 *
 * @see millis
 * @see second
 * @see month
 * @see hour
 * @see day
 * @see year
 */
minute = function () {
    return new Date().getMinutes();
};
/**
 * Processing communicates with the clock on your computer.
 * The second() function returns the current second as a value from 0 - 59.
 *
 * @returns {float} The current minute.
 *
 * @see millis
 * @see month
 * @see minute
 * @see hour
 * @see day
 * @see year
 */
second = function () {
    return new Date().getSeconds();
};
/**
 * Returns the number of milliseconds (thousandths of a second) since starting a sketch.
 * This information is often used for timing animation sequences.
 *
 * @returns {long} The number of milliseconds since starting the sketch.
 *
 * @see month
 * @see second
 * @see minute
 * @see hour
 * @see day
 * @see year
 */
millis = function () {
    return Date.now() - start;
};

/**
 * Executes the code within draw() one time. This functions allows the program to update
 * the display window only when necessary, for example when an event registered by
 * mousePressed() or keyPressed() occurs.
 * In structuring a program, it only makes sense to call redraw() within events such as
 * mousePressed(). This is because redraw() does not run draw() immediately (it only sets
 * a flag that indicates an update is needed).
 * Calling redraw() within draw() has no effect because draw() is continuously called anyway.
 *
 * @returns none
 *
 * @see noLoop
 * @see loop
 */
redraw = function () {
    redrawHelper();

    curContext.lineWidth = lineWidth;
    var pmouseXLastEvent = pmouseX,
        pmouseYLastEvent = pmouseY;
    pmouseX = pmouseXLastFrame;
    pmouseY = pmouseYLastFrame;

    saveContext();
    draw();
    restoreContext();

    pmouseXLastFrame = mouseX;
    pmouseYLastFrame = mouseY;
    pmouseX = pmouseXLastEvent;
    pmouseY = pmouseYLastEvent;

    // Even if the user presses the mouse for less than the time of a single
    // frame, we want mouseIsPressed to be true for a single frame when
    // clicking, otherwise code that uses this boolean misses the click
    // completely. (This is hard to reproduce on a real mouse, but easy on a
    // trackpad with tap-to-click enabled.)
    mouseIsPressed = __mousePressed;
};

redraw = function () {
    redrawHelper();

    var pmouseXLastEvent = pmouseX,
        pmouseYLastEvent = pmouseY;
    pmouseX = pmouseXLastFrame;
    pmouseY = pmouseYLastFrame;
    // even if the color buffer isn't cleared with background(),
    // the depth buffer needs to be cleared regardless.
    curContext.clear(curContext.DEPTH_BUFFER_BIT);
    curContextCache = { attributes: {}, locations: {} };
    // Delete all the lighting states and the materials the
    // user set in the last draw() call.
    noLights();
    lightFalloff(1, 0, 0);
    shininess(1);
    ambient(255, 255, 255);
    specular(0, 0, 0);
    emissive(0, 0, 0);
    camera();
    draw();

    pmouseXLastFrame = mouseX;
    pmouseYLastFrame = mouseY;
    pmouseX = pmouseXLastEvent;
    pmouseY = pmouseYLastEvent;
    // (See comment about mouseIsPressed in redraw)
    mouseIsPressed = __mousePressed;
};

/**
 * Stops Processing from continuously executing the code within draw(). If loop() is
 * called, the code in draw() begin to run continuously again. If using noLoop() in
 * setup(), it should be the last line inside the block.
 * When noLoop() is used, it's not possible to manipulate or access the screen inside event
 * handling functions such as mousePressed() or keyPressed(). Instead, use those functions
 * to call redraw() or loop(), which will run draw(), which can update the screen properly.
 * This means that when noLoop() has been called, no drawing can happen, and functions like
 * saveFrame() or loadPixels() may not be used.
 * Note that if the sketch is resized, redraw() will be called to update the sketch, even
 * after noLoop() has been specified. Otherwise, the sketch would enter an odd state until
 * loop() was called.
 *
 * @returns none
 *
 * @see redraw
 * @see draw
 * @see loop
 */
noLoop = function () {
    doLoop = false;
    loopStarted = false;
    clearInterval(looping);
    curSketch.onPause();
};

/**
 * Causes Processing to continuously execute the code within draw(). If noLoop() is called,
 * the code in draw() stops executing.
 *
 * @returns none
 *
 * @see noLoop
 */
loop = function () {
    if (loopStarted) {
        return;
    }

    timeSinceLastFPS = Date.now();
    framesSinceLastFPS = 0;

    looping = window.setInterval(function () {
        try {
            curSketch.onFrameStart();
            redraw();
            curSketch.onFrameEnd();
        } catch (e_loop) {
            window.clearInterval(looping);
            throw e_loop;
        }
    }, curMsPerFrame);
    doLoop = true;
    loopStarted = true;
    curSketch.onLoop();
};

/**
 * Specifies the number of frames to be displayed every second. If the processor is not
 * fast enough to maintain the specified rate, it will not be achieved. For example, the
 * function call frameRate(30) will attempt to refresh 30 times a second. It is recommended
 * to set the frame rate within setup(). The default rate is 60 frames per second.
 *
 * @param {int} aRate        number of frames per second.
 *
 * @returns none
 *
 * @see delay
 */
frameRate = function (aRate) {
    curFrameRate = aRate;
    curMsPerFrame = 1000 / curFrameRate;

    // clear and reset interval
    if (doLoop) {
        noLoop();
        loop();
    }
};

////////////////////////////////////////////////////////////////////////////
// JavaScript event binding and releasing
////////////////////////////////////////////////////////////////////////////

/**
 * Quits/stops/exits the program. Programs without a draw() function exit automatically
 * after the last line has run, but programs with draw() run continuously until the
 * program is manually stopped or exit() is run.
 * Rather than terminating immediately, exit() will cause the sketch to exit after draw()
 * has completed (or after setup() completes if called during the setup() method).
 *
 * @returns none
 */
exit = function () {
    window.clearInterval(looping);

    removeInstance(externals.canvas.id);

    // Step through the libraries to detach them
    for (var lib in Processing.lib) {
        if (Processing.lib.hasOwnProperty(lib)) {
            if (Processing.lib[lib].hasOwnProperty("detach")) {
                Processing.lib[lib].detach(p);
            }
        }
    }

    var i = eventHandlers.length;
    while (i--) {
        detachEventHandler(eventHandlers[i]);
    }
    curSketch.onExit();
};

////////////////////////////////////////////////////////////////////////////
// MISC functions
////////////////////////////////////////////////////////////////////////////

/**
 * Sets the cursor to a predefined symbol, an image, or turns it on if already hidden.
 * If you are trying to set an image as the cursor, it is recommended to make the size
 * 16x16 or 32x32 pixels. It is not possible to load an image as the cursor if you are
 * exporting your program for the Web. The values for parameters x and y must be less
 * than the dimensions of the image.
 *
 * @param {MODE} MODE either ARROW, CROSS, HAND, MOVE, TEXT, WAIT
 * @param {PImage} image       any variable of type PImage
 * @param {int}    x           the horizonal active spot of the cursor
 * @param {int}    y           the vertical active spot of the cursor
 *
 * @returns none
 *
 * @see noCursor
 */
cursor = function () {
    if (arguments.length > 1 || (arguments.length === 1 && arguments[0] instanceof PImage)) {
        var image = arguments[0],
            x, y;
        if (arguments.length >= 3) {
            x = arguments[1];
            y = arguments[2];
            if (x < 0 || y < 0 || y >= image.height || x >= image.width) {
                throw "x and y must be non-negative and less than the dimensions of the image";
            }
        } else {
            x = image.width >>> 1;
            y = image.height >>> 1;
        }

        // see https://developer.mozilla.org/en/Using_URL_values_for_the_cursor_property
        var imageDataURL = image.toDataURL();
        var style = "url(\"" + imageDataURL + "\") " + x + " " + y + ", default";
        curCursor = curElement.style.cursor = style;
    } else if (arguments.length === 1) {
        var mode = arguments[0];
        curCursor = curElement.style.cursor = mode;
    } else {
        curCursor = curElement.style.cursor = oldCursor;
    }
};

/**
 * Hides the cursor from view.
 *
 * @returns none
 *
 * @see cursor
 */
noCursor = function () {
    curCursor = curElement.style.cursor = PConstants.NOCURSOR;
};

/**
 * Links to a webpage either in the same window or in a new window. The complete URL
 * must be specified.
 *
 * @param {String} href      complete url as a String in quotes
 * @param {String} target    name of the window to load the URL as a string in quotes
 *
 * @returns none
 */
link = function (href, target) {
    if (target !== undef) {
        window.open(href, target);
    } else {
        window.location = href;
    }
};

// PGraphics methods
/**
 * These functions exist only for compatibility with P5
 */
beginDraw = nop;
/**
 * These functions exist only for compatibility with P5
 */
endDraw = nop;

/**
 * This function takes content from a canvas and turns it into an ImageData object to be used with a PImage
 *
 * @returns {ImageData}        ImageData object to attach to a PImage (1D array of pixel data)
 *
 * @see PImage
 */
toImageData = function (x, y, w, h) {
    x = x !== undef ? x : 0;
    y = y !== undef ? y : 0;
    w = w !== undef ? w : width;
    h = h !== undef ? h : height;
    return curContext.getImageData(x, y, w, h);
};

toImageData = function (x, y, w, h) {
    x = x !== undef ? x : 0;
    y = y !== undef ? y : 0;
    w = w !== undef ? w : width;
    h = h !== undef ? h : height;
    var c = document.createElement("canvas"),
        ctx = c.getContext("2d"),
        obj = ctx.createImageData(w, h),
        uBuff = new Uint8Array(w * h * 4);
    curContext.readPixels(x, y, w, h, curContext.RGBA, curContext.UNSIGNED_BYTE, uBuff);
    for (var i = 0, ul = uBuff.length, obj_data = obj.data; i < ul; i++) {
        obj_data[i] = uBuff[(h - 1 - Math.floor(i / 4 / w)) * w * 4 + (i % (w * 4))];
    }
    return obj;
};

/**
 * Displays message in the browser's status area. This is the text area in the lower
 * left corner of the browser. The status() function will only work when the
 * Processing program is running in a web browser.
 *
 * @param {String} text      any valid String
 *
 * @returns none
 */
status = function (text) {
    window.status = text;
};

////////////////////////////////////////////////////////////////////////////
// Binary Functions
////////////////////////////////////////////////////////////////////////////

/**
 * Converts a byte, char, int, or color to a String containing the equivalent binary
 * notation. For example color(0, 102, 153, 255) will convert to the String
 * "11111111000000000110011010011001". This function can help make your geeky debugging
 * sessions much happier.
 *
 * @param {byte|char|int|color} num          byte, char, int, color: value to convert
 * @param {int} numBits                      number of digits to return
 *
 * @returns {String}
 *
 * @see unhex
 * @see hex
 * @see unbinary
 */
binary = function (num, numBits) {
    var bit;
    if (numBits > 0) {
        bit = numBits;
    } else if (num instanceof Char) {
        bit = 16;
        num |= 0; // making it int
    } else {
        // autodetect, skipping zeros
        bit = 32;
        while (bit > 1 && !((num >>> (bit - 1)) & 1)) {
            bit--;
        }
    }
    var result = "";
    while (bit > 0) {
        result += ((num >>> (--bit)) & 1) ? "1" : "0";
    }
    return result;
};

/**
 * Converts a String representation of a binary number to its equivalent integer value.
 * For example, unbinary("00001000") will return 8.
 *
 * @param {String} binaryString String
 *
 * @returns {Int}
 *
 * @see hex
 * @see binary
 * @see unbinary
 */
unbinary = function (binaryString) {
    var i = binaryString.length - 1, mask = 1, result = 0;
    while (i >= 0) {
        var ch = binaryString[i--];
        if (ch !== '0' && ch !== '1') {
            throw "the value passed into unbinary was not an 8 bit binary number";
        }
        if (ch === '1') {
            result += mask;
        }
        mask <<= 1;
    }
    return result;
};

/**
 * Utility function for formatting numbers into strings. There are two versions, one for
 * formatting floats and one for formatting ints. The values for the digits, left, and
 * right parameters should always be positive integers.
 * As shown in the above example, nf() is used to add zeros to the left and/or right
 * of a number. This is typically for aligning a list of numbers. To remove digits from
 * a floating-point number, use the int(), ceil(), floor(), or round() functions.
 *
 * @param {int|int[]|float|float[]} value   the number(s) to format
 * @param {int} left                        number of digits to the left of the decimal point
 * @param {int} right                       number of digits to the right of the decimal point
 *
 * @returns {String or String[]}
 *
 * @see nfs
 * @see nfp
 * @see nfc
 */
nf = function (value, leftDigits, rightDigits) { return nfCore(value, "", "-", leftDigits, rightDigits); };

/**
 * Utility function for formatting numbers into strings. Similar to nf()  but leaves a blank space in front
 * of positive numbers so they align with negative numbers in spite of the minus symbol. There are two
 * versions, one for formatting floats and one for formatting ints. The values for the digits, left,
 * and right parameters should always be positive integers.
 *
 * @param {int|int[]|float|float[]} value   the number(s) to format
 * @param {int} left                        number of digits to the left of the decimal point
 * @param {int} right                       number of digits to the right of the decimal point
 *
 * @returns {String or String[]}
 *
 * @see nf
 * @see nfp
 * @see nfc
 */
nfs = function (value, leftDigits, rightDigits) { return nfCore(value, " ", "-", leftDigits, rightDigits); };

/**
 * Utility function for formatting numbers into strings. Similar to nf()  but puts a "+" in front of
 * positive numbers and a "-" in front of negative numbers. There are two versions, one for formatting
 * floats and one for formatting ints. The values for the digits, left, and right parameters should
 * always be positive integers.
 *
 * @param {int|int[]|float|float[]} value   the number(s) to format
 * @param {int} left                        number of digits to the left of the decimal point
 * @param {int} right                       number of digits to the right of the decimal point
 *
 * @returns {String or String[]}
 *
 * @see nfs
 * @see nf
 * @see nfc
 */
nfp = function (value, leftDigits, rightDigits) { return nfCore(value, "+", "-", leftDigits, rightDigits); };

/**
 * Utility function for formatting numbers into strings and placing appropriate commas to mark
 * units of 1000. There are two versions, one for formatting ints and one for formatting an array
 * of ints. The value for the digits parameter should always be a positive integer.
 *
 * @param {int|int[]|float|float[]} value   the number(s) to format
 * @param {int} left                        number of digits to the left of the decimal point
 * @param {int} right                       number of digits to the right of the decimal point
 *
 * @returns {String or String[]}
 *
 * @see nf
 * @see nfs
 * @see nfp
 */
nfc = function (value, leftDigits, rightDigits) { return nfCore(value, "", "-", leftDigits, rightDigits, ","); };

// note: since we cannot keep track of byte, int types by default the returned string is 8 chars long
// if no 2nd argument is passed.  closest compromise we can use to match java implementation Feb 5 2010
// also the char parser has issues with chars that are not digits or letters IE: !@#$%^&*
/**
 * Converts a byte, char, int, or color to a String containing the equivalent hexadecimal notation.
 * For example color(0, 102, 153, 255) will convert to the String "FF006699". This function can help
 * make your geeky debugging sessions much happier.
 *
 * @param {byte|char|int|Color} value   the value to turn into a hex string
 * @param {int} digits                 the number of digits to return
 *
 * @returns {String}
 *
 * @see unhex
 * @see binary
 * @see unbinary
 */
hex = function (value, len) {
    if (arguments.length === 1) {
        if (value instanceof Char) {
            len = 4;
        } else { // int or byte, indistinguishable at the moment, default to 8
            len = 8;
        }
    }
    return decimalToHex(value, len);
};

/**
 * Converts a String representation of a hexadecimal number to its equivalent integer value.
 *
 * @param {String} hex   the hex string to convert to an int
 *
 * @returns {int}
 *
 * @see hex
 * @see binary
 * @see unbinary
 */
unhex = function (hex) {
    if (hex instanceof Array) {
        var arr = [];
        for (var i = 0; i < hex.length; i++) {
            arr.push(unhexScalar(hex[i]));
        }
        return arr;
    }
    return unhexScalar(hex);
};

// Load a file or URL into strings
/**
 * Reads the contents of a file or url and creates a String array of its individual lines.
 * The filename parameter can also be a URL to a file found online.  If the file is not available or an error occurs,
 * null will be returned and an error message will be printed to the console. The error message does not halt
 * the program.
 *
 * @param {String} filename    name of the file or url to load
 *
 * @returns {String[]}
 *
 * @see loadBytes
 * @see saveStrings
 * @see saveBytes
 */
loadStrings = function (filename) {
    if (localStorage[filename]) {
        return localStorage[filename].split("\n");
    }

    var filecontent = ajax(filename);
    if (typeof filecontent !== "string" || filecontent === "") {
        return [];
    }

    // deal with the fact that Windows uses \r\n, Unix uses \n,
    // Mac uses \r, and we actually expect \n
    filecontent = filecontent.replace(/(\r\n?)/g, "\n").replace(/\n$/, "");

    return filecontent.split("\n");
};

// Writes an array of strings to a file, one line per string
/**
 * Writes an array of strings to a file, one line per string. This file is saved to the localStorage.
 *
 * @param {String} filename    name of the file to save to localStorage
 * @param {String[]} strings   string array to be written
 *
 * @see loadBytes
 * @see loadStrings
 * @see saveBytes
 */
saveStrings = function (filename, strings) {
    localStorage[filename] = strings.join('\n');
};

/**
 * 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.
 * The filename parameter can also be a URL to a file found online.
 *
 * @param {String} filename   name of a file in the localStorage or a URL.
 *
 * @returns {byte[]}
 *
 * @see loadStrings
 * @see saveStrings
 * @see saveBytes
 */
loadBytes = function (url) {
    var string = ajax(url);
    var ret = [];

    for (var i = 0; i < string.length; i++) {
        ret.push(string.charCodeAt(i));
    }

    return ret;
};

/**
 * Removes the first argument from the arguments set -- shifts.
 *
 * @param {Arguments} args  The Arguments object.
 *
 * @return {Object[]}       Returns an array of arguments except first one.
 *
 * @see #match
 */
function removeFirstArgument(args) {
    return Array.prototype.slice.call(args, 1);
}

////////////////////////////////////////////////////////////////////////////
// String Functions
////////////////////////////////////////////////////////////////////////////
/**
 * The matchAll() function is identical to match(), except that it returns an array of all matches in
 * the specified String, rather than just the first.
 *
 * @param {String} aString  the String to search inside
 * @param {String} aRegExp  the regexp to be used for matching
 *
 * @return {String[]} returns an array of matches
 *
 * @see #match
 */
matchAll = function (aString, aRegExp) {
    var results = [],
        latest;
    var regexp = new RegExp(aRegExp, "g");
    while ((latest = regexexec(aString)) !== null) {
        results.push(latest);
        if (latest[0].length === 0) {
            ++regexlastIndex;
        }
    }
    return results.length > 0 ? results : null;
};
/**
 * The contains(string) function returns true if the string passed in the parameter
 * is a substring of this string. It returns false if the string passed
 * in the parameter is not a substring of this string.
 *
 * @param {String} The string to look for in the current string
 *
 * @return {boolean} returns true if this string contains
 * the string passed as parameter. returns false, otherwise.
 *
 */
__contains = function (subject, subStr) {
    if (typeof subject !== "string") {
        return subject.contains.apply(subject, removeFirstArgument(arguments));
    }
    //Parameter is not null AND
    //The type of the parameter is the same as this object (string)
    //The javascript function that finds a substring returns 0 or higher
    return (
        (subject !== null) &&
        (subStr !== null) &&
        (typeof subStr === "string") &&
        (subject.indexOf(subStr) > -1)
    );
};
/**
 * The __replaceAll() function searches all matches between a substring (or regular expression) and a string,
 * and replaces the matched substring with a new substring
 *
 * @param {String} subject    a substring
 * @param {String} regex      a substring or a regular expression
 * @param {String} replace    the string to replace the found value
 *
 * @return {String} returns result
 *
 * @see #match
 */
__replaceAll = function (subject, regex, replacement) {
    if (typeof subject !== "string") {
        return subject.replaceAll.apply(subject, removeFirstArgument(arguments));
    }

    return subject.replace(new RegExp(regex, "g"), replacement);
};
/**
 * The __replaceFirst() function searches first matche between a substring (or regular expression) and a string,
 * and replaces the matched substring with a new substring
 *
 * @param {String} subject    a substring
 * @param {String} regex      a substring or a regular expression
 * @param {String} replace    the string to replace the found value
 *
 * @return {String} returns result
 *
 * @see #match
 */
__replaceFirst = function (subject, regex, replacement) {
    if (typeof subject !== "string") {
        return subject.replaceFirst.apply(subject, removeFirstArgument(arguments));
    }

    return subject.replace(new RegExp(regex, ""), replacement);
};
/**
 * The __replace() function searches all matches between a substring and a string,
 * and replaces the matched substring with a new substring
 *
 * @param {String} subject         a substring
 * @param {String} what         a substring to find
 * @param {String} replacement    the string to replace the found value
 *
 * @return {String} returns result
 */
__replace = function (subject, what, replacement) {
    if (typeof subject !== "string") {
        return subject.replace.apply(subject, removeFirstArgument(arguments));
    }
    if (what instanceof RegExp) {
        return subject.replace(what, replacement);
    }

    if (typeof what !== "string") {
        what = what.toString();
    }
    if (what === "") {
        return subject;
    }

    var i = subject.indexOf(what);
    if (i < 0) {
        return subject;
    }

    var j = 0, result = "";
    do {
        result += subject.substring(j, i) + replacement;
        j = i + what.length;
    } while ((i = subject.indexOf(what, j)) >= 0);
    return result + subject.substring(j);
};
/**
 * The __equals() function compares two strings (or objects) to see if they are the same.
 * This method is necessary because it's not possible to compare strings using the equality operator (==).
 * Returns true if the strings are the same and false if they are not.
 *
 * @param {String} subject  a string used for comparison
 * @param {String} other  a string used for comparison with
 *
 * @return {boolean} true is the strings are the same false otherwise
 */
__equals = function (subject, other) {
    if (subject.equals instanceof Function) {
        return subject.equals.apply(subject, removeFirstArgument(arguments));
    }

    // TODO use virtEquals for HashMap here
    return subject.valueOf() === other.valueOf();
};
/**
 * The __equalsIgnoreCase() function compares two strings to see if they are the same.
 * Returns true if the strings are the same, either when forced to all lower case or
 * all upper case.
 *
 * @param {String} subject  a string used for comparison
 * @param {String} other  a string used for comparison with
 *
 * @return {boolean} true is the strings are the same, ignoring case. false otherwise
 */
__equalsIgnoreCase = function (subject, other) {
    if (typeof subject !== "string") {
        return subject.equalsIgnoreCase.apply(subject, removeFirstArgument(arguments));
    }

    return subject.toLowerCase() === other.toLowerCase();
};
/**
 * The __toCharArray() function splits the string into a char array.
 *
 * @param {String} subject The string
 *
 * @return {Char[]} a char array
 */
__toCharArray = function (subject) {
    if (typeof subject !== "string") {
        return subject.toCharArray.apply(subject, removeFirstArgument(arguments));
    }

    var chars = [];
    for (var i = 0, len = subject.length; i < len; ++i) {
        chars[i] = new Char(subject.charAt(i));
    }
    return chars;
};
/**
 * The __split() function splits a string using the regex delimiter
 * specified. If limit is specified, the resultant array will have number
 * of elements equal to or less than the limit.
 *
 * @param {String} subject string to be split
 * @param {String} regexp  regex string used to split the subject
 * @param {int}    limit   max number of tokens to be returned
 *
 * @return {String[]} an array of tokens from the split string
 */
__split = function (subject, regex, limit) {
    if (typeof subject !== "string") {
        return subject.split.apply(subject, removeFirstArgument(arguments));
    }

    var pattern = new RegExp(regex);

    // If limit is not specified, use JavaScript's built-in String.split.
    if ((limit === undef) || (limit < 1)) {
        return subject.split(pattern);
    }

    // If limit is specified, JavaScript's built-in String.split has a
    // different behaviour than Java's. A Java-compatible implementation is
    // provided here.
    var result = [], currSubject = subject, pos;
    while (((pos = currSubject.search(pattern)) !== -1)
        && (result.length < (limit - 1))) {
        var match = pattern.exec(currSubject).toString();
        result.push(currSubject.substring(0, pos));
        currSubject = currSubject.substring(pos + match.length);
    }
    if ((pos !== -1) || (currSubject !== "")) {
        result.push(currSubject);
    }
    return result;
};
/**
 * The codePointAt() function returns the unicode value of the character at a given index of a string.
 *
 * @param  {int} idx         the index of the character
 *
 * @return {String} code     the String containing the unicode value of the character
 */
__codePointAt = function (subject, idx) {
    var code = subject.charCodeAt(idx),
        hi,
        low;
    if (0xD800 <= code && code <= 0xDBFF) {
        hi = code;
        low = subject.charCodeAt(idx + 1);
        return ((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000;
    }
    return code;
};
/**
 * The match() function matches a string with a regular expression, and returns the match as an
 * array. The first index is the matching expression, and array elements
 * [1] and higher represent each of the groups (sequences found in parens).
 *
 * @param {String} str      the String to be searched
 * @param {String} regexp   the regexp to be used for matching
 *
 * @return {String[]} an array of matching strings
 */
match = function (str, regexp) {
    return str.match(regexp);
};
/**
 * The startsWith() function tests if a string starts with the specified prefix.  If the prefix
 * is the empty String or equal to the subject String, startsWith() will also return true.
 *
 * @param {String} prefix   the String used to compare against the start of the subject String.
 * @param {int}    toffset  (optional) an offset into the subject String where searching should begin.
 *
 * @return {boolean} true if the subject String starts with the prefix.
 */
__startsWith = function (subject, prefix, toffset) {
    if (typeof subject !== "string") {
        return subject.startsWith.apply(subject, removeFirstArgument(arguments));
    }

    toffset = toffset || 0;
    if (toffset < 0 || toffset > subject.length) {
        return false;
    }
    return (prefix === '' || prefix === subject) ? true : (subject.indexOf(prefix) === toffset);
};
/**
 * The endsWith() function tests if a string ends with the specified suffix.  If the suffix
 * is the empty String, endsWith() will also return true.
 *
 * @param {String} suffix   the String used to compare against the end of the subject String.
 *
 * @return {boolean} true if the subject String starts with the prefix.
 */
__endsWith = function (subject, suffix) {
    if (typeof subject !== "string") {
        return subject.endsWith.apply(subject, removeFirstArgument(arguments));
    }

    var suffixLen = suffix ? suffix.length : 0;
    return (suffix === '' || suffix === subject) ? true :
        (subject.indexOf(suffix) === subject.length - suffixLen);
};

////////////////////////////////////////////////////////////////////////////
// Other java specific functions
////////////////////////////////////////////////////////////////////////////

/**
 * The returns hash code of the.
 *
 * @param {Object} subject The string
 *
 * @return {int} a hash code
 */
__hashCode = function (subject) {
    if (subject.hashCode instanceof Function) {
        return subject.hashCode.apply(subject, removeFirstArgument(arguments));
    }
    return virtHashCode(subject);
};
/**
 * The __printStackTrace() prints stack trace to the console.
 *
 * @param {Exception} subject The error
 */
__printStackTrace = function (subject) {
    println("Exception: " + subject.toString());
};

/**
 * Clears logs, if logger has been initialized
 */
_clearLogs = function () {
    if (Processing.logger.clear) {
        Processing.logger.clear();
    }
};

/**
 * The println() function writes to the console area of the Processing environment.
 * 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 (+).
 *
 * @param {String} message the string to write to the console
 *
 * @see #join
 * @see #print
 */
println = function (message) {
    var bufferLen = logBuffer.length;
    if (bufferLen) {
        Processing.logger.log(logBuffer.join(""));
        logBuffer.length = 0; // clear log buffer
    }

    if (arguments.length === 0 && bufferLen === 0) {
        Processing.logger.log("");
    } else if (arguments.length !== 0) {
        Processing.logger.log(message);
    }
};
/**
 * The print() function writes to the console area of the Processing environment.
 *
 * @param {String} message the string to write to the console
 *
 * @see #join
 */
print = function (message) {
    logBuffer.push(message);
};

/**
 * Alphanumeric chars arguments automatically converted to numbers when
 * passed in, and will come out as numbers.
 */
str = function (val) {
    if (val instanceof Array) {
        var arr = [];
        for (var i = 0; i < val.length; i++) {
            arr.push(val[i].toString() + "");
        }
        return arr;
    }
    return (val.toString() + "");
};
/**
 * Remove whitespace characters from the beginning and ending
 * of a String or a String array. Works like String.trim() but includes the
 * unicode nbsp character as well. If an array is passed in the function will return a new array not effecting the array passed in.
 *
 * @param {String} str    the string to trim
 * @param {String[]} str  the string array to trim
 *
 * @return {String|String[]} retrurns a string or an array will removed whitespaces
 */
trim = function (str) {
    if (str instanceof Array) {
        var arr = [];
        for (var i = 0; i < str.length; i++) {
            arr.push(str[i].replace(/^\s*/, '').replace(/\s*$/, '').replace(/\r*$/, ''));
        }
        return arr;
    }
    return str.replace(/^\s*/, '').replace(/\s*$/, '').replace(/\r*$/, '');
};

/**
 * Converts the passed parameter to the function to its boolean value.
 * It will return an array of booleans if an array is passed in.
 *
 * @param {int, byte, string} val          the parameter to be converted to boolean
 * @param {int[], byte[], string[]} val    the array to be converted to boolean[]
 *
 * @return {boolean|boolean[]} returns a boolean or an array of booleans
 */
parseBoolean = function (val) {
    if (val instanceof Array) {
        var ret = [];
        for (var i = 0; i < val.length; i++) {
            ret.push(booleanScalar(val[i]));
        }
        return ret;
    }
    return booleanScalar(val);
};

/**
 * Converts the passed parameter to the function to its byte value.
 * A byte is a number between -128 and 127.
 * It will return an array of bytes if an array is passed in.
 *
 * @param {int, char} what        the parameter to be conveted to byte
 * @param {int[], char[]} what    the array to be converted to byte[]
 *
 * @return {byte|byte[]} returns a byte or an array of bytes
 */
parseByte = function (what) {
    if (what instanceof Array) {
        var bytes = [];
        for (var i = 0; i < what.length; i++) {
            bytes.push((0 - (what[i] & 0x80)) | (what[i] & 0x7F));
        }
        return bytes;
    }
    return (0 - (what & 0x80)) | (what & 0x7F);
};

/**
 * Converts the passed parameter to the function to its char value.
 * It will return an array of chars if an array is passed in.
 *
 * @param {int, byte} key        the parameter to be conveted to char
 * @param {int[], byte[]} key    the array to be converted to char[]
 *
 * @return {char|char[]} returns a char or an array of chars
 */
parseChar = function (key) {
    if (typeof key === "number") {
        return new Char(String.fromCharCode(key & 0xFFFF));
    }
    if (key instanceof Array) {
        var ret = [];
        for (var i = 0; i < key.length; i++) {
            ret.push(new Char(String.fromCharCode(key[i] & 0xFFFF)));
        }
        return ret;
    }
    throw "char() may receive only one argument of type int, byte, int[], or byte[].";
};

/**
 * Converts the passed parameter to the function to its float value.
 * It will return an array of floats if an array is passed in.
 *
 * @param {int, char, boolean, string} val            the parameter to be conveted to float
 * @param {int[], char[], boolean[], string[]} val    the array to be converted to float[]
 *
 * @return {float|float[]} returns a float or an array of floats
 */
parseFloat = function (val) {
    if (val instanceof Array) {
        var ret = [];
        for (var i = 0; i < val.length; i++) {
            ret.push(floatScalar(val[i]));
        }
        return ret;
    }
    return floatScalar(val);
};

/**
 * Converts the passed parameter to the function to its int value.
 * It will return an array of ints if an array is passed in.
 *
 * @param {string, char, boolean, float} val            the parameter to be conveted to int
 * @param {string[], char[], boolean[], float[]} val    the array to be converted to int[]
 * @param {int} radix                                   optional the radix of the number (for js compatibility)
 *
 * @return {int|int[]} returns a int or an array of ints
 */
parseInt = function (val, radix) {
    if (val instanceof Array) {
        var ret = [];
        for (var i = 0; i < val.length; i++) {
            if (typeof val[i] === 'string' && !/^\s*[+\-]?\d+\s*$/.test(val[i])) {
                ret.push(0);
            } else {
                ret.push(intScalar(val[i], radix));
            }
        }
        return ret;
    }
    return intScalar(val, radix);
};

/**
 * @param {*} val
 */
__int_cast = function (val) {
    return 0 | val;
};

/**
 * @param {*} obj
 * @param {*} type
 */
__instanceof = function (obj, type) {
    if (typeof type !== "function") {
        throw "Function is expected as type argument for instanceof operator";
    }

    if (typeof obj === "string") {
        // special case for strings
        return type === Object || type === String;
    }

    if (obj instanceof type) {
        // fast check if obj is already of type instance
        return true;
    }

    if (typeof obj !== "object" || obj === null) {
        return false; // not an object or null
    }

    var objType = obj.constructor;
    if (type.$isInterface) {
        // expecting the interface
        // queueing interfaces from type and its base classes
        var interfaces = [];
        while (objType) {
            if (objType.$interfaces) {
                interfaces = interfaces.concat(objType.$interfaces);
            }
            objType = objType.$base;
        }
        while (interfaces.length > 0) {
            var i = interfaces.shift();
            if (i === type) {
                return true;
            }
            // wide search in base interfaces
            if (i.$interfaces) {
                interfaces = interfaces.concat(i.$interfaces);
            }
        }
        return false;
    }

    while (objType.hasOwnProperty("$base")) {
        objType = objType.$base;
        if (objType === type) {
            return true; // object was found
        }
    }

    return false;
};

////////////////////////////////////////////////////////////////////////////
// Math functions
////////////////////////////////////////////////////////////////////////////

// Calculation
/**
 * Calculates the absolute value (magnitude) of a number. The absolute value of a number is always positive.
 *
 * @param {int|float} value   int or float
 *
 * @returns {int|float}
 */
abs = Math.abs;

/**
 * Calculates the closest int value that is greater than or equal to the value of the parameter.
 * For example, ceil(9.03) returns the value 10.
 *
 * @param {float} value   float
 *
 * @returns {int}
 *
 * @see floor
 * @see round
 */
ceil = Math.ceil;

/**
 * Constrains a value to not exceed a maximum and minimum value.
 *
 * @param {int|float} value   the value to constrain
 * @param {int|float} value   minimum limit
 * @param {int|float} value   maximum limit
 *
 * @returns {int|float}
 *
 * @see max
 * @see min
 */
constrain = function (aNumber, aMin, aMax) {
    return aNumber > aMax ? aMax : aNumber < aMin ? aMin : aNumber;
};

/**
 * Calculates the distance between two points.
 *
 * @param {int|float} x1     int or float: x-coordinate of the first point
 * @param {int|float} y1     int or float: y-coordinate of the first point
 * @param {int|float} z1     int or float: z-coordinate of the first point
 * @param {int|float} x2     int or float: x-coordinate of the second point
 * @param {int|float} y2     int or float: y-coordinate of the second point
 * @param {int|float} z2     int or float: z-coordinate of the second point
 *
 * @returns {float}
 */
dist = function () {
    var dx, dy, dz;
    if (arguments.length === 4) {
        dx = arguments[0] - arguments[2];
        dy = arguments[1] - arguments[3];
        return Math.sqrt(dx * dx + dy * dy);
    }
    if (arguments.length === 6) {
        dx = arguments[0] - arguments[3];
        dy = arguments[1] - arguments[4];
        dz = arguments[2] - arguments[5];
        return Math.sqrt(dx * dx + dy * dy + dz * dz);
    }
};

/**
 * Returns Euler's number e (2.71828...) raised to the power of the value parameter.
 *
 * @param {int|float} value   int or float: the exponent to raise e to
 *
 * @returns {float}
 */
exp = Math.exp;

/**
 * Calculates the closest int value that is less than or equal to the value of the parameter.
 *
 * @param {int|float} value        the value to floor
 *
 * @returns {int|float}
 *
 * @see ceil
 * @see round
 */
floor = Math.floor;

/**
 * Calculates a number between two numbers at a specific increment. The amt  parameter is the
 * amount to interpolate between the two values where 0.0 equal to the first point, 0.1 is very
 * near the first point, 0.5 is half-way in between, etc. The lerp function is convenient for
 * creating motion along a straight path and for drawing dotted lines.
 *
 * @param {int|float} value1       float or int: first value
 * @param {int|float} value2       float or int: second value
 * @param {int|float} amt          float: between 0.0 and 1.0
 *
 * @returns {float}
 *
 * @see curvePoint
 * @see bezierPoint
 */
lerp = function (value1, value2, amt) {
    return ((value2 - value1) * amt) + value1;
};

/**
 * Calculates the natural logarithm (the base-e logarithm) of a number. This function
 * expects the values greater than 0.0.
 *
 * @param {int|float} value        int or float: number must be greater then 0.0
 *
 * @returns {float}
 */
log = Math.log;

/**
 * Calculates the magnitude (or length) of a vector. A vector is a direction in space commonly
 * used in computer graphics and linear algebra. Because it has no "start" position, the magnitude
 * of a vector can be thought of as the distance from coordinate (0,0) to its (x,y) value.
 * Therefore, mag() is a shortcut for writing "dist(0, 0, x, y)".
 *
 * @param {int|float} a       float or int: first value
 * @param {int|float} b       float or int: second value
 * @param {int|float} c       float or int: third value
 *
 * @returns {float}
 *
 * @see dist
 */
mag = function (a, b, c) {
    if (c) {
        return Math.sqrt(a * a + b * b + c * c);
    }

    return Math.sqrt(a * a + b * b);
};

/**
 * Re-maps a number from one range to another. In the example above, the number '25' is converted from
 * 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.
 * Numbers outside the range are not clamped to 0 and 1, because out-of-range values are often intentional and useful.
 *
 * @param {float} value        The incoming value to be converted
 * @param {float} istart       Lower bound of the value's current range
 * @param {float} istop        Upper bound of the value's current range
 * @param {float} ostart       Lower bound of the value's target range
 * @param {float} ostop        Upper bound of the value's target range
 *
 * @returns {float}
 *
 * @see norm
 * @see lerp
 */
map = function (value, istart, istop, ostart, ostop) {
    return ostart + (ostop - ostart) * ((value - istart) / (istop - istart));
};

/**
 * Determines the largest value in a sequence of numbers.
 *
 * @param {int|float} value1         int or float
 * @param {int|float} value2         int or float
 * @param {int|float} value3         int or float
 * @param {int|float} array          int or float array
 *
 * @returns {int|float}
 *
 * @see min
 */
max = function () {
    if (arguments.length === 2) {
        return arguments[0] < arguments[1] ? arguments[1] : arguments[0];
    }
    var numbers = arguments.length === 1 ? arguments[0] : arguments; // if single argument, array is used
    if (!("length" in numbers && numbers.length > 0)) {
        throw "Non-empty array is expected";
    }
    var max = numbers[0],
        count = numbers.length;
    for (var i = 1; i < count; ++i) {
        if (max < numbers[i]) {
            max = numbers[i];
        }
    }
    return max;
};

/**
 * Determines the smallest value in a sequence of numbers.
 *
 * @param {int|float} value1         int or float
 * @param {int|float} value2         int or float
 * @param {int|float} value3         int or float
 * @param {int|float} array          int or float array
 *
 * @returns {int|float}
 *
 * @see max
 */
min = function () {
    if (arguments.length === 2) {
        return arguments[0] < arguments[1] ? arguments[0] : arguments[1];
    }
    var numbers = arguments.length === 1 ? arguments[0] : arguments; // if single argument, array is used
    if (!("length" in numbers && numbers.length > 0)) {
        throw "Non-empty array is expected";
    }
    var min = numbers[0],
        count = numbers.length;
    for (var i = 1; i < count; ++i) {
        if (min > numbers[i]) {
            min = numbers[i];
        }
    }
    return min;
};

/**
 * Normalizes a number from another range into a value between 0 and 1.
 * Identical to map(value, low, high, 0, 1);
 * Numbers outside the range are not clamped to 0 and 1, because out-of-range
 * values are often intentional and useful.
 *
 * @param {float} aNumber    The incoming value to be converted
 * @param {float} low        Lower bound of the value's current range
 * @param {float} high       Upper bound of the value's current range
 *
 * @returns {float}
 *
 * @see map
 * @see lerp
 */
norm = function (aNumber, low, high) {
    return (aNumber - low) / (high - low);
};

/**
 * Facilitates exponential expressions. The pow() function is an efficient way of
 * multiplying numbers by themselves (or their reciprocal) in large quantities.
 * For example, pow(3, 5) is equivalent to the expression 3*3*3*3*3 and pow(3, -5)
 * is equivalent to 1 / 3*3*3*3*3.
 *
 * @param {int|float} num        base of the exponential expression
 * @param {int|float} exponent   power of which to raise the base
 *
 * @returns {float}
 *
 * @see sqrt
 */
pow = Math.pow;

/**
 * Calculates the integer closest to the value parameter. For example, round(9.2) returns the value 9.
 *
 * @param {float} value        number to round
 *
 * @returns {int}
 *
 * @see floor
 * @see ceil
 */
round = Math.round;

/**
 * Squares a number (multiplies a number by itself). The result is always a positive number,
 * as multiplying two negative numbers always yields a positive result. For example, -1 * -1 = 1.
 *
 * @param {float} value        int or float
 *
 * @returns {float}
 *
 * @see sqrt
 */
sq = function (aNumber) {
    return aNumber * aNumber;
};

/**
 * Calculates the square root of a number. The square root of a number is always positive,
 * even though there may be a valid negative root. The square root s of number a is such
 * that s*s = a. It is the opposite of squaring.
 *
 * @param {float} value        int or float, non negative
 *
 * @returns {float}
 *
 * @see pow
 * @see sq
 */
sqrt = Math.sqrt;

// Trigonometry
convertToDegrees = function (angle) {
    return angleMode === "degrees" ?
        degrees(angle) :
        angle;
};

convertToRadians = function (angle) {
    return angleMode === "degrees" ?
        radians(angle) :
        angle;
};

/**
 * The inverse of cos(), returns the arc cosine of a value. This function expects the
 * values in the range of -1 to 1 and values are returned in the range 0 to PI (3.1415927).
 *
 * @param {float} value        the value whose arc cosine is to be returned
 *
 * @returns {float}
 *
 * @see cos
 * @see asin
 * @see atan
 */
acos = compose(Math.acos, convertToDegrees);

/**
 * The inverse of sin(), returns the arc sine of a value. This function expects the values
 * in the range of -1 to 1 and values are returned in the range -PI/2 to PI/2.
 *
 * @param {float} value        the value whose arc sine is to be returned
 *
 * @returns {float}
 *
 * @see sin
 * @see acos
 * @see atan
 */
asin = compose(Math.asin, convertToDegrees);

/**
 * The inverse of tan(), returns the arc tangent of a value. This function expects the values
 * in the range of -Infinity to Infinity (exclusive) and values are returned in the range -PI/2 to PI/2 .
 *
 * @param {float} value        -Infinity to Infinity (exclusive)
 *
 * @returns {float}
 *
 * @see tan
 * @see asin
 * @see acos
 */
atan = compose(Math.atan, convertToDegrees);

/**
 * Calculates the angle (in radians) from a specified point to the coordinate origin as measured from
 * the positive x-axis. Values are returned as a float in the range from PI to -PI. The atan2() function
 * is most often used for orienting geometry to the position of the cursor. Note: The y-coordinate of the
 * point is the first parameter and the x-coordinate is the second due the the structure of calculating the tangent.
 *
 * @param {float} y        y-coordinate of the point
 * @param {float} x        x-coordinate of the point
 *
 * @returns {float}
 *
 * @see tan
 */
atan2 = compose(Math.atan2, convertToDegrees);

/**
 * Calculates the cosine of an angle. This function expects the values of the angle parameter to be provided
 * in radians (values from 0 to PI*2). Values are returned in the range -1 to 1.
 *
 * @param {float} value        an angle in radians
 *
 * @returns {float}
 *
 * @see tan
 * @see sin
 */
cos = compose(convertToRadians, Math.cos);

/**
 * Converts a radian measurement to its corresponding value in degrees. Radians and degrees are two ways of
 * measuring the same thing. There are 360 degrees in a circle and 2*PI radians in a circle. For example,
 * 90 degrees = PI/2 = 1.5707964. All trigonometric methods in Processing require their parameters to be specified in radians.
 *
 * @param {int|float} value        an angle in radians
 *
 * @returns {float}
 *
 * @see radians
 */
degrees = function (aAngle) {
    return (aAngle * 180) / Math.PI;
};

/**
 * Converts a degree measurement to its corresponding value in radians. Radians and degrees are two ways of
 * measuring the same thing. There are 360 degrees in a circle and 2*PI radians in a circle. For example,
 * 90 degrees = PI/2 = 1.5707964. All trigonometric methods in Processing require their parameters to be specified in radians.
 *
 * @param {int|float} value        an angle in radians
 *
 * @returns {float}
 *
 * @see degrees
 */
radians = function (aAngle) {
    return (aAngle / 180) * Math.PI;
};

/**
 * Calculates the sine of an angle. This function expects the values of the angle parameter to be provided in
 * radians (values from 0 to 6.28). Values are returned in the range -1 to 1.
 *
 * @param {float} value        an angle in radians
 *
 * @returns {float}
 *
 * @see cos
 * @see radians
 */
sin = compose(convertToRadians, Math.sin);

/**
 * Calculates the ratio of the sine and cosine of an angle. This function expects the values of the angle
 * parameter to be provided in radians (values from 0 to PI*2). Values are returned in the range infinity to -infinity.
 *
 * @param {float} value        an angle in radians
 *
 * @returns {float}
 *
 * @see cos
 * @see sin
 * @see radians
 */
tan = compose(convertToRadians, Math.tan);

/**
 * Generates random numbers. Each time the random() function is called, it returns an unexpected value within
 * the specified range. If one parameter is passed to the function it will return a float between zero and the
 * value of the high parameter. The function call random(5) returns values between 0 and 5 (starting at zero,
 * up to but not including 5). If two parameters are passed, it will return a float with a value between the
 * parameters. The function call random(-5, 10.2) returns values starting at -5 up to (but not including) 10.2.
 * To convert a floating-point random number to an integer, use the int() function.
 *
 * @param {int|float} value1         if one parameter is used, the top end to random from, if two params the low end
 * @param {int|float} value2         the top end of the random range
 *
 * @returns {float}
 *
 * @see randomSeed
 * @see noise
 */
random = function () {
    if (arguments.length === 0) {
        return currentRandom();
    }
    if (arguments.length === 1) {
        return currentRandom() * arguments[0];
    }
    var aMin = arguments[0], aMax = arguments[1];
    return currentRandom() * (aMax - aMin) + aMin;
};

/**
 * Sets the seed value for random(). By default, random() produces different results each time the
 * program is run. Set the value parameter to a constant to return the same pseudo-random numbers
 * each time the software is run.
 *
 * @param {int|float} seed         int
 *
 * @see random
 * @see noise
 * @see noiseSeed
 */
randomSeed = function (seed) {
    currentRandom = (new Marsaglia(seed)).nextDouble;
};

// Random
/**
 * We have two random()'s in the code... what does this do ? and which one is current ?
 * 
 * @param {*} seed
 */
Random = function (seed) {
    var haveNextNextGaussian = false, nextNextGaussian, random;

    /**
     * 
     * @returns 
     * 
     * @method Random
     */
    this.nextGaussian = function () {
        if (haveNextNextGaussian) {
            haveNextNextGaussian = false;
            return nextNextGaussian;
        }
        var v1, v2, s;
        do {
            v1 = 2 * random() - 1; // between -1.0 and 1.0
            v2 = 2 * random() - 1; // between -1.0 and 1.0
            s = v1 * v1 + v2 * v2;
        }
        while (s >= 1 || s === 0);

        var multiplier = Math.sqrt(-2 * Math.log(s) / s);
        nextNextGaussian = v2 * multiplier;
        haveNextNextGaussian = true;

        return v1 * multiplier;
    };

    // by default use standard random, otherwise seeded
    random = (seed === undef) ? Math.random : (new Marsaglia(seed)).nextDouble;
};

/**
 * Returns the Perlin noise value at specified coordinates. Perlin noise is a random sequence
 * generator producing a more natural ordered, harmonic succession of numbers compared to the
 * standard random() function. It was invented by Ken Perlin in the 1980s and been used since
 * in graphical applications to produce procedural textures, natural motion, shapes, terrains etc.
 * The main difference to the random() function is that Perlin noise is defined in an infinite
 * n-dimensional space where each pair of coordinates corresponds to a fixed semi-random value
 * (fixed only for the lifespan of the program). The resulting value will always be between 0.0
 * and 1.0. Processing can compute 1D, 2D and 3D noise, depending on the number of coordinates
 * given. The noise value can be animated by moving through the noise space as demonstrated in
 * the example above. The 2nd and 3rd dimension can also be interpreted as time.
 * The actual noise is structured similar to an audio signal, in respect to the function's use
 * of frequencies. Similar to the concept of harmonics in physics, perlin noise is computed over
 * several octaves which are added together for the final result.
 * Another way to adjust the character of the resulting sequence is the scale of the input
 * coordinates. As the function works within an infinite space the value of the coordinates
 * doesn't matter as such, only the distance between successive coordinates does (eg. when using
 * noise() within a loop). As a general rule the smaller the difference between coordinates, the
 * smoother the resulting noise sequence will be. Steps of 0.005-0.03 work best for most applications,
 * but this will differ depending on use.
 *
 * @param {float} x          x coordinate in noise space
 * @param {float} y          y coordinate in noise space
 * @param {float} z          z coordinate in noise space
 *
 * @returns {float}
 *
 * @see random
 * @see noiseDetail
 */
noise = function (x, y, z) {
    if (noiseProfile.generator === undef) {
        // caching
        noiseProfile.generator = new PerlinNoise(noiseProfile.seed);
    }
    var generator = noiseProfile.generator;
    var effect = 1, k = 1, sum = 0;
    for (var i = 0; i < noiseProfile.octaves; ++i) {
        effect *= noiseProfile.fallout;
        switch (arguments.length) {
            case 1:
                sum += effect * (1 + generator.noise1d(k * x)) / 2; break;
            case 2:
                sum += effect * (1 + generator.noise2d(k * x, k * y)) / 2; break;
            case 3:
                sum += effect * (1 + generator.noise3d(k * x, k * y, k * z)) / 2; break;
        }
        k *= 2;
    }
    return sum;
};

/**
 * Adjusts the character and level of detail produced by the Perlin noise function.
 * Similar to harmonics in physics, noise is computed over several octaves. Lower octaves
 * contribute more to the output signal and as such define the overal intensity of the noise,
 * whereas higher octaves create finer grained details in the noise sequence. By default,
 * noise is computed over 4 octaves with each octave contributing exactly half than its
 * predecessor, starting at 50% strength for the 1st octave. This falloff amount can be
 * changed by adding an additional function parameter. Eg. a falloff factor of 0.75 means
 * each octave will now have 75% impact (25% less) of the previous lower octave. Any value
 * between 0.0 and 1.0 is valid, however note that values greater than 0.5 might result in
 * greater than 1.0 values returned by noise(). By changing these parameters, the signal
 * created by the noise() function can be adapted to fit very specific needs and characteristics.
 *
 * @param {int} octaves          number of octaves to be used by the noise() function
 * @param {float} falloff        falloff factor for each octave
 *
 * @see noise
 */
noiseDetail = function (octaves, fallout) {
    noiseProfile.octaves = octaves;
    if (fallout !== undef) {
        noiseProfile.fallout = fallout;
    }
};

/**
 * Sets the seed value for noise(). By default, noise() produces different results each
 * time the program is run. Set the value parameter to a constant to return the same
 * pseudo-random numbers each time the software is run.
 *
 * @param {int} seed         int
 *
 * @returns {float}
 *
 * @see random
 * @see radomSeed
 * @see noise
 * @see noiseDetail
 */
noiseSeed = function (seed) {
    noiseProfile.seed = seed;
    noiseProfile.generator = undef;
};

/**
 * Defines the dimension of the display window in units of pixels. The size() function must
 * be the first line in setup(). If size() is not called, the default size of the window is
 * 100x100 pixels. The system variables width and height are set by the parameters passed to
 * the size() function.
 *
 * @param {int} aWidth     width of the display window in units of pixels
 * @param {int} aHeight    height of the display window in units of pixels
 * @param {MODE} aMode     Either P2D, P3D, JAVA2D, or OPENGL
 *
 * @see createGraphics
 * @see screen
 */
size = function (aWidth, aHeight, aMode) {
    if (doStroke) {
        stroke(0);
    }

    if (doFill) {
        fill(255);
    }

    // The default 2d context has already been created in the init() stage if
    // a 3d context was not specified. This is so that a 2d context will be
    // available if size() was not called.
    var savedProperties = {
        fillStyle: curContext.fillStyle,
        strokeStyle: curContext.strokeStyle,
        lineCap: curContext.lineCap,
        lineJoin: curContext.lineJoin
    };
    // remove the style width and height properties to ensure that the canvas gets set to
    // aWidth and aHeight coming in
    if (curElement.style.length > 0) {
        curElement.style.removeProperty("width");
        curElement.style.removeProperty("height");
    }

    curElement.width = width = aWidth || 100;
    curElement.height = height = aHeight || 100;

    for (var prop in savedProperties) {
        if (savedProperties.hasOwnProperty(prop)) {
            curContext[prop] = savedProperties[prop];
        }
    }

    // make sure to set the default font the first time round.
    textFont(curTextFont);

    // Set the background to whatever it was called last as if background() was called before size()
    // If background() hasn't been called before, set background() to a light gray
    background();

    // set 5% for pixels to cache (or 1000)
    maxPixelsCached = Math.max(1000, aWidth * aHeight * 0.05);

    // Externalize the context
    externals.context = curContext;

    for (var i = 0; i < PConstants.SINCOS_LENGTH; i++) {
        // XXX(jeresig)
        sinLUT[i] = sin(angleMode === "degrees" ? i : radians(i));
        cosLUT[i] = cos(angleMode === "degrees" ? i : radians(i));
    }
};

size = function (aWidth, aHeight, aMode) {
    if (curContext === undef) {
        // size() was called without init() default context, i.e. createGraphics()
        curContext = curElement.getContext("2d");
        userMatrixStack = new PMatrixStack();
        userReverseMatrixStack = new PMatrixStack();
        modelView = new PMatrix2D();
        modelViewInv = new PMatrix2D();
    }

    size.apply(this, arguments);
};

size = (function () {
    var size3DCalled = false;

    return function size(aWidth, aHeight, aMode) {
        if (size3DCalled) {
            throw "Multiple calls to size() for 3D renders are not allowed.";
        }
        size3DCalled = true;

        function getGLContext(canvas) {
            var ctxNames = ['experimental-webgl', 'webgl', 'webkit-3d'],
                gl;

            for (var i = 0, l = ctxNames.length; i < l; i++) {
                gl = canvas.getContext(ctxNames[i], { antialias: false });
                if (gl) {
                    break;
                }
            }

            return gl;
        }

        // get the 3D rendering context
        try {
            // If the HTML <canvas> dimensions differ from the
            // dimensions specified in the size() call in the sketch, for
            // 3D sketches, browsers will either not render or render the
            // scene incorrectly. To fix this, we need to adjust the
            // width and height attributes of the canvas.
            curElement.width = width = aWidth || 100;
            curElement.height = height = aHeight || 100;
            curContext = getGLContext(curElement);
            canTex = curContext.createTexture(); // texture
            textTex = curContext.createTexture(); // texture
        } catch (e_size) {
            Processing.debug(e_size);
        }

        if (!curContext) {
            throw "WebGL context is not supported on this browser.";
        }

        // Set defaults
        curContext.viewport(0, 0, curElement.width, curElement.height);
        curContext.enable(curContext.DEPTH_TEST);
        curContext.enable(curContext.BLEND);
        curContext.blendFunc(curContext.SRC_ALPHA, curContext.ONE_MINUS_SRC_ALPHA);

        // Create the program objects to render 2D (points, lines) and
        // 3D (spheres, boxes) shapes. Because 2D shapes are not lit,
        // lighting calculations could be ommitted from that program object.
        programObject2D = createProgramObject(curContext, vertexShaderSource2D, fragmentShaderSource2D);

        programObjectUnlitShape = createProgramObject(curContext, vShaderSrcUnlitShape, fShaderSrcUnlitShape);

        // Set the default point and line width for the 2D and unlit shapes.
        strokeWeight(1.0);

        // Now that the programs have been compiled, we can set the default
        // states for the lights.
        programObject3D = createProgramObject(curContext, vertexShaderSource3D, fragmentShaderSource3D);
        curContext.useProgram(programObject3D);

        // assume we aren't using textures by default
        uniformi("usingTexture3d", programObject3D, "usingTexture", usingTexture);
        // assume that we arn't tinting by default
        lightFalloff(1, 0, 0);
        shininess(1);
        ambient(255, 255, 255);
        specular(0, 0, 0);
        emissive(0, 0, 0);

        // Create buffers for 3D primitives
        boxBuffer = curContext.createBuffer();
        curContext.bindBuffer(curContext.ARRAY_BUFFER, boxBuffer);
        curContext.bufferData(curContext.ARRAY_BUFFER, boxVerts, curContext.STATIC_DRAW);

        boxNormBuffer = curContext.createBuffer();
        curContext.bindBuffer(curContext.ARRAY_BUFFER, boxNormBuffer);
        curContext.bufferData(curContext.ARRAY_BUFFER, boxNorms, curContext.STATIC_DRAW);

        boxOutlineBuffer = curContext.createBuffer();
        curContext.bindBuffer(curContext.ARRAY_BUFFER, boxOutlineBuffer);
        curContext.bufferData(curContext.ARRAY_BUFFER, boxOutlineVerts, curContext.STATIC_DRAW);

        // used to draw the rectangle and the outline
        rectBuffer = curContext.createBuffer();
        curContext.bindBuffer(curContext.ARRAY_BUFFER, rectBuffer);
        curContext.bufferData(curContext.ARRAY_BUFFER, rectVerts, curContext.STATIC_DRAW);

        rectNormBuffer = curContext.createBuffer();
        curContext.bindBuffer(curContext.ARRAY_BUFFER, rectNormBuffer);
        curContext.bufferData(curContext.ARRAY_BUFFER, rectNorms, curContext.STATIC_DRAW);

        // The sphere vertices are specified dynamically since the user
        // can change the level of detail. Everytime the user does that
        // using sphereDetail(), the new vertices are calculated.
        sphereBuffer = curContext.createBuffer();

        lineBuffer = curContext.createBuffer();

        // Shape buffers
        fillBuffer = curContext.createBuffer();
        fillColorBuffer = curContext.createBuffer();
        strokeColorBuffer = curContext.createBuffer();
        shapeTexVBO = curContext.createBuffer();

        pointBuffer = curContext.createBuffer();
        curContext.bindBuffer(curContext.ARRAY_BUFFER, pointBuffer);
        curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array([0, 0, 0]), curContext.STATIC_DRAW);

        textBuffer = curContext.createBuffer();
        curContext.bindBuffer(curContext.ARRAY_BUFFER, textBuffer);
        curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array([1, 1, 0, -1, 1, 0, -1, -1, 0, 1, -1, 0]), curContext.STATIC_DRAW);

        textureBuffer = curContext.createBuffer();
        curContext.bindBuffer(curContext.ARRAY_BUFFER, textureBuffer);
        curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array([0, 0, 1, 0, 1, 1, 0, 1]), curContext.STATIC_DRAW);

        indexBuffer = curContext.createBuffer();
        curContext.bindBuffer(curContext.ELEMENT_ARRAY_BUFFER, indexBuffer);
        curContext.bufferData(curContext.ELEMENT_ARRAY_BUFFER, new Uint16Array([0, 1, 2, 2, 3, 0]), curContext.STATIC_DRAW);

        cam = new PMatrix3D();
        cameraInv = new PMatrix3D();
        modelView = new PMatrix3D();
        modelViewInv = new PMatrix3D();
        projection = new PMatrix3D();
        camera();
        perspective();

        userMatrixStack = new PMatrixStack();
        userReverseMatrixStack = new PMatrixStack();
        // used by both curve and bezier, so just init here
        curveBasisMatrix = new PMatrix3D();
        curveToBezierMatrix = new PMatrix3D();
        curveDrawMatrix = new PMatrix3D();
        bezierDrawMatrix = new PMatrix3D();
        bezierBasisInverse = new PMatrix3D();
        bezierBasisMatrix = new PMatrix3D();
        bezierBasisMatrix.set(-1, 3, -3, 1, 3, -6, 3, 0, -3, 3, 0, 0, 1, 0, 0, 0);

        size.apply(this, arguments);
    };
}());

////////////////////////////////////////////////////////////////////////////
// Lights
////////////////////////////////////////////////////////////////////////////

/**
 * Adds an ambient light. Ambient light doesn't come from a specific direction,
 * the rays have light have bounced around so much that objects are evenly lit
 * from all sides. Ambient lights are almost always used in combination with
 * other types of lights. Lights need to be included in the <b>draw()</b> to
 * remain persistent in a looping program. Placing them in the <b>setup()</b>
 * of a looping program will cause them to only have an effect the first time
 * through the loo The effect of the parameters is determined by the current
 * color mode.
 *
 * @param {int | float} r red or hue value
 * @param {int | float} g green or hue value
 * @param {int | float} b blue or hue value
 *
 * @param {int | float} x x position of light (used for falloff)
 * @param {int | float} y y position of light (used for falloff)
 * @param {int | float} z z position of light (used for falloff)
 *
 * @returns none
 *
 * @see lights
 * @see directionalLight
 * @see pointLight
 * @see spotLight
 */
ambientLight = a3DOnlyFunction;

ambientLight = function (r, g, b, x, y, z) {
    if (lightCount === PConstants.MAX_LIGHTS) {
        throw "can only create " + PConstants.MAX_LIGHTS + " lights";
    }

    var pos = new PVector(x, y, z);
    var view = new PMatrix3D();
    view.scale(1, -1, 1);
    view.apply(modelView.array());
    view.mult(pos, pos);

    // Instead of calling color, we do the calculations ourselves to
    // reduce property lookups.
    var col = color$4(r, g, b, 0);
    var normalizedCol = [((col & PConstants.RED_MASK) >>> 16) / 255,
    ((col & PConstants.GREEN_MASK) >>> 8) / 255,
    (col & PConstants.BLUE_MASK) / 255];

    curContext.useProgram(programObject3D);
    uniformf("lights.color.3d." + lightCount, programObject3D, "lights" + lightCount + ".color", normalizedCol);
    uniformf("lights.position.3d." + lightCount, programObject3D, "lights" + lightCount + ".position", pos.array());
    uniformi("lights.type.3d." + lightCount, programObject3D, "lights" + lightCount + ".type", 0);
    uniformi("lightCount3d", programObject3D, "lightCount", ++lightCount);
};

/**
 * Adds a directional light. Directional light comes from one direction and
 * is stronger when hitting a surface squarely and weaker if it hits at a
 * gentle angle. After hitting a surface, a directional lights scatters in
 * all directions. Lights need to be included in the <b>draw()</b> to remain
 * persistent in a looping program. Placing them in the <b>setup()</b> of a
 * looping program will cause them to only have an effect the first time
 * through the loo The affect of the <br>r</b>, <br>g</b>, and <br>b</b>
 * parameters is determined by the current color mode. The <b>nx</b>,
 * <b>ny</b>, and <b>nz</b> parameters specify the direction the light is
 * facing. For example, setting <b>ny</b> to -1 will cause the geometry to be
 * lit from below (the light is facing directly upward).
 *
 * @param {int | float} r red or hue value
 * @param {int | float} g green or hue value
 * @param {int | float} b blue or hue value
 *
 * @param {int | float} nx direction along the x axis
 * @param {int | float} ny direction along the y axis
 * @param {int | float} nz direction along the z axis
 *
 * @returns none
 *
 * @see lights
 * @see ambientLight
 * @see pointLight
 * @see spotLight
 */
directionalLight = a3DOnlyFunction;

directionalLight = function (r, g, b, nx, ny, nz) {
    if (lightCount === PConstants.MAX_LIGHTS) {
        throw "can only create " + PConstants.MAX_LIGHTS + " lights";
    }

    curContext.useProgram(programObject3D);

    var mvm = new PMatrix3D();
    mvm.scale(1, -1, 1);
    mvm.apply(modelView.array());
    mvm = mvm.array();

    // We need to multiply the direction by the model view matrix, but
    // the mult function checks the w component of the vector, if it isn't
    // present, it uses 1, so we manually multiply.
    var dir = [
        mvm[0] * nx + mvm[4] * ny + mvm[8] * nz,
        mvm[1] * nx + mvm[5] * ny + mvm[9] * nz,
        mvm[2] * nx + mvm[6] * ny + mvm[10] * nz
    ];

    // Instead of calling color, we do the calculations ourselves to
    // reduce property lookups.
    var col = color$4(r, g, b, 0);
    var normalizedCol = [((col & PConstants.RED_MASK) >>> 16) / 255,
    ((col & PConstants.GREEN_MASK) >>> 8) / 255,
    (col & PConstants.BLUE_MASK) / 255];

    uniformf("lights.color.3d." + lightCount, programObject3D, "lights" + lightCount + ".color", normalizedCol);
    uniformf("lights.position.3d." + lightCount, programObject3D, "lights" + lightCount + ".position", dir);
    uniformi("lights.type.3d." + lightCount, programObject3D, "lights" + lightCount + ".type", 1);
    uniformi("lightCount3d", programObject3D, "lightCount", ++lightCount);
};

/**
 * Sets the falloff rates for point lights, spot lights, and ambient lights.
 * The parameters are used to determine the falloff with the following equation:
 *
 * d = distance from light position to vertex position
 * falloff = 1 / (CONSTANT + d * LINEAR + (d*d) * QUADRATIC)
 *
 * Like <b>fill()</b>, it affects only the elements which are created after it in the
 * code. The default value if <b>LightFalloff(1.0, 0.0, 0.0)</b>. Thinking about an
 * ambient light with a falloff can be tricky. It is used, for example, if you
 * wanted a region of your scene to be lit ambiently one color and another region
 * to be lit ambiently by another color, you would use an ambient light with location
 * and falloff. You can think of it as a point light that doesn't care which direction
 * a surface is facing.
 *
 * @param {int | float} constant constant value for determining falloff
 * @param {int | float} linear linear value for determining falloff
 * @param {int | float} quadratic quadratic value for determining falloff
 *
 * @returns none
 *
 * @see lights
 * @see ambientLight
 * @see pointLight
 * @see spotLight
 * @see lightSpecular
 */
lightFalloff = a3DOnlyFunction;

lightFalloff = function (constant, linear, quadratic) {
    curContext.useProgram(programObject3D);
    uniformf("falloff3d", programObject3D, "falloff", [constant, linear, quadratic]);
};

/**
 * Sets the specular color for lights. Like <b>fill()</b>, it affects only the
 * elements which are created after it in the code. Specular refers to light
 * which bounces off a surface in a perferred direction (rather than bouncing
 * in all directions like a diffuse light) and is used for creating highlights.
 * The specular quality of a light interacts with the specular material qualities
 * set through the <b>specular()</b> and <b>shininess()</b> functions.
 *
 * @param {int | float} r red or hue value
 * @param {int | float} g green or hue value
 * @param {int | float} b blue or hue value
 *
 * @returns none
 *
 * @see lights
 * @see ambientLight
 * @see pointLight
 * @see spotLight
 */
lightSpecular = a3DOnlyFunction;

lightSpecular = function (r, g, b) {

    // Instead of calling color, we do the calculations ourselves to
    // reduce property lookups.
    var col = color$4(r, g, b, 0);
    var normalizedCol = [((col & PConstants.RED_MASK) >>> 16) / 255,
    ((col & PConstants.GREEN_MASK) >>> 8) / 255,
    (col & PConstants.BLUE_MASK) / 255];

    curContext.useProgram(programObject3D);
    uniformf("specular3d", programObject3D, "specular", normalizedCol);
};

/**
 * Sets the default ambient light, directional light, falloff, and specular
 * values. The defaults are ambientLight(128, 128, 128) and
 * directionalLight(128, 128, 128, 0, 0, -1), lightFalloff(1, 0, 0), and
 * lightSpecular(0, 0, 0). Lights need to be included in the draw() to remain
 * persistent in a looping program. Placing them in the setup() of a looping
 * program will cause them to only have an effect the first time through the
 * loo
 *
 * @returns none
 *
 * @see ambientLight
 * @see directionalLight
 * @see pointLight
 * @see spotLight
 * @see noLights
 *
 */
lights = function () {
    ambientLight(128, 128, 128);
    directionalLight(128, 128, 128, 0, 0, -1);
    lightFalloff(1, 0, 0);
    lightSpecular(0, 0, 0);
};

/**
 * Adds a point light. Lights need to be included in the <b>draw()</b> to remain
 * persistent in a looping program. Placing them in the <b>setup()</b> of a
 * looping program will cause them to only have an effect the first time through
 * the loo The affect of the <b>r</b>, <b>g</b>, and <b>b</b> parameters
 * is determined by the current color mode. The <b>x</b>, <b>y</b>, and <b>z</b>
 * parameters set the position of the light.
 *
 * @param {int | float} r red or hue value
 * @param {int | float} g green or hue value
 * @param {int | float} b blue or hue value
 * @param {int | float} x x coordinate of the light
 * @param {int | float} y y coordinate of the light
 * @param {int | float} z z coordinate of the light
 *
 * @returns none
 *
 * @see lights
 * @see directionalLight
 * @see ambientLight
 * @see spotLight
 */
pointLight = a3DOnlyFunction;

pointLight = function (r, g, b, x, y, z) {
    if (lightCount === PConstants.MAX_LIGHTS) {
        throw "can only create " + PConstants.MAX_LIGHTS + " lights";
    }

    // Place the point in view space once instead of once per vertex
    // in the shader.
    var pos = new PVector(x, y, z);
    var view = new PMatrix3D();
    view.scale(1, -1, 1);
    view.apply(modelView.array());
    view.mult(pos, pos);

    // Instead of calling color, we do the calculations ourselves to
    // reduce property lookups.
    var col = color$4(r, g, b, 0);
    var normalizedCol = [((col & PConstants.RED_MASK) >>> 16) / 255,
    ((col & PConstants.GREEN_MASK) >>> 8) / 255,
    (col & PConstants.BLUE_MASK) / 255];

    curContext.useProgram(programObject3D);
    uniformf("lights.color.3d." + lightCount, programObject3D, "lights" + lightCount + ".color", normalizedCol);
    uniformf("lights.position.3d." + lightCount, programObject3D, "lights" + lightCount + ".position", pos.array());
    uniformi("lights.type.3d." + lightCount, programObject3D, "lights" + lightCount + ".type", 2);
    uniformi("lightCount3d", programObject3D, "lightCount", ++lightCount);
};

/**
 * Disable all lighting. Lighting is turned off by default and enabled with
 * the lights() method. This function can be used to disable lighting so
 * that 2D geometry (which does not require lighting) can be drawn after a
 * set of lighted 3D geometry.
 *
 * @returns none
 *
 * @see lights
 */
noLights = a3DOnlyFunction;

noLights = function () {
    lightCount = 0;
    curContext.useProgram(programObject3D);
    uniformi("lightCount3d", programObject3D, "lightCount", lightCount);
};

/**
 * Adds a spot light. Lights need to be included in the <b>draw()</b> to
 * remain persistent in a looping program. Placing them in the <b>setup()</b>
 * of a looping program will cause them to only have an effect the first time
 * through the loo The affect of the <b>r</b>, <b>g</b>, and <b>b</b> parameters
 * is determined by the current color mode. The <b>x</b>, <b>y</b>, and <b>z</b>
 * parameters specify the position of the light and <b>nx</b>, <b>ny</b>, <b>nz</b>
 * specify the direction or light. The angle parameter affects <b>angle</b> of the
 * spotlight cone.
 *
 * @param {int | float} r red or hue value
 * @param {int | float} g green or hue value
 * @param {int | float} b blue or hue value
 * @param {int | float} x coordinate of the light
 * @param {int | float} y coordinate of the light
 * @param {int | float} z coordinate of the light
 * @param {int | float} nx direction along the x axis
 * @param {int | float} ny direction along the y axis
 * @param {int | float} nz direction along the z axis
 * @param {float} angle angle of the spotlight cone
 * @param {float} concentration exponent determining the center bias of the cone
 *
 * @returns none
 *
 * @see lights
 * @see directionalLight
 * @see ambientLight
 * @see pointLight
 */
spotLight = a3DOnlyFunction;

spotLight = function (r, g, b, x, y, z, nx, ny, nz, angle, concentration) {
    if (lightCount === PConstants.MAX_LIGHTS) {
        throw "can only create " + PConstants.MAX_LIGHTS + " lights";
    }

    curContext.useProgram(programObject3D);

    // multiply the position and direction by the model view matrix
    // once per object rather than once per vertex.
    var pos = new PVector(x, y, z);
    var mvm = new PMatrix3D();
    mvm.scale(1, -1, 1);
    mvm.apply(modelView.array());
    mvm.mult(pos, pos);

    // Convert to array since we need to directly access the elements.
    mvm = mvm.array();

    // We need to multiply the direction by the model view matrix, but
    // the mult function checks the w component of the vector, if it isn't
    // present, it uses 1, so we use a very small value as a work around.
    var dir = [
        mvm[0] * nx + mvm[4] * ny + mvm[8] * nz,
        mvm[1] * nx + mvm[5] * ny + mvm[9] * nz,
        mvm[2] * nx + mvm[6] * ny + mvm[10] * nz
    ];

    // Instead of calling color, we do the calculations ourselves to
    // reduce property lookups.
    var col = color$4(r, g, b, 0);
    var normalizedCol = [((col & PConstants.RED_MASK) >>> 16) / 255,
    ((col & PConstants.GREEN_MASK) >>> 8) / 255,
    (col & PConstants.BLUE_MASK) / 255];

    uniformf("lights.color.3d." + lightCount, programObject3D, "lights" + lightCount + ".color", normalizedCol);
    uniformf("lights.position.3d." + lightCount, programObject3D, "lights" + lightCount + ".position", pos.array());
    uniformf("lights.direction.3d." + lightCount, programObject3D, "lights" + lightCount + ".direction", dir);
    uniformf("lights.concentration.3d." + lightCount, programObject3D, "lights" + lightCount + ".concentration", concentration);
    uniformf("lights.angle.3d." + lightCount, programObject3D, "lights" + lightCount + ".angle", angle);
    uniformi("lights.type.3d." + lightCount, programObject3D, "lights" + lightCount + ".type", 3);
    uniformi("lightCount3d", programObject3D, "lightCount", ++lightCount);
};

////////////////////////////////////////////////////////////////////////////
// Camera functions
////////////////////////////////////////////////////////////////////////////

/**
 * The <b>beginCamera()</b> and <b>endCamera()</b> functions enable advanced customization of the camera space.
 * The functions are useful if you want to more control over camera movement, however for most users, the <b>camera()</b>
 * function will be sufficient.<br /><br />The camera functions will replace any transformations (such as <b>rotate()</b>
 * or <b>translate()</b>) that occur before them in <b>draw()</b>, but they will not automatically replace the camera
 * transform itself. For this reason, camera functions should be placed at the beginning of <b>draw()</b> (so that
 * transformations happen afterwards), and the <b>camera()</b> function can be used after <b>beginCamera()</b> if
 * you want to reset the camera before applying transformations.<br /><br />This function sets the matrix mode to the
 * camera matrix so calls such as <b>translate()</b>, <b>rotate()</b>, applyMatrix() and resetMatrix() affect the camera.
 * <b>beginCamera()</b> should always be used with a following <b>endCamera()</b> and pairs of <b>beginCamera()</b> and
 * <b>endCamera()</b> cannot be nested.
 *
 * @see camera
 * @see endCamera
 * @see applyMatrix
 * @see resetMatrix
 * @see translate
 * @see rotate
 * @see scale
 */
beginCamera = function () {
    throw ("beginCamera() is not available in 2D mode");
};

beginCamera = function () {
    if (manipulatingCamera) {
        throw ("You cannot call beginCamera() again before calling endCamera()");
    }
    manipulatingCamera = true;
    modelView = cameraInv;
    modelViewInv = cam;
};

/**
 * The <b>beginCamera()</b> and <b>endCamera()</b> functions enable advanced customization of the camera space.
 * Please see the reference for <b>beginCamera()</b> for a description of how the functions are used.
 *
 * @see beginCamera
 */
endCamera = function () {
    throw ("endCamera() is not available in 2D mode");
};

endCamera = function () {
    if (!manipulatingCamera) {
        throw ("You cannot call endCamera() before calling beginCamera()");
    }
    modelView.set(cam);
    modelViewInv.set(cameraInv);
    manipulatingCamera = false;
};

/**
 * Sets the position of the camera through setting the eye position, the center of the scene, and which axis is facing
 * upward. Moving the eye position and the direction it is pointing (the center of the scene) allows the images to be
 * seen from different angles. The version without any parameters sets the camera to the default position, pointing to
 * the center of the display window with the Y axis as u The default values are camera(width/2.0, height/2.0,
 * (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()
 * in OpenGL, but it first clears the current camera settings.
 *
 * @param {float} eyeX    x-coordinate for the eye
 * @param {float} eyeY    y-coordinate for the eye
 * @param {float} eyeZ    z-coordinate for the eye
 * @param {float} centerX x-coordinate for the center of the scene
 * @param {float} centerY y-coordinate for the center of the scene
 * @param {float} centerZ z-coordinate for the center of the scene
 * @param {float} upX     usually 0.0, 1.0, -1.0
 * @param {float} upY     usually 0.0, 1.0, -1.0
 * @param {float} upZ     usually 0.0, 1.0, -1.0
 *
 * @see beginCamera
 * @see endCamera
 * @see frustum
 */
camera = function (eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ) {
    if (eyeX === undef) {
        // Workaround if createGraphics is used.
        cameraX = width / 2;
        cameraY = height / 2;
        // XXX(jeresig)
        cameraZ = cameraY / tan(cameraFOV / 2);
        eyeX = cameraX;
        eyeY = cameraY;
        eyeZ = cameraZ;
        centerX = cameraX;
        centerY = cameraY;
        centerZ = 0;
        upX = 0;
        upY = 1;
        upZ = 0;
    }

    var z = new PVector(eyeX - centerX, eyeY - centerY, eyeZ - centerZ);
    var y = new PVector(upX, upY, upZ);
    z.normalize();
    var x = PVector.cross(y, z);
    y = PVector.cross(z, x);
    x.normalize();
    y.normalize();

    var xX = x.x,
        xY = x.y,
        xZ = x.z;

    var yX = y.x,
        yY = y.y,
        yZ = y.z;

    var zX = z.x,
        zY = z.y,
        zZ = z.z;

    cam.set(xX, xY, xZ, 0, yX, yY, yZ, 0, zX, zY, zZ, 0, 0, 0, 0, 1);

    cam.translate(-eyeX, -eyeY, -eyeZ);

    cameraInv.reset();
    cameraInv.invApply(xX, xY, xZ, 0, yX, yY, yZ, 0, zX, zY, zZ, 0, 0, 0, 0, 1);

    cameraInv.translate(eyeX, eyeY, eyeZ);

    modelView.set(cam);
    modelViewInv.set(cameraInv);
};

/**
 * Sets a perspective projection applying foreshortening, making distant objects appear smaller than closer ones. The
 * parameters define a viewing volume with the shape of truncated pyramid. Objects near to the front of the volume appear
 * their actual size, while farther objects appear smaller. This projection simulates the perspective of the world more
 * accurately than orthographic projection. The version of perspective without parameters sets the default perspective and
 * the version with four parameters allows the programmer to set the area precisely. The default values are:
 * perspective(PI/3.0, width/height, cameraZ/10.0, cameraZ*10.0) where cameraZ is ((height/2.0) / tan(PI*60.0/360.0));
 *
 * @param {float} fov     field-of-view angle (in radians) for vertical direction
 * @param {float} aspect  ratio of width to height
 * @param {float} zNear   z-position of nearest clipping plane
 * @param {float} zFar    z-positions of farthest clipping plane
 */
perspective = function (fov, aspect, near, far) {
    if (arguments.length === 0) {
        //in case canvas is resized
        cameraY = curElement.height / 2;
        // XXX(jeresig)
        cameraZ = cameraY / tan(cameraFOV / 2);
        cameraNear = cameraZ / 10;
        cameraFar = cameraZ * 10;
        cameraAspect = width / height;
        fov = cameraFOV;
        aspect = cameraAspect;
        near = cameraNear;
        far = cameraFar;
    }

    var yMax, yMin, xMax, xMin;
    // XXX(jeresig)
    yMax = near * tan(fov / 2);
    yMin = -yMax;
    xMax = yMax * aspect;
    xMin = yMin * aspect;
    frustum(xMin, xMax, yMin, yMax, near, far);
};

/**
 * Sets a perspective matrix defined through the parameters. Works like glFrustum, except it wipes out the current
 * perspective matrix rather than muliplying itself with it.
 *
 * @param {float} left   left coordinate of the clipping plane
 * @param {float} right  right coordinate of the clipping plane
 * @param {float} bottom bottom coordinate of the clipping plane
 * @param {float} top    top coordinate of the clipping plane
 * @param {float} near   near coordinate of the clipping plane
 * @param {float} far    far coordinate of the clipping plane
 *
 * @see beginCamera
 * @see camera
 * @see endCamera
 * @see perspective
 */
frustum = function () {
    throw ("Processing.js: frustum() is not supported in 2D mode");
};

frustum = function (left, right, bottom, top, near, far) {
    frustumMode = true;
    projection = new PMatrix3D();
    projection.set((2 * near) / (right - left), 0, (right + left) / (right - left),
        0, 0, (2 * near) / (top - bottom), (top + bottom) / (top - bottom),
        0, 0, 0, -(far + near) / (far - near), -(2 * far * near) / (far - near),
        0, 0, -1, 0);
    var proj = new PMatrix3D();
    proj.set(projection);
    proj.transpose();
    curContext.useProgram(programObject2D);
    uniformMatrix("projection2d", programObject2D, "projection", false, proj.array());
    curContext.useProgram(programObject3D);
    uniformMatrix("projection3d", programObject3D, "projection", false, proj.array());
    curContext.useProgram(programObjectUnlitShape);
    uniformMatrix("uProjectionUS", programObjectUnlitShape, "uProjection", false, proj.array());
};

/**
 * Sets an orthographic projection and defines a parallel clipping volume. All objects with the same dimension appear
 * the same size, regardless of whether they are near or far from the camera. The parameters to this function specify
 * the clipping volume where left and right are the minimum and maximum x values, top and bottom are the minimum and
 * maximum y values, and near and far are the minimum and maximum z values. If no parameters are given, the default
 * is used: ortho(0, width, 0, height, -10, 10).
 *
 * @param {float} left   left plane of the clipping volume
 * @param {float} right  right plane of the clipping volume
 * @param {float} bottom bottom plane of the clipping volume
 * @param {float} top    top plane of the clipping volume
 * @param {float} near   maximum distance from the origin to the viewer
 * @param {float} far    maximum distance from the origin away from the viewer
 */
ortho = function (left, right, bottom, top, near, far) {
    if (arguments.length === 0) {
        left = 0;
        right = width;
        bottom = 0;
        top = height;
        near = -10;
        far = 10;
    }

    var x = 2 / (right - left);
    var y = 2 / (top - bottom);
    var z = -2 / (far - near);

    var tx = -(right + left) / (right - left);
    var ty = -(top + bottom) / (top - bottom);
    var tz = -(far + near) / (far - near);

    projection = new PMatrix3D();
    projection.set(x, 0, 0, tx, 0, y, 0, ty, 0, 0, z, tz, 0, 0, 0, 1);

    var proj = new PMatrix3D();
    proj.set(projection);
    proj.transpose();
    curContext.useProgram(programObject2D);
    uniformMatrix("projection2d", programObject2D, "projection", false, proj.array());
    curContext.useProgram(programObject3D);
    uniformMatrix("projection3d", programObject3D, "projection", false, proj.array());
    curContext.useProgram(programObjectUnlitShape);
    uniformMatrix("uProjectionUS", programObjectUnlitShape, "uProjection", false, proj.array());
    frustumMode = false;
};
/**
 * The printProjection() prints the current projection matrix to the text window.
 */
printProjection = function () {
    projection.print();
};
/**
 * The printCamera() function prints the current camera matrix.
 */
printCamera = function () {
    cam.print();
};

////////////////////////////////////////////////////////////////////////////
// Shapes
////////////////////////////////////////////////////////////////////////////
/**
 * The box() function renders a box. A box is an extruded rectangle. A box with equal dimension on all sides is a cube.
 * Calling this function with only one parameter will create a cube.
 *
 * @param {int|float} w  dimension of the box in the x-dimension
 * @param {int|float} h  dimension of the box in the y-dimension
 * @param {int|float} d  dimension of the box in the z-dimension
 */
box = a3DOnlyFunction;

box = function (w, h, d) {
    // user can uniformly scale the box by
    // passing in only one argument.
    if (!h || !d) {
        h = d = w;
    }

    // Modeling transformation
    var model = new PMatrix3D();
    model.scale(w, h, d);

    // viewing transformation needs to have Y flipped
    // becuase that's what Processing does.
    var view = new PMatrix3D();
    view.scale(1, -1, 1);
    view.apply(modelView.array());
    view.transpose();

    if (doFill) {
        curContext.useProgram(programObject3D);
        uniformMatrix("model3d", programObject3D, "model", false, model.array());
        uniformMatrix("view3d", programObject3D, "view", false, view.array());
        // fix stitching problems. (lines get occluded by triangles
        // since they share the same depth values). This is not entirely
        // working, but it's a start for drawing the outline. So
        // developers can start playing around with styles.
        curContext.enable(curContext.POLYGON_OFFSET_FILL);
        curContext.polygonOffset(1, 1);
        uniformf("color3d", programObject3D, "color", fillStyle);

        // Calculating the normal matrix can be expensive, so only
        // do it if it's necessary
        if (lightCount > 0) {
            // Create the normal transformation matrix
            var v = new PMatrix3D();
            v.set(view);

            var m = new PMatrix3D();
            m.set(model);

            v.mult(m);

            var normalMatrix = new PMatrix3D();
            normalMatrix.set(v);
            normalMatrix.invert();
            normalMatrix.transpose();

            uniformMatrix("normalTransform3d", programObject3D, "normalTransform", false, normalMatrix.array());
            vertexAttribPointer("normal3d", programObject3D, "Normal", 3, boxNormBuffer);
        }
        else {
            disableVertexAttribPointer("normal3d", programObject3D, "Normal");
        }

        vertexAttribPointer("vertex3d", programObject3D, "Vertex", 3, boxBuffer);

        // Turn off per vertex colors
        disableVertexAttribPointer("aColor3d", programObject3D, "aColor");
        disableVertexAttribPointer("aTexture3d", programObject3D, "aTexture");

        curContext.drawArrays(curContext.TRIANGLES, 0, boxVerts.length / 3);
        curContext.disable(curContext.POLYGON_OFFSET_FILL);
    }

    if (lineWidth > 0 && doStroke) {
        curContext.useProgram(programObject2D);
        uniformMatrix("model2d", programObject2D, "model", false, model.array());
        uniformMatrix("view2d", programObject2D, "view", false, view.array());
        uniformf("color2d", programObject2D, "color", strokeStyle);
        uniformi("picktype2d", programObject2D, "picktype", 0);
        vertexAttribPointer("vertex2d", programObject2D, "Vertex", 3, boxOutlineBuffer);
        disableVertexAttribPointer("aTextureCoord2d", programObject2D, "aTextureCoord");
        curContext.drawArrays(curContext.LINES, 0, boxOutlineVerts.length / 3);
    }
};

/**
 * The initSphere() function is a helper function used by <b>sphereDetail()</b>
 * This function creates and stores sphere vertices every time the user changes sphere detail.
 *
 * @see #sphereDetail
 */
var initSphere = function () {
    var i;
    sphereVerts = [];

    for (i = 0; i < sphereDetailU; i++) {
        sphereVerts.push(0);
        sphereVerts.push(-1);
        sphereVerts.push(0);
        sphereVerts.push(sphereX[i]);
        sphereVerts.push(sphereY[i]);
        sphereVerts.push(sphereZ[i]);
    }
    sphereVerts.push(0);
    sphereVerts.push(-1);
    sphereVerts.push(0);
    sphereVerts.push(sphereX[0]);
    sphereVerts.push(sphereY[0]);
    sphereVerts.push(sphereZ[0]);

    var v1, v11, v2;

    // middle rings
    var voff = 0;
    for (i = 2; i < sphereDetailV; i++) {
        v1 = v11 = voff;
        voff += sphereDetailU;
        v2 = voff;
        for (var j = 0; j < sphereDetailU; j++) {
            sphereVerts.push(sphereX[v1]);
            sphereVerts.push(sphereY[v1]);
            sphereVerts.push(sphereZ[v1++]);
            sphereVerts.push(sphereX[v2]);
            sphereVerts.push(sphereY[v2]);
            sphereVerts.push(sphereZ[v2++]);
        }

        // close each ring
        v1 = v11;
        v2 = voff;

        sphereVerts.push(sphereX[v1]);
        sphereVerts.push(sphereY[v1]);
        sphereVerts.push(sphereZ[v1]);
        sphereVerts.push(sphereX[v2]);
        sphereVerts.push(sphereY[v2]);
        sphereVerts.push(sphereZ[v2]);
    }

    // add the northern cap
    for (i = 0; i < sphereDetailU; i++) {
        v2 = voff + i;

        sphereVerts.push(sphereX[v2]);
        sphereVerts.push(sphereY[v2]);
        sphereVerts.push(sphereZ[v2]);
        sphereVerts.push(0);
        sphereVerts.push(1);
        sphereVerts.push(0);
    }

    sphereVerts.push(sphereX[voff]);
    sphereVerts.push(sphereY[voff]);
    sphereVerts.push(sphereZ[voff]);
    sphereVerts.push(0);
    sphereVerts.push(1);
    sphereVerts.push(0);

    //set the buffer data
    curContext.bindBuffer(curContext.ARRAY_BUFFER, sphereBuffer);
    curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array(sphereVerts), curContext.STATIC_DRAW);
};

/**
 * The sphereDetail() function controls the detail used to render a sphere by adjusting the number of
 * vertices of the sphere mesh. The default resolution is 30, which creates
 * a fairly detailed sphere definition with vertices every 360/30 = 12
 * degrees. If you're going to render a great number of spheres per frame,
 * it is advised to reduce the level of detail using this function.
 * The setting stays active until <b>sphereDetail()</b> is called again with
 * a new parameter and so should <i>not</i> be called prior to every
 * <b>sphere()</b> statement, unless you wish to render spheres with
 * different settings, e.g. using less detail for smaller spheres or ones
 * further away from the camera. To control the detail of the horizontal
 * and vertical resolution independently, use the version of the functions
 * with two parameters. Calling this function with one parameter sets the number of segments
 *(minimum of 3) used per full circle revolution. This is equivalent to calling the function with
 * two identical values.
 *
 * @param {int} ures    number of segments used horizontally (longitudinally) per full circle revolution
 * @param {int} vres    number of segments used vertically (latitudinally) from top to bottom
 *
 * @see #sphere()
 */
sphereDetail = function (ures, vres) {
    var i;

    if (arguments.length === 1) {
        ures = vres = arguments[0];
    }

    if (ures < 3) {
        ures = 3;
    } // force a minimum res
    if (vres < 2) {
        vres = 2;
    } // force a minimum res
    // if it hasn't changed do nothing
    if ((ures === sphereDetailU) && (vres === sphereDetailV)) {
        return;
    }

    var delta = PConstants.SINCOS_LENGTH / ures;
    var cx = new Float32Array(ures);
    var cz = new Float32Array(ures);
    // calc unit circle in XZ plane
    for (i = 0; i < ures; i++) {
        cx[i] = cosLUT[((i * delta) % PConstants.SINCOS_LENGTH) | 0];
        cz[i] = sinLUT[((i * delta) % PConstants.SINCOS_LENGTH) | 0];
    }

    // computing vertexlist
    // vertexlist starts at south pole
    var vertCount = ures * (vres - 1) + 2;
    var currVert = 0;

    // re-init arrays to store vertices
    sphereX = new Float32Array(vertCount);
    sphereY = new Float32Array(vertCount);
    sphereZ = new Float32Array(vertCount);

    var angle_step = (PConstants.SINCOS_LENGTH * 0.5) / vres;
    var angle = angle_step;

    // step along Y axis
    for (i = 1; i < vres; i++) {
        var curradius = sinLUT[(angle % PConstants.SINCOS_LENGTH) | 0];
        var currY = -cosLUT[(angle % PConstants.SINCOS_LENGTH) | 0];
        for (var j = 0; j < ures; j++) {
            sphereX[currVert] = cx[j] * curradius;
            sphereY[currVert] = currY;
            sphereZ[currVert++] = cz[j] * curradius;
        }
        angle += angle_step;
    }
    sphereDetailU = ures;
    sphereDetailV = vres;

    // make the sphere verts and norms
    initSphere();
};

/**
 * The sphere() function draws a sphere with radius r centered at coordinate 0, 0, 0.
 * A sphere is a hollow ball made from tessellated triangles.
 *
 * @param {int|float} r the radius of the sphere
 */
sphere = a3DOnlyFunction;

sphere = function () {
    var sRad = arguments[0];

    if ((sphereDetailU < 3) || (sphereDetailV < 2)) {
        sphereDetail(30);
    }

    // Modeling transformation
    var model = new PMatrix3D();
    model.scale(sRad, sRad, sRad);

    // viewing transformation needs to have Y flipped
    // becuase that's what Processing does.
    var view = new PMatrix3D();
    view.scale(1, -1, 1);
    view.apply(modelView.array());
    view.transpose();

    if (doFill) {
        // Calculating the normal matrix can be expensive, so only
        // do it if it's necessary
        if (lightCount > 0) {
            // Create a normal transformation matrix
            var v = new PMatrix3D();
            v.set(view);

            var m = new PMatrix3D();
            m.set(model);

            v.mult(m);

            var normalMatrix = new PMatrix3D();
            normalMatrix.set(v);
            normalMatrix.invert();
            normalMatrix.transpose();

            uniformMatrix("normalTransform3d", programObject3D, "normalTransform", false, normalMatrix.array());
            vertexAttribPointer("normal3d", programObject3D, "Normal", 3, sphereBuffer);
        }
        else {
            disableVertexAttribPointer("normal3d", programObject3D, "Normal");
        }

        curContext.useProgram(programObject3D);
        disableVertexAttribPointer("aTexture3d", programObject3D, "aTexture");

        uniformMatrix("model3d", programObject3D, "model", false, model.array());
        uniformMatrix("view3d", programObject3D, "view", false, view.array());
        vertexAttribPointer("vertex3d", programObject3D, "Vertex", 3, sphereBuffer);

        // Turn off per vertex colors
        disableVertexAttribPointer("aColor3d", programObject3D, "aColor");

        // fix stitching problems. (lines get occluded by triangles
        // since they share the same depth values). This is not entirely
        // working, but it's a start for drawing the outline. So
        // developers can start playing around with styles.
        curContext.enable(curContext.POLYGON_OFFSET_FILL);
        curContext.polygonOffset(1, 1);
        uniformf("color3d", programObject3D, "color", fillStyle);
        curContext.drawArrays(curContext.TRIANGLE_STRIP, 0, sphereVerts.length / 3);
        curContext.disable(curContext.POLYGON_OFFSET_FILL);
    }

    if (lineWidth > 0 && doStroke) {
        curContext.useProgram(programObject2D);
        uniformMatrix("model2d", programObject2D, "model", false, model.array());
        uniformMatrix("view2d", programObject2D, "view", false, view.array());
        vertexAttribPointer("vertex2d", programObject2D, "Vertex", 3, sphereBuffer);
        disableVertexAttribPointer("aTextureCoord2d", programObject2D, "aTextureCoord");
        uniformf("color2d", programObject2D, "color", strokeStyle);
        uniformi("picktype2d", programObject2D, "picktype", 0);
        curContext.drawArrays(curContext.LINE_STRIP, 0, sphereVerts.length / 3);
    }
};

////////////////////////////////////////////////////////////////////////////
// Coordinates
////////////////////////////////////////////////////////////////////////////

/**
 * Returns the three-dimensional X, Y, Z position in model space. This returns
 * the X value for a given coordinate based on the current set of transformations
 * (scale, rotate, translate, etc.) The X value can be used to place an object
 * in space relative to the location of the original point once the transformations
 * are no longer in use.<br />
 * <br />
 *
 * @param {int | float} x 3D x coordinate to be mapped
 * @param {int | float} y 3D y coordinate to be mapped
 * @param {int | float} z 3D z coordinate to be mapped
 *
 * @returns {float}
 *
 * @see modelY
 * @see modelZ
 */
modelX = function (x, y, z) {
    var mv = modelView.array();
    var ci = cameraInv.array();

    var ax = mv[0] * x + mv[1] * y + mv[2] * z + mv[3];
    var ay = mv[4] * x + mv[5] * y + mv[6] * z + mv[7];
    var az = mv[8] * x + mv[9] * y + mv[10] * z + mv[11];
    var aw = mv[12] * x + mv[13] * y + mv[14] * z + mv[15];

    var ox = ci[0] * ax + ci[1] * ay + ci[2] * az + ci[3] * aw;
    var ow = ci[12] * ax + ci[13] * ay + ci[14] * az + ci[15] * aw;

    return (ow !== 0) ? ox / ow : ox;
};

/**
 * Returns the three-dimensional X, Y, Z position in model space. This returns
 * the Y value for a given coordinate based on the current set of transformations
 * (scale, rotate, translate, etc.) The Y value can be used to place an object in
 * space relative to the location of the original point once the transformations
 * are no longer in use.<br />
 * <br />
 *
 * @param {int | float} x 3D x coordinate to be mapped
 * @param {int | float} y 3D y coordinate to be mapped
 * @param {int | float} z 3D z coordinate to be mapped
 *
 * @returns {float}
 *
 * @see modelX
 * @see modelZ
 */
modelY = function (x, y, z) {
    var mv = modelView.array();
    var ci = cameraInv.array();

    var ax = mv[0] * x + mv[1] * y + mv[2] * z + mv[3];
    var ay = mv[4] * x + mv[5] * y + mv[6] * z + mv[7];
    var az = mv[8] * x + mv[9] * y + mv[10] * z + mv[11];
    var aw = mv[12] * x + mv[13] * y + mv[14] * z + mv[15];

    var oy = ci[4] * ax + ci[5] * ay + ci[6] * az + ci[7] * aw;
    var ow = ci[12] * ax + ci[13] * ay + ci[14] * az + ci[15] * aw;

    return (ow !== 0) ? oy / ow : oy;
};

/**
 * Returns the three-dimensional X, Y, Z position in model space. This returns
 * the Z value for a given coordinate based on the current set of transformations
 * (scale, rotate, translate, etc.) The Z value can be used to place an object in
 * space relative to the location of the original point once the transformations
 * are no longer in use.
 *
 * @param {int | float} x 3D x coordinate to be mapped
 * @param {int | float} y 3D y coordinate to be mapped
 * @param {int | float} z 3D z coordinate to be mapped
 *
 * @returns {float}
 *
 * @see modelX
 * @see modelY
 */
modelZ = function (x, y, z) {
    var mv = modelView.array();
    var ci = cameraInv.array();

    var ax = mv[0] * x + mv[1] * y + mv[2] * z + mv[3];
    var ay = mv[4] * x + mv[5] * y + mv[6] * z + mv[7];
    var az = mv[8] * x + mv[9] * y + mv[10] * z + mv[11];
    var aw = mv[12] * x + mv[13] * y + mv[14] * z + mv[15];

    var oz = ci[8] * ax + ci[9] * ay + ci[10] * az + ci[11] * aw;
    var ow = ci[12] * ax + ci[13] * ay + ci[14] * az + ci[15] * aw;

    return (ow !== 0) ? oz / ow : oz;
};

////////////////////////////////////////////////////////////////////////////
// Material Properties
////////////////////////////////////////////////////////////////////////////

/**
 * Sets the ambient reflectance for shapes drawn to the screen. This is
 * combined with the ambient light component of environment. The color
 * components set through the parameters define the reflectance. For example in
 * the default color mode, setting v1=255, v2=126, v3=0, would cause all the
 * red light to reflect and half of the green light to reflect. Used in combination
 * with <b>emissive()</b>, <b>specular()</b>, and <b>shininess()</b> in setting
 * the materal properties of shapes.
 *
 * @param {int | float} gray
 *
 * @returns none
 *
 * @see emissive
 * @see specular
 * @see shininess
 */
ambient = a3DOnlyFunction;

ambient = function (v1, v2, v3) {
    curContext.useProgram(programObject3D);
    uniformi("usingMat3d", programObject3D, "usingMat", true);
    var col = color(v1, v2, v3);
    uniformf("mat_ambient3d", programObject3D, "mat_ambient", color.toGLArray(col).slice(0, 3));
};

/**
 * Sets the emissive color of the material used for drawing shapes
 * drawn to the screen. Used in combination with ambient(), specular(),
 * and shininess() in setting the material properties of shapes.
 *
 * Can be called in the following ways:
 *
 * emissive(gray)
 * @param {int | float} gray number specifying value between white and black
 *
 * emissive(color)
 * @param {color} color any value of the color datatype
 *
 * emissive(v1, v2, v3)
 * @param {int | float} v1 red or hue value
 * @param {int | float} v2 green or saturation value
 * @param {int | float} v3 blue or brightness value
 *
 * @returns none
 *
 * @see ambient
 * @see specular
 * @see shininess
 */
emissive = a3DOnlyFunction;

emissive = function (v1, v2, v3) {
    curContext.useProgram(programObject3D);
    uniformi("usingMat3d", programObject3D, "usingMat", true);
    var col = color(v1, v2, v3);
    uniformf("mat_emissive3d", programObject3D, "mat_emissive", color.toGLArray(col).slice(0, 3));
};

/**
 * Sets the amount of gloss in the surface of shapes. Used in combination with
 * <b>ambient()</b>, <b>specular()</b>, and <b>emissive()</b> in setting the
 * material properties of shapes.
 *
 * @param {float} shine degree of shininess
 *
 * @returns none
 */
shininess = a3DOnlyFunction;

shininess = function (shine) {
    curContext.useProgram(programObject3D);
    uniformi("usingMat3d", programObject3D, "usingMat", true);
    uniformf("shininess3d", programObject3D, "shininess", shine);
};

/**
 * Sets the specular color of the materials used for shapes drawn to the screen,
 * which sets the color of hightlights. Specular refers to light which bounces
 * off a surface in a perferred direction (rather than bouncing in all directions
 * like a diffuse light). Used in combination with emissive(), ambient(), and
 * shininess() in setting the material properties of shapes.
 *
 * Can be called in the following ways:
 *
 * specular(gray)
 * @param {int | float} gray number specifying value between white and black
 *
 * specular(gray, alpha)
 * @param {int | float} gray number specifying value between white and black
 * @param {int | float} alpha opacity
 *
 * specular(color)
 * @param {color} color any value of the color datatype
 *
 * specular(v1, v2, v3)
 * @param {int | float} v1 red or hue value
 * @param {int | float} v2 green or saturation value
 * @param {int | float} v3 blue or brightness value
 *
 * specular(v1, v2, v3, alpha)
 * @param {int | float} v1 red or hue value
 * @param {int | float} v2 green or saturation value
 * @param {int | float} v3 blue or brightness value
 * @param {int | float} alpha opacity
 *
 * @returns none
 *
 * @see ambient
 * @see emissive
 * @see shininess
 */
specular = a3DOnlyFunction;

specular = function (v1, v2, v3) {
    curContext.useProgram(programObject3D);
    uniformi("usingMat3d", programObject3D, "usingMat", true);
    var col = color(v1, v2, v3);
    uniformf("mat_specular3d", programObject3D, "mat_specular", color.toGLArray(col).slice(0, 3));
};

////////////////////////////////////////////////////////////////////////////
// Coordinates
////////////////////////////////////////////////////////////////////////////

/**
 * Takes a three-dimensional X, Y, Z position and returns the X value for
 * where it will appear on a (two-dimensional) screen.
 *
 * @param {int | float} x 3D x coordinate to be mapped
 * @param {int | float} y 3D y coordinate to be mapped
 * @param {int | float} z 3D z optional coordinate to be mapped
 *
 * @returns {float}
 *
 * @see screenY
 * @see screenZ
 */
screenX = function (x, y, z) {
    var mv = modelView.array();
    if (mv.length === 16) {
        var ax = mv[0] * x + mv[1] * y + mv[2] * z + mv[3];
        var ay = mv[4] * x + mv[5] * y + mv[6] * z + mv[7];
        var az = mv[8] * x + mv[9] * y + mv[10] * z + mv[11];
        var aw = mv[12] * x + mv[13] * y + mv[14] * z + mv[15];

        var pj = projection.array();

        var ox = pj[0] * ax + pj[1] * ay + pj[2] * az + pj[3] * aw;
        var ow = pj[12] * ax + pj[13] * ay + pj[14] * az + pj[15] * aw;

        if (ow !== 0) {
            ox /= ow;
        }
        return width * (1 + ox) / 2.0;
    }
    // We assume that we're in 2D
    return modelView.multX(x, y);
};

/**
 * Takes a three-dimensional X, Y, Z position and returns the Y value for
 * where it will appear on a (two-dimensional) screen.
 *
 * @param {int | float} x 3D x coordinate to be mapped
 * @param {int | float} y 3D y coordinate to be mapped
 * @param {int | float} z 3D z optional coordinate to be mapped
 *
 * @returns {float}
 *
 * @see screenX
 * @see screenZ
 */
screenY = function screenY(x, y, z) {
    var mv = modelView.array();
    if (mv.length === 16) {
        var ax = mv[0] * x + mv[1] * y + mv[2] * z + mv[3];
        var ay = mv[4] * x + mv[5] * y + mv[6] * z + mv[7];
        var az = mv[8] * x + mv[9] * y + mv[10] * z + mv[11];
        var aw = mv[12] * x + mv[13] * y + mv[14] * z + mv[15];

        var pj = projection.array();

        var oy = pj[4] * ax + pj[5] * ay + pj[6] * az + pj[7] * aw;
        var ow = pj[12] * ax + pj[13] * ay + pj[14] * az + pj[15] * aw;

        if (ow !== 0) {
            oy /= ow;
        }
        return height * (1 + oy) / 2.0;
    }
    // We assume that we're in 2D
    return modelView.multY(x, y);
};

/**
 * Takes a three-dimensional X, Y, Z position and returns the Z value for
 * where it will appear on a (two-dimensional) screen.
 *
 * @param {int | float} x 3D x coordinate to be mapped
 * @param {int | float} y 3D y coordinate to be mapped
 * @param {int | float} z 3D z coordinate to be mapped
 *
 * @returns {float}
 *
 * @see screenX
 * @see screenY
 */
screenZ = function screenZ(x, y, z) {
    var mv = modelView.array();
    if (mv.length !== 16) {
        return 0;
    }

    var pj = projection.array();

    var ax = mv[0] * x + mv[1] * y + mv[2] * z + mv[3];
    var ay = mv[4] * x + mv[5] * y + mv[6] * z + mv[7];
    var az = mv[8] * x + mv[9] * y + mv[10] * z + mv[11];
    var aw = mv[12] * x + mv[13] * y + mv[14] * z + mv[15];

    var oz = pj[8] * ax + pj[9] * ay + pj[10] * az + pj[11] * aw;
    var ow = pj[12] * ax + pj[13] * ay + pj[14] * az + pj[15] * aw;

    if (ow !== 0) {
        oz /= ow;
    }
    return (oz + 1) / 2.0;
};

////////////////////////////////////////////////////////////////////////////
// Style functions
////////////////////////////////////////////////////////////////////////////
/**
 * 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.
 * This color is either specified in terms of the RGB or HSB color depending on the current <b>colorMode()</b>
 *(the default color space is RGB, with each value in the range from 0 to 255).
 * <br><br>When using hexadecimal notation to specify a color, use "#" or "0x" before the values (e.g. #CCFFAA, 0xFFCCFFAA).
 * 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",
 * 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.
 * <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.
 * <br><br>To change the color of an image (or a texture), use tint().
 *
 * @param {int|float} gray    number specifying value between white and black
 * @param {int|float} value1  red or hue value
 * @param {int|float} value2  green or saturation value
 * @param {int|float} value3  blue or brightness value
 * @param {int|float} alpha   opacity of the fill
 * @param {Color} color       any value of the color datatype
 * @param {int} hex           color value in hexadecimal notation (i.e. #FFCC00 or 0xFFFFCC00)
 *
 * @see #noFill()
 * @see #stroke()
 * @see #tint()
 * @see #background()
 * @see #colorMode()
 */
fill = function () {
    var color = color(arguments[0], arguments[1], arguments[2], arguments[3]);
    if (color === currentFillColor && doFill) {
        return;
    }
    doFill = true;
    currentFillColor = color;
};

fill = function () {
    fill.apply(this, arguments);
    isFillDirty = true;
};

fill = function () {
    fill.apply(this, arguments);
    fillStyle = color.toGLArray(currentFillColor);
};

/**
 * The noFill() function disables filling geometry. If both <b>noStroke()</b> and <b>noFill()</b>
 * are called, no shapes will be drawn to the screen.
 *
 * @see #fill()
 *
 */
noFill = function () {
    doFill = false;
};

/**
 * The stroke() function sets the color used to draw lines and borders around shapes. This color
 * is either specified in terms of the RGB or HSB color depending on the
 * current <b>colorMode()</b> (the default color space is RGB, with each
 * value in the range from 0 to 255).
 * <br><br>When using hexadecimal notation to specify a color, use "#" or
 * "0x" before the values (e.g. #CCFFAA, 0xFFCCFFAA). 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", 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.
 * <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.
 *
 * @param {int|float} gray    number specifying value between white and black
 * @param {int|float} value1  red or hue value
 * @param {int|float} value2  green or saturation value
 * @param {int|float} value3  blue or brightness value
 * @param {int|float} alpha   opacity of the stroke
 * @param {Color} color       any value of the color datatype
 * @param {int} hex           color value in hexadecimal notation (i.e. #FFCC00 or 0xFFFFCC00)
 *
 * @see #fill()
 * @see #noStroke()
 * @see #tint()
 * @see #background()
 * @see #colorMode()
 */
stroke = function () {
    var color = color(arguments[0], arguments[1], arguments[2], arguments[3]);
    if (color === currentStrokeColor && doStroke) {
        return;
    }
    doStroke = true;
    currentStrokeColor = color;
};

stroke = function () {
    stroke.apply(this, arguments);
    isStrokeDirty = true;
};

stroke = function () {
    stroke.apply(this, arguments);
    strokeStyle = color.toGLArray(currentStrokeColor);
};

/**
 * The noStroke() function disables drawing the stroke (outline). If both <b>noStroke()</b> and
 * <b>noFill()</b> are called, no shapes will be drawn to the screen.
 *
 * @see #stroke()
 */
noStroke = function () {
    doStroke = false;
};

/**
 * The strokeWeight() function sets the width of the stroke used for lines, points, and the border around shapes.
 * All widths are set in units of pixels.
 *
 * @param {int|float} w the weight (in pixels) of the stroke
 */
strokeWeight = function (w) {
    lineWidth = w;
};

strokeWeight = function (w) {
    strokeWeight.apply(this, arguments);
    curContext.lineWidth = w;
};

strokeWeight = function (w) {
    strokeWeight.apply(this, arguments);

    // Processing groups the weight of points and lines under this one function,
    // but for WebGL, we need to set a uniform for points and call a function for line.

    curContext.useProgram(programObject2D);
    uniformf("pointSize2d", programObject2D, "pointSize", w);

    curContext.useProgram(programObjectUnlitShape);
    uniformf("pointSizeUnlitShape", programObjectUnlitShape, "pointSize", w);

    curContext.lineWidth(w);
};

/**
 * The strokeCap() function sets the style for rendering line endings. These ends are either squared, extended, or rounded and
 * specified with the corresponding parameters SQUARE, PROJECT, and ROUND. The default cap is ROUND.
 * This function is not available with the P2D, P3D, or OPENGL renderers
 *
 * @param {int} value Either SQUARE, PROJECT, or ROUND
 */
strokeCap = function (value) {
    drawing.$ensureContext().lineCap = value;
};

/**
 * The strokeJoin() function sets the style of the joints which connect line segments.
 * These joints are either mitered, beveled, or rounded and specified with the corresponding parameters MITER, BEVEL, and ROUND. The default joint is MITER.
 * This function is not available with the P2D, P3D, or OPENGL renderers
 *
 * @param {int} value Either SQUARE, PROJECT, or ROUND
 */
strokeJoin = function (value) {
    drawing.$ensureContext().lineJoin = value;
};

/**
 * The smooth() function draws all geometry with smooth (anti-aliased) edges. This will slow down the frame rate of the application,
 * but will enhance the visual refinement. <br/><br/>
 * Note that smooth() will also improve image quality of resized images, and noSmooth() will disable image (and font) smoothing altogether.
 *
 * @see #noSmooth()
 * @see #hint()
 * @see #size()
 */

smooth = function () {
    renderSmooth = true;
    var style = curElement.style;
    style.setProperty("image-rendering", "optimizeQuality", "important");
    style.setProperty("-ms-interpolation-mode", "bicubic", "important");
    if (curContext.hasOwnProperty("mozImageSmoothingEnabled")) {
        curContext.mozImageSmoothingEnabled = true;
    }
};

smooth = nop;

/**
 * The noSmooth() function draws all geometry with jagged (aliased) edges.
 *
 * @see #smooth()
 */

noSmooth = function () {
    renderSmooth = false;
    var style = curElement.style;
    style.setProperty("image-rendering", "optimizeSpeed", "important");
    style.setProperty("image-rendering", "-moz-crisp-edges", "important");
    style.setProperty("image-rendering", "-webkit-optimize-contrast", "important");
    style.setProperty("image-rendering", "optimize-contrast", "important");
    style.setProperty("-ms-interpolation-mode", "nearest-neighbor", "important");
    if (curContext.hasOwnProperty("mozImageSmoothingEnabled")) {
        curContext.mozImageSmoothingEnabled = false;
    }
};

noSmooth = nop;

////////////////////////////////////////////////////////////////////////////
// Vector drawing functions
////////////////////////////////////////////////////////////////////////////
/**
 * The point() function draws a point, a coordinate in space at the dimension of one pixel.
 * The first parameter is the horizontal value for the point, the second
 * value is the vertical value for the point, and the optional third value
 * is the depth value. Drawing this shape in 3D using the <b>z</b>
 * parameter requires the P3D or OPENGL parameter in combination with
 * size as shown in the above example.
 *
 * @param {int|float} x x-coordinate of the point
 * @param {int|float} y y-coordinate of the point
 * @param {int|float} z z-coordinate of the point
 *
 * @see #beginShape()
 */
point = function (x, y) {
    if (!doStroke) {
        return;
    }

    if (!renderSmooth) {
        x = Math.round(x);
        y = Math.round(y);
    }
    curContext.fillStyle = color.toString(currentStrokeColor);
    isFillDirty = true;
    // Draw a circle for any point larger than 1px
    if (lineWidth > 1) {
        curContext.beginPath();
        curContext.arc(x, y, lineWidth / 2, 0, PConstants.TWO_PI, false);
        curContext.fill();
    } else {
        curContext.fillRect(x, y, 1, 1);
    }
};

point = function (x, y, z) {
    var model = new PMatrix3D();

    // move point to position
    model.translate(x, y, z || 0);
    model.transpose();

    var view = new PMatrix3D();
    view.scale(1, -1, 1);
    view.apply(modelView.array());
    view.transpose();

    curContext.useProgram(programObject2D);
    uniformMatrix("model2d", programObject2D, "model", false, model.array());
    uniformMatrix("view2d", programObject2D, "view", false, view.array());

    if (lineWidth > 0 && doStroke) {
        // this will be replaced with the new bit shifting color code
        uniformf("color2d", programObject2D, "color", strokeStyle);
        uniformi("picktype2d", programObject2D, "picktype", 0);
        vertexAttribPointer("vertex2d", programObject2D, "Vertex", 3, pointBuffer);
        disableVertexAttribPointer("aTextureCoord2d", programObject2D, "aTextureCoord");
        curContext.drawArrays(curContext.POINTS, 0, 1);
    }
};

/**
 * Using the <b>beginShape()</b> and <b>endShape()</b> functions allow creating more complex forms.
 * <b>beginShape()</b> begins recording vertices for a shape and <b>endShape()</b> stops recording.
 * The value of the <b>MODE</b> parameter tells it which types of shapes to create from the provided vertices.
 * With no mode specified, the shape can be any irregular polygon. After calling the <b>beginShape()</b> function,
 * a series of <b>vertex()</b> commands must follow. To stop drawing the shape, call <b>endShape()</b>.
 * The <b>vertex()</b> function with two parameters specifies a position in 2D and the <b>vertex()</b>
 * function with three parameters specifies a position in 3D. Each shape will be outlined with the current
 * stroke color and filled with the fill color.
 *
 * @param {int} MODE either POINTS, LINES, TRIANGLES, TRIANGLE_FAN, TRIANGLE_STRIP, QUADS, and QUAD_STRI
 *
 * @see endShape
 * @see vertex
 * @see curveVertex
 * @see bezierVertex
 */
beginShape = function (type) {
    curShape = type;
    curvePoints = [];
    vertArray = [];
};

/**
 * All shapes are constructed by connecting a series of vertices. <b>vertex()</b> is used to specify the vertex
 * coordinates for points, lines, triangles, quads, and polygons and is used exclusively within the <b>beginShape()</b>
 * and <b>endShape()</b> function. <br /><br />Drawing a vertex in 3D using the <b>z</b> parameter requires the P3D or
 * OPENGL parameter in combination with size as shown in the above example.<br /><br />This function is also used to map a
 * texture onto the geometry. The <b>texture()</b> function declares the texture to apply to the geometry and the <b>u</b>
 * and <b>v</b> coordinates set define the mapping of this texture to the form. By default, the coordinates used for
 * <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
 * <b>textureMode()</b>.
 *
 * @param {int | float} x x-coordinate of the vertex
 * @param {int | float} y y-coordinate of the vertex
 * @param {int | float} z z-coordinate of the vertex
 * @param {int | float} u horizontal coordinate for the texture mapping
 * @param {int | float} v vertical coordinate for the texture mapping
 *
 * @see beginShape
 * @see endShape
 * @see bezierVertex
 * @see curveVertex
 * @see texture
 */

vertex = function (x, y, u, v) {
    var vert = [];

    if (firstVert) { firstVert = false; }
    vert["isVert"] = true;

    vert[0] = x;
    vert[1] = y;
    vert[2] = 0;
    vert[3] = u;
    vert[4] = v;

    // fill and stroke color
    vert[5] = currentFillColor;
    vert[6] = currentStrokeColor;

    vertArray.push(vert);
};

vertex = function (x, y, z, u, v) {
    var vert = [];

    if (firstVert) { firstVert = false; }
    vert["isVert"] = true;

    if (v === undef && usingTexture) {
        v = u;
        u = z;
        z = 0;
    }

    // Convert u and v to normalized coordinates
    if (u !== undef && v !== undef) {
        if (curTextureMode === PConstants.IMAGE) {
            u /= curTexture.width;
            v /= curTexture.height;
        }
        u = u > 1 ? 1 : u;
        u = u < 0 ? 0 : u;
        v = v > 1 ? 1 : v;
        v = v < 0 ? 0 : v;
    }

    vert[0] = x;
    vert[1] = y;
    vert[2] = z || 0;
    vert[3] = u || 0;
    vert[4] = v || 0;

    // fill rgba
    vert[5] = fillStyle[0];
    vert[6] = fillStyle[1];
    vert[7] = fillStyle[2];
    vert[8] = fillStyle[3];
    // stroke rgba
    vert[9] = strokeStyle[0];
    vert[10] = strokeStyle[1];
    vert[11] = strokeStyle[2];
    vert[12] = strokeStyle[3];
    //normals
    vert[13] = normalX;
    vert[14] = normalY;
    vert[15] = normalZ;

    vertArray.push(vert);
};

/**
 * The endShape() function is the companion to beginShape() and may only be called after beginShape().
 * When endshape() is called, all of image data defined since the previous call to beginShape() is written
 * into the image buffer.
 *
 * @param {int} MODE Use CLOSE to close the shape
 *
 * @see beginShape
 */
endShape = function (mode) {
    // Duplicated in Drawing3D; too many variables used
    if (vertArray.length === 0) { return; }

    var closeShape = mode === PConstants.CLOSE;

    // if the shape is closed, the first element is also the last element
    if (closeShape) {
        vertArray.push(vertArray[0]);
    }

    var lineVertArray = [];
    var fillVertArray = [];
    var colorVertArray = [];
    var strokeVertArray = [];
    var texVertArray = [];
    var cachedVertArray;

    firstVert = true;
    var i, j, k;
    var vertArrayLength = vertArray.length;

    for (i = 0; i < vertArrayLength; i++) {
        cachedVertArray = vertArray[i];
        for (j = 0; j < 3; j++) {
            fillVertArray.push(cachedVertArray[j]);
        }
    }

    // 5,6,7,8
    // R,G,B,A - fill colour
    for (i = 0; i < vertArrayLength; i++) {
        cachedVertArray = vertArray[i];
        for (j = 5; j < 9; j++) {
            colorVertArray.push(cachedVertArray[j]);
        }
    }

    // 9,10,11,12
    // R, G, B, A - stroke colour
    for (i = 0; i < vertArrayLength; i++) {
        cachedVertArray = vertArray[i];
        for (j = 9; j < 13; j++) {
            strokeVertArray.push(cachedVertArray[j]);
        }
    }

    // texture u,v
    for (i = 0; i < vertArrayLength; i++) {
        cachedVertArray = vertArray[i];
        texVertArray.push(cachedVertArray[3]);
        texVertArray.push(cachedVertArray[4]);
    }

    // curveVertex
    if (isCurve && (curShape === PConstants.POLYGON || curShape === undef)) {
        if (vertArrayLength > 3) {
            var b = [],
                s = 1 - curTightness;
            curContext.beginPath();
            curContext.moveTo(vertArray[1][0], vertArray[1][1]);
            /**
            * Matrix to convert from Catmull-Rom to cubic Bezier
            * where t = curTightness
            * |0         1          0         0       |
            * |(t-1)/6   1          (1-t)/6   0       |
            * |0         (1-t)/6    1         (t-1)/6 |
            * |0         0          0         0       |
            */
            for (i = 1; (i + 2) < vertArrayLength; i++) {
                cachedVertArray = vertArray[i];
                b[0] = [cachedVertArray[0], cachedVertArray[1]];
                b[1] = [cachedVertArray[0] + (s * vertArray[i + 1][0] - s * vertArray[i - 1][0]) / 6,
                cachedVertArray[1] + (s * vertArray[i + 1][1] - s * vertArray[i - 1][1]) / 6];
                b[2] = [vertArray[i + 1][0] + (s * vertArray[i][0] - s * vertArray[i + 2][0]) / 6,
                vertArray[i + 1][1] + (s * vertArray[i][1] - s * vertArray[i + 2][1]) / 6];
                b[3] = [vertArray[i + 1][0], vertArray[i + 1][1]];
                curContext.bezierCurveTo(b[1][0], b[1][1], b[2][0], b[2][1], b[3][0], b[3][1]);
            }
            fillStrokeClose();
        }
    }

    // bezierVertex
    else if (isBezier && (curShape === PConstants.POLYGON || curShape === undef)) {
        curContext.beginPath();
        for (i = 0; i < vertArrayLength; i++) {
            cachedVertArray = vertArray[i];
            if (vertArray[i]["isVert"]) { //if it is a vertex move to the position
                if (vertArray[i]["moveTo"]) {
                    curContext.moveTo(cachedVertArray[0], cachedVertArray[1]);
                } else {
                    curContext.lineTo(cachedVertArray[0], cachedVertArray[1]);
                }
            } else { //otherwise continue drawing bezier
                curContext.bezierCurveTo(vertArray[i][0], vertArray[i][1], vertArray[i][2], vertArray[i][3], vertArray[i][4], vertArray[i][5]);
            }
        }
        fillStrokeClose();
    }

    // render the vertices provided
    else {
        if (curShape === PConstants.POINTS) {
            for (i = 0; i < vertArrayLength; i++) {
                cachedVertArray = vertArray[i];
                if (doStroke) {
                    stroke(cachedVertArray[6]);
                }
                point(cachedVertArray[0], cachedVertArray[1]);
            }
        } else if (curShape === PConstants.LINES) {
            for (i = 0; (i + 1) < vertArrayLength; i += 2) {
                cachedVertArray = vertArray[i];
                if (doStroke) {
                    stroke(vertArray[i + 1][6]);
                }
                line(cachedVertArray[0], cachedVertArray[1], vertArray[i + 1][0], vertArray[i + 1][1]);
            }
        } else if (curShape === PConstants.TRIANGLES) {
            for (i = 0; (i + 2) < vertArrayLength; i += 3) {
                cachedVertArray = vertArray[i];
                curContext.beginPath();
                curContext.moveTo(cachedVertArray[0], cachedVertArray[1]);
                curContext.lineTo(vertArray[i + 1][0], vertArray[i + 1][1]);
                curContext.lineTo(vertArray[i + 2][0], vertArray[i + 2][1]);
                curContext.lineTo(cachedVertArray[0], cachedVertArray[1]);

                if (doFill) {
                    fill(vertArray[i + 2][5]);
                    executeContextFill();
                }
                if (doStroke) {
                    stroke(vertArray[i + 2][6]);
                    executeContextStroke();
                }

                curContext.closePath();
            }
        } else if (curShape === PConstants.TRIANGLE_STRIP) {
            for (i = 0; (i + 1) < vertArrayLength; i++) {
                cachedVertArray = vertArray[i];
                curContext.beginPath();
                curContext.moveTo(vertArray[i + 1][0], vertArray[i + 1][1]);
                curContext.lineTo(cachedVertArray[0], cachedVertArray[1]);

                if (doStroke) {
                    stroke(vertArray[i + 1][6]);
                }
                if (doFill) {
                    fill(vertArray[i + 1][5]);
                }

                if (i + 2 < vertArrayLength) {
                    curContext.lineTo(vertArray[i + 2][0], vertArray[i + 2][1]);
                    if (doStroke) {
                        stroke(vertArray[i + 2][6]);
                    }
                    if (doFill) {
                        fill(vertArray[i + 2][5]);
                    }
                }
                fillStrokeClose();
            }
        } else if (curShape === PConstants.TRIANGLE_FAN) {
            if (vertArrayLength > 2) {
                curContext.beginPath();
                curContext.moveTo(vertArray[0][0], vertArray[0][1]);
                curContext.lineTo(vertArray[1][0], vertArray[1][1]);
                curContext.lineTo(vertArray[2][0], vertArray[2][1]);

                if (doFill) {
                    fill(vertArray[2][5]);
                    executeContextFill();
                }
                if (doStroke) {
                    stroke(vertArray[2][6]);
                    executeContextStroke();
                }

                curContext.closePath();
                for (i = 3; i < vertArrayLength; i++) {
                    cachedVertArray = vertArray[i];
                    curContext.beginPath();
                    curContext.moveTo(vertArray[0][0], vertArray[0][1]);
                    curContext.lineTo(vertArray[i - 1][0], vertArray[i - 1][1]);
                    curContext.lineTo(cachedVertArray[0], cachedVertArray[1]);

                    if (doFill) {
                        fill(cachedVertArray[5]);
                        executeContextFill();
                    }
                    if (doStroke) {
                        stroke(cachedVertArray[6]);
                        executeContextStroke();
                    }

                    curContext.closePath();
                }
            }
        } else if (curShape === PConstants.QUADS) {
            for (i = 0; (i + 3) < vertArrayLength; i += 4) {
                cachedVertArray = vertArray[i];
                curContext.beginPath();
                curContext.moveTo(cachedVertArray[0], cachedVertArray[1]);
                for (j = 1; j < 4; j++) {
                    curContext.lineTo(vertArray[i + j][0], vertArray[i + j][1]);
                }
                curContext.lineTo(cachedVertArray[0], cachedVertArray[1]);

                if (doFill) {
                    fill(vertArray[i + 3][5]);
                    executeContextFill();
                }
                if (doStroke) {
                    stroke(vertArray[i + 3][6]);
                    executeContextStroke();
                }

                curContext.closePath();
            }
        } else if (curShape === PConstants.QUAD_STRIP) {
            if (vertArrayLength > 3) {
                for (i = 0; (i + 1) < vertArrayLength; i += 2) {
                    cachedVertArray = vertArray[i];
                    curContext.beginPath();
                    if (i + 3 < vertArrayLength) {
                        curContext.moveTo(vertArray[i + 2][0], vertArray[i + 2][1]);
                        curContext.lineTo(cachedVertArray[0], cachedVertArray[1]);
                        curContext.lineTo(vertArray[i + 1][0], vertArray[i + 1][1]);
                        curContext.lineTo(vertArray[i + 3][0], vertArray[i + 3][1]);

                        if (doFill) {
                            fill(vertArray[i + 3][5]);
                        }
                        if (doStroke) {
                            stroke(vertArray[i + 3][6]);
                        }
                    } else {
                        curContext.moveTo(cachedVertArray[0], cachedVertArray[1]);
                        curContext.lineTo(vertArray[i + 1][0], vertArray[i + 1][1]);
                    }
                    fillStrokeClose();
                }
            }
        } else {
            curContext.beginPath();
            curContext.moveTo(vertArray[0][0], vertArray[0][1]);
            for (i = 1; i < vertArrayLength; i++) {
                cachedVertArray = vertArray[i];
                if (cachedVertArray["isVert"]) { //if it is a vertex move to the position
                    if (cachedVertArray["moveTo"]) {
                        curContext.moveTo(cachedVertArray[0], cachedVertArray[1]);
                    } else {
                        curContext.lineTo(cachedVertArray[0], cachedVertArray[1]);
                    }
                }
            }
            fillStrokeClose();
        }
    }

    // Reset some settings
    isCurve = false;
    isBezier = false;
    curveVertArray = [];
    curveVertCount = 0;

    // If the shape is closed, the first element was added as last element.
    // We must remove it again to prevent the list of vertices from growing
    // over successive calls to endShape(CLOSE)
    if (closeShape) {
        vertArray.pop();
    }
};

endShape = function (mode) {
    // Duplicated in Drawing3D; too many variables used
    if (vertArray.length === 0) { return; }

    var closeShape = mode === PConstants.CLOSE;
    var lineVertArray = [];
    var fillVertArray = [];
    var colorVertArray = [];
    var strokeVertArray = [];
    var texVertArray = [];
    var cachedVertArray;

    firstVert = true;
    var i, j, k;
    var vertArrayLength = vertArray.length;

    for (i = 0; i < vertArrayLength; i++) {
        cachedVertArray = vertArray[i];
        for (j = 0; j < 3; j++) {
            fillVertArray.push(cachedVertArray[j]);
        }
    }

    // 5,6,7,8
    // R,G,B,A - fill colour
    for (i = 0; i < vertArrayLength; i++) {
        cachedVertArray = vertArray[i];
        for (j = 5; j < 9; j++) {
            colorVertArray.push(cachedVertArray[j]);
        }
    }

    // 9,10,11,12
    // R, G, B, A - stroke colour
    for (i = 0; i < vertArrayLength; i++) {
        cachedVertArray = vertArray[i];
        for (j = 9; j < 13; j++) {
            strokeVertArray.push(cachedVertArray[j]);
        }
    }

    // texture u,v
    for (i = 0; i < vertArrayLength; i++) {
        cachedVertArray = vertArray[i];
        texVertArray.push(cachedVertArray[3]);
        texVertArray.push(cachedVertArray[4]);
    }

    // if shape is closed, push the first point into the last point (including colours)
    if (closeShape) {
        fillVertArray.push(vertArray[0][0]);
        fillVertArray.push(vertArray[0][1]);
        fillVertArray.push(vertArray[0][2]);

        for (i = 5; i < 9; i++) {
            colorVertArray.push(vertArray[0][i]);
        }

        for (i = 9; i < 13; i++) {
            strokeVertArray.push(vertArray[0][i]);
        }

        texVertArray.push(vertArray[0][3]);
        texVertArray.push(vertArray[0][4]);
    }
    // End duplication

    // curveVertex
    if (isCurve && (curShape === PConstants.POLYGON || curShape === undef)) {
        lineVertArray = fillVertArray;
        if (doStroke) {
            line3D(lineVertArray, null, strokeVertArray);
        }
        if (doFill) {
            fill3D(fillVertArray, null, colorVertArray);
        }
    }
    // bezierVertex
    else if (isBezier && (curShape === PConstants.POLYGON || curShape === undef)) {
        lineVertArray = fillVertArray;
        lineVertArray.splice(lineVertArray.length - 3);
        strokeVertArray.splice(strokeVertArray.length - 4);
        if (doStroke) {
            line3D(lineVertArray, null, strokeVertArray);
        }
        if (doFill) {
            fill3D(fillVertArray, "TRIANGLES", colorVertArray);
        }
    }

    // render the vertices provided
    else {
        if (curShape === PConstants.POINTS) {       // if POINTS was the specified parameter in beginShape
            for (i = 0; i < vertArrayLength; i++) {  // loop through and push the point location information to the array
                cachedVertArray = vertArray[i];
                for (j = 0; j < 3; j++) {
                    lineVertArray.push(cachedVertArray[j]);
                }
            }
            point3D(lineVertArray, strokeVertArray);  // render function for points
        } else if (curShape === PConstants.LINES) { // if LINES was the specified parameter in beginShape
            for (i = 0; i < vertArrayLength; i++) {  // loop through and push the point location information to the array
                cachedVertArray = vertArray[i];
                for (j = 0; j < 3; j++) {
                    lineVertArray.push(cachedVertArray[j]);
                }
            }
            for (i = 0; i < vertArrayLength; i++) {  // loop through and push the color information to the array
                cachedVertArray = vertArray[i];
                for (j = 5; j < 9; j++) {
                    colorVertArray.push(cachedVertArray[j]);
                }
            }
            line3D(lineVertArray, "LINES", strokeVertArray);  // render function for lines
        } else if (curShape === PConstants.TRIANGLES) {     // if TRIANGLES was the specified parameter in beginShape
            if (vertArrayLength > 2) {
                for (i = 0; (i + 2) < vertArrayLength; i += 3) {   // loop through the array per triangle
                    fillVertArray = [];
                    texVertArray = [];
                    lineVertArray = [];
                    colorVertArray = [];
                    strokeVertArray = [];
                    for (j = 0; j < 3; j++) {
                        for (k = 0; k < 3; k++) {                   // loop through and push
                            lineVertArray.push(vertArray[i + j][k]);    // the line point location information
                            fillVertArray.push(vertArray[i + j][k]);    // and fill point location information
                        }
                    }
                    for (j = 0; j < 3; j++) {                     // loop through and push the texture information
                        for (k = 3; k < 5; k++) {
                            texVertArray.push(vertArray[i + j][k]);
                        }
                    }
                    for (j = 0; j < 3; j++) {
                        for (k = 5; k < 9; k++) {                   // loop through and push
                            colorVertArray.push(vertArray[i + j][k]);   // the colour information
                            strokeVertArray.push(vertArray[i + j][k + 4]);// and the stroke information
                        }
                    }
                    if (doStroke) {
                        line3D(lineVertArray, "LINE_LOOP", strokeVertArray);               // line render function
                    }
                    if (doFill || usingTexture) {
                        fill3D(fillVertArray, "TRIANGLES", colorVertArray, texVertArray);   // fill shape render function
                    }
                }
            }
        } else if (curShape === PConstants.TRIANGLE_STRIP) {    // if TRIANGLE_STRIP was the specified parameter in beginShape
            if (vertArrayLength > 2) {
                for (i = 0; (i + 2) < vertArrayLength; i++) {
                    lineVertArray = [];
                    fillVertArray = [];
                    strokeVertArray = [];
                    colorVertArray = [];
                    texVertArray = [];
                    for (j = 0; j < 3; j++) {
                        for (k = 0; k < 3; k++) {
                            lineVertArray.push(vertArray[i + j][k]);
                            fillVertArray.push(vertArray[i + j][k]);
                        }
                    }
                    for (j = 0; j < 3; j++) {
                        for (k = 3; k < 5; k++) {
                            texVertArray.push(vertArray[i + j][k]);
                        }
                    }
                    for (j = 0; j < 3; j++) {
                        for (k = 5; k < 9; k++) {
                            strokeVertArray.push(vertArray[i + j][k + 4]);
                            colorVertArray.push(vertArray[i + j][k]);
                        }
                    }

                    if (doFill || usingTexture) {
                        fill3D(fillVertArray, "TRIANGLE_STRIP", colorVertArray, texVertArray);
                    }
                    if (doStroke) {
                        line3D(lineVertArray, "LINE_LOOP", strokeVertArray);
                    }
                }
            }
        } else if (curShape === PConstants.TRIANGLE_FAN) {
            if (vertArrayLength > 2) {
                for (i = 0; i < 3; i++) {
                    cachedVertArray = vertArray[i];
                    for (j = 0; j < 3; j++) {
                        lineVertArray.push(cachedVertArray[j]);
                    }
                }
                for (i = 0; i < 3; i++) {
                    cachedVertArray = vertArray[i];
                    for (j = 9; j < 13; j++) {
                        strokeVertArray.push(cachedVertArray[j]);
                    }
                }
                if (doStroke) {
                    line3D(lineVertArray, "LINE_LOOP", strokeVertArray);
                }

                for (i = 2; (i + 1) < vertArrayLength; i++) {
                    lineVertArray = [];
                    strokeVertArray = [];
                    lineVertArray.push(vertArray[0][0]);
                    lineVertArray.push(vertArray[0][1]);
                    lineVertArray.push(vertArray[0][2]);

                    strokeVertArray.push(vertArray[0][9]);
                    strokeVertArray.push(vertArray[0][10]);
                    strokeVertArray.push(vertArray[0][11]);
                    strokeVertArray.push(vertArray[0][12]);

                    for (j = 0; j < 2; j++) {
                        for (k = 0; k < 3; k++) {
                            lineVertArray.push(vertArray[i + j][k]);
                        }
                    }
                    for (j = 0; j < 2; j++) {
                        for (k = 9; k < 13; k++) {
                            strokeVertArray.push(vertArray[i + j][k]);
                        }
                    }
                    if (doStroke) {
                        line3D(lineVertArray, "LINE_STRIP", strokeVertArray);
                    }
                }
                if (doFill || usingTexture) {
                    fill3D(fillVertArray, "TRIANGLE_FAN", colorVertArray, texVertArray);
                }
            }
        } else if (curShape === PConstants.QUADS) {
            for (i = 0; (i + 3) < vertArrayLength; i += 4) {
                lineVertArray = [];
                for (j = 0; j < 4; j++) {
                    cachedVertArray = vertArray[i + j];
                    for (k = 0; k < 3; k++) {
                        lineVertArray.push(cachedVertArray[k]);
                    }
                }
                if (doStroke) {
                    line3D(lineVertArray, "LINE_LOOP", strokeVertArray);
                }

                if (doFill) {
                    fillVertArray = [];
                    colorVertArray = [];
                    texVertArray = [];
                    for (j = 0; j < 3; j++) {
                        fillVertArray.push(vertArray[i][j]);
                    }
                    for (j = 5; j < 9; j++) {
                        colorVertArray.push(vertArray[i][j]);
                    }

                    for (j = 0; j < 3; j++) {
                        fillVertArray.push(vertArray[i + 1][j]);
                    }
                    for (j = 5; j < 9; j++) {
                        colorVertArray.push(vertArray[i + 1][j]);
                    }

                    for (j = 0; j < 3; j++) {
                        fillVertArray.push(vertArray[i + 3][j]);
                    }
                    for (j = 5; j < 9; j++) {
                        colorVertArray.push(vertArray[i + 3][j]);
                    }

                    for (j = 0; j < 3; j++) {
                        fillVertArray.push(vertArray[i + 2][j]);
                    }
                    for (j = 5; j < 9; j++) {
                        colorVertArray.push(vertArray[i + 2][j]);
                    }

                    if (usingTexture) {
                        texVertArray.push(vertArray[i + 0][3]);
                        texVertArray.push(vertArray[i + 0][4]);
                        texVertArray.push(vertArray[i + 1][3]);
                        texVertArray.push(vertArray[i + 1][4]);
                        texVertArray.push(vertArray[i + 3][3]);
                        texVertArray.push(vertArray[i + 3][4]);
                        texVertArray.push(vertArray[i + 2][3]);
                        texVertArray.push(vertArray[i + 2][4]);
                    }

                    fill3D(fillVertArray, "TRIANGLE_STRIP", colorVertArray, texVertArray);
                }
            }
        } else if (curShape === PConstants.QUAD_STRIP) {
            var tempArray = [];
            if (vertArrayLength > 3) {
                for (i = 0; i < 2; i++) {
                    cachedVertArray = vertArray[i];
                    for (j = 0; j < 3; j++) {
                        lineVertArray.push(cachedVertArray[j]);
                    }
                }

                for (i = 0; i < 2; i++) {
                    cachedVertArray = vertArray[i];
                    for (j = 9; j < 13; j++) {
                        strokeVertArray.push(cachedVertArray[j]);
                    }
                }

                line3D(lineVertArray, "LINE_STRIP", strokeVertArray);
                if (vertArrayLength > 4 && vertArrayLength % 2 > 0) {
                    tempArray = fillVertArray.splice(fillVertArray.length - 3);
                    vertArray.pop();
                }
                for (i = 0; (i + 3) < vertArrayLength; i += 2) {
                    lineVertArray = [];
                    strokeVertArray = [];
                    for (j = 0; j < 3; j++) {
                        lineVertArray.push(vertArray[i + 1][j]);
                    }
                    for (j = 0; j < 3; j++) {
                        lineVertArray.push(vertArray[i + 3][j]);
                    }
                    for (j = 0; j < 3; j++) {
                        lineVertArray.push(vertArray[i + 2][j]);
                    }
                    for (j = 0; j < 3; j++) {
                        lineVertArray.push(vertArray[i + 0][j]);
                    }
                    for (j = 9; j < 13; j++) {
                        strokeVertArray.push(vertArray[i + 1][j]);
                    }
                    for (j = 9; j < 13; j++) {
                        strokeVertArray.push(vertArray[i + 3][j]);
                    }
                    for (j = 9; j < 13; j++) {
                        strokeVertArray.push(vertArray[i + 2][j]);
                    }
                    for (j = 9; j < 13; j++) {
                        strokeVertArray.push(vertArray[i + 0][j]);
                    }
                    if (doStroke) {
                        line3D(lineVertArray, "LINE_STRIP", strokeVertArray);
                    }
                }

                if (doFill || usingTexture) {
                    fill3D(fillVertArray, "TRIANGLE_LIST", colorVertArray, texVertArray);
                }
            }
        }
        // If the user didn't specify a type (LINES, TRIANGLES, etc)
        else {
            // If only one vertex was specified, it must be a point
            if (vertArrayLength === 1) {
                for (j = 0; j < 3; j++) {
                    lineVertArray.push(vertArray[0][j]);
                }
                for (j = 9; j < 13; j++) {
                    strokeVertArray.push(vertArray[0][j]);
                }
                point3D(lineVertArray, strokeVertArray);
            } else {
                for (i = 0; i < vertArrayLength; i++) {
                    cachedVertArray = vertArray[i];
                    for (j = 0; j < 3; j++) {
                        lineVertArray.push(cachedVertArray[j]);
                    }
                    for (j = 5; j < 9; j++) {
                        strokeVertArray.push(cachedVertArray[j]);
                    }
                }
                if (doStroke && closeShape) {
                    line3D(lineVertArray, "LINE_LOOP", strokeVertArray);
                } else if (doStroke && !closeShape) {
                    line3D(lineVertArray, "LINE_STRIP", strokeVertArray);
                }

                // fill is ignored if textures are used
                if (doFill || usingTexture) {
                    fill3D(fillVertArray, "TRIANGLE_FAN", colorVertArray, texVertArray);
                }
            }
        }
        // everytime beginShape is followed by a call to
        // texture(), texturing it turned back on. We do this to
        // figure out if the shape should be textured or filled
        // with a color.
        usingTexture = false;
        curContext.useProgram(programObject3D);
        uniformi("usingTexture3d", programObject3D, "usingTexture", usingTexture);
    }

    // Reset some settings
    isCurve = false;
    isBezier = false;
    curveVertArray = [];
    curveVertCount = 0;
};

/**
 * Specifies vertex coordinates for Bezier curves. Each call to <b>bezierVertex()</b> defines the position of two control
 * points and one anchor point of a Bezier curve, adding a new segment to a line or shape. The first time
 * <b>bezierVertex()</b> is used within a <b>beginShape()</b> call, it must be prefaced with a call to <b>vertex()</b>
 * to set the first anchor point. This function must be used between <b>beginShape()</b> and <b>endShape()</b> and only
 * when there is no MODE parameter specified to <b>beginShape()</b>. Using the 3D version of requires rendering with P3D
 * or OPENGL (see the Environment reference for more information). <br /> <br /> <b>NOTE: </b> Fill does not work properly yet.
 *
 * @param {float | int} cx1 The x-coordinate of 1st control point
 * @param {float | int} cy1 The y-coordinate of 1st control point
 * @param {float | int} cz1 The z-coordinate of 1st control point
 * @param {float | int} cx2 The x-coordinate of 2nd control point
 * @param {float | int} cy2 The y-coordinate of 2nd control point
 * @param {float | int} cz2 The z-coordinate of 2nd control point
 * @param {float | int} x   The x-coordinate of the anchor point
 * @param {float | int} y   The y-coordinate of the anchor point
 * @param {float | int} z   The z-coordinate of the anchor point
 *
 * @see curveVertex
 * @see vertex
 * @see bezier
 */
bezierVertex = function () {
    isBezier = true;
    var vert = [];
    if (firstVert) {
        throw ("vertex() must be used at least once before calling bezierVertex()");
    }

    for (var i = 0; i < arguments.length; i++) {
        vert[i] = arguments[i];
    }
    vertArray.push(vert);
    vertArray[vertArray.length - 1]["isVert"] = false;
};

bezierVertex = function () {
    isBezier = true;
    var vert = [];
    if (firstVert) {
        throw ("vertex() must be used at least once before calling bezierVertex()");
    }

    if (arguments.length === 9) {
        if (bezierDrawMatrix === undef) {
            bezierDrawMatrix = new PMatrix3D();
        }
        // setup matrix for forward differencing to speed up drawing
        var lastPoint = vertArray.length - 1;
        splineForward(bezDetail, bezierDrawMatrix);
        bezierDrawMatrix.apply(bezierBasisMatrix);
        var draw = bezierDrawMatrix.array();
        var x1 = vertArray[lastPoint][0],
            y1 = vertArray[lastPoint][1],
            z1 = vertArray[lastPoint][2];
        var xplot1 = draw[4] * x1 + draw[5] * arguments[0] + draw[6] * arguments[3] + draw[7] * arguments[6];
        var xplot2 = draw[8] * x1 + draw[9] * arguments[0] + draw[10] * arguments[3] + draw[11] * arguments[6];
        var xplot3 = draw[12] * x1 + draw[13] * arguments[0] + draw[14] * arguments[3] + draw[15] * arguments[6];

        var yplot1 = draw[4] * y1 + draw[5] * arguments[1] + draw[6] * arguments[4] + draw[7] * arguments[7];
        var yplot2 = draw[8] * y1 + draw[9] * arguments[1] + draw[10] * arguments[4] + draw[11] * arguments[7];
        var yplot3 = draw[12] * y1 + draw[13] * arguments[1] + draw[14] * arguments[4] + draw[15] * arguments[7];

        var zplot1 = draw[4] * z1 + draw[5] * arguments[2] + draw[6] * arguments[5] + draw[7] * arguments[8];
        var zplot2 = draw[8] * z1 + draw[9] * arguments[2] + draw[10] * arguments[5] + draw[11] * arguments[8];
        var zplot3 = draw[12] * z1 + draw[13] * arguments[2] + draw[14] * arguments[5] + draw[15] * arguments[8];
        for (var j = 0; j < bezDetail; j++) {
            x1 += xplot1; xplot1 += xplot2; xplot2 += xplot3;
            y1 += yplot1; yplot1 += yplot2; yplot2 += yplot3;
            z1 += zplot1; zplot1 += zplot2; zplot2 += zplot3;
            vertex(x1, y1, z1);
        }
        vertex(arguments[6], arguments[7], arguments[8]);
    }
};

/**
 * Sets a texture to be applied to vertex points. The <b>texture()</b> function
 * must be called between <b>beginShape()</b> and <b>endShape()</b> and before
 * any calls to vertex().
 *
 * When textures are in use, the fill color is ignored. Instead, use tint() to
 * specify the color of the texture as it is applied to the shape.
 *
 * @param {PImage} pimage the texture to apply
 *
 * @returns none
 *
 * @see textureMode
 * @see beginShape
 * @see endShape
 * @see vertex
 */
texture = function (pimage) {
    var curContext = drawing.$ensureContext();

    if (pimage.__texture) {
        curContext.bindTexture(curContext.TEXTURE_2D, pimage.__texture);
    } else if (pimage.localName === "canvas") {
        curContext.bindTexture(curContext.TEXTURE_2D, canTex);
        curContext.texImage2D(curContext.TEXTURE_2D, 0, curContext.RGBA, curContext.RGBA, curContext.UNSIGNED_BYTE, pimage);
        curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_MAG_FILTER, curContext.LINEAR);
        curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_MIN_FILTER, curContext.LINEAR);
        curContext.generateMipmap(curContext.TEXTURE_2D);
        curTexture.width = pimage.width;
        curTexture.height = pimage.height;
    } else {
        var texture = curContext.createTexture(),
            cvs = document.createElement('canvas'),
            cvsTextureCtx = cvs.getContext('2d'),
            pot;

        // WebGL requires power of two textures
        if (pimage.width & (pimage.width - 1) === 0) {
            cvs.width = pimage.width;
        } else {
            pot = 1;
            while (pot < pimage.width) {
                pot *= 2;
            }
            cvs.width = pot;
        }

        if (pimage.height & (pimage.height - 1) === 0) {
            cvs.height = pimage.height;
        } else {
            pot = 1;
            while (pot < pimage.height) {
                pot *= 2;
            }
            cvs.height = pot;
        }

        cvsTextureCtx.drawImage(pimage.sourceImg, 0, 0, pimage.width, pimage.height, 0, 0, cvs.width, cvs.height);

        curContext.bindTexture(curContext.TEXTURE_2D, texture);
        curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_MIN_FILTER, curContext.LINEAR_MIPMAP_LINEAR);
        curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_MAG_FILTER, curContext.LINEAR);
        curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_WRAP_T, curContext.CLAMP_TO_EDGE);
        curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_WRAP_S, curContext.CLAMP_TO_EDGE);
        curContext.texImage2D(curContext.TEXTURE_2D, 0, curContext.RGBA, curContext.RGBA, curContext.UNSIGNED_BYTE, cvs);
        curContext.generateMipmap(curContext.TEXTURE_2D);

        pimage.__texture = texture;
        curTexture.width = pimage.width;
        curTexture.height = pimage.height;
    }

    usingTexture = true;
    curContext.useProgram(programObject3D);
    uniformi("usingTexture3d", programObject3D, "usingTexture", usingTexture);
};

/**
 * Sets the coordinate space for texture mapping. There are two options, IMAGE,
 * which refers to the actual coordinates of the image, and NORMALIZED, which
 * refers to a normalized space of values ranging from 0 to 1. The default mode
 * is IMAGE. In IMAGE, if an image is 100 x 200 pixels, mapping the image onto
 * the entire size of a quad would require the points (0,0) (0,100) (100,200) (0,200).
 * The same mapping in NORMAL_SPACE is (0,0) (0,1) (1,1) (0,1).
 *
 * @param MODE either IMAGE or NORMALIZED
 *
 * @returns none
 *
 * @see texture
 */
textureMode = function (mode) {
    curTextureMode = mode;
};

/**
 * Specifies vertex coordinates for curves. This function may only be used between <b>beginShape()</b> and
 * <b>endShape()</b> and only when there is no MODE parameter specified to <b>beginShape()</b>. The first and last points
 * 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
 * points is required to draw a tiny curve between the second and third points. Adding a fifth point with
 * <b>curveVertex()</b> will draw the curve between the second, third, and fourth points. The <b>curveVertex()</b> function
 * is an implementation of Catmull-Rom splines. Using the 3D version of requires rendering with P3D or OPENGL (see the
 * Environment reference for more information). <br /> <br /><b>NOTE: </b> Fill does not work properly yet.
 *
 * @param {float | int} x The x-coordinate of the vertex
 * @param {float | int} y The y-coordinate of the vertex
 * @param {float | int} z The z-coordinate of the vertex
 *
 * @see curve
 * @see beginShape
 * @see endShape
 * @see vertex
 * @see bezierVertex
 */
curveVertex = function (x, y) {
    isCurve = true;

    vertex(x, y);
};

curveVertex = function (x, y, z) {
    isCurve = true;

    if (!curveInited) {
        curveInit();
    }
    var vert = [];
    vert[0] = x;
    vert[1] = y;
    vert[2] = z;
    curveVertArray.push(vert);
    curveVertCount++;

    if (curveVertCount > 3) {
        curveVertexSegment(curveVertArray[curveVertCount - 4][0],
            curveVertArray[curveVertCount - 4][1],
            curveVertArray[curveVertCount - 4][2],
            curveVertArray[curveVertCount - 3][0],
            curveVertArray[curveVertCount - 3][1],
            curveVertArray[curveVertCount - 3][2],
            curveVertArray[curveVertCount - 2][0],
            curveVertArray[curveVertCount - 2][1],
            curveVertArray[curveVertCount - 2][2],
            curveVertArray[curveVertCount - 1][0],
            curveVertArray[curveVertCount - 1][1],
            curveVertArray[curveVertCount - 1][2]);
    }
};

/**
 * The curve() function draws a curved line on the screen. The first and second parameters
 * specify the beginning control point and the last two parameters specify
 * the ending control point. The middle parameters specify the start and
 * stop of the curve. Longer curves can be created by putting a series of
 * <b>curve()</b> functions together or using <b>curveVertex()</b>.
 * An additional function called <b>curveTightness()</b> provides control
 * for the visual quality of the curve. The <b>curve()</b> function is an
 * implementation of Catmull-Rom splines. Using the 3D version of requires
 * rendering with P3D or OPENGL (see the Environment reference for more
 * information).
 *
 * @param {int|float} x1 coordinates for the beginning control point
 * @param {int|float} y1 coordinates for the beginning control point
 * @param {int|float} z1 coordinates for the beginning control point
 * @param {int|float} x2 coordinates for the first point
 * @param {int|float} y2 coordinates for the first point
 * @param {int|float} z2 coordinates for the first point
 * @param {int|float} x3 coordinates for the second point
 * @param {int|float} y3 coordinates for the second point
 * @param {int|float} z3 coordinates for the second point
 * @param {int|float} x4 coordinates for the ending control point
 * @param {int|float} y4 coordinates for the ending control point
 * @param {int|float} z4 coordinates for the ending control point
 *
 * @see #curveVertex()
 * @see #curveTightness()
 * @see #bezier()
 */
curve = function () {
    if (arguments.length === 8) { // curve(x1, y1, x2, y2, x3, y3, x4, y4)
        beginShape();
        curveVertex(arguments[0], arguments[1]);
        curveVertex(arguments[2], arguments[3]);
        curveVertex(arguments[4], arguments[5]);
        curveVertex(arguments[6], arguments[7]);
        endShape();
    }
};

curve = function () {
    if (arguments.length === 12) { // curve( x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4);
        beginShape();
        curveVertex(arguments[0], arguments[1], arguments[2]);
        curveVertex(arguments[3], arguments[4], arguments[5]);
        curveVertex(arguments[6], arguments[7], arguments[8]);
        curveVertex(arguments[9], arguments[10], arguments[11]);
        endShape();
    }
};

/**
 * The curveTightness() function modifies the quality of forms created with <b>curve()</b> and
 * <b>curveVertex()</b>. The parameter <b>squishy</b> determines how the
 * curve fits to the vertex points. The value 0.0 is the default value for
 * <b>squishy</b> (this value defines the curves to be Catmull-Rom splines)
 * and the value 1.0 connects all the points with straight lines.
 * Values within the range -5.0 and 5.0 will deform the curves but
 * will leave them recognizable and as values increase in magnitude,
 * they will continue to deform.
 *
 * @param {float} tightness amount of deformation from the original vertices
 *
 * @see #curve()
 * @see #curveVertex()
 *
 */
curveTightness = function (tightness) {
    curTightness = tightness;
};

/**
 * The curveDetail() function sets the resolution at which curves display. The default value is 20.
 * This function is only useful when using the P3D or OPENGL renderer.
 *
 * @param {int} detail resolution of the curves
 *
 * @see curve()
 * @see curveVertex()
 * @see curveTightness()
 */
curveDetail = function (detail) {
    curveDet = detail;
    curveInit();
};

/**
 * Modifies the location from which rectangles draw. The default mode is rectMode(CORNER), which
 * specifies the location to be the upper left corner of the shape and uses the third and fourth
 * parameters of rect() to specify the width and height. The syntax rectMode(CORNERS) uses the
 * first and second parameters of rect() to set the location of one corner and uses the third and
 * fourth parameters to set the opposite corner. The syntax rectMode(CENTER) draws the image from
 * its center point and uses the third and forth parameters of rect() to specify the image's width
 * and height. The syntax rectMode(RADIUS) draws the image from its center point and uses the third
 * and forth parameters of rect()  to specify half of the image's width and height. The parameter must
 * be written in ALL CAPS because Processing is a case sensitive language. Note: In version 125, the
 * mode named CENTER_RADIUS was shortened to RADIUS.
 *
 * @param {MODE} MODE      Either CORNER, CORNERS, CENTER, or RADIUS
 *
 * @see rect
 */
rectMode = function (aRectMode) {
    curRectMode = aRectMode;
};

/**
 * Modifies the location from which images draw. The default mode is imageMode(CORNER), which specifies
 * the location to be the upper left corner and uses the fourth and fifth parameters of image() to set
 * the image's width and height. The syntax imageMode(CORNERS) uses the second and third parameters of
 * image() to set the location of one corner of the image and uses the fourth and fifth parameters to
 * set the opposite corner. Use imageMode(CENTER) to draw images centered at the given x and y position.
 * The parameter to imageMode() must be written in ALL CAPS because Processing is a case sensitive language.
 *
 * @param {MODE} MODE      Either CORNER, CORNERS, or CENTER
 *
 * @see loadImage
 * @see PImage
 * @see image
 * @see background
 */
imageMode = function (mode) {
    switch (mode) {
        case PConstants.CORNER:
            imageModeConvert = imageModeCorner;
            break;
        case PConstants.CORNERS:
            imageModeConvert = imageModeCorners;
            break;
        case PConstants.CENTER:
            imageModeConvert = imageModeCenter;
            break;
        default:
            throw "Invalid imageMode";
    }
};

/**
 * The origin of the ellipse is modified by the ellipseMode() function. The default configuration is
 * ellipseMode(CENTER), which specifies the location of the ellipse as the center of the shape. The RADIUS
 * mode is the same, but the width and height parameters to ellipse()  specify the radius of the ellipse,
 * rather than the diameter. The CORNER mode draws the shape from the upper-left corner of its bounding box.
 * The CORNERS mode uses the four parameters to ellipse() to set two opposing corners of the ellipse's bounding
 * box. The parameter must be written in "ALL CAPS" because Processing is a case sensitive language.
 *
 * @param {MODE} MODE      Either CENTER, RADIUS, CORNER, or CORNERS.
 *
 * @see ellipse
 */
ellipseMode = function (aEllipseMode) {
    curEllipseMode = aEllipseMode;
};

/**
 * The arc() function draws an arc in the display window.
 * Arcs are drawn along the outer edge of an ellipse defined by the
 * <b>x</b>, <b>y</b>, <b>width</b> and <b>height</b> parameters.
 * The origin or the arc's ellipse may be changed with the
 * <b>ellipseMode()</b> function.
 * The <b>start</b> and <b>stop</b> parameters specify the angles
 * at which to draw the arc.
 *
 * @param {float} a       x-coordinate of the arc's ellipse
 * @param {float} b       y-coordinate of the arc's ellipse
 * @param {float} c       width of the arc's ellipse
 * @param {float} d       height of the arc's ellipse
 * @param {float} start   angle to start the arc, specified in radians
 * @param {float} stop    angle to stop the arc, specified in radians
 *
 * @see #ellipseMode()
 * @see #ellipse()
 */
arc = function (x, y, width, height, start, stop) {
    if (width <= 0 || stop < start) { return; }

    // XXX(jeresig)
    start = convertToRadians(start);
    stop = convertToRadians(stop);

    if (curEllipseMode === PConstants.CORNERS) {
        width = width - x;
        height = height - y;
    } else if (curEllipseMode === PConstants.RADIUS) {
        x = x - width;
        y = y - height;
        width = width * 2;
        height = height * 2;
    } else if (curEllipseMode === PConstants.CENTER) {
        x = x - width / 2;
        y = y - height / 2;
    }
    // make sure that we're starting at a useful point
    while (start < 0) {
        start += PConstants.TWO_PI;
        stop += PConstants.TWO_PI;
    }
    if (stop - start > PConstants.TWO_PI) {
        start = 0;
        stop = PConstants.TWO_PI;
    }
    var hr = width / 2;
    var vr = height / 2;
    var centerX = x + hr;
    var centerY = y + vr;
    // XXX(jeresig): Removed * 2 from these lines
    // seems to have been a mistake.
    var startLUT = 0 | (-0.5 + start * RAD_TO_DEG);
    var stopLUT = 0 | (0.5 + stop * RAD_TO_DEG);
    var i, j;
    if (doFill) {
        // shut off stroke for a minute
        var savedStroke = doStroke;
        doStroke = false;
        beginShape();
        vertex(centerX, centerY);
        for (i = startLUT; i <= stopLUT; i++) {
            j = i % PConstants.SINCOS_LENGTH;
            vertex(centerX + cosLUT[j] * hr, centerY + sinLUT[j] * vr);
        }
        endShape(PConstants.CLOSE);
        doStroke = savedStroke;
    }

    if (doStroke) {
        // and doesn't include the first (center) vertex.
        var savedFill = doFill;
        doFill = false;
        beginShape();
        for (i = startLUT; i <= stopLUT; i++) {
            j = i % PConstants.SINCOS_LENGTH;
            vertex(centerX + cosLUT[j] * hr, centerY + sinLUT[j] * vr);
        }
        endShape();
        doFill = savedFill;
    }
};

/**
 * Draws a line (a direct path between two points) to the screen. The version of line() with four parameters
 * draws the line in 2D. To color a line, use the stroke() function. A line cannot be filled, therefore the
 * fill()  method will not affect the color of a line. 2D lines are drawn with a width of one pixel by default,
 * but this can be changed with the strokeWeight()  function. The version with six parameters allows the line
 * to be placed anywhere within XYZ space. Drawing this shape in 3D using the z parameter requires the P3D or
 * OPENGL parameter in combination with size.
 *
 * @param {int|float} x1       x-coordinate of the first point
 * @param {int|float} y1       y-coordinate of the first point
 * @param {int|float} z1       z-coordinate of the first point
 * @param {int|float} x2       x-coordinate of the second point
 * @param {int|float} y2       y-coordinate of the second point
 * @param {int|float} z2       z-coordinate of the second point
 *
 * @see strokeWeight
 * @see strokeJoin
 * @see strokeCap
 * @see beginShape
 */
line = function (x1, y1, x2, y2) {
    if (!doStroke) {
        return;
    }
    if (!renderSmooth) {
        x1 = Math.round(x1);
        x2 = Math.round(x2);
        y1 = Math.round(y1);
        y2 = Math.round(y2);
    }
    // A line is only defined if it has different start and end coordinates.
    // If they are the same, we call point instead.
    if (x1 === x2 && y1 === y2) {
        point(x1, y1);
        return;
    }

    var swap = undef,
        lineCap = undef,
        drawCrisp = true,
        currentModelView = modelView.array(),
        identityMatrix = [1, 0, 0, 0, 1, 0];
    // Test if any transformations have been applied to the sketch
    for (var i = 0; i < 6 && drawCrisp; i++) {
        drawCrisp = currentModelView[i] === identityMatrix[i];
    }
    /** Draw crisp lines if the line is vertical or horizontal with the following method
     * If any transformations have been applied to the sketch, don't make the line crisp
     * If the line is directed up or to the left, reverse it by swapping x1/x2 or y1/y2
     * Make the line 1 pixel longer to work around cross-platform canvas implementations
     * If the lineWidth is odd, translate the line by 0.5 in the perpendicular direction
     * Even lineWidths do not need to be translated because the canvas will draw them on pixel boundaries
     * Change the cap to butt-end to work around cross-platform canvas implementations
     * Reverse the translate and lineCap canvas state changes after drawing the line
     */
    if (drawCrisp) {
        if (x1 === x2) {
            if (y1 > y2) {
                swap = y1;
                y1 = y2;
                y2 = swap;
            }
            y2++;
            if (lineWidth % 2 === 1) {
                curContext.translate(0.5, 0.0);
            }
        } else if (y1 === y2) {
            if (x1 > x2) {
                swap = x1;
                x1 = x2;
                x2 = swap;
            }
            x2++;
            if (lineWidth % 2 === 1) {
                curContext.translate(0.0, 0.5);
            }
        }
        if (lineWidth === 1) {
            lineCap = curContext.lineCap;
            curContext.lineCap = 'butt';
        }
    }
    curContext.beginPath();
    curContext.moveTo(x1 || 0, y1 || 0);
    curContext.lineTo(x2 || 0, y2 || 0);
    executeContextStroke();
    if (drawCrisp) {
        if (x1 === x2 && lineWidth % 2 === 1) {
            curContext.translate(-0.5, 0.0);
        } else if (y1 === y2 && lineWidth % 2 === 1) {
            curContext.translate(0.0, -0.5);
        }
        if (lineWidth === 1) {
            curContext.lineCap = lineCap;
        }
    }
};

line = function (x1, y1, z1, x2, y2, z2) {
    if (y2 === undef || z2 === undef) { // 2D line called in 3D context
        z2 = 0;
        y2 = x2;
        x2 = z1;
        z1 = 0;
    }

    // a line is only defined if it has different start and end coordinates.
    // If they are the same, we call point instead.
    if (x1 === x2 && y1 === y2 && z1 === z2) {
        point(x1, y1, z1);
        return;
    }

    var lineVerts = [x1, y1, z1, x2, y2, z2];

    var view = new PMatrix3D();
    view.scale(1, -1, 1);
    view.apply(modelView.array());
    view.transpose();

    if (lineWidth > 0 && doStroke) {
        curContext.useProgram(programObject2D);

        uniformMatrix("model2d", programObject2D, "model", false, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
        uniformMatrix("view2d", programObject2D, "view", false, view.array());

        uniformf("color2d", programObject2D, "color", strokeStyle);
        uniformi("picktype2d", programObject2D, "picktype", 0);

        vertexAttribPointer("vertex2d", programObject2D, "Vertex", 3, lineBuffer);
        disableVertexAttribPointer("aTextureCoord2d", programObject2D, "aTextureCoord");

        curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array(lineVerts), curContext.STREAM_DRAW);
        curContext.drawArrays(curContext.LINES, 0, 2);
    }
};

/**
 * Draws a Bezier curve on the screen. These curves are defined by a series of anchor and control points. The first
 * two parameters specify the first anchor point and the last two parameters specify the other anchor point. The
 * middle parameters specify the control points which define the shape of the curve. Bezier curves were developed
 * by French engineer Pierre Bezier. Using the 3D version of requires rendering with P3D or OPENGL (see the
 * Environment reference for more information).
 *
 * @param {int | float} x1,y1,z1    coordinates for the first anchor point
 * @param {int | float} cx1,cy1,cz1 coordinates for the first control point
 * @param {int | float} cx2,cy2,cz2 coordinates for the second control point
 * @param {int | float} x2,y2,z2    coordinates for the second anchor point
 *
 * @see bezierVertex
 * @see curve
 */
bezier = function () {
    if (arguments.length !== 8) {
        throw ("You must use 8 parameters for bezier() in 2D mode");
    }

    beginShape();
    vertex(arguments[0], arguments[1]);
    bezierVertex(arguments[2], arguments[3],
        arguments[4], arguments[5],
        arguments[6], arguments[7]);
    endShape();
};

bezier = function () {
    if (arguments.length !== 12) {
        throw ("You must use 12 parameters for bezier() in 3D mode");
    }

    beginShape();
    vertex(arguments[0], arguments[1], arguments[2]);
    bezierVertex(arguments[3], arguments[4], arguments[5],
        arguments[6], arguments[7], arguments[8],
        arguments[9], arguments[10], arguments[11]);
    endShape();
};

/**
 * Sets the resolution at which Beziers display. The default value is 20. This function is only useful when using the P3D
 * or OPENGL renderer as the default (JAVA2D) renderer does not use this information.
 *
 * @param {int} detail resolution of the curves
 *
 * @see curve
 * @see curveVertex
 * @see curveTightness
 */
bezierDetail = function (detail) {
    bezDetail = detail;
};

/**
 * The bezierPoint() function evalutes quadratic bezier at point t for points a, b, c, d.
 * The parameter t varies between 0 and 1. The a and d parameters are the
 * on-curve points, b and c are the control points. To make a two-dimensional
 * curve, call this function once with the x coordinates and a second time
 * with the y coordinates to get the location of a bezier curve at t.
 *
 * @param {float} a   coordinate of first point on the curve
 * @param {float} b   coordinate of first control point
 * @param {float} c   coordinate of second control point
 * @param {float} d   coordinate of second point on the curve
 * @param {float} t   value between 0 and 1
 *
 * @see #bezier()
 * @see #bezierVertex()
 * @see #curvePoint()
 */
bezierPoint = function (a, b, c, d, t) {
    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;
};

/**
 * The bezierTangent() function calculates the tangent of a point on a Bezier curve. 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>
 *
 * @param {float} a   coordinate of first point on the curve
 * @param {float} b   coordinate of first control point
 * @param {float} c   coordinate of second control point
 * @param {float} d   coordinate of second point on the curve
 * @param {float} t   value between 0 and 1
 *
 * @see #bezier()
 * @see #bezierVertex()
 * @see #curvePoint()
 */
bezierTangent = function (a, b, c, d, t) {
    return (3 * t * t * (-a + 3 * b - 3 * c + d) + 6 * t * (a - 2 * b + c) + 3 * (-a + b));
};

/**
 * The curvePoint() function evalutes the Catmull-Rom curve at point t for points a, b, c, d. The
 * parameter t varies between 0 and 1, a and d are points on the curve,
 * and b and c are the control points. This can be done once with the x
 * coordinates and a second time with the y coordinates to get the
 * location of a curve at t.
 *
 * @param {int|float} a   coordinate of first point on the curve
 * @param {int|float} b   coordinate of second point on the curve
 * @param {int|float} c   coordinate of third point on the curve
 * @param {int|float} d   coordinate of fourth point on the curve
 * @param {float} t       value between 0 and 1
 *
 * @see #curve()
 * @see #curveVertex()
 * @see #bezierPoint()
 */
curvePoint = function (a, b, c, d, t) {
    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);
};

/**
 * The curveTangent() function calculates the tangent of a point on a Catmull-Rom curve.
 * 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>.
 *
 * @param {int|float} a   coordinate of first point on the curve
 * @param {int|float} b   coordinate of first control point
 * @param {int|float} c   coordinate of second control point
 * @param {int|float} d   coordinate of second point on the curve
 * @param {float} t       value between 0 and 1
 *
 * @see #curve()
 * @see #curveVertex()
 * @see #curvePoint()
 * @see #bezierTangent()
 */
curveTangent = function (a, b, c, d, t) {
    return 0.5 * ((-a + c) + 2 * (2 * a - 5 * b + 4 * c - d) * t + 3 * (-a + 3 * b - 3 * c + d) * t * t);
};

/**
 * A triangle is a plane created by connecting three points. The first two arguments specify the first point,
 * the middle two arguments specify the second point, and the last two arguments specify the third point.
 *
 * @param {int | float} x1 x-coordinate of the first point
 * @param {int | float} y1 y-coordinate of the first point
 * @param {int | float} x2 x-coordinate of the second point
 * @param {int | float} y2 y-coordinate of the second point
 * @param {int | float} x3 x-coordinate of the third point
 * @param {int | float} y3 y-coordinate of the third point
 */
triangle = function (x1, y1, x2, y2, x3, y3) {
    beginShape(PConstants.TRIANGLES);
    vertex(x1, y1, 0);
    vertex(x2, y2, 0);
    vertex(x3, y3, 0);
    endShape();
};

/**
 * A quad is a quadrilateral, a four sided polygon. It is similar to a rectangle, but the angles between its
 * edges are not constrained to ninety degrees. The first pair of parameters (x1,y1) sets the first vertex
 * and the subsequent pairs should proceed clockwise or counter-clockwise around the defined shape.
 *
 * @param {float | int} x1 x-coordinate of the first corner
 * @param {float | int} y1 y-coordinate of the first corner
 * @param {float | int} x2 x-coordinate of the second corner
 * @param {float | int} y2 y-coordinate of the second corner
 * @param {float | int} x3 x-coordinate of the third corner
 * @param {float | int} y3 y-coordinate of the third corner
 * @param {float | int} x4 x-coordinate of the fourth corner
 * @param {float | int} y4 y-coordinate of the fourth corner
 */
quad = function (x1, y1, x2, y2, x3, y3, x4, y4) {
    beginShape(PConstants.QUADS);
    vertex(x1, y1, 0);
    vertex(x2, y2, 0);
    vertex(x3, y3, 0);
    vertex(x4, y4, 0);
    endShape();
};

/**
 * Draws a rectangle to the screen. A rectangle is a four-sided shape with every angle at ninety
 * degrees. The first two parameters set the location, the third sets the width, and the fourth
 * sets the height. The origin is changed with the rectMode() function.
 *
 * @param {int|float} x        x-coordinate of the rectangle
 * @param {int|float} y        y-coordinate of the rectangle
 * @param {int|float} width    width of the rectangle
 * @param {int|float} height   height of the rectangle
 *
 * @see rectMode
 * @see quad
 */
rect = function (x, y, width, height, tl, tr, br, bl) {
    if (!width && !height) {
        return;
    }

    if (curRectMode === PConstants.CORNERS) {
        width -= x;
        height -= y;
    } else if (curRectMode === PConstants.RADIUS) {
        width *= 2;
        height *= 2;
        x -= width / 2;
        y -= height / 2;
    } else if (curRectMode === PConstants.CENTER) {
        x -= width / 2;
        y -= height / 2;
    }

    if (!renderSmooth) {
        x = Math.round(x);
        y = Math.round(y);
        width = Math.round(width);
        height = Math.round(height);
    }
    if (tl !== undef) {
        roundedRect$2d(x, y, width, height, tl, tr, br, bl);
        return;
    }

    // Translate the line by (0.5, 0.5) to draw a crisp rectangle border
    if (doStroke && lineWidth % 2 === 1) {
        curContext.translate(0.5, 0.5);
    }
    curContext.beginPath();
    curContext.rect(x, y, width, height);
    executeContextFill();
    executeContextStroke();
    if (doStroke && lineWidth % 2 === 1) {
        curContext.translate(-0.5, -0.5);
    }
};

rect = function (x, y, width, height, tl, tr, br, bl) {
    if (tl !== undef) {
        throw "rect() with rounded corners is not supported in 3D mode";
    }

    if (curRectMode === PConstants.CORNERS) {
        width -= x;
        height -= y;
    } else if (curRectMode === PConstants.RADIUS) {
        width *= 2;
        height *= 2;
        x -= width / 2;
        y -= height / 2;
    } else if (curRectMode === PConstants.CENTER) {
        x -= width / 2;
        y -= height / 2;
    }

    // Modeling transformation
    var model = new PMatrix3D();
    model.translate(x, y, 0);
    model.scale(width, height, 1);
    model.transpose();

    // viewing transformation needs to have Y flipped
    // becuase that's what Processing does.
    var view = new PMatrix3D();
    view.scale(1, -1, 1);
    view.apply(modelView.array());
    view.transpose();

    if (lineWidth > 0 && doStroke) {
        curContext.useProgram(programObject2D);
        uniformMatrix("model2d", programObject2D, "model", false, model.array());
        uniformMatrix("view2d", programObject2D, "view", false, view.array());
        uniformf("color2d", programObject2D, "color", strokeStyle);
        uniformi("picktype2d", programObject2D, "picktype", 0);
        vertexAttribPointer("vertex2d", programObject2D, "Vertex", 3, rectBuffer);
        disableVertexAttribPointer("aTextureCoord2d", programObject2D, "aTextureCoord");
        curContext.drawArrays(curContext.LINE_LOOP, 0, rectVerts.length / 3);
    }

    if (doFill) {
        curContext.useProgram(programObject3D);
        uniformMatrix("model3d", programObject3D, "model", false, model.array());
        uniformMatrix("view3d", programObject3D, "view", false, view.array());

        // fix stitching problems. (lines get occluded by triangles
        // since they share the same depth values). This is not entirely
        // working, but it's a start for drawing the outline. So
        // developers can start playing around with styles.
        curContext.enable(curContext.POLYGON_OFFSET_FILL);
        curContext.polygonOffset(1, 1);

        uniformf("color3d", programObject3D, "color", fillStyle);

        if (lightCount > 0) {
            var v = new PMatrix3D();
            v.set(view);

            var m = new PMatrix3D();
            m.set(model);

            v.mult(m);

            var normalMatrix = new PMatrix3D();
            normalMatrix.set(v);
            normalMatrix.invert();
            normalMatrix.transpose();

            uniformMatrix("normalTransform3d", programObject3D, "normalTransform", false, normalMatrix.array());
            vertexAttribPointer("normal3d", programObject3D, "Normal", 3, rectNormBuffer);
        }
        else {
            disableVertexAttribPointer("normal3d", programObject3D, "Normal");
        }

        vertexAttribPointer("vertex3d", programObject3D, "Vertex", 3, rectBuffer);

        curContext.drawArrays(curContext.TRIANGLE_FAN, 0, rectVerts.length / 3);
        curContext.disable(curContext.POLYGON_OFFSET_FILL);
    }
};

/**
 * Draws an ellipse (oval) in the display window. An ellipse with an equal <b>width</b> and <b>height</b> is a circle.
 * The first two parameters set the location, the third sets the width, and the fourth sets the height. The origin may be
 * changed with the <b>ellipseMode()</b> function.
 *
 * @param {float|int} x      x-coordinate of the ellipse
 * @param {float|int} y      y-coordinate of the ellipse
 * @param {float|int} width  width of the ellipse
 * @param {float|int} height height of the ellipse
 *
 * @see ellipseMode
 */
ellipse = function (x, y, width, height) {
    x = x || 0;
    y = y || 0;

    if (width <= 0 && height <= 0) {
        return;
    }

    if (curEllipseMode === PConstants.RADIUS) {
        width *= 2;
        height *= 2;
    } else if (curEllipseMode === PConstants.CORNERS) {
        width = width - x;
        height = height - y;
        x += width / 2;
        y += height / 2;
    } else if (curEllipseMode === PConstants.CORNER) {
        x += width / 2;
        y += height / 2;
    }

    // Shortcut for drawing a 2D circle
    if (width === height) {
        curContext.beginPath();
        curContext.arc(x, y, width / 2, 0, PConstants.TWO_PI, false);
        executeContextFill();
        executeContextStroke();
    } else {
        var w = width / 2,
            h = height / 2,
            C = 0.5522847498307933,
            c_x = C * w,
            c_y = C * h;

        beginShape();
        vertex(x + w, y);
        bezierVertex(x + w, y - c_y, x + c_x, y - h, x, y - h);
        bezierVertex(x - c_x, y - h, x - w, y - c_y, x - w, y);
        bezierVertex(x - w, y + c_y, x - c_x, y + h, x, y + h);
        bezierVertex(x + c_x, y + h, x + w, y + c_y, x + w, y);
        endShape();
    }
};

ellipse = function (x, y, width, height) {
    x = x || 0;
    y = y || 0;

    if (width <= 0 && height <= 0) {
        return;
    }

    if (curEllipseMode === PConstants.RADIUS) {
        width *= 2;
        height *= 2;
    } else if (curEllipseMode === PConstants.CORNERS) {
        width = width - x;
        height = height - y;
        x += width / 2;
        y += height / 2;
    } else if (curEllipseMode === PConstants.CORNER) {
        x += width / 2;
        y += height / 2;
    }

    var w = width / 2,
        h = height / 2,
        C = 0.5522847498307933,
        c_x = C * w,
        c_y = C * h;

    beginShape();
    vertex(x + w, y);
    bezierVertex(x + w, y - c_y, 0, x + c_x, y - h, 0, x, y - h, 0);
    bezierVertex(x - c_x, y - h, 0, x - w, y - c_y, 0, x - w, y, 0);
    bezierVertex(x - w, y + c_y, 0, x - c_x, y + h, 0, x, y + h, 0);
    bezierVertex(x + c_x, y + h, 0, x + w, y + c_y, 0, x + w, y, 0);
    endShape();

    if (doFill) {
        //temporary workaround to not working fills for bezier -- will fix later
        var xAv = 0, yAv = 0, i, j;
        for (i = 0; i < vertArray.length; i++) {
            xAv += vertArray[i][0];
            yAv += vertArray[i][1];
        }
        xAv /= vertArray.length;
        yAv /= vertArray.length;
        var vert = [],
            fillVertArray = [],
            colorVertArray = [];
        vert[0] = xAv;
        vert[1] = yAv;
        vert[2] = 0;
        vert[3] = 0;
        vert[4] = 0;
        vert[5] = fillStyle[0];
        vert[6] = fillStyle[1];
        vert[7] = fillStyle[2];
        vert[8] = fillStyle[3];
        vert[9] = strokeStyle[0];
        vert[10] = strokeStyle[1];
        vert[11] = strokeStyle[2];
        vert[12] = strokeStyle[3];
        vert[13] = normalX;
        vert[14] = normalY;
        vert[15] = normalZ;
        vertArray.unshift(vert);
        for (i = 0; i < vertArray.length; i++) {
            for (j = 0; j < 3; j++) {
                fillVertArray.push(vertArray[i][j]);
            }
            for (j = 5; j < 9; j++) {
                colorVertArray.push(vertArray[i][j]);
            }
        }
        fill3D(fillVertArray, "TRIANGLE_FAN", colorVertArray);
    }
};

/**
 * Sets the current normal vector. This is for drawing three dimensional shapes and surfaces and
 * specifies a vector perpendicular to the surface of the shape which determines how lighting affects
 * it. Processing attempts to automatically assign normals to shapes, but since that's imperfect,
 * this is a better option when you want more control. This function is identical to glNormal3f() in OpenGL.
 *
 * @param {float} nx       x direction
 * @param {float} ny       y direction
 * @param {float} nz       z direction
 *
 * @see beginShape
 * @see endShape
 * @see lights
 */
normal = function (nx, ny, nz) {
    if (arguments.length !== 3 || !(typeof nx === "number" && typeof ny === "number" && typeof nz === "number")) {
        throw "normal() requires three numeric arguments.";
    }

    normalX = nx;
    normalY = ny;
    normalZ = nz;

    if (curShape !== 0) {
        if (normalMode === PConstants.NORMAL_MODE_AUTO) {
            normalMode = PConstants.NORMAL_MODE_SHAPE;
        } else if (normalMode === PConstants.NORMAL_MODE_SHAPE) {
            normalMode = PConstants.NORMAL_MODE_VERTEX;
        }
    }
};

////////////////////////////////////////////////////////////////////////////
// Raster drawing functions
////////////////////////////////////////////////////////////////////////////

/**
 * Saves an image from the display window. Images are saved in TIFF, TARGA, JPEG, and PNG format
 * depending on the extension within the filename  parameter. For example, "image.tif" will have
 * a TIFF image and "image.png" will save a PNG image. If no extension is included in the filename,
 * the image will save in TIFF format and .tif will be added to the name. These files are saved to
 * the sketch's folder, which may be opened by selecting "Show sketch folder" from the "Sketch" menu.
 * It is not possible to use save() while running the program in a web browser.  All images saved
 * from the main drawing window will be opaque. To save images without a background, use createGraphics().
 *
 * @param {String} filename      any sequence of letters and numbers
 *
 * @see saveFrame
 * @see createGraphics
 */
save = function (file, img) {
    // file is unused at the moment
    // may implement this differently in later release
    if (img !== undef) {
        return window.open(img.toDataURL(), "_blank");
    }
    return window.open(externals.canvas.toDataURL(), "_blank");
};

var saveNumber = 0;

/**
 * @param {*} title
 */
saveFrame = function (file) {
    if (file === undef) {
        // use default name template if parameter is not specified
        file = "screen-####.png";
    }
    // Increment changeable part: screen-0000.png, screen-0001.png, ...
    var frameFilename = file.replace(/#+/, function (all) {
        var s = "" + (saveNumber++);
        while (s.length < all.length) {
            s = "0" + s;
        }
        return s;
    });
    save(frameFilename);
};

/**
 * Datatype for storing images. Processing can display .gif, .jpg, .tga, and .png images. Images may be
 * displayed in 2D and 3D space. Before an image is used, it must be loaded with the loadImage() function.
 * The PImage object contains fields for the width and height of the image, as well as an array called
 * pixels[]  which contains the values for every pixel in the image. A group of methods, described below,
 * allow easy access to the image's pixels and alpha channel and simplify the process of compositing.
 * Before using the pixels[] array, be sure to use the loadPixels() method on the image to make sure that the
 * pixel data is properly loaded. To create a new image, use the createImage() function (do not use new PImage()).
 *
 * @param {int} width                image width
 * @param {int} height               image height
 * @param {MODE} format              Either RGB, ARGB, ALPHA (grayscale alpha channel)
 *
 * @returns {PImage}
 *
 * @see loadImage
 * @see imageMode
 * @see createImage
 */
var PImage = function (aWidth, aHeight, aFormat) {

    // Keep track of whether or not the cached imageData has been touched.
    this.__isDirty = false;

    if (aWidth instanceof HTMLImageElement) {
        // convert an <img> to a PImage
        this.fromHTMLImageData(aWidth);
    } else if (aHeight || aFormat) {
        this.width = aWidth || 1;
        this.height = aHeight || 1;

        // Stuff a canvas into sourceImg so image() calls can use drawImage like an <img>
        var canvas = this.sourceImg = document.createElement("canvas");
        canvas.width = this.width;
        canvas.height = this.height;

        //XXX(jeresig): Commenting out imageData stuff
        //var imageData = this.imageData = canvas.getContext('2d').createImageData(this.width, this.height);
        this.format = (aFormat === PConstants.ARGB || aFormat === PConstants.ALPHA) ? aFormat : PConstants.RGB;
        //if (this.format === PConstants.RGB) {
        // Set the alpha channel of an RGB image to opaque.
        //for (var i = 3, data = this.imageData.data, len = data.length; i < len; i += 4) {
        //data[i] = 255;
        //}
        //}

        //this.__isDirty = true;
        //this.updatePixels();
    } else {
        this.width = 0;
        this.height = 0;
        //XXX(jeresig): Commenting out imageData stuff
        //this.imageData = utilityContext2d.createImageData(1, 1);
        this.format = PConstants.ARGB;
    }

    //XXX(jeresig): Commenting out imageData stuff
    //this.pixels = buildPixelsObject(this);
};
PImage.prototype = {

    /**
     * Temporary hack to deal with cross-Processing-instance created PImage.  See
     * tickets #1623 and #1644.
     */
    __isPImage: true,

    /**
    * Updates the image with the data in its pixels[] array. Use in conjunction with loadPixels(). If
    * you're only reading pixels from the array, there's no need to call updatePixels().
    * Certain renderers may or may not seem to require loadPixels() or updatePixels(). However, the rule
    * is that any time you want to manipulate the pixels[] array, you must first call loadPixels(), and
    * after changes have been made, call updatePixels(). Even if the renderer may not seem to use this
    * function in the current Processing release, this will always be subject to change.
    * Currently, none of the renderers use the additional parameters to updatePixels().
    * 
    * @method PImage
    */
    updatePixels: function () {
        var canvas = this.sourceImg;
        if (canvas && canvas instanceof HTMLCanvasElement && this.__isDirty) {
            canvas.getContext('2d').putImageData(this.imageData, 0, 0);
        }
        this.__isDirty = false;
    },

    fromHTMLImageData: function (htmlImg) {
        // convert an <img> to a PImage
        var canvasData = getCanvasData(htmlImg);
        //XXX(jeresig): Commenting out imageData stuff
        //try {
        //var imageData = canvasData.context.getImageData(0, 0, htmlImg.width, htmlImg.height);
        //this.fromImageData(imageData);
        //} catch(e) {
        if (htmlImg.width && htmlImg.height) {
            this.isRemote = true;
            this.width = htmlImg.width;
            this.height = htmlImg.height;
        }
        //}
        this.sourceImg = htmlImg;
    },

    'get': function (x, y, w, h) {
        if (!arguments.length) {
            return get(this);
        }
        if (arguments.length === 2) {
            return get(x, y, this);
        }
        if (arguments.length === 4) {
            return get(x, y, w, h, this);
        }
    },

    /**
    * Changes the color of any pixel or writes an image directly into the image. The x and y parameter
    * specify the pixel or the upper-left corner of the image. The color parameter specifies the color value.
    * Setting the color of a single pixel with set(x, y) is easy, but not as fast as putting the data
    * directly into pixels[]. The equivalent statement to "set(x, y, #000000)" using pixels[] is
    * "pixels[y*width+x] = #000000". Processing requires calling loadPixels() to load the display window
    * data into the pixels[] array before getting the values and calling updatePixels() to update the window.
    *
    * @param {int} x        x-coordinate of the pixel or upper-left corner of the image
    * @param {int} y        y-coordinate of the pixel or upper-left corner of the image
    * @param {color} color  any value of the color datatype
    *
    * @see get
    * @see pixels[]
    * @see copy
    * 
    * @method PImage
    */
    'set': function (x, y, c) {
        set(x, y, c, this);
        this.__isDirty = true;
    },

    /**
    * Blends a region of pixels into the image specified by the img parameter. These copies utilize full
    * alpha channel support and a choice of the following modes to blend the colors of source pixels (A)
    * with the ones of pixels in the destination image (B):
    * BLEND - linear interpolation of colours: C = A*factor + B
    * ADD - additive blending with white clip: C = min(A*factor + B, 255)
    * SUBTRACT - subtractive blending with black clip: C = max(B - A*factor, 0)
    * DARKEST - only the darkest colour succeeds: C = min(A*factor, B)
    * LIGHTEST - only the lightest colour succeeds: C = max(A*factor, B)
    * DIFFERENCE - subtract colors from underlying image.
    * EXCLUSION - similar to DIFFERENCE, but less extreme.
    * MULTIPLY - Multiply the colors, result will always be darker.
    * SCREEN - Opposite multiply, uses inverse values of the colors.
    * OVERLAY - A mix of MULTIPLY and SCREEN. Multiplies dark values, and screens light values.
    * HARD_LIGHT - SCREEN when greater than 50% gray, MULTIPLY when lower.
    * SOFT_LIGHT - Mix of DARKEST and LIGHTEST. Works like OVERLAY, but not as harsh.
    * DODGE - Lightens light tones and increases contrast, ignores darks. Called "Color Dodge" in Illustrator and Photosho
    * BURN - Darker areas are applied, increasing contrast, ignores lights. Called "Color Burn" in Illustrator and Photosho
    * All modes use the alpha information (highest byte) of source image pixels as the blending factor.
    * If the source and destination regions are different sizes, the image will be automatically resized to
    * match the destination size. If the srcImg parameter is not used, the display window is used as the source image.
    * This function ignores imageMode().
    *
    * @param {int} x              X coordinate of the source's upper left corner
    * @param {int} y              Y coordinate of the source's upper left corner
    * @param {int} width          source image width
    * @param {int} height         source image height
    * @param {int} dx             X coordinate of the destinations's upper left corner
    * @param {int} dy             Y coordinate of the destinations's upper left corner
    * @param {int} dwidth         destination image width
    * @param {int} dheight        destination image height
    * @param {PImage} srcImg      an image variable referring to the source image
    * @param {MODE} MODE          Either BLEND, ADD, SUBTRACT, LIGHTEST, DARKEST, DIFFERENCE, EXCLUSION,
    * MULTIPLY, SCREEN, OVERLAY, HARD_LIGHT, SOFT_LIGHT, DODGE, BURN
    *
    * @see alpha
    * @see copy
    * 
    * @method PImage
    */
    blend: function (srcImg, x, y, width, height, dx, dy, dwidth, dheight, MODE) {
        if (arguments.length === 9) {
            blend(this, srcImg, x, y, width, height, dx, dy, dwidth, dheight, this);
        } else if (arguments.length === 10) {
            blend(srcImg, x, y, width, height, dx, dy, dwidth, dheight, MODE, this);
        }
        delete this.sourceImg;
    },

    /**
    * Copies a region of pixels from one image into another. If the source and destination regions
    * aren't the same size, it will automatically resize source pixels to fit the specified target region.
    * No alpha information is used in the process, however if the source image has an alpha channel set,
    * it will be copied as well. This function ignores imageMode().
    *
    * @param {int} sx             X coordinate of the source's upper left corner
    * @param {int} sy             Y coordinate of the source's upper left corner
    * @param {int} swidth         source image width
    * @param {int} sheight        source image height
    * @param {int} dx             X coordinate of the destinations's upper left corner
    * @param {int} dy             Y coordinate of the destinations's upper left corner
    * @param {int} dwidth         destination image width
    * @param {int} dheight        destination image height
    * @param {PImage} srcImg      an image variable referring to the source image
    *
    * @see alpha
    * @see blend
    * 
    * @method PImage
    */
    copy: function (srcImg, sx, sy, swidth, sheight, dx, dy, dwidth, dheight) {
        if (arguments.length === 8) {
            blend(this, srcImg, sx, sy, swidth, sheight, dx, dy, dwidth, PConstants.REPLACE, this);
        } else if (arguments.length === 9) {
            blend(srcImg, sx, sy, swidth, sheight, dx, dy, dwidth, dheight, PConstants.REPLACE, this);
        }
        delete this.sourceImg;
    },

    /**
    * Filters an image as defined by one of the following modes:
    * THRESHOLD - converts the image to black and white pixels depending if they are above or below
    * the threshold 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.
    * GRAY - converts any colors in the image to grayscale equivalents
    * INVERT - sets each pixel to its inverse value
    * POSTERIZE - limits each channel of the image to the number of colors specified as the level parameter
    * BLUR - executes a Guassian blur with the level parameter specifying the extent of the blurring.
    * If no level parameter is used, the blur is equivalent to Guassian blur of radius 1.
    * OPAQUE - sets the alpha channel to entirely opaque.
    * ERODE - reduces the light areas with the amount defined by the level parameter.
    * DILATE - increases the light areas with the amount defined by the level parameter
    *
    * @param {MODE} MODE        Either THRESHOLD, GRAY, INVERT, POSTERIZE, BLUR, OPAQUE, ERODE, or DILATE
    * @param {int|float} param  in the range from 0 to 1
    * 
    * @method PImage
    */
    filter: function (mode, param) {
        if (arguments.length === 2) {
            filter(mode, param, this);
        } else if (arguments.length === 1) {
            // no param specified, send null to show its invalid
            filter(mode, null, this);
        }
        delete this.sourceImg;
    },

    /**
    * Saves the image into a file. Images are saved in TIFF, TARGA, JPEG, and PNG format depending on
    * the extension within the filename  parameter. For example, "image.tif" will have a TIFF image and
    * "image.png" will save a PNG image. If no extension is included in the filename, the image will save
    * in TIFF format and .tif will be added to the name. These files are saved to the sketch's folder,
    * which may be opened by selecting "Show sketch folder" from the "Sketch" menu. It is not possible to
    * use save() while running the program in a web browser.
    * To save an image created within the code, rather than through loading, it's necessary to make the
    * image with the createImage() function so it is aware of the location of the program and can therefore
    * save the file to the right place. See the createImage() reference for more information.
    *
    * @param {String} filename        a sequence of letters and numbers
    * 
    * @method PImage
    */
    save: function (file) {
        save(file, this);
    },

    /**
    * Resize the image to a new width and height. To make the image scale proportionally, use 0 as the
    * value for the wide or high parameter.
    *
    * @param {int} wide         the resized image width
    * @param {int} high         the resized image height
    *
    * @see get
    * 
    * @method PImage
    */
    resize: function (w, h) {
        if (this.isRemote) { // Remote images cannot access imageData
            throw "Image is loaded remotely. Cannot resize.";
        }
        if (this.width !== 0 || this.height !== 0) {
            // make aspect ratio if w or h is 0
            if (w === 0 && h !== 0) {
                w = Math.floor(this.width / this.height * h);
            } else if (h === 0 && w !== 0) {
                h = Math.floor(this.height / this.width * w);
            }
            // put 'this.imageData' into a new canvas
            var canvas = getCanvasData(this.imageData).canvas;
            // pull imageData object out of canvas into ImageData object
            var imageData = getCanvasData(canvas, w, h).context.getImageData(0, 0, w, h);
            // set this as new pimage
            this.fromImageData(imageData);
        }
    },

    /**
    * Masks part of an image from displaying by loading another image and using it as an alpha channel.
    * This mask image should only contain grayscale data, but only the blue color channel is used. The
    * mask image needs to be the same size as the image to which it is applied.
    * In addition to using a mask image, an integer array containing the alpha channel data can be
    * specified directly. This method is useful for creating dynamically generated alpha masks. This
    * array must be of the same length as the target image's pixels array and should contain only grayscale
    * data of values between 0-255.
    *
    * @param {PImage} maskImg         any PImage object used as the alpha channel for "img", needs to be same
    *                                size as "img"
    * @param {int[]} maskArray        any array of Integer numbers used as the alpha channel, needs to be same
    *                                length as the image's pixel array
    * 
    * @method PImage
    */
    mask: function (mask) {
        var obj = this.toImageData(),
            i,
            size;

        if (mask instanceof PImage || mask.__isPImage) {
            if (mask.width === this.width && mask.height === this.height) {
                mask = mask.toImageData();

                for (i = 2, size = this.width * this.height * 4; i < size; i += 4) {
                    // using it as an alpha channel
                    obj.data[i + 1] = mask.data[i];
                    // but only the blue color channel
                }
            } else {
                throw "mask must have the same dimensions as PImage.";
            }
        } else if (mask instanceof Array) {
            if (this.width * this.height === mask.length) {
                for (i = 0, size = mask.length; i < size; ++i) {
                    obj.data[i * 4 + 3] = mask[i];
                }
            } else {
                throw "mask array must be the same length as PImage pixels array.";
            }
        }

        this.fromImageData(obj);
    },

    // These are intentionally left blank for PImages, we work live with pixels and draw as necessary
    /**
    * Loads the pixel data for the image into its pixels[] array. This function must always be called
    * before reading from or writing to pixels[].
    * Certain renderers may or may not seem to require loadPixels() or updatePixels(). However, the
    * rule is that any time you want to manipulate the pixels[] array, you must first call loadPixels(),
    * and after changes have been made, call updatePixels(). Even if the renderer may not seem to use
    * this function in the current Processing release, this will always be subject to change.
    * 
    * @method PImage
    */
    loadPixels: nop,

    toImageData: function () {
        if (this.isRemote) {
            return this.sourceImg;
        }

        if (!this.__isDirty) {
            return this.imageData;
        }

        var canvasData = getCanvasData(this.imageData);
        return canvasData.context.getImageData(0, 0, this.width, this.height);
    },

    toDataURL: function () {
        if (this.isRemote) { // Remote images cannot access imageData
            throw "Image is loaded remotely. Cannot create dataURI.";
        }
        var canvasData = getCanvasData(this.imageData);
        return canvasData.canvas.toDataURL();
    },

    fromImageData: function (canvasImg) {
        var w = canvasImg.width,
            h = canvasImg.height,
            canvas = document.createElement('canvas'),
            ctx = canvas.getContext('2d');

        this.width = canvas.width = w;
        this.height = canvas.height = h;

        ctx.putImageData(canvasImg, 0, 0);

        // changed for 0.9
        this.format = PConstants.ARGB;

        this.imageData = canvasImg;
        this.sourceImg = canvas;
    }
};

/**
 * Creates a new PImage (the datatype for storing images). This provides a fresh buffer of pixels to play
 * with. Set the size of the buffer with the width and height parameters. The format parameter defines how
 * the pixels are stored. See the PImage reference for more information.
 * Be sure to include all three parameters, specifying only the width and height (but no format) will
 * produce a strange error.
 * Advanced users please note that createImage() should be used instead of the syntax new PImage().
 *
 * @param {int} width                image width
 * @param {int} height               image height
 * @param {MODE} format              Either RGB, ARGB, ALPHA (grayscale alpha channel)
 *
 * @returns {PImage}
 *
 * @see PImage
 * @see PGraphics
 */
createImage = function (w, h, mode) {
    return new PImage(w, h, mode);
};

// Loads an image for display. Type is an extension. Callback is fired on load.
/**
 * Loads an image into a variable of type PImage. Four types of images ( .gif, .jpg, .tga, .png) images may
 * be loaded. To load correctly, images must be located in the data directory of the current sketch. In most
 * cases, load all images in setup() to preload them at the start of the program. Loading images inside draw()
 * will reduce the speed of a program.
 * The filename parameter can also be a URL to a file found online. For security reasons, a Processing sketch
 * found online can only download files from the same server from which it came. Getting around this restriction
 * requires a signed applet.
 * The extension parameter is used to determine the image type in cases where the image filename does not end
 * with a proper extension. Specify the extension as the second parameter to loadImage(), as shown in the
 * third example on this page.
 * If an image is not loaded successfully, the null value is returned and an error message will be printed to
 * the console. The error message does not halt the program, however the null value may cause a NullPointerException
 * if your code does not check whether the value returned from loadImage() is null.
 * Depending on the type of error, a PImage object may still be returned, but the width and height of the image
 * will be set to -1. This happens if bad image data is returned or cannot be decoded properly. Sometimes this happens
 * with image URLs that produce a 403 error or that redirect to a password prompt, because loadImage() will attempt
 * to interpret the HTML as image data.
 *
 * @param {String} filename        name of file to load, can be .gif, .jpg, .tga, or a handful of other image
 *                                types depending on your platform.
 * @param {String} extension       the type of image to load, for example "png", "gif", "jpg"
 *
 * @returns {PImage}
 *
 * @see PImage
 * @see image
 * @see imageMode
 * @see background
 */
loadImage = function (file, type, callback) {
    // if type is specified add it with a . to file to make the filename
    if (type) {
        file = file + "." + type;
    }
    var pimg;
    // if image is in the preloader cache return a new PImage
    if (curSketch.imageCache.images[file]) {
        pimg = new PImage(curSketch.imageCache.images[file]);
        pimg.loaded = true;
        return pimg;
    }
    // else async load it
    pimg = new PImage();
    var img = document.createElement('img');

    pimg.sourceImg = img;

    img.onload = (function (aImage, aPImage, aCallback) {
        var image = aImage;
        var pimg = aPImage;
        var callback = aCallback;
        return function () {
            // change the <img> object into a PImage now that its loaded
            pimg.fromHTMLImageData(image);
            pimg.loaded = true;
            if (callback) {
                callback();
            }
        };
    }(img, pimg, callback));

    img.src = file; // needs to be called after the img.onload function is declared or it wont work in opera
    return pimg;
};

// async loading of large images, same functionality as loadImage above
/**
 * This function load images on a separate thread so that your sketch does not freeze while images load during
 * setup(). While the image is loading, its width and height will be 0. If an error occurs while loading the image,
 * its width and height will be set to -1. You'll know when the image has loaded properly because its width and
 * height will be greater than 0. Asynchronous image loading (particularly when downloading from a server) can
 * dramatically improve performance.
 * The extension parameter is used to determine the image type in cases where the image filename does not end
 * with a proper extension. Specify the extension as the second parameter to requestImage().
 *
 * @param {String} filename        name of file to load, can be .gif, .jpg, .tga, or a handful of other image
 *                                types depending on your platform.
 * @param {String} extension       the type of image to load, for example "png", "gif", "jpg"
 *
 * @returns {PImage}
 *
 * @see PImage
 * @see loadImage
 */
requestImage = loadImage;

// Gets a single pixel or block of pixels from the current Canvas Context or a PImage
/**
 * Reads the color of any pixel or grabs a section of an image. If no parameters are specified, the entire
 * image is returned. Get the value of one pixel by specifying an x,y coordinate. Get a section of the display
 * window by specifying an additional width and height parameter. If the pixel requested is outside of the image
 * window, black is returned. The numbers returned are scaled according to the current color ranges, but only RGB
 * values are returned by this function. For example, even though you may have drawn a shape with colorMode(HSB),
 * the numbers returned will be in RGB.
 * Getting the color of a single pixel with get(x, y) is easy, but not as fast as grabbing the data directly
 * from pixels[]. The equivalent statement to "get(x, y)" using pixels[] is "pixels[y*width+x]". Processing
 * requires calling loadPixels() to load the display window data into the pixels[] array before getting the values.
 * This function ignores imageMode().
 *
 * @param {int} x            x-coordinate of the pixel
 * @param {int} y            y-coordinate of the pixel
 * @param {int} width        width of pixel rectangle to get
 * @param {int} height       height of pixel rectangle to get
 *
 * @returns {Color|PImage}
 *
 * @see set
 * @see pixels[]
 * @see imageMode
 */
get = function (x, y, w, h, img) {
    // for 0 2 and 4 arguments use curContext, otherwise PImage.get was called
    if (img !== undefined) {
        return get$5(x, y, w, h, img);
    }
    if (h !== undefined) {
        return get$4(x, y, w, h);
    }
    if (w !== undefined) {
        return get$3(x, y, w);
    }
    if (y !== undefined) {
        return get$2(x, y);
    }
    if (x !== undefined) {
        // PImage.get() was called, return a new PImage
        return get$5(0, 0, x.width, x.height, x);
    }

    return get$4(0, 0, width, height);
};

/**
 * Creates and returns a new <b>PGraphics</b> object of the types P2D, P3D, and JAVA2D. Use this class if you need to draw
 * into an off-screen graphics buffer. It's not possible to use <b>createGraphics()</b> with OPENGL, because it doesn't
 * allow offscreen use. The DXF and PDF renderers require the filename parameter. <br /><br /> It's important to call
 * any drawing commands between beginDraw() and endDraw() statements. This is also true for any commands that affect
 * drawing, such as smooth() or colorMode().<br /><br /> Unlike the main drawing surface which is completely opaque,
 * surfaces created with createGraphics() can have transparency. This makes it possible to draw into a graphics and
 * maintain the alpha channel.
 *
 * @param {int} width       width in pixels
 * @param {int} height      height in pixels
 * @param {int} renderer    Either P2D, P3D, JAVA2D, PDF, DXF
 * @param {String} filename the name of the file (not supported yet)
 */
createGraphics = function (w, h, render) {
    var pg = new Processing();
    pg.size(w, h, render);
    return pg;
};

// Paints a pixel array into the canvas
/**
 * Changes the color of any pixel or writes an image directly into the display window. The x and y parameters
 * specify the pixel to change and the color  parameter specifies the color value. The color parameter is affected
 * by the current color mode (the default is RGB values from 0 to 255). When setting an image, the x and y
 * parameters define the coordinates for the upper-left corner of the image.
 * Setting the color of a single pixel with set(x, y) is easy, but not as fast as putting the data directly
 * into pixels[]. The equivalent statement to "set(x, y, #000000)" using pixels[] is "pixels[y*width+x] = #000000".
 * You must call loadPixels() to load the display window data into the pixels[] array before setting the values
 * and calling updatePixels() to update the window with any changes. This function ignores imageMode().
 *
 * @param {int} x            x-coordinate of the pixel
 * @param {int} y            y-coordinate of the pixel
 * @param {Color} obj        any value of the color datatype
 * @param {PImage} img       any valid variable of type PImage
 *
 * @see get
 * @see pixels[]
 * @see imageMode
 */
set = function (x, y, obj, img) {
    var color, oldFill;
    if (arguments.length === 3) {
        // called set(), was it with a color or a img ?
        if (typeof obj === "number") {
            set$3(x, y, obj);
        } else if (obj instanceof PImage || obj.__isPImage) {
            image(obj, x, y);
        }
    } else if (arguments.length === 4) {
        // PImage.set(x,y,c) was called, set coordinate x,y color to c of img
        set$4(x, y, obj, img);
    }
};
imageData = {};

// handle the sketch code for pixels[]
// parser code converts pixels[] to getPixels() or setPixels(),
// .length becomes getLength()
/**
 * Array containing the values for all the pixels in the display window. These values are of the color datatype.
 * This array is the size of the display window. For example, if the image is 100x100 pixels, there will be 10000
 * values and if the window is 200x300 pixels, there will be 60000 values. The index value defines the position
 * of a value within the array. For example, the statment color b = pixels[230] will set the variable b to be
 * equal to the value at that location in the array.
 * Before accessing this array, the data must loaded with the loadPixels() function. After the array data has
 * been modified, the updatePixels() function must be run to update the changes.
 *
 * @param {int} index      must not exceed the size of the array
 *
 * @see loadPixels
 * @see updatePixels
 * @see get
 * @see set
 * @see PImage
 */
pixels = {
    getLength: function () { return imageData.data.length ? imageData.data.length / 4 : 0; },
    getPixel: function (i) {
        var offset = i * 4, data = imageData.data;
        return (data[offset + 3] << 24) & 0xff000000 |
            (data[offset + 0] << 16) & 0x00ff0000 |
            (data[offset + 1] << 8) & 0x0000ff00 |
            data[offset + 2] & 0x000000ff;
    },
    setPixel: function (i, c) {
        var offset = i * 4, data = imageData.data;
        data[offset + 0] = (c & 0x00ff0000) >>> 16; // RED_MASK
        data[offset + 1] = (c & 0x0000ff00) >>> 8;  // GREEN_MASK
        data[offset + 2] = (c & 0x000000ff);        // BLUE_MASK
        data[offset + 3] = (c & 0xff000000) >>> 24; // ALPHA_MASK
    },
    toArray: function () {
        var arr = [], length = imageData.width * imageData.height, data = imageData.data;
        for (var i = 0, offset = 0; i < length; i++, offset += 4) {
            arr.push((data[offset + 3] << 24) & 0xff000000 |
                (data[offset + 0] << 16) & 0x00ff0000 |
                (data[offset + 1] << 8) & 0x0000ff00 |
                data[offset + 2] & 0x000000ff);
        }
        return arr;
    },
    set: function (arr) {
        for (var i = 0, aL = arr.length; i < aL; i++) {
            this.setPixel(i, arr[i]);
        }
    }
};

// Gets a 1-Dimensional pixel array from Canvas
/**
 * Loads the pixel data for the display window into the pixels[] array. This function must always be called
 * before reading from or writing to pixels[].
 * Certain renderers may or may not seem to require loadPixels() or updatePixels(). However, the rule is that
 * any time you want to manipulate the pixels[] array, you must first call loadPixels(), and after changes
 * have been made, call updatePixels(). Even if the renderer may not seem to use this function in the current
 * Processing release, this will always be subject to change.
 *
 * @see pixels[]
 * @see updatePixels
 */
loadPixels = function () {
    imageData = drawing.$ensureContext().getImageData(0, 0, width, height);
};

// Draws a 1-Dimensional pixel array to Canvas
/**
 * Updates the display window with the data in the pixels[] array. Use in conjunction with loadPixels(). If
 * you're only reading pixels from the array, there's no need to call updatePixels() unless there are changes.
 * Certain renderers may or may not seem to require loadPixels() or updatePixels(). However, the rule is that
 * any time you want to manipulate the pixels[] array, you must first call loadPixels(), and after changes
 * have been made, call updatePixels(). Even if the renderer may not seem to use this function in the current
 * Processing release, this will always be subject to change.
 * Currently, none of the renderers use the additional parameters to updatePixels(), however this may be
 * implemented in the future.
 *
 * @see loadPixels
 * @see pixels[]
 */
updatePixels = function () {
    if (imageData) {
        drawing.$ensureContext().putImageData(imageData, 0, 0);
    }
};

/**
 * Set various hints and hacks for the renderer. This is used to handle obscure rendering features that cannot be
 * implemented in a consistent manner across renderers. Many options will often graduate to standard features
 * instead of hints over time.
 * hint(ENABLE_OPENGL_4X_SMOOTH) - Enable 4x anti-aliasing for OpenGL. This can help force anti-aliasing if
 * it has not been enabled by the user. On some graphics cards, this can also be set by the graphics driver's
 * control panel, however not all cards make this available. This hint must be called immediately after the
 * size() command because it resets the renderer, obliterating any settings and anything drawn (and like size(),
 * re-running the code that came before it again).
 * hint(DISABLE_OPENGL_2X_SMOOTH) - In Processing 1.0, Processing always enables 2x smoothing when the OpenGL
 * renderer is used. This hint disables the default 2x smoothing and returns the smoothing behavior found in
 * earlier releases, where smooth() and noSmooth() could be used to enable and disable smoothing, though the
 * quality was inferior.
 * hint(ENABLE_NATIVE_FONTS) - Use the native version fonts when they are installed, rather than the bitmapped
 * version from a .vlw file. This is useful with the JAVA2D renderer setting, as it will improve font rendering
 * speed. This is not enabled by default, because it can be misleading while testing because the type will look
 * great on your machine (because you have the font installed) but lousy on others' machines if the identical
 * font is unavailable. This option can only be set per-sketch, and must be called before any use of textFont().
 * hint(DISABLE_DEPTH_TEST) - Disable the zbuffer, allowing you to draw on top of everything at will. When depth
 * testing is disabled, items will be drawn to the screen sequentially, like a painting. This hint is most often
 * 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
 * interface). Starting in release 0149, this will also clear the depth buffer. Restore the default with
 * hint(ENABLE_DEPTH_TEST), but note that with the depth buffer cleared, any 3D drawing that happens later in
 * draw() will ignore existing shapes on the screen.
 * hint(ENABLE_DEPTH_SORT) - Enable primitive z-sorting of triangles and lines in P3D and OPENGL. This can slow
 * performance considerably, and the algorithm is not yet perfect. Restore the default with hint(DISABLE_DEPTH_SORT).
 * hint(DISABLE_OPENGL_ERROR_REPORT) - Speeds up the OPENGL renderer setting by not checking for errors while
 * running. Undo with hint(ENABLE_OPENGL_ERROR_REPORT).
 * As of release 0149, unhint() has been removed in favor of adding additional ENABLE/DISABLE constants to reset
 * the default behavior. This prevents the double negatives, and also reinforces which hints can be enabled or disabled.
 *
 * @param {MODE} item          constant: name of the hint to be enabled or disabled
 *
 * @see PGraphics
 * @see createGraphics
 * @see size
 */
hint = function (which) {
    var curContext = drawing.$ensureContext();
    if (which === PConstants.DISABLE_DEPTH_TEST) {
        curContext.disable(curContext.DEPTH_TEST);
        curContext.depthMask(false);
        curContext.clear(curContext.DEPTH_BUFFER_BIT);
    }
    else if (which === PConstants.ENABLE_DEPTH_TEST) {
        curContext.enable(curContext.DEPTH_TEST);
        curContext.depthMask(true);
    }
};

/**
 * The background() function sets the color used for the background of the Processing window.
 * 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.
 * 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.
 * To resize an image 'b' to the size of the sketch window, use b.resize(width, height).
 * Images used as background will ignore the current <b>tint()</b> setting.
 * For the main drawing surface, the alpha value will be ignored. However,
 * alpha can be used on PGraphics objects from <b>createGraphics()</b>. This is
 * the only way to set all the pixels partially transparent, for instance.
 * If the 'gray' parameter is passed in the function sets the background to a grayscale value, based on the
 * current colorMode.
 * <p>
 * Note that background() should be called before any transformations occur,
 * because some implementations may require the current transformation matrix
 * to be identity before drawing.
 *
 * @param {int|float} gray    specifies a value between white and black
 * @param {int|float} value1  red or hue value (depending on the current color mode)
 * @param {int|float} value2  green or saturation value (depending on the current color mode)
 * @param {int|float} value3  blue or brightness value (depending on the current color mode)
 * @param {int|float} alpha   opacity of the background
 * @param {Color} color       any value of the color datatype
 * @param {int} hex           color value in hexadecimal notation (i.e. #FFCC00 or 0xFFFFCC00)
 * @param {PImage} image      an instance of a PImage to use as a background
 *
 * @see #stroke()
 * @see #fill()
 * @see #tint()
 * @see #colorMode()
 */
background = function (arg1, arg2, arg3, arg4) {
    if (arg1 !== undef) {
        backgroundHelper(arg1, arg2, arg3, arg4);
    }

    if (backgroundObj instanceof PImage || backgroundObj.__isPImage) {
        saveContext();
        curContext.setTransform(1, 0, 0, 1, 0, 0);
        image(backgroundObj, 0, 0);
        restoreContext();
    } else {
        saveContext();
        curContext.setTransform(1, 0, 0, 1, 0, 0);

        // If the background is transparent
        if (alpha(backgroundObj) !== colorModeA) {
            curContext.clearRect(0, 0, width, height);
        }
        curContext.fillStyle = color.toString(backgroundObj);
        curContext.fillRect(0, 0, width, height);
        isFillDirty = true;
        restoreContext();
    }
};

background = function (arg1, arg2, arg3, arg4) {
    if (arguments.length > 0) {
        backgroundHelper(arg1, arg2, arg3, arg4);
    }

    var c = color.toGLArray(backgroundObj);
    curContext.clearColor(c[0], c[1], c[2], c[3]);
    curContext.clear(curContext.COLOR_BUFFER_BIT | curContext.DEPTH_BUFFER_BIT);

    // An image as a background in 3D is not implemented yet
};

// Draws an image to the Canvas
/**
 * Displays images to the screen. The images must be in the sketch's "data" directory to load correctly. Select "Add
 * file..." from the "Sketch" menu to add the image. Processing currently works with GIF, JPEG, and Targa images. The
 * color of an image may be modified with the tint() function and if a GIF has transparency, it will maintain its
 * transparency. The img parameter specifies the image to display and the x and y parameters define the location of
 * the image from its upper-left corner. The image is displayed at its original size unless the width and height
 * parameters specify a different size. The imageMode() function changes the way the parameters work. A call to
 * imageMode(CORNERS) will change the width and height parameters to define the x and y values of the opposite
 * corner of the image.
 *
 * @param {PImage} img            the image to display
 * @param {int|float} x           x-coordinate of the image
 * @param {int|float} y           y-coordinate of the image
 * @param {int|float} width       width to display the image
 * @param {int|float} height      height to display the image
 *
 * @see loadImage
 * @see PImage
 * @see imageMode
 * @see tint
 * @see background
 * @see alpha
 */
image = function (img, x, y, w, h) {
    // Fix fractional positions
    x = Math.round(x);
    y = Math.round(y);

    if (img.width > 0) {
        var wid = w || img.width;
        var hgt = h || img.height;

        var bounds = imageModeConvert(x || 0, y || 0, w || img.width, h || img.height, arguments.length < 4);
        var fastImage = !!img.sourceImg && curTint === null;
        if (fastImage) {
            var htmlElement = img.sourceImg;
            if (img.__isDirty) {
                img.updatePixels();
            }
            // Using HTML element's width and height in case if the image was resized.
            curContext.drawImage(htmlElement, 0, 0,
                htmlElement.width, htmlElement.height, bounds.x, bounds.y, bounds.w, bounds.h);
        } else {
            var obj = img.toImageData();

            // Tint the image
            if (curTint !== null) {
                curTint(obj);
                img.__isDirty = true;
            }

            curContext.drawImage(getCanvasData(obj).canvas, 0, 0,
                img.width, img.height, bounds.x, bounds.y, bounds.w, bounds.h);
        }
    }
};

image = function (img, x, y, w, h) {
    if (img.width > 0) {
        // Fix fractional positions
        x = Math.round(x);
        y = Math.round(y);
        w = w || img.width;
        h = h || img.height;

        beginShape(QUADS);
        texture(img);
        vertex(x, y, 0, 0, 0);
        vertex(x, y + h, 0, 0, h);
        vertex(x + w, y + h, 0, w, h);
        vertex(x + w, y, 0, w, 0);
        endShape();
    }
};

/**
 * The tint() function sets the fill value for displaying images. Images can be tinted to
 * specified colors or made transparent by setting the alpha.
 * <br><br>To make an image transparent, but not change it's color,
 * use white as the tint color and specify an alpha value. For instance,
 * tint(255, 128) will make an image 50% transparent (unless
 * <b>colorMode()</b> has been used).
 *
 * <br><br>When using hexadecimal notation to specify a color, use "#" or
 * "0x" before the values (e.g. #CCFFAA, 0xFFCCFFAA). 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", 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.
 * <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.
 * <br><br>The tint() method is also used to control the coloring of
 * textures in 3D.
 *
 * @param {int|float} gray    any valid number
 * @param {int|float} alpha    opacity of the image
 * @param {int|float} value1  red or hue value
 * @param {int|float} value2  green or saturation value
 * @param {int|float} value3  blue or brightness value
 * @param {int|float} color    any value of the color datatype
 * @param {int} hex            color value in hexadecimal notation (i.e. #FFCC00 or 0xFFFFCC00)
 *
 * @see #noTint()
 * @see #image()
 */
tint = function (a1, a2, a3, a4) {
    var tintColor = color(a1, a2, a3, a4);
    var r = red(tintColor) / colorModeX;
    var g = green(tintColor) / colorModeY;
    var b = blue(tintColor) / colorModeZ;
    var a = alpha(tintColor) / colorModeA;
    curTint = function (obj) {
        var data = obj.data,
            length = 4 * obj.width * obj.height;
        for (var i = 0; i < length;) {
            data[i++] *= r;
            data[i++] *= g;
            data[i++] *= b;
            data[i++] *= a;
        }
    };
    // for overriding the color buffer when 3d rendering
    curTint3d = function (data) {
        for (var i = 0; i < data.length;) {
            data[i++] = r;
            data[i++] = g;
            data[i++] = b;
            data[i++] = a;
        }
    };
};

/**
 * The noTint() function removes the current fill value for displaying images and reverts to displaying images with their original hues.
 *
 * @see #tint()
 * @see #image()
 */
noTint = function () {
    curTint = null;
    curTint3d = null;
};

/**
 * Copies a region of pixels from the display window to another area of the display window and copies a region of pixels from an
 * image used as the srcImg  parameter into the display window. If the source and destination regions aren't the same size, it will
 * automatically resize the source pixels to fit the specified target region. No alpha information is used in the process, however
 * if the source image has an alpha channel set, it will be copied as well. This function ignores imageMode().
 *
 * @param {int} x            X coordinate of the source's upper left corner
 * @param {int} y            Y coordinate of the source's upper left corner
 * @param {int} width        source image width
 * @param {int} height       source image height
 * @param {int} dx           X coordinate of the destination's upper left corner
 * @param {int} dy           Y coordinate of the destination's upper left corner
 * @param {int} dwidth       destination image width
 * @param {int} dheight      destination image height
 * @param {PImage} srcImg    image variable referring to the source image
 *
 * @see blend
 * @see get
 */
copy = function (src, sx, sy, sw, sh, dx, dy, dw, dh) {
    if (dh === undef) {
        // shift everything, and introduce p
        dh = dw;
        dw = dy;
        dy = dx;
        dx = sh;
        sh = sw;
        sw = sy;
        sy = sx;
        sx = src;
        src = p;
    }
    blend(src, sx, sy, sw, sh, dx, dy, dw, dh, PConstants.REPLACE);
};

/**
 * Blends a region of pixels from one image into another (or in itself again) with full alpha channel support. There
 * is a choice of the following modes to blend the source pixels (A) with the ones of pixels in the destination image (B):
 * BLEND - linear interpolation of colours: C = A*factor + B
 * ADD - additive blending with white clip: C = min(A*factor + B, 255)
 * SUBTRACT - subtractive blending with black clip: C = max(B - A*factor, 0)
 * DARKEST - only the darkest colour succeeds: C = min(A*factor, B)
 * LIGHTEST - only the lightest colour succeeds: C = max(A*factor, B)
 * DIFFERENCE - subtract colors from underlying image.
 * EXCLUSION - similar to DIFFERENCE, but less extreme.
 * MULTIPLY - Multiply the colors, result will always be darker.
 * SCREEN - Opposite multiply, uses inverse values of the colors.
 * OVERLAY - A mix of MULTIPLY and SCREEN. Multiplies dark values, and screens light values.
 * HARD_LIGHT - SCREEN when greater than 50% gray, MULTIPLY when lower.
 * SOFT_LIGHT - Mix of DARKEST and LIGHTEST. Works like OVERLAY, but not as harsh.
 * DODGE - Lightens light tones and increases contrast, ignores darks. Called "Color Dodge" in Illustrator and Photosho
 * BURN - Darker areas are applied, increasing contrast, ignores lights. Called "Color Burn" in Illustrator and Photosho
 * All modes use the alpha information (highest byte) of source image pixels as the blending factor. If the source and
 * destination regions are different sizes, the image will be automatically resized to match the destination size. If the
 * srcImg parameter is not used, the display window is used as the source image.  This function ignores imageMode().
 *
 * @param {int} x            X coordinate of the source's upper left corner
 * @param {int} y            Y coordinate of the source's upper left corner
 * @param {int} width        source image width
 * @param {int} height       source image height
 * @param {int} dx           X coordinate of the destination's upper left corner
 * @param {int} dy           Y coordinate of the destination's upper left corner
 * @param {int} dwidth       destination image width
 * @param {int} dheight      destination image height
 * @param {PImage} srcImg    image variable referring to the source image
 * @param {PImage} MODE      Either BLEND, ADD, SUBTRACT, LIGHTEST, DARKEST, DIFFERENCE, EXCLUSION, MULTIPLY, SCREEN,
 *                          OVERLAY, HARD_LIGHT, SOFT_LIGHT, DODGE, BURN
 * @see filter
 */
blend = function (src, sx, sy, sw, sh, dx, dy, dw, dh, mode, pimgdest) {
    if (src.isRemote) {
        throw "Image is loaded remotely. Cannot blend image.";
    }

    if (mode === undef) {
        // shift everything, and introduce p
        mode = dh;
        dh = dw;
        dw = dy;
        dy = dx;
        dx = sh;
        sh = sw;
        sw = sy;
        sy = sx;
        sx = src;
        src = p;
    }

    var sx2 = sx + sw,
        sy2 = sy + sh,
        dx2 = dx + dw,
        dy2 = dy + dh,
        dest = pimgdest || p;

    // check if pimgdest is there and pixels, if so this was a call from pimg.blend
    if (pimgdest === undef || mode === undef) {
        loadPixels();
    }

    src.loadPixels();

    if (src === p && intersect(sx, sy, sx2, sy2, dx, dy, dx2, dy2)) {
        blit_resize(get(sx, sy, sx2 - sx, sy2 - sy), 0, 0, sx2 - sx - 1, sy2 - sy - 1,
            dest.imageData.data, dest.width, dest.height, dx, dy, dx2, dy2, mode);
    } else {
        blit_resize(src, sx, sy, sx2, sy2, dest.imageData.data, dest.width, dest.height, dx, dy, dx2, dy2, mode);
    }

    if (pimgdest === undef) {
        updatePixels();
    }
};

/**
 * Filters the display window as defined by one of the following modes:
 * THRESHOLD - converts the image to black and white pixels depending if they are above or below the threshold
 * 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.
 * GRAY - converts any colors in the image to grayscale equivalents
 * INVERT - sets each pixel to its inverse value
 * POSTERIZE - limits each channel of the image to the number of colors specified as the level parameter
 * BLUR - executes a Guassian blur with the level parameter specifying the extent of the blurring. If no level parameter is
 * used, the blur is equivalent to Guassian blur of radius 1.
 * OPAQUE - sets the alpha channel to entirely opaque.
 * ERODE - reduces the light areas with the amount defined by the level parameter.
 * DILATE - increases the light areas with the amount defined by the level parameter.
 *
 * @param {MODE} MODE          Either THRESHOLD, GRAY, INVERT, POSTERIZE, BLUR, OPAQUE, ERODE, or DILATE
 * @param {int|float} level    defines the quality of the filter
 *
 * @see blend
 */
filter = function (kind, param, aImg) {
    var img, col, lum, i;

    if (arguments.length === 3) {
        aImg.loadPixels();
        img = aImg;
    } else {
        loadPixels();
        img = p;
    }

    if (param === undef) {
        param = null;
    }
    if (img.isRemote) { // Remote images cannot access imageData
        throw "Image is loaded remotely. Cannot filter image.";
    }
    // begin filter process
    var imglen = img.pixels.getLength();
    switch (kind) {
        case PConstants.BLUR:
            var radius = param || 1; // if no param specified, use 1 (default for p5)
            blurARGB(radius, img);
            break;

        case PConstants.GRAY:
            if (img.format === PConstants.ALPHA) { //trouble
                // for an alpha image, convert it to an opaque grayscale
                for (i = 0; i < imglen; i++) {
                    col = 255 - img.pixels.getPixel(i);
                    img.pixels.setPixel(i, (0xff000000 | (col << 16) | (col << 8) | col));
                }
                img.format = PConstants.RGB; //trouble
            } else {
                for (i = 0; i < imglen; i++) {
                    col = img.pixels.getPixel(i);
                    lum = (77 * (col >> 16 & 0xff) + 151 * (col >> 8 & 0xff) + 28 * (col & 0xff)) >> 8;
                    img.pixels.setPixel(i, ((col & PConstants.ALPHA_MASK) | lum << 16 | lum << 8 | lum));
                }
            }
            break;

        case PConstants.INVERT:
            for (i = 0; i < imglen; i++) {
                img.pixels.setPixel(i, (img.pixels.getPixel(i) ^ 0xffffff));
            }
            break;

        case PConstants.POSTERIZE:
            if (param === null) {
                throw "Use filter(POSTERIZE, int levels) instead of filter(POSTERIZE)";
            }
            var levels = floor(param);
            if ((levels < 2) || (levels > 255)) {
                throw "Levels must be between 2 and 255 for filter(POSTERIZE, levels)";
            }
            var levels1 = levels - 1;
            for (i = 0; i < imglen; i++) {
                var rlevel = (img.pixels.getPixel(i) >> 16) & 0xff;
                var glevel = (img.pixels.getPixel(i) >> 8) & 0xff;
                var blevel = img.pixels.getPixel(i) & 0xff;
                rlevel = (((rlevel * levels) >> 8) * 255) / levels1;
                glevel = (((glevel * levels) >> 8) * 255) / levels1;
                blevel = (((blevel * levels) >> 8) * 255) / levels1;
                img.pixels.setPixel(i, ((0xff000000 & img.pixels.getPixel(i)) | (rlevel << 16) | (glevel << 8) | blevel));
            }
            break;

        case PConstants.OPAQUE:
            for (i = 0; i < imglen; i++) {
                img.pixels.setPixel(i, (img.pixels.getPixel(i) | 0xff000000));
            }
            img.format = PConstants.RGB; //trouble
            break;

        case PConstants.THRESHOLD:
            if (param === null) {
                param = 0.5;
            }
            if ((param < 0) || (param > 1)) {
                throw "Level must be between 0 and 1 for filter(THRESHOLD, level)";
            }
            var thresh = floor(param * 255);
            for (i = 0; i < imglen; i++) {
                var max = max((img.pixels.getPixel(i) & PConstants.RED_MASK) >> 16, max((img.pixels.getPixel(i) & PConstants.GREEN_MASK) >> 8, (img.pixels.getPixel(i) & PConstants.BLUE_MASK)));
                img.pixels.setPixel(i, ((img.pixels.getPixel(i) & PConstants.ALPHA_MASK) | ((max < thresh) ? 0x000000 : 0xffffff)));
            }
            break;

        case PConstants.ERODE:
            dilate(true, img);
            break;

        case PConstants.DILATE:
            dilate(false, img);
            break;
    }
    img.updatePixels();
};


/**
 * shared variables for blit_resize(), filter_new_scanline(), filter_bilinear(), filter()
 * change this in the future to not be exposed to p
 */
shared = {
    fracU: 0,
    ifU: 0,
    fracV: 0,
    ifV: 0,
    u1: 0,
    u2: 0,
    v1: 0,
    v2: 0,
    sX: 0,
    sY: 0,
    iw: 0,
    iw1: 0,
    ih1: 0,
    ul: 0,
    ll: 0,
    ur: 0,
    lr: 0,
    cUL: 0,
    cLL: 0,
    cUR: 0,
    cLR: 0,
    srcXOffset: 0,
    srcYOffset: 0,
    r: 0,
    g: 0,
    b: 0,
    a: 0,
    srcBuffer: null,
    blurRadius: 0,
    blurKernelSize: 0,
    blurKernel: null
};

/**
 * @param {*} sx1 
 * @param {*} sy1 
 * @param {*} sx2 
 * @param {*} sy2 
 * @param {*} dx1 
 * @param {*} dy1 
 * @param {*} dx2 
 * @param {*} dy2 
 * @returns 
 */
intersect = function (sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2) {
    var sw = sx2 - sx1 + 1;
    var sh = sy2 - sy1 + 1;
    var dw = dx2 - dx1 + 1;
    var dh = dy2 - dy1 + 1;
    if (dx1 < sx1) {
        dw += dx1 - sx1;
        if (dw > sw) {
            dw = sw;
        }
    } else {
        var w = sw + sx1 - dx1;
        if (dw > w) {
            dw = w;
        }
    }
    if (dy1 < sy1) {
        dh += dy1 - sy1;
        if (dh > sh) {
            dh = sh;
        }
    } else {
        var h = sh + sy1 - dy1;
        if (dh > h) {
            dh = h;
        }
    }
    return !(dw <= 0 || dh <= 0);
};

/** 
 * @param {*} img
 * @param {*} srcX1
 * @param {*} srcY1
 * @param {*} srcX2
 * @param {*} srcY2
 * @param {*} destPixels
 * @param {*} screenW
 * @param {*} screenH
 * @param {*} destX1
 * @param {*} destY1
 * @param {*} destX2
 * @param {*} destY2
 * @param {*} mode
 */
blit_resize = function (img, srcX1, srcY1, srcX2, srcY2, destPixels,
    screenW, screenH, destX1, destY1, destX2, destY2, mode) {
    var x, y;
    if (srcX1 < 0) {
        srcX1 = 0;
    }
    if (srcY1 < 0) {
        srcY1 = 0;
    }
    if (srcX2 >= img.width) {
        srcX2 = img.width - 1;
    }
    if (srcY2 >= img.height) {
        srcY2 = img.height - 1;
    }
    var srcW = srcX2 - srcX1;
    var srcH = srcY2 - srcY1;
    var destW = destX2 - destX1;
    var destH = destY2 - destY1;

    if (destW <= 0 || destH <= 0 || srcW <= 0 || srcH <= 0 || destX1 >= screenW ||
        destY1 >= screenH || srcX1 >= img.width || srcY1 >= img.height) {
        return;
    }

    var dx = Math.floor(srcW / destW * PConstants.PRECISIONF);
    var dy = Math.floor(srcH / destH * PConstants.PRECISIONF);

    var pshared = shared;

    pshared.srcXOffset = Math.floor(destX1 < 0 ? -destX1 * dx : srcX1 * PConstants.PRECISIONF);
    pshared.srcYOffset = Math.floor(destY1 < 0 ? -destY1 * dy : srcY1 * PConstants.PRECISIONF);
    if (destX1 < 0) {
        destW += destX1;
        destX1 = 0;
    }
    if (destY1 < 0) {
        destH += destY1;
        destY1 = 0;
    }
    destW = Math.min(destW, screenW - destX1);
    destH = Math.min(destH, screenH - destY1);

    var destOffset = destY1 * screenW + destX1;
    var destColor;

    pshared.srcBuffer = img.imageData.data;
    pshared.iw = img.width;
    pshared.iw1 = img.width - 1;
    pshared.ih1 = img.height - 1;

    // cache for speed
    var filterBilinear = filter_bilinear,
        filterNewScanline = filter_new_scanline,
        blendFunc = blendFuncs[mode],
        blendedColor,
        idx,
        cULoffset,
        cURoffset,
        cLLoffset,
        cLRoffset,
        ALPHA_MASK = PConstants.ALPHA_MASK,
        RED_MASK = PConstants.RED_MASK,
        GREEN_MASK = PConstants.GREEN_MASK,
        BLUE_MASK = PConstants.BLUE_MASK,
        PREC_MAXVAL = PConstants.PREC_MAXVAL,
        PRECISIONB = PConstants.PRECISIONB,
        PREC_RED_SHIFT = PConstants.PREC_RED_SHIFT,
        PREC_ALPHA_SHIFT = PConstants.PREC_ALPHA_SHIFT,
        srcBuffer = pshared.srcBuffer,
        min = Math.min;

    for (y = 0; y < destH; y++) {

        pshared.sX = pshared.srcXOffset;
        pshared.fracV = pshared.srcYOffset & PREC_MAXVAL;
        pshared.ifV = PREC_MAXVAL - pshared.fracV;
        pshared.v1 = (pshared.srcYOffset >> PRECISIONB) * pshared.iw;
        pshared.v2 = min((pshared.srcYOffset >> PRECISIONB) + 1, pshared.ih1) * pshared.iw;

        for (x = 0; x < destW; x++) {
            idx = (destOffset + x) * 4;

            destColor = (destPixels[idx + 3] << 24) &
                ALPHA_MASK | (destPixels[idx] << 16) &
                RED_MASK | (destPixels[idx + 1] << 8) &
                GREEN_MASK | destPixels[idx + 2] & BLUE_MASK;

            pshared.fracU = pshared.sX & PREC_MAXVAL;
            pshared.ifU = PREC_MAXVAL - pshared.fracU;
            pshared.ul = (pshared.ifU * pshared.ifV) >> PRECISIONB;
            pshared.ll = (pshared.ifU * pshared.fracV) >> PRECISIONB;
            pshared.ur = (pshared.fracU * pshared.ifV) >> PRECISIONB;
            pshared.lr = (pshared.fracU * pshared.fracV) >> PRECISIONB;
            pshared.u1 = (pshared.sX >> PRECISIONB);
            pshared.u2 = min(pshared.u1 + 1, pshared.iw1);

            cULoffset = (pshared.v1 + pshared.u1) * 4;
            cURoffset = (pshared.v1 + pshared.u2) * 4;
            cLLoffset = (pshared.v2 + pshared.u1) * 4;
            cLRoffset = (pshared.v2 + pshared.u2) * 4;

            pshared.cUL = (srcBuffer[cULoffset + 3] << 24) &
                ALPHA_MASK | (srcBuffer[cULoffset] << 16) &
                RED_MASK | (srcBuffer[cULoffset + 1] << 8) &
                GREEN_MASK | srcBuffer[cULoffset + 2] & BLUE_MASK;

            pshared.cUR = (srcBuffer[cURoffset + 3] << 24) &
                ALPHA_MASK | (srcBuffer[cURoffset] << 16) &
                RED_MASK | (srcBuffer[cURoffset + 1] << 8) &
                GREEN_MASK | srcBuffer[cURoffset + 2] & BLUE_MASK;

            pshared.cLL = (srcBuffer[cLLoffset + 3] << 24) &
                ALPHA_MASK | (srcBuffer[cLLoffset] << 16) &
                RED_MASK | (srcBuffer[cLLoffset + 1] << 8) &
                GREEN_MASK | srcBuffer[cLLoffset + 2] & BLUE_MASK;

            pshared.cLR = (srcBuffer[cLRoffset + 3] << 24) &
                ALPHA_MASK | (srcBuffer[cLRoffset] << 16) &
                RED_MASK | (srcBuffer[cLRoffset + 1] << 8) &
                GREEN_MASK | srcBuffer[cLRoffset + 2] & BLUE_MASK;

            pshared.r = ((pshared.ul * ((pshared.cUL & RED_MASK) >> 16) +
                pshared.ll * ((pshared.cLL & RED_MASK) >> 16) +
                pshared.ur * ((pshared.cUR & RED_MASK) >> 16) +
                pshared.lr * ((pshared.cLR & RED_MASK) >> 16)) << PREC_RED_SHIFT) & RED_MASK;
            pshared.g = ((pshared.ul * (pshared.cUL & GREEN_MASK) +
                pshared.ll * (pshared.cLL & GREEN_MASK) +
                pshared.ur * (pshared.cUR & GREEN_MASK) +
                pshared.lr * (pshared.cLR & GREEN_MASK)) >>> PRECISIONB) & GREEN_MASK;
            pshared.b = (pshared.ul * (pshared.cUL & BLUE_MASK) +
                pshared.ll * (pshared.cLL & BLUE_MASK) +
                pshared.ur * (pshared.cUR & BLUE_MASK) +
                pshared.lr * (pshared.cLR & BLUE_MASK)) >>> PRECISIONB;
            pshared.a = ((pshared.ul * ((pshared.cUL & ALPHA_MASK) >>> 24) +
                pshared.ll * ((pshared.cLL & ALPHA_MASK) >>> 24) +
                pshared.ur * ((pshared.cUR & ALPHA_MASK) >>> 24) +
                pshared.lr * ((pshared.cLR & ALPHA_MASK) >>> 24)) << PREC_ALPHA_SHIFT) & ALPHA_MASK;

            blendedColor = blendFunc(destColor, (pshared.a | pshared.r | pshared.g | pshared.b));

            destPixels[idx] = (blendedColor & RED_MASK) >>> 16;
            destPixels[idx + 1] = (blendedColor & GREEN_MASK) >>> 8;
            destPixels[idx + 2] = (blendedColor & BLUE_MASK);
            destPixels[idx + 3] = (blendedColor & ALPHA_MASK) >>> 24;

            pshared.sX += dx;
        }
        destOffset += screenW;
        pshared.srcYOffset += dy;
    }
};

////////////////////////////////////////////////////////////////////////////
// Font handling
////////////////////////////////////////////////////////////////////////////

/**
 * loadFont() Loads a font into a variable of type PFont.
 *
 * @param {String} name filename of the font to load
 * @param {int|float} size option font size (used internally)
 *
 * @returns {PFont} new PFont object
 *
 * @see #PFont
 * @see #textFont
 * @see #text
 * @see #createFont
 */
loadFont = function (name, size) {
    if (name === undef) {
        throw ("font name required in loadFont.");
    }
    if (name.indexOf(".svg") === -1) {
        if (size === undef) {
            size = curTextFont.size;
        }
        return PFont.get(name, size);
    }
    // If the font is a glyph, calculate by SVG table
    var font = loadGlyphs(name);

    return {
        name: name,
        css: '12px sans-serif',
        glyph: true,
        units_per_em: font.units_per_em,
        horiz_adv_x: 1 / font.units_per_em * font.horiz_adv_x,
        ascent: font.ascent,
        descent: font.descent,
        width: function (str) {
            var width = 0;
            var len = str.length;
            for (var i = 0; i < len; i++) {
                try {
                    width += parseFloat(glyphLook(glyphTable[name], str[i]).horiz_adv_x);
                }
                catch (e) {
                    Processing.debug(e);
                }
            }
            return width / glyphTable[name].units_per_em;
        }
    };
};

/**
 * createFont() Loads a font into a variable of type PFont.
 * Smooth and charset are ignored in Processing.js.
 *
 * @param {String}    name    filename of the font to load
 * @param {int|float} size    font size in pixels
 * @param {boolean}   smooth  not used in Processing.js
 * @param {char[]}    charset not used in Processing.js
 *
 * @returns {PFont} new PFont object
 *
 * @see #PFont
 * @see #textFont
 * @see #text
 * @see #loadFont
 */
createFont = function (name, size) {
    // because Processing.js only deals with real fonts,
    // createFont is simply a wrapper for loadFont/2
    return loadFont(name, size);
};

/**
 * textFont() Sets the current font.
 *
 * @param {PFont}     pfont the PFont to load as current text font
 * @param {int|float} size optional font size in pixels
 *
 * @see #createFont
 * @see #loadFont
 * @see #PFont
 * @see #text
 */
textFont = function (pfont, size) {
    if (size !== undef) {
        // If we're using an SVG glyph font, don't load from cache
        if (!pfont.glyph) {
            pfont = PFont.get(pfont.name, size);
        }
        curTextSize = size;
    }
    curTextFont = pfont;
    curFontName = curTextFont.name;
    curTextAscent = curTextFont.ascent;
    curTextDescent = curTextFont.descent;
    curTextLeading = curTextFont.leading;
    var curContext = drawing.$ensureContext();
    curContext.font = curTextFont.css;
};

/**
 * textSize() Sets the current font size in pixels.
 *
 * @param {int|float} size font size in pixels
 *
 * @see #textFont
 * @see #loadFont
 * @see #PFont
 * @see #text
 */
textSize = function (size) {
    if (size !== curTextSize) {
        // round size to the nearest tenth so that we don't explode the cache
        size = Math.round(10 * size) / 10;
        curTextFont = PFont.get(curFontName, size);
        curTextSize = size;
        // recache metrics
        curTextAscent = curTextFont.ascent;
        curTextDescent = curTextFont.descent;
        curTextLeading = curTextFont.leading;
        var curContext = drawing.$ensureContext();
        curContext.font = curTextFont.css;
    }
};

/**
 * textAscent() returns the maximum height a character extends above the baseline of the
 * current font at its current size, in pixels.
 *
 * @returns {float} height of the current font above the baseline, at its current size, in pixels
 *
 * @see #textDescent
 */
textAscent = function () {
    return curTextAscent;
};

/**
 * textDescent() returns the maximum depth a character will protrude below the baseline of
 * the current font at its current size, in pixels.
 *
 * @returns {float} depth of the current font below the baseline, at its current size, in pixels
 *
 * @see #textAscent
 */
textDescent = function () {
    return curTextDescent;
};

/**
 * textLeading() Sets the current font's leading, which is the distance
 * from baseline to baseline over consecutive lines, with additional vertical
 * spacing taking into account. Usually this value is 1.2 or 1.25 times the
 * textsize, but this value can be changed to effect vertically compressed
 * or stretched text.
 *
 * @param {int|float} the desired baseline-to-baseline size in pixels
 */
textLeading = function (leading) {
    curTextLeading = leading;
};

/**
 * textAlign() Sets the current alignment for drawing text.
 *
 * @param {int} ALIGN  Horizontal alignment, either LEFT, CENTER, or RIGHT
 * @param {int} YALIGN optional vertical alignment, either TOP, BOTTOM, CENTER, or BASELINE
 *
 * @see #loadFont
 * @see #PFont
 * @see #text
 */
textAlign = function (xalign, yalign) {
    horizontalTextAlignment = xalign;
    verticalTextAlignment = yalign || PConstants.BASELINE;
};

/**
 * textWidth() Calculates and returns the width of any character or text string in pixels.
 *
 * @param {char|String} str char or String to be measured
 *
 * @return {float} width of char or String in pixels
 *
 * @see #loadFont
 * @see #PFont
 * @see #text
 * @see #textFont
 */
textWidth = function (str) {
    var lines = toP5String(str).split(/\r?\n/g), width = 0;
    var i, linesCount = lines.length;

    curContext.font = curTextFont.css;
    for (i = 0; i < linesCount; ++i) {
        width = Math.max(width, curTextFont.measureTextWidth(lines[i]));
    }
    return width | 0;
};

textWidth = function (str) {
    var lines = toP5String(str).split(/\r?\n/g), width = 0;
    var i, linesCount = lines.length;
    if (textcanvas === undef) {
        textcanvas = document.createElement("canvas");
    }

    var textContext = textcanvas.getContext("2d");
    textContext.font = curTextFont.css;

    for (i = 0; i < linesCount; ++i) {
        width = Math.max(width, textContext.measureText(lines[i]).width);
    }
    return width | 0;
};

/**
 * A lookup table for characters that can not be referenced by Object
 * 
 * @param {*} font 
 * @param {*} chr 
 * @returns 
 */
glyphLook = function (font, chr) {
    try {
        switch (chr) {
            case "1":
                return font.one;
            case "2":
                return font.two;
            case "3":
                return font.three;
            case "4":
                return font.four;
            case "5":
                return font.five;
            case "6":
                return font.six;
            case "7":
                return font.seven;
            case "8":
                return font.eight;
            case "9":
                return font.nine;
            case "0":
                return font.zero;
            case " ":
                return font.space;
            case "$":
                return font.dollar;
            case "!":
                return font.exclam;
            case '"':
                return font.quotedbl;
            case "#":
                return font.numbersign;
            case "%":
                return font.percent;
            case "&":
                return font.ampersand;
            case "'":
                return font.quotesingle;
            case "(":
                return font.parenleft;
            case ")":
                return font.parenright;
            case "*":
                return font.asterisk;
            case "+":
                return font.plus;
            case ",":
                return font.comma;
            case "-":
                return font.hyphen;
            case ".":
                return font.period;
            case "/":
                return font.slash;
            case "_":
                return font.underscore;
            case ":":
                return font.colon;
            case ";":
                return font.semicolon;
            case "<":
                return font.less;
            case "=":
                return font.equal;
            case ">":
                return font.greater;
            case "?":
                return font.question;
            case "@":
                return font.at;
            case "[":
                return font.bracketleft;
            case "\\":
                return font.backslash;
            case "]":
                return font.bracketright;
            case "^":
                return font.asciicircum;
            case "`":
                return font.grave;
            case "{":
                return font.braceleft;
            case "|":
                return font.bar;
            case "}":
                return font.braceright;
            case "~":
                return font.asciitilde;
            // If the character is not 'special', access it by object reference
            default:
                return font[chr];
        }
    } catch (e) {
        Processing.debug(e);
    }
};

/**
 * text() Draws text to the screen.
 *
 * @param {String|char|int|float} data       the alphanumeric symbols to be displayed
 * @param {int|float}             x          x-coordinate of text
 * @param {int|float}             y          y-coordinate of text
 * @param {int|float}             z          optional z-coordinate of text
 * @param {String}                stringdata optional letters to be displayed
 * @param {int|float}             width      optional width of text box
 * @param {int|float}             height     optional height of text box
 *
 * @see #textAlign
 * @see #textMode
 * @see #loadFont
 * @see #PFont
 * @see #textFont
 */
text = function () {
    //XXX(jeresig): Fix font constantly resetting
    if (curContext.font !== curTextFont.css) {
        curContext.font = curTextFont.css;
    }
    if (textMode === PConstants.SHAPE) {
        // TODO: requires beginRaw function
        return;
    }
    if (arguments.length === 3) { // for text( str, x, y)
        text$4(toP5String(arguments[0]), arguments[1], arguments[2], 0);
    } else if (arguments.length === 4) { // for text( str, x, y, z)
        text$4(toP5String(arguments[0]), arguments[1], arguments[2], arguments[3]);
    } else if (arguments.length === 5) { // for text( str, x, y , width, height)
        text$6(toP5String(arguments[0]), arguments[1], arguments[2], arguments[3], arguments[4], 0);
    } else if (arguments.length === 6) { // for text( stringdata, x, y , width, height, z)
        text$6(toP5String(arguments[0]), arguments[1], arguments[2], arguments[3], arguments[4], arguments[5]);
    }
};

/**
 * Sets the way text draws to the screen. In the default configuration (the MODEL mode), it's possible to rotate,
 * scale, and place letters in two and three dimensional space. <br /><br /> Changing to SCREEN mode draws letters
 * directly to the front of the window and greatly increases rendering quality and speed when used with the P2D and
 * P3D renderers. textMode(SCREEN) with OPENGL and JAVA2D (the default) renderers will generally be slower, though
 * pixel accurate with P2D and P3D. With textMode(SCREEN), the letters draw at the actual size of the font (in pixels)
 * and therefore calls to <b>textSize()</b> will not affect the size of the letters. To create a font at the size you
 * desire, use the "Create font..." option in the Tools menu, or use the createFont() function. When using textMode(SCREEN),
 * any z-coordinate passed to a text() command will be ignored, because your computer screen is...flat!
 *
 * @param {int} MODE Either MODEL, SCREEN or SHAPE (not yet supported)
 *
 * @see loadFont
 * @see PFont
 * @see text
 * @see textFont
 * @see createFont
 */
textMode = function (mode) {
    textMode = mode;
};

/**
 * Load Batik SVG Fonts and parse to pre-def objects for quick rendering
 * 
 * @param {*} url
 */
loadGlyphs = function (url) {
    var x, y, cx, cy, nx, ny, d, a, lastCom, lenC, horiz_adv_x, getXY = '[0-9\\-]+', path;

    // Return arrays of SVG commands and coords
    // get this to use matchAll() - will need to work around the lack of null return
    var regex = function (needle, hay) {
        var i = 0,
            results = [],
            latest, regexp = new RegExp(needle, "g");
        latest = results[i] = regexexec(hay);
        while (latest) {
            i++;
            latest = results[i] = regexexec(hay);
        }
        return results;
    };

    var buildPath = function (d) {
        var c = regex("[A-Za-z][0-9\\- ]+|Z", d);
        var beforePathDraw = function () {
            saveContext();
            return drawing.$ensureContext();
        };
        var afterPathDraw = function () {
            executeContextFill();
            executeContextStroke();
            restoreContext();
        };

        // Begin storing path object
        path = "return {draw:function(){var curContext=beforePathDraw();curContext.beginPath();";

        x = 0;
        y = 0;
        cx = 0;
        cy = 0;
        nx = 0;
        ny = 0;
        d = 0;
        a = 0;
        lastCom = "";
        lenC = c.length - 1;

        // Loop through SVG commands translating to canvas eqivs functions in path object
        for (var j = 0; j < lenC; j++) {
            var com = c[j][0], xy = regex(getXY, com);

            switch (com[0]) {
                case "M":
                    //curContext.moveTo(x,-y);
                    x = parseFloat(xy[0][0]);
                    y = parseFloat(xy[1][0]);
                    path += "curContext.moveTo(" + x + "," + (-y) + ");";
                    break;

                case "L":
                    //curContext.lineTo(x,-y);
                    x = parseFloat(xy[0][0]);
                    y = parseFloat(xy[1][0]);
                    path += "curContext.lineTo(" + x + "," + (-y) + ");";
                    break;

                case "H":
                    //curContext.lineTo(x,-y)
                    x = parseFloat(xy[0][0]);
                    path += "curContext.lineTo(" + x + "," + (-y) + ");";
                    break;

                case "V":
                    //curContext.lineTo(x,-y);
                    y = parseFloat(xy[0][0]);
                    path += "curContext.lineTo(" + x + "," + (-y) + ");";
                    break;

                case "T":
                    //curContext.quadraticCurveTo(cx,-cy,nx,-ny);
                    nx = parseFloat(xy[0][0]);
                    ny = parseFloat(xy[1][0]);

                    if (lastCom === "Q" || lastCom === "T") {
                        d = Math.sqrt(Math.pow(x - cx, 2) + Math.pow(cy - y, 2));
                        // XXX(jeresig)
                        a = (angleMode === "degrees" ? 180 : Math.PI) + atan2(cx - x, cy - y);
                        cx = x + sin(a) * d;
                        cy = y + cos(a) * d;
                    } else {
                        cx = x;
                        cy = y;
                    }

                    path += "curContext.quadraticCurveTo(" + cx + "," + (-cy) + "," + nx + "," + (-ny) + ");";
                    x = nx;
                    y = ny;
                    break;

                case "Q":
                    //curContext.quadraticCurveTo(cx,-cy,nx,-ny);
                    cx = parseFloat(xy[0][0]);
                    cy = parseFloat(xy[1][0]);
                    nx = parseFloat(xy[2][0]);
                    ny = parseFloat(xy[3][0]);
                    path += "curContext.quadraticCurveTo(" + cx + "," + (-cy) + "," + nx + "," + (-ny) + ");";
                    x = nx;
                    y = ny;
                    break;

                case "Z":
                    //curContext.closePath();
                    path += "curContext.closePath();";
                    break;
            }
            lastCom = com[0];
        }

        path += "afterPathDraw();";
        path += "curContext.translate(" + horiz_adv_x + ",0);";
        path += "}}";

        return ((new Function("beforePathDraw", "afterPathDraw", path))(beforePathDraw, afterPathDraw));
    };

    // Parse SVG font-file into block of Canvas commands
    var parseSVGFont = function (svg) {
        // Store font attributes
        var font = svg.getElementsByTagName("font");
        glyphTable[url].horiz_adv_x = font[0].getAttribute("horiz-adv-x");

        var font_face = svg.getElementsByTagName("font-face")[0];
        glyphTable[url].units_per_em = parseFloat(font_face.getAttribute("units-per-em"));
        glyphTable[url].ascent = parseFloat(font_face.getAttribute("ascent"));
        glyphTable[url].descent = parseFloat(font_face.getAttribute("descent"));

        var glyph = svg.getElementsByTagName("glyph"),
            len = glyph.length;

        // Loop through each glyph in the SVG
        for (var i = 0; i < len; i++) {
            // Store attributes for this glyph
            var unicode = glyph[i].getAttribute("unicode");
            var name = glyph[i].getAttribute("glyph-name");
            horiz_adv_x = glyph[i].getAttribute("horiz-adv-x");
            if (horiz_adv_x === null) {
                horiz_adv_x = glyphTable[url].horiz_adv_x;
            }
            d = glyph[i].getAttribute("d");
            // Split path commands in glpyh
            if (d !== undef) {
                path = buildPath(d);
                // Store glyph data to table object
                glyphTable[url][name] = {
                    name: name,
                    unicode: unicode,
                    horiz_adv_x: horiz_adv_x,
                    draw: path.draw
                };
            }
        } // finished adding glyphs to table
    };

    // Load and parse Batik SVG font as XML into a Processing Glyph object
    var loadXML = function () {
        var xmlDoc;

        try {
            xmlDoc = document.implementation.createDocument("", "", null);
        }
        catch (e_fx_op) {
            Processing.debug(e_fx_omessage);
            return;
        }

        try {
            xmlDoc.async = false;
            xmlDoc.load(url);
            parseSVGFont(xmlDoc.getElementsByTagName("svg")[0]);
        }
        catch (e_sf_ch) {
            // Google Chrome, Safari etc.
            Processing.debug(e_sf_ch);
            try {
                var xmlhttp = new window.XMLHttpRequest();
                xmlhttopen("GET", url, false);
                xmlhttsend(null);
                parseSVGFont(xmlhttresponseXML.documentElement);
            }
            catch (e) {
                Processing.debug(e_sf_ch);
            }
        }
    };

    // Create a new object in glyphTable to store this font
    glyphTable[url] = {};

    // Begin loading the Batik SVG font...
    loadXML(url);

    // Return the loaded font for attribute grabbing
    return glyphTable[url];
};

/**
 * Gets the sketch parameter value. The parameter can be defined as the canvas attribute with
 * the "data-processing-" prefix or provided in the pjs directive (e.g. param-test="52").
 * The function tries the canvas attributes, then the pjs directive content.
 *
 * @param   {String}    name          The name of the param to read.
 *
 * @returns {String}    The parameter value, or null if parameter is not defined.
 */
param = function (name) {
    // trying attribute that was specified in CANVAS
    var attributeName = "data-processing-" + name;
    if (curElement.hasAttribute(attributeName)) {
        return curElement.getAttribute(attributeName);
    }
    // trying child PARAM elements of the CANVAS
    for (var i = 0, len = curElement.childNodes.length; i < len; ++i) {
        var item = curElement.childNodes.item(i);
        if (item.nodeType !== 1 || item.tagName.toLowerCase() !== "param") {
            continue;
        }
        if (item.getAttribute("name") === name) {
            return item.getAttribute("value");
        }
    }
    // fallback to default params
    if (curSketch.params.hasOwnProperty(name)) {
        return curSketch.params[name];
    }
    return null;
};

////////////////////////////////////////////////////////////////////////////
// 2D/3D methods wiring utils
////////////////////////////////////////////////////////////////////////////

/**
 * @param {*} aWidth
 * @param {*} aHeight
 * @param {*} aMode
 */
size = function (aWidth, aHeight, aMode) {
    wireDimensionalFunctions(aMode === PConstants.WEBGL ? "3D" : "2D");
    size(aWidth, aHeight, aMode);
};

//////////////////////////////////////////////////////////////////////////
// Touch and Mouse event handling
//////////////////////////////////////////////////////////////////////////

/**
 * Disables context menu.
 */
disableContextMenu = function () {
    if (!enabled) {
        return;
    }
    attachEventHandler(curElement, 'contextmenu', contextMenu);
    enabled = false;
};

/**
 * Enables context menu.
 */
enableContextMenu = function () {
    if (enabled) {
        return;
    }
    detachEventHandler({ elem: curElement, type: 'contextmenu', fn: contextMenu });
    enabled = true;
};