/*
P R O C E S S I N G . J S - 1.1.0
a port of the Processing visualization language
License : MIT
Developer : John Resig: http://ejohn.org
Web Site : http://processingjs.org
Java Version : http://processing.org
Github Repo. : http://github.com/jeresig/processing-js
Bug Tracking : http://processing-js.lighthouseapp.com
Mozilla POW! : http://wiki.Mozilla.org/Education/Projects/ProcessingForTheWeb
Maintained by : Seneca: http://zenit.senecac.on.ca/wiki/index.php/Processing.js
Hyper-Metrix: http://hyper-metrix.com/#Processing
BuildingSky: http://weare.buildingsky.net/pages/processing-js
*/
(function() {
var undef; // intentionally left undefined
var ajax = function ajax(url) {
var xhr = new XMLHttpRequest();
xhr.open("GET", url, false);
if (xhr.overrideMimeType) {
xhr.overrideMimeType("text/plain");
}
xhr.setRequestHeader("If-Modified-Since", "Fri, 01 Jan 1960 00:00:00 GMT");
xhr.send(null);
// failed request?
if (xhr.status !== 200 && xhr.status !== 0) { throw ("XMLHttpRequest failed, status code " + xhr.status); }
return xhr.responseText;
};
var isDOMPresent = ("document" in this) && !("fake" in this.document);
/* Browsers fixes start */
function fixReplaceByRegExp() {
var re = /t/g;
if ("t".replace(re,"") !== null && re.exec("t")) {
return; // it is not necessary
}
var _ie_replace = String.prototype.replace;
String.prototype.replace = function(searchValue, repaceValue) {
var result = _ie_replace.apply(this, arguments);
if (searchValue instanceof RegExp && searchValue.global) {
searchValue.lastIndex = 0;
}
return result;
};
}
function fixMatchByRegExp() {
var re = /t/g;
if ("t".match(re) !== null && re.exec("t")) {
return; // it is not necessary
}
var _ie_match = String.prototype.match;
String.prototype.match = function(searchValue) {
var result = _ie_match.apply(this, arguments);
if(searchValue instanceof RegExp && searchValue.global) {
searchValue.lastIndex = 0;
}
return result;
};
}
fixReplaceByRegExp();
fixMatchByRegExp();
(function fixOperaCreateImageData() {
try {
if (!("createImageData" in CanvasRenderingContext2D.prototype)) {
CanvasRenderingContext2D.prototype.createImageData = function (sw, sh) {
return new ImageData(sw, sh);
};
}
} catch(e) {}
}());
/* Browsers fixes end */
var PConstants = {
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,
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,
// p.key will be CODED and p.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,
INS: 45,
DEL: 46,
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,
// 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: parseInt(360 / 0.5, 10),
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
};
// Typed Arrays: fallback to WebGL arrays or Native JS arrays if unavailable
function setupTypedArray(name, fallback) {
// check if TypedArray exists
// typeof on Minefield and Chrome return function, typeof on Webkit returns object.
if (typeof this[name] !== "function" && typeof this[name] !== "object") {
// nope.. check if WebGLArray exists
if (typeof this[fallback] === "function") {
this[name] = this[fallback];
} else {
// nope.. set as Native JS array
this[name] = function(obj) {
if (obj instanceof Array) {
return obj;
} else if (typeof obj === "number") {
var arr = [];
arr.length = obj;
return arr;
}
};
}
}
}
setupTypedArray("Float32Array", "WebGLFloatArray");
setupTypedArray("Int32Array", "WebGLIntArray");
setupTypedArray("Uint16Array", "WebGLUnsignedShortArray");
setupTypedArray("Uint8Array", "WebGLUnsignedByteArray");
/**
* Returns Java hashCode() result for the object. If the object has the "hashCode" function,
* it preforms the call of this function. Otherwise it uses/creates the "$id" property,
* which is used as the hashCode.
*
* @param {Object} obj The object.
* @returns {int} The object's hash code.
*/
function virtHashCode(obj) {
if (obj.constructor === String) {
var hash = 0;
for (var i = 0; i < obj.length; ++i) {
hash = (hash * 31 + obj.charCodeAt(i)) & 0xFFFFFFFF;
}
return hash;
} else if (typeof(obj) !== "object") {
return obj & 0xFFFFFFFF;
} else if (obj.hashCode instanceof Function) {
return obj.hashCode();
} else {
if (obj.$id === undef) {
obj.$id = ((Math.floor(Math.random() * 0x10000) - 0x8000) << 16) | Math.floor(Math.random() * 0x10000);
}
return obj.$id;
}
}
/**
* Returns Java equals() result for two objects. If the first object
* has the "equals" function, it preforms the call of this function.
* Otherwise the method uses the JavaScript === operator.
*
* @param {Object} obj The first object.
* @param {Object} other The second object.
*
* @returns {boolean} true if the objects are equal.
*/
function virtEquals(obj, other) {
if (obj === null || other === null) {
return (obj === null) && (other === null);
} else if (obj.constructor === String) {
return obj === other;
} else if (typeof(obj) !== "object") {
return obj === other;
} else if (obj.equals instanceof Function) {
return obj.equals(other);
} else {
return obj === other;
}
}
/**
* 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];
} else {
array = [];
array.length = 0 | arguments[0];
}
/**
* @member ArrayList
* 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.
*/
this.get = function(i) {
return array[i];
};
/**
* @member ArrayList
* 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.
*/
this.contains = function(item) {
for (var i = 0, len = array.length; i < len; ++i) {
if (virtEquals(item, array[i])) {
return true;
}
}
return false;
};
/**
* @member ArrayList
* 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
*/
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.");
}
};
/**
* @member ArrayList
* 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
*/
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.");
}
};
/**
* @member ArrayList
* ArrayList.size() Returns the number of elements in this list.
*
* @returns {int} the number of elements in this list
*/
this.size = function() {
return array.length;
};
/**
* @member ArrayList
* ArrayList.clear() Removes all of the elements from this list. The list will be empty after this call returns.
*/
this.clear = function() {
array.length = 0;
};
/**
* @member ArrayList
* ArrayList.remove() Removes the element at the specified position in this list.
* Shifts any subsequent elements to the left (subtracts one from their indices).
*
* @param {int} index the index of the element to removed.
*
* @returns {Object} the element that was removed from the list
*/
this.remove = function(i) {
return array.splice(i, 1)[0];
};
/**
* @member ArrayList
* ArrayList.isEmpty() Tests if this list has no elements.
*
* @returns {boolean} true if this list has no elements; false otherwise
*/
this.isEmpty = function() {
return !array.length;
};
/**
* @member ArrayList
* ArrayList.clone() Returns a shallow copy of this ArrayList instance. (The elements themselves are not copied.)
*
* @returns {ArrayList} a clone of this ArrayList instance
*/
this.clone = function() {
return new ArrayList(array.slice(0));
};
/**
* @member ArrayList
* 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
*/
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() {
/**
* @member HashMap
* 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
*/
function HashMap() {
if (arguments.length === 1 && arguments[0].constructor === 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 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]);
}
}
buckets = [];
buckets.length = buckets.length * 2;
for (var j = 0; j < allEntries.length; ++j) {
var index = virtHashCode(allEntries[j].key) % buckets.length;
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;
}
}
}
/*
* @member Iterator
* Checks if the Iterator has more items
*/
this.hasNext = function() {
return !endOfBuckets;
};
/*
* @member Iterator
* Return the next Item
*/
this.next = function() {
var result = conversion(buckets[bucketIndex][itemIndex]);
findNext();
return result;
};
/*
* @member Iterator
* Remove the current item
*/
this.remove = function() {
removeItem(this.next());
--itemIndex;
};
findNext();
}
function Set(conversion, isIn, removeItem) {
this.clear = function() {
hashMap.clear();
};
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 hashMap.isEmpty();
};
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 hashMap.size();
};
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();
map.putAll(this);
return map;
};
this.containsKey = function(key) {
var index = virtHashCode(key) % buckets.length;
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.constructor === Entry && pair._isIn(hashMap);
},
function(pair) {
return hashMap.remove(pair.getKey());
});
};
this.get = function(key) {
var index = virtHashCode(key) % buckets.length;
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(
function(pair) {
return pair.key;
},
function(key) {
return hashMap.containsKey(key);
},
function(key) {
return hashMap.remove(key);
});
};
this.put = function(key, value) {
var index = virtHashCode(key) % buckets.length;
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 = virtHashCode(key) % buckets.length;
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.size = function() {
return count;
};
this.values = function() {
var result = [];
var it = this.entrySet().iterator();
while (it.hasNext()) {
var entry = it.next();
result.push(entry.getValue());
}
return result;
};
}
return HashMap;
}());
var PVector = (function() {
function PVector(x, y, z) {
this.x = x || 0;
this.y = y || 0;
this.z = z || 0;
}
function createPVectorMethod(method) {
return function(v1, v2) {
var v = v1.get();
v[method](v2);
return v;
};
}
function createSimplePVectorMethod(method) {
return function(v1, v2) {
return v1[method](v2);
};
}
var simplePVMethods = "dist dot cross".split(" ");
var method = simplePVMethods.length;
PVector.angleBetween = function(v1, v2) {
return Math.acos(v1.dot(v2) / (v1.mag() * v2.mag()));
};
// Common vector operations for PVector
PVector.prototype = {
set: function(v, y, z) {
if (arguments.length === 1) {
this.set(v.x || v[0], v.y || v[1], v.z || v[2]);
} else {
this.x = v;
this.y = y;
this.z = z;
}
},
get: function() {
return new PVector(this.x, this.y, this.z);
},
mag: function() {
return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
},
add: function(v, y, z) {
if (arguments.length === 3) {
this.x += v;
this.y += y;
this.z += z;
} else if (arguments.length === 1) {
this.x += v.x;
this.y += v.y;
this.z += v.z;
}
},
sub: function(v, y, z) {
if (arguments.length === 3) {
this.x -= v;
this.y -= y;
this.z -= z;
} else if (arguments.length === 1) {
this.x -= v.x;
this.y -= v.y;
this.z -= v.z;
}
},
mult: function(v) {
if (typeof v === 'number') {
this.x *= v;
this.y *= v;
this.z *= v;
} else if (typeof v === 'object') {
this.x *= v.x;
this.y *= v.y;
this.z *= v.z;
}
},
div: function(v) {
if (typeof v === 'number') {
this.x /= v;
this.y /= v;
this.z /= v;
} else if (typeof v === 'object') {
this.x /= v.x;
this.y /= v.y;
this.z /= v.z;
}
},
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);
},
dot: function(v, y, z) {
if (arguments.length === 3) {
return (this.x * v + this.y * y + this.z * z);
} else if (arguments.length === 1) {
return (this.x * v.x + this.y * v.y + this.z * v.z);
}
},
cross: function(v) {
return new PVector(this.y * v.z - v.y * this.z,
this.z * v.x - v.z * this.x,
this.x * v.y - v.x * this.y);
},
normalize: function() {
var m = this.mag();
if (m > 0) {
this.div(m);
}
},
limit: function(high) {
if (this.mag() > high) {
this.normalize();
this.mult(high);
}
},
heading2D: function() {
return (-Math.atan2(-this.y, this.x));
},
toString: function() {
return "[" + this.x + ", " + this.y + ", " + this.z + "]";
},
array: function() {
return [this.x, this.y, this.z];
}
};
while (method--) {
PVector[simplePVMethods[method]] = createSimplePVectorMethod(simplePVMethods[method]);
}
for (method in PVector.prototype) {
if (PVector.prototype.hasOwnProperty(method) && !PVector.hasOwnProperty(method)) {
PVector[method] = createPVectorMethod(method);
}
}
return PVector;
}());
/**
* 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();
} else 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;
}
};
// Building defaultScope. Changing of the prototype protects
// internal Processing code from the changes in defaultScope
function DefaultScope() {}
DefaultScope.prototype = PConstants;
var defaultScope = new DefaultScope();
defaultScope.ArrayList = ArrayList;
defaultScope.HashMap = HashMap;
defaultScope.PVector = PVector;
defaultScope.ObjectIterator = ObjectIterator;
//defaultScope.PImage = PImage; // TODO
//defaultScope.PShape = PShape; // TODO
//defaultScope.PShapeSVG = PShapeSVG; // TODO
var Processing = this.Processing = function Processing(curElement, aCode) {
// Previously we allowed calling Processing as a func instead of ctor, but no longer.
if (!(this instanceof Processing)) {
throw("called Processing constructor as if it were a function: missing 'new'.");
}
// When something new is added to "p." it must also be added to the "names" array.
// The names array contains the names of everything that is inside "p."
var p = this;
// PJS specific (non-p5) methods and properties to externalize
p.externals = {
canvas: curElement,
context: undef,
sketch: undef
};
p.name = 'Processing.js Instance'; // Set Processing defaults / environment variables
p.use3DContext = false; // default '2d' canvas context
/**
* 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.
*/
p.focused = false;
p.breakShape = false;
// Glyph path storage for textFonts
p.glyphTable = {};
// Global vars for tracking mouse position
p.pmouseX = 0;
p.pmouseY = 0;
p.mouseX = 0;
p.mouseY = 0;
p.mouseButton = 0;
p.mouseScroll = 0;
// Undefined event handlers to be replaced by user when needed
p.mouseClicked = undef;
p.mouseDragged = undef;
p.mouseMoved = undef;
p.mousePressed = undef;
p.mouseReleased = undef;
p.mouseScrolled = undef;
p.mouseOver = undef;
p.mouseOut = undef;
p.touchStart = undef;
p.touchEnd = undef;
p.touchMove = undef;
p.touchCancel = undef;
p.key = undef;
p.keyCode = undef;
p.keyPressed = function(){}; // needed to remove function checks
p.keyReleased = function(){};
p.keyTyped = function(){};
p.draw = undef;
p.setup = undef;
// Remapped vars
p.__mousePressed = false;
p.__keyPressed = false;
p.__frameRate = 0;
// The current animation frame
p.frameCount = 0;
// The height/width of the canvas
p.width = curElement.width - 0;
p.height = curElement.height - 0;
p.defineProperty = function(obj, name, desc) {
if("defineProperty" in Object) {
Object.defineProperty(obj, name, desc);
} else {
if (desc.hasOwnProperty("get")) {
obj.__defineGetter__(name, desc.get);
}
if (desc.hasOwnProperty("set")) {
obj.__defineSetter__(name, desc.set);
}
}
};
// "Private" variables used to maintain state
var curContext,
curSketch,
online = true,
doFill = true,
fillStyle = [1.0, 1.0, 1.0, 1.0],
currentFillColor = 0xFFFFFFFF,
isFillDirty = true,
doStroke = true,
strokeStyle = [0.8, 0.8, 0.8, 1.0],
currentStrokeColor = 0xFFFDFDFD,
isStrokeDirty = true,
lineWidth = 1,
loopStarted = false,
doLoop = true,
looping = 0,
curRectMode = PConstants.CORNER,
curEllipseMode = PConstants.CENTER,
normalX = 0,
normalY = 0,
normalZ = 0,
normalMode = PConstants.NORMAL_MODE_AUTO,
inDraw = false,
curFrameRate = 60,
curCursor = PConstants.ARROW,
oldCursor = curElement.style.cursor,
curMsPerFrame = 1,
curShape = PConstants.POLYGON,
curShapeCount = 0,
curvePoints = [],
curTightness = 0,
curveDet = 20,
curveInited = false,
bezDetail = 20,
colorModeA = 255,
colorModeX = 255,
colorModeY = 255,
colorModeZ = 255,
pathOpen = false,
mouseDragging = false,
curColorMode = PConstants.RGB,
curTint = null,
curTextSize = 12,
curTextFont = {name: "\"Arial\", sans-serif", origName: "Arial"},
curTextLeading = 14,
getLoaded = false,
start = new Date().getTime(),
timeSinceLastFPS = start,
framesSinceLastFPS = 0,
textcanvas,
curveBasisMatrix,
curveToBezierMatrix,
curveDrawMatrix,
bezierDrawMatrix,
bezierBasisInverse,
bezierBasisMatrix,
// Keys and Keystrokes
firstCodedDown = true, // first coded key stroke
firstEDGKeyDown = true, // first Enter - Delete Google key stroke
firstEDMKeyDown = true, // first Enter - Delete Mozilla key stroke
firstMKeyDown = true, // first Mozilla key stroke
firstGKeyDown = true, // first Google key stroke
gRefire = false, // Google refire
curContextCache = { attributes: {}, locations: {} },
// Shaders
programObject3D,
programObject2D,
programObjectUnlitShape,
boxBuffer,
boxNormBuffer,
boxOutlineBuffer,
rectBuffer,
rectNormBuffer,
sphereBuffer,
lineBuffer,
fillBuffer,
fillColorBuffer,
strokeColorBuffer,
pointBuffer,
shapeTexVBO,
canTex, // texture for createGraphics
textTex, // texture for 3d tex
curTexture = {width:0,height:0},
curTextureMode = PConstants.IMAGE,
usingTexture = false,
textBuffer,
textureBuffer,
indexBuffer,
// Text alignment
horizontalTextAlignment = PConstants.LEFT,
verticalTextAlignment = PConstants.BASELINE,
baselineOffset = 0.2, // percent
tMode = PConstants.MODEL,
// Pixels cache
originalContext,
proxyContext = null,
isContextReplaced = false,
setPixelsCached,
maxPixelsCached = 1000,
codedKeys = [ PConstants.SHIFT, PConstants.CONTROL, PConstants.ALT, PConstants.CAPSLK, PConstants.PGUP, PConstants.PGDN,
PConstants.END, PConstants.HOME, PConstants.LEFT, PConstants.UP, PConstants.RIGHT, PConstants.DOWN, PConstants.NUMLK,
PConstants.INS, PConstants.F1, PConstants.F2, PConstants.F3, PConstants.F4, PConstants.F5, PConstants.F6, PConstants.F7,
PConstants.F8, PConstants.F9, PConstants.F10, PConstants.F11, PConstants.F12 ];
// Get padding and border style widths for mouse offsets
var stylePaddingLeft, stylePaddingTop, styleBorderLeft, styleBorderTop;
if (document.defaultView && document.defaultView.getComputedStyle) {
stylePaddingLeft = parseInt(document.defaultView.getComputedStyle(curElement, null)['paddingLeft'], 10) || 0;
stylePaddingTop = parseInt(document.defaultView.getComputedStyle(curElement, null)['paddingTop'], 10) || 0;
styleBorderLeft = parseInt(document.defaultView.getComputedStyle(curElement, null)['borderLeftWidth'], 10) || 0;
styleBorderTop = parseInt(document.defaultView.getComputedStyle(curElement, null)['borderTopWidth'], 10) || 0;
}
// User can only have MAX_LIGHTS lights
var lightCount = 0;
//sphere stuff
var sphereDetailV = 0,
sphereDetailU = 0,
sphereX = [],
sphereY = [],
sphereZ = [],
sinLUT = new Float32Array(PConstants.SINCOS_LENGTH),
cosLUT = new Float32Array(PConstants.SINCOS_LENGTH),
sphereVerts,
sphereNorms;
// Camera defaults and settings
var cam,
cameraInv,
forwardTransform,
reverseTransform,
modelView,
modelViewInv,
userMatrixStack,
inverseCopy,
projection,
manipulatingCamera = false,
frustumMode = false,
cameraFOV = 60 * (Math.PI / 180),
cameraX = curElement.width / 2,
cameraY = curElement.height / 2,
cameraZ = cameraY / Math.tan(cameraFOV / 2),
cameraNear = cameraZ / 10,
cameraFar = cameraZ * 10,
cameraAspect = curElement.width / curElement.height;
var vertArray = [],
curveVertArray = [],
curveVertCount = 0,
isCurve = false,
isBezier = false,
firstVert = true;
//PShape stuff
var curShapeMode = PConstants.CORNER;
var colors = {
aliceblue: "#f0f8ff",
antiquewhite: "#faebd7",
aqua: "#00ffff",
aquamarine: "#7fffd4",
azure: "#f0ffff",
beige: "#f5f5dc",
bisque: "#ffe4c4",
black: "#000000",
blanchedalmond: "#ffebcd",
blue: "#0000ff",
blueviolet: "#8a2be2",
brown: "#a52a2a",
burlywood: "#deb887",
cadetblue: "#5f9ea0",
chartreuse: "#7fff00",
chocolate: "#d2691e",
coral: "#ff7f50",
cornflowerblue: "#6495ed",
cornsilk: "#fff8dc",
crimson: "#dc143c",
cyan: "#00ffff",
darkblue: "#00008b",
darkcyan: "#008b8b",
darkgoldenrod: "#b8860b",
darkgray: "#a9a9a9",
darkgreen: "#006400",
darkkhaki: "#bdb76b",
darkmagenta: "#8b008b",
darkolivegreen: "#556b2f",
darkorange: "#ff8c00",
darkorchid: "#9932cc",
darkred: "#8b0000",
darksalmon: "#e9967a",
darkseagreen: "#8fbc8f",
darkslateblue: "#483d8b",
darkslategray: "#2f4f4f",
darkturquoise: "#00ced1",
darkviolet: "#9400d3",
deeppink: "#ff1493",
deepskyblue: "#00bfff",
dimgray: "#696969",
dodgerblue: "#1e90ff",
firebrick: "#b22222",
floralwhite: "#fffaf0",
forestgreen: "#228b22",
fuchsia: "#ff00ff",
gainsboro: "#dcdcdc",
ghostwhite: "#f8f8ff",
gold: "#ffd700",
goldenrod: "#daa520",
gray: "#808080",
green: "#008000",
greenyellow: "#adff2f",
honeydew: "#f0fff0",
hotpink: "#ff69b4",
indianred: "#cd5c5c",
indigo: "#4b0082",
ivory: "#fffff0",
khaki: "#f0e68c",
lavender: "#e6e6fa",
lavenderblush: "#fff0f5",
lawngreen: "#7cfc00",
lemonchiffon: "#fffacd",
lightblue: "#add8e6",
lightcoral: "#f08080",
lightcyan: "#e0ffff",
lightgoldenrodyellow: "#fafad2",
lightgrey: "#d3d3d3",
lightgreen: "#90ee90",
lightpink: "#ffb6c1",
lightsalmon: "#ffa07a",
lightseagreen: "#20b2aa",
lightskyblue: "#87cefa",
lightslategray: "#778899",
lightsteelblue: "#b0c4de",
lightyellow: "#ffffe0",
lime: "#00ff00",
limegreen: "#32cd32",
linen: "#faf0e6",
magenta: "#ff00ff",
maroon: "#800000",
mediumaquamarine: "#66cdaa",
mediumblue: "#0000cd",
mediumorchid: "#ba55d3",
mediumpurple: "#9370d8",
mediumseagreen: "#3cb371",
mediumslateblue: "#7b68ee",
mediumspringgreen: "#00fa9a",
mediumturquoise: "#48d1cc",
mediumvioletred: "#c71585",
midnightblue: "#191970",
mintcream: "#f5fffa",
mistyrose: "#ffe4e1",
moccasin: "#ffe4b5",
navajowhite: "#ffdead",
navy: "#000080",
oldlace: "#fdf5e6",
olive: "#808000",
olivedrab: "#6b8e23",
orange: "#ffa500",
orangered: "#ff4500",
orchid: "#da70d6",
palegoldenrod: "#eee8aa",
palegreen: "#98fb98",
paleturquoise: "#afeeee",
palevioletred: "#d87093",
papayawhip: "#ffefd5",
peachpuff: "#ffdab9",
peru: "#cd853f",
pink: "#ffc0cb",
plum: "#dda0dd",
powderblue: "#b0e0e6",
purple: "#800080",
red: "#ff0000",
rosybrown: "#bc8f8f",
royalblue: "#4169e1",
saddlebrown: "#8b4513",
salmon: "#fa8072",
sandybrown: "#f4a460",
seagreen: "#2e8b57",
seashell: "#fff5ee",
sienna: "#a0522d",
silver: "#c0c0c0",
skyblue: "#87ceeb",
slateblue: "#6a5acd",
slategray: "#708090",
snow: "#fffafa",
springgreen: "#00ff7f",
steelblue: "#4682b4",
tan: "#d2b48c",
teal: "#008080",
thistle: "#d8bfd8",
tomato: "#ff6347",
turquoise: "#40e0d0",
violet: "#ee82ee",
wheat: "#f5deb3",
white: "#ffffff",
whitesmoke: "#f5f5f5",
yellow: "#ffff00",
yellowgreen: "#9acd32"
};
// Stores states for pushStyle() and popStyle().
var styleArray = [];
// Vertices are specified in a counter-clockwise order
// triangles are in this order: back, front, right, bottom, left, top
var boxVerts = new Float32Array([
0.5, 0.5, -0.5, 0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5, 0.5, -0.5, 0.5, 0.5, -0.5,
0.5, 0.5, 0.5, -0.5, 0.5, 0.5, -0.5, -0.5, 0.5, -0.5, -0.5, 0.5, 0.5, -0.5, 0.5, 0.5, 0.5, 0.5,
0.5, 0.5, -0.5, 0.5, 0.5, 0.5, 0.5, -0.5, 0.5, 0.5, -0.5, 0.5, 0.5, -0.5, -0.5, 0.5, 0.5, -0.5,
0.5, -0.5, -0.5, 0.5, -0.5, 0.5, -0.5, -0.5, 0.5, -0.5, -0.5, 0.5, -0.5, -0.5, -0.5, 0.5, -0.5, -0.5,
-0.5, -0.5, -0.5, -0.5, -0.5, 0.5, -0.5, 0.5, 0.5, -0.5, 0.5, 0.5, -0.5, 0.5, -0.5, -0.5, -0.5, -0.5,
0.5, 0.5, 0.5, 0.5, 0.5, -0.5, -0.5, 0.5, -0.5, -0.5, 0.5, -0.5, -0.5, 0.5, 0.5, 0.5, 0.5, 0.5]);
var boxOutlineVerts = new Float32Array([
0.5, 0.5, 0.5, 0.5, -0.5, 0.5, 0.5, 0.5, -0.5, 0.5, -0.5, -0.5,
-0.5, 0.5, -0.5, -0.5, -0.5, -0.5, -0.5, 0.5, 0.5, -0.5, -0.5, 0.5,
0.5, 0.5, 0.5, 0.5, 0.5, -0.5, 0.5, 0.5, -0.5, -0.5, 0.5, -0.5,
-0.5, 0.5, -0.5, -0.5, 0.5, 0.5, -0.5, 0.5, 0.5, 0.5, 0.5, 0.5,
0.5, -0.5, 0.5, 0.5, -0.5, -0.5, 0.5, -0.5, -0.5, -0.5, -0.5, -0.5,
-0.5, -0.5, -0.5, -0.5, -0.5, 0.5, -0.5, -0.5, 0.5, 0.5, -0.5, 0.5]);
var boxNorms = new Float32Array([
0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1,
0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1,
1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0,
0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0,
-1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0,
0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0]);
// These verts are used for the fill and stroke using TRIANGLE_FAN and LINE_LOOP
var rectVerts = new Float32Array([0,0,0, 0,1,0, 1,1,0, 1,0,0]);
var rectNorms = new Float32Array([0,0,-1, 0,0,-1, 0,0,-1, 0,0,-1]);
// Vertex shader for points and lines
var vShaderSrcUnlitShape =
"varying vec4 frontColor;" +
"attribute vec3 aVertex;" +
"attribute vec4 aColor;" +
"uniform mat4 uView;" +
"uniform mat4 uProjection;" +
"void main(void) {" +
" frontColor = aColor;" +
" gl_Position = uProjection * uView * vec4(aVertex, 1.0);" +
"}";
var fShaderSrcUnlitShape =
"#ifdef GL_ES\n" +
"precision highp float;\n" +
"#endif\n" +
"varying vec4 frontColor;" +
"void main(void){" +
" gl_FragColor = frontColor;" +
"}";
// Vertex shader for points and lines
var vertexShaderSource2D =
"varying vec4 frontColor;" +
"attribute vec3 Vertex;" +
"attribute vec2 aTextureCoord;" +
"uniform vec4 color;" +
"uniform mat4 model;" +
"uniform mat4 view;" +
"uniform mat4 projection;" +
"uniform float pointSize;" +
"varying vec2 vTextureCoord;"+
"void main(void) {" +
" gl_PointSize = pointSize;" +
" frontColor = color;" +
" gl_Position = projection * view * model * vec4(Vertex, 1.0);" +
" vTextureCoord = aTextureCoord;" +
"}";
var fragmentShaderSource2D =
"#ifdef GL_ES\n" +
"precision highp float;\n" +
"#endif\n" +
"varying vec4 frontColor;" +
"varying vec2 vTextureCoord;"+
"uniform sampler2D uSampler;"+
"uniform int picktype;"+
"void main(void){" +
" if(picktype == 0){"+
" gl_FragColor = frontColor;" +
" }" +
" else if(picktype == 1){"+
" float alpha = texture2D(uSampler, vTextureCoord).a;"+
" gl_FragColor = vec4(frontColor.rgb*alpha, alpha);\n"+
" }"+
"}";
// Vertex shader for boxes and spheres
var vertexShaderSource3D =
"varying vec4 frontColor;" +
"attribute vec3 Vertex;" +
"attribute vec3 Normal;" +
"attribute vec4 aColor;" +
"attribute vec2 aTexture;" +
"varying vec2 vTexture;" +
"uniform vec4 color;" +
"uniform bool usingMat;" +
"uniform vec3 specular;" +
"uniform vec3 mat_emissive;" +
"uniform vec3 mat_ambient;" +
"uniform vec3 mat_specular;" +
"uniform float shininess;" +
"uniform mat4 model;" +
"uniform mat4 view;" +
"uniform mat4 projection;" +
"uniform mat4 normalTransform;" +
"uniform int lightCount;" +
"uniform vec3 falloff;" +
// careful changing the order of these fields. Some cards
// have issues with memory alignment
"struct Light {" +
" int type;" +
" vec3 color;" +
" vec3 position;" +
" vec3 direction;" +
" float angle;" +
" vec3 halfVector;" +
" float concentration;" +
"};" +
// nVidia cards have issues with arrays of structures
// so instead we create 8 instances of Light
"uniform Light lights0;" +
"uniform Light lights1;" +
"uniform Light lights2;" +
"uniform Light lights3;" +
"uniform Light lights4;" +
"uniform Light lights5;" +
"uniform Light lights6;" +
"uniform Light lights7;" +
// GLSL does not support switch
"Light getLight(int index){" +
" if(index == 0) return lights0;" +
" if(index == 1) return lights1;" +
" if(index == 2) return lights2;" +
" if(index == 3) return lights3;" +
" if(index == 4) return lights4;" +
" if(index == 5) return lights5;" +
" if(index == 6) return lights6;" +
// some cards complain that not all paths return if we have
// this last one in a conditional.
" return lights7;" +
"}" +
"void AmbientLight( inout vec3 totalAmbient, in vec3 ecPos, in Light light ) {" +
// Get the vector from the light to the vertex
// Get the distance from the current vector to the light position
" float d = length( light.position - ecPos );" +
" float attenuation = 1.0 / ( falloff[0] + ( falloff[1] * d ) + ( falloff[2] * d * d ));" +
" totalAmbient += light.color * attenuation;" +
"}" +
"void DirectionalLight( inout vec3 col, inout vec3 spec, in vec3 vertNormal, in vec3 ecPos, in Light light ) {" +
" float powerfactor = 0.0;" +
" float nDotVP = max(0.0, dot( vertNormal, normalize(-light.position) ));" +
" float nDotVH = max(0.0, dot( vertNormal, normalize(-light.position-ecPos )));" +
" if( nDotVP != 0.0 ){" +
" powerfactor = pow( nDotVH, shininess );" +
" }" +
" col += light.color * nDotVP;" +
" spec += specular * powerfactor;" +
"}" +
"void PointLight( inout vec3 col, inout vec3 spec, in vec3 vertNormal, in vec3 ecPos, in vec3 eye, in Light light ) {" +
" float powerfactor;" +
// Get the vector from the light to the vertex
" vec3 VP = light.position - ecPos;" +
// Get the distance from the current vector to the light position
" float d = length( VP ); " +
// Normalize the light ray so it can be used in the dot product operation.
" VP = normalize( VP );" +
" float attenuation = 1.0 / ( falloff[0] + ( falloff[1] * d ) + ( falloff[2] * d * d ));" +
" float nDotVP = max( 0.0, dot( vertNormal, VP ));" +
" vec3 halfVector = normalize( VP + eye );" +
" float nDotHV = max( 0.0, dot( vertNormal, halfVector ));" +
" if( nDotVP == 0.0) {" +
" powerfactor = 0.0;" +
" }" +
" else{" +
" powerfactor = pow( nDotHV, shininess );" +
" }" +
" spec += specular * powerfactor * attenuation;" +
" col += light.color * nDotVP * attenuation;" +
"}" +
/*
*/
"void SpotLight( inout vec3 col, inout vec3 spec, in vec3 vertNormal, in vec3 ecPos, in vec3 eye, in Light light ) {" +
" float spotAttenuation;" +
" float powerfactor;" +
// calculate the vector from the current vertex to the light.
" vec3 VP = light.position - ecPos; " +
" vec3 ldir = normalize( -light.direction );" +
// get the distance from the spotlight and the vertex
" float d = length( VP );" +
" VP = normalize( VP );" +
" float attenuation = 1.0 / ( falloff[0] + ( falloff[1] * d ) + ( falloff[2] * d * d ) );" +
// dot product of the vector from vertex to light and light direction.
" float spotDot = dot( VP, ldir );" +
// if the vertex falls inside the cone
// The following is failing on Windows systems
// removed until we find a workaround
//" if( spotDot < cos( light.angle ) ) {" +
//" spotAttenuation = pow( spotDot, light.concentration );" +
//" }" +
//" else{" +
" spotAttenuation = 1.0;" +
//" }" +
" attenuation *= spotAttenuation;" +
" float nDotVP = max( 0.0, dot( vertNormal, VP ));" +
" vec3 halfVector = normalize( VP + eye );" +
" float nDotHV = max( 0.0, dot( vertNormal, halfVector ));" +
" if( nDotVP == 0.0 ) {" +
" powerfactor = 0.0;" +
" }" +
" else {" +
" powerfactor = pow( nDotHV, shininess );" +
" }" +
" spec += specular * powerfactor * attenuation;" +
" col += light.color * nDotVP * attenuation;" +
"}" +
"void main(void) {" +
" vec3 finalAmbient = vec3( 0.0, 0.0, 0.0 );" +
" vec3 finalDiffuse = vec3( 0.0, 0.0, 0.0 );" +
" vec3 finalSpecular = vec3( 0.0, 0.0, 0.0 );" +
" vec4 col = color;" +
" if(color[0] == -1.0){" +
" col = aColor;" +
" }" +
" vec3 norm = vec3( normalTransform * vec4( Normal, 0.0 ) );" +
" vec4 ecPos4 = view * model * vec4(Vertex,1.0);" +
" vec3 ecPos = (vec3(ecPos4))/ecPos4.w;" +
" vec3 eye = vec3( 0.0, 0.0, 1.0 );" +
// If there were no lights this draw call, just use the
// assigned fill color of the shape and the specular value
" if( lightCount == 0 ) {" +
" frontColor = col + vec4(mat_specular,1.0);" +
" }" +
" else {" +
// WebGL forces us to iterate over a constant value
// so we can't iterate using lightCount
" for( int i = 0; i < 8; i++ ) {" +
" Light l = getLight(i);" +
// We can stop iterating if we know we have gone past
// the number of lights which are on
" if( i >= lightCount ){" +
" break;" +
" }" +
" if( l.type == 0 ) {" +
" AmbientLight( finalAmbient, ecPos, l );" +
" }" +
" else if( l.type == 1 ) {" +
" DirectionalLight( finalDiffuse, finalSpecular, norm, ecPos, l );" +
" }" +
" else if( l.type == 2 ) {" +
" PointLight( finalDiffuse, finalSpecular, norm, ecPos, eye, l );" +
" }" +
" else {" +
" SpotLight( finalDiffuse, finalSpecular, norm, ecPos, eye, l );" +
" }" +
" }" +
" if( usingMat == false ) {" +
" frontColor = vec4(" +
" vec3(col) * finalAmbient +" +
" vec3(col) * finalDiffuse +" +
" vec3(col) * finalSpecular," +
" col[3] );" +
" }" +
" else{" +
" frontColor = vec4( " +
" mat_emissive + " +
" (vec3(col) * mat_ambient * finalAmbient) + " +
" (vec3(col) * finalDiffuse) + " +
" (mat_specular * finalSpecular), " +
" col[3] );" +
" }" +
" }" +
" vTexture.xy = aTexture.xy;" +
" gl_Position = projection * view * model * vec4( Vertex, 1.0 );" +
"}";
var fragmentShaderSource3D =
"#ifdef GL_ES\n" +
"precision highp float;\n" +
"#endif\n" +
"varying vec4 frontColor;" +
"uniform sampler2D sampler;" +
"uniform bool usingTexture;" +
"varying vec2 vTexture;" +
// In Processing, when a texture is used, the fill color is ignored
"void main(void){" +
" if(usingTexture){" +
" gl_FragColor = vec4(texture2D(sampler, vTexture.xy));" +
" }"+
" else{" +
" gl_FragColor = frontColor;" +
" }" +
"}";
////////////////////////////////////////////////////////////////////////////
// 3D Functions
////////////////////////////////////////////////////////////////////////////
/*
* Sets a uniform variable in a program object to a particular
* value. Before calling this function, ensure the correct
* program object has been installed as part of the current
* rendering state by calling useProgram.
*
* On some systems, if the variable exists in the shader but isn't used,
* the compiler will optimize it out and this function will fail.
*
* @param {WebGLProgram} programObj program object returned from
* createProgramObject
* @param {String} varName the name of the variable in the shader
* @param {float | Array} varValue either a scalar value or an Array
*
* @returns none
*
* @see uniformi
* @see uniformMatrix
*/
function uniformf(cacheId, programObj, varName, varValue) {
var varLocation = curContextCache.locations[cacheId];
if(varLocation === undef) {
varLocation = curContext.getUniformLocation(programObj, varName);
curContextCache.locations[cacheId] = varLocation;
}
// the variable won't be found if it was optimized out.
if (varLocation !== -1) {
if (varValue.length === 4) {
curContext.uniform4fv(varLocation, varValue);
} else if (varValue.length === 3) {
curContext.uniform3fv(varLocation, varValue);
} else if (varValue.length === 2) {
curContext.uniform2fv(varLocation, varValue);
} else {
curContext.uniform1f(varLocation, varValue);
}
}
}
/**
* Sets a uniform int or int array in a program object to a particular
* value. Before calling this function, ensure the correct
* program object has been installed as part of the current
* rendering state.
*
* On some systems, if the variable exists in the shader but isn't used,
* the compiler will optimize it out and this function will fail.
*
* @param {WebGLProgram} programObj program object returned from
* createProgramObject
* @param {String} varName the name of the variable in the shader
* @param {int | Array} varValue either a scalar value or an Array
*
* @returns none
*
* @see uniformf
* @see uniformMatrix
*/
function uniformi(cacheId, programObj, varName, varValue) {
var varLocation = curContextCache.locations[cacheId];
if(varLocation === undef) {
varLocation = curContext.getUniformLocation(programObj, varName);
curContextCache.locations[cacheId] = varLocation;
}
// the variable won't be found if it was optimized out.
if (varLocation !== -1) {
if (varValue.length === 4) {
curContext.uniform4iv(varLocation, varValue);
} else if (varValue.length === 3) {
curContext.uniform3iv(varLocation, varValue);
} else if (varValue.length === 2) {
curContext.uniform2iv(varLocation, varValue);
} else {
curContext.uniform1i(varLocation, varValue);
}
}
}
/**
* Binds the VBO, sets the vertex attribute data for the program
* object and enables the attribute.
*
* On some systems, if the attribute exists in the shader but
* isn't used, the compiler will optimize it out and this
* function will fail.
*
* @param {WebGLProgram} programObj program object returned from
* createProgramObject
* @param {String} varName the name of the variable in the shader
* @param {int} size the number of components per vertex attribute
* @param {WebGLBuffer} VBO Vertex Buffer Object
*
* @returns none
*
* @see disableVertexAttribPointer
*/
function vertexAttribPointer(cacheId, programObj, varName, size, VBO) {
var varLocation = curContextCache.attributes[cacheId];
if(varLocation === undef) {
varLocation = curContext.getAttribLocation(programObj, varName);
curContextCache.attributes[cacheId] = varLocation;
}
if (varLocation !== -1) {
curContext.bindBuffer(curContext.ARRAY_BUFFER, VBO);
curContext.vertexAttribPointer(varLocation, size, curContext.FLOAT, false, 0, 0);
curContext.enableVertexAttribArray(varLocation);
}
}
/**
* Disables a program object attribute from being sent to WebGL.
*
* @param {WebGLProgram} programObj program object returned from
* createProgramObject
* @param {String} varName name of the attribute
*
* @returns none
*
* @see vertexAttribPointer
*/
function disableVertexAttribPointer(cacheId, programObj, varName){
var varLocation = curContextCache.attributes[cacheId];
if(varLocation === undef) {
varLocation = curContext.getAttribLocation(programObj, varName);
curContextCache.attributes[cacheId] = varLocation;
}
if (varLocation !== -1) {
curContext.disableVertexAttribArray(varLocation);
}
}
/**
* Sets the value of a uniform matrix variable in a program
* object. Before calling this function, ensure the correct
* program object has been installed as part of the current
* rendering state.
*
* On some systems, if the variable exists in the shader but
* isn't used, the compiler will optimize it out and this
* function will fail.
*
* @param {WebGLProgram} programObj program object returned from
* createProgramObject
* @param {String} varName the name of the variable in the shader
* @param {boolean} transpose must be false
* @param {Array} matrix an array of 4, 9 or 16 values
*
* @returns none
*
* @see uniformi
* @see uniformf
*/
function uniformMatrix(cacheId, programObj, varName, transpose, matrix) {
var varLocation = curContextCache.locations[cacheId];
if(varLocation === undef) {
varLocation = curContext.getUniformLocation(programObj, varName);
curContextCache.locations[cacheId] = varLocation;
}
// the variable won't be found if it was optimized out.
if (varLocation !== -1) {
if (matrix.length === 16) {
curContext.uniformMatrix4fv(varLocation, transpose, matrix);
} else if (matrix.length === 9) {
curContext.uniformMatrix3fv(varLocation, transpose, matrix);
} else {
curContext.uniformMatrix2fv(varLocation, transpose, matrix);
}
}
}
var imageModeCorner = function imageModeCorner(x, y, w, h, whAreSizes) {
return {
x: x,
y: y,
w: w,
h: h
};
};
var imageModeConvert = imageModeCorner;
var imageModeCorners = function imageModeCorners(x, y, w, h, whAreSizes) {
return {
x: x,
y: y,
w: whAreSizes ? w : w - x,
h: whAreSizes ? h : h - y
};
};
var imageModeCenter = function imageModeCenter(x, y, w, h, whAreSizes) {
return {
x: x - w / 2,
y: y - h / 2,
w: w,
h: h
};
};
/**
* Creates a WebGL program object.
*
* @param {String} vetexShaderSource
* @param {String} fragmentShaderSource
*
* @returns {WebGLProgram} A program object
*/
var createProgramObject = function(curContext, vetexShaderSource, fragmentShaderSource) {
var vertexShaderObject = curContext.createShader(curContext.VERTEX_SHADER);
curContext.shaderSource(vertexShaderObject, vetexShaderSource);
curContext.compileShader(vertexShaderObject);
if (!curContext.getShaderParameter(vertexShaderObject, curContext.COMPILE_STATUS)) {
throw curContext.getShaderInfoLog(vertexShaderObject);
}
var fragmentShaderObject = curContext.createShader(curContext.FRAGMENT_SHADER);
curContext.shaderSource(fragmentShaderObject, fragmentShaderSource);
curContext.compileShader(fragmentShaderObject);
if (!curContext.getShaderParameter(fragmentShaderObject, curContext.COMPILE_STATUS)) {
throw curContext.getShaderInfoLog(fragmentShaderObject);
}
var programObject = curContext.createProgram();
curContext.attachShader(programObject, vertexShaderObject);
curContext.attachShader(programObject, fragmentShaderObject);
curContext.linkProgram(programObject);
if (!curContext.getProgramParameter(programObject, curContext.LINK_STATUS)) {
throw "Error linking shaders.";
}
return programObject;
};
////////////////////////////////////////////////////////////////////////////
// Char handling
////////////////////////////////////////////////////////////////////////////
var charMap = {};
var Char = p.Character = function Char(chr) {
if (typeof chr === 'string' && chr.length === 1) {
this.code = chr.charCodeAt(0);
} else {
this.code = NaN;
}
return (charMap[this.code] === undef) ? charMap[this.code] = this : charMap[this.code];
};
Char.prototype.toString = function() {
return String.fromCharCode(this.code);
};
Char.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 loadShape() function. The shape() function is used to draw the shape to the display window.
* The PShape object contain a group of methods, linked below, that can operate on the shape data.
*
The loadShape() 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()
*/
var PShape = p.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 = {
/**
* @member PShape
* The isVisible() function returns a boolean value "true" if the image is set to be visible, "false" if not. This is modified with the setVisible() parameter.
*
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
*/
isVisible: function(){
return this.visible;
},
/**
* @member PShape
* The setVisible() function sets the shape to be visible or invisible. This is determined by the value of the visible parameter.
*
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
*/
setVisible: function (visible){
this.visible = visible;
},
/**
* @member PShape
* 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.
*/
disableStyle: function(){
this.style = false;
for(var i = 0, j=this.children.length; i part of the SVG document.
*/
drawPath: function(){
var i, j;
if (this.vertices.length === 0) { return; }
p.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++) {
p.vertex(this.vertices[i][0], this.vertices[i][1]);
}
} else { // drawing 3D vertices
for (i = 0, j = this.vertices.length; i < j; i++) {
p.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) {
p.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;
}
p.breakShape = false;
index++;
} else if (this.vertexCodes[i] === PConstants.BEZIER_VERTEX) {
p.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) {
p.curveVertex(this.vertices[index][0],
this.vertices[index][1]);
index++;
} else if (this.vertexCodes[i] === PConstants.BREAK) {
p.breakShape = true;
}
}
} else { // drawing a 3D path
for (i = 0, j = this.vertexCodes.length; i < j; i++) {
if (this.vertexCodes[i] === PConstants.VERTEX) {
p.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;
}
p.breakShape = false;
} else if (this.vertexCodes[i] === PConstants.BEZIER_VERTEX) {
p.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) {
p.curveVertex(this.vertices[index][0],
this.vertices[index][1],
this.vertices[index][2]);
index++;
} else if (this.vertexCodes[i] === PConstants.BREAK) {
p.breakShape = true;
}
}
}
}
p.endShape(this.close ? PConstants.CLOSE : PConstants.OPEN);
},
/**
* @member PShape
* The drawGeometry() function draws the geometry part of the SVG document.
*/
drawGeometry: function() {
var i, j;
p.beginShape(this.kind);
if (this.style) {
for (i = 0, j = this.vertices.length; i < j; i++) {
p.vertex(this.vertices[i]);
}
} else {
for (i = 0, j = this.vertices.length; i < j; i++) {
var vert = this.vertices[i];
if (vert[2] === 0) {
p.vertex(vert[0], vert[1]);
} else {
p.vertex(vert[0], vert[1], vert[2]);
}
}
}
p.endShape();
},
/**
* @member PShape
* The drawGroup() function draws the part of the SVG document.
*/
drawGroup: function() {
for (var i = 0, j = this.children.length; i < j; i++) {
this.children[i].draw();
}
},
/**
* @member PShape
* The drawPrimitive() function draws SVG document shape elements. These can be point, line, triangle, quad, rect, ellipse, arc, box, or sphere.
*/
drawPrimitive: function() {
if (this.kind === PConstants.POINT) {
p.point(this.params[0], this.params[1]);
} else if (this.kind === PConstants.LINE) {
if (this.params.length === 4) { // 2D
p.line(this.params[0], this.params[1],
this.params[2], this.params[3]);
} else { // 3D
p.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) {
p.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) {
p.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) {
p.imageMode(PConstants.CORNER);
p.image(this.image,
this.params[0],
this.params[1],
this.params[2],
this.params[3]);
} else {
p.rectMode(PConstants.CORNER);
p.rect(this.params[0],
this.params[1],
this.params[2],
this.params[3]);
}
} else if (this.kind === PConstants.ELLIPSE) {
p.ellipseMode(PConstants.CORNER);
p.ellipse(this.params[0],
this.params[1],
this.params[2],
this.params[3]);
} else if (this.kind === PConstants.ARC) {
p.ellipseMode(PConstants.CORNER);
p.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) {
p.box(this.params[0]);
} else {
p.box(this.params[0], this.params[1], this.params[2]);
}
} else if (this.kind === PConstants.SPHERE) {
p.sphere(this.params[0]);
}
},
/**
* @member PShape
* The pre() function performs the preparations before the SVG is drawn. This includes doing transformations and storing previous styles.
*/
pre: function() {
if (this.matrix) {
p.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]);
//p.applyMatrix(this.matrix.elements[0],this.matrix.elements[0]);
}
if (this.style) {
p.pushStyle();
this.styles();
}
},
/**
* @member PShape
* The post() function performs the necessary actions after the SVG is drawn. This includes removing transformations and removing added styles.
*/
post: function() {
if (this.matrix) {
p.popMatrix();
}
if (this.style) {
p.popStyle();
}
},
/**
* @member PShape
* The styles() function changes the Processing's current styles
*/
styles: function() {
if (this.stroke) {
p.stroke(this.strokeColor);
p.strokeWeight(this.strokeWeight);
p.strokeCap(this.strokeCap);
p.strokeJoin(this.strokeJoin);
} else {
p.noStroke();
}
if (this.fill) {
p.fill(this.fillColor);
} else {
p.noFill();
}
},
/**
* @member PShape
* The getChild() function extracts a child shape from a parent shape. Specify the name of the shape with the target parameter or the
* layer position of the shape to get with the index parameter.
* The shape is returned as a PShape object, or null 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
*/
getChild: function(child) {
var i, j;
if (typeof child === 'number') {
return this.children[child];
} else {
var found;
if(child === "" || this.name === child){
return this;
} else {
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];
}
}
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;
}
},
/**
* @member PShape
* The getChildCount() returns the number of children
*
* @return {int} returns a count of children
*/
getChildCount: function () {
return this.children.length;
},
/**
* @member PShape
* The addChild() adds a child to the PShape.
*
* @param {PShape} child the child to add
*/
addChild: function( child ) {
this.children.push(child);
child.parent = this;
if (child.getName() !== null) {
this.addName(child.getName(), child);
}
},
/**
* @member PShape
* The addName() functions adds a shape to the name lookup table.
*
* @param {String} name the name to be added
* @param {PShape} shape the shape
*/
addName: function(name, shape) {
if (this.parent !== null) {
this.parent.addName( name, shape );
} else {
this.nameTable.push( [name, shape] );
}
},
/**
* @member PShape
* The translate() function specifies an amount to displace the shape. 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.
* Subsequent calls to the method accumulates the effect. For example, calling translate(50, 0) and then translate(20, 0) is the same as translate(70, 0).
* This transformation is applied directly to the shape, it's not refreshed each time draw() is run.
*
Using this method with the z 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
*/
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);
}
},
/**
* @member PShape
* The checkMatrix() function makes sure that the shape's matrix is 1) not null, and 2) has a matrix
* that can handle at least the specified number of dimensions.
*
* @param {int} dimensions the specified number of dimensions
*/
checkMatrix: function(dimensions) {
if(this.matrix === null) {
if(dimensions === 2) {
this.matrix = new p.PMatrix2D();
} else {
this.matrix = new p.PMatrix3D();
}
}else if(dimensions === 3 && this.matrix instanceof p.PMatrix2D) {
this.matrix = new p.PMatrix3D();
}
},
/**
* @member PShape
* The rotateX() function rotates a shape around the x-axis 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() method.
*
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 rotateX(HALF_PI) and then rotateX(HALF_PI) is the same as rotateX(PI).
* This transformation is applied directly to the shape, it's not refreshed each time draw() is run.
*
This method requires a 3D renderer. You need to pass P3D or OPENGL as a third parameter into the size() method as shown in the example above.
*
* @param {float}angle angle of rotation specified in radians
*
* @see PMatrix3D#rotateX
*/
rotateX: function(angle) {
this.rotate(angle, 1, 0, 0);
},
/**
* @member PShape
* The rotateY() function rotates a shape around the y-axis 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() method.
*
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 rotateY(HALF_PI) and then rotateY(HALF_PI) is the same as rotateY(PI).
* This transformation is applied directly to the shape, it's not refreshed each time draw() is run.
*
This method requires a 3D renderer. You need to pass P3D or OPENGL as a third parameter into the size() method as shown in the example above.
*
* @param {float}angle angle of rotation specified in radians
*
* @see PMatrix3D#rotateY
*/
rotateY: function(angle) {
this.rotate(angle, 0, 1, 0);
},
/**
* @member PShape
* The rotateZ() function rotates a shape around the z-axis 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() method.
*
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 rotateZ(HALF_PI) and then rotateZ(HALF_PI) is the same as rotateZ(PI).
* This transformation is applied directly to the shape, it's not refreshed each time draw() is run.
*
This method requires a 3D renderer. You need to pass P3D or OPENGL as a third parameter into the size() method as shown in the example above.
*
* @param {float}angle angle of rotation specified in radians
*
* @see PMatrix3D#rotateZ
*/
rotateZ: function(angle) {
this.rotate(angle, 0, 0, 1);
},
/**
* @member PShape
* The rotate() function 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() method.
*
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 rotate(HALF_PI) and then rotate(HALF_PI) is the same as rotate(PI).
* This transformation is applied directly to the shape, it's not refreshed each time draw() 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
*/
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]);
}
},
/**
* @member PShape
* 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 scale(2.0) increases the dimension of a shape by 200%.
* Subsequent calls to the method multiply the effect. For example, calling scale(2.0) and then scale(1.5) is the same as scale(3.0).
* This transformation is applied directly to the shape, it's not refreshed each time draw() is run.
*
Using this fuction with the z 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
*/
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]);
}
},
/**
* @member PShape
* The resetMatrix() function resets the matrix
*
* @see PMatrix2D#reset
* @see PMatrix3D#reset
*/
resetMatrix: function() {
this.checkMatrix(2);
this.matrix.reset();
},
/**
* @member PShape
* 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
*/
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
*/
var PShapeSVG = p.PShapeSVG = function() {
p.PShape.call( this ); // PShape is the base class.
if (arguments.length === 1) { //xml element coming in
this.element = arguments[0] ;//new p.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