Files
FreeJ/scripts/javascript/lib/processing.js
Jaromil ae7b1ad056 progresses on processing script
processing-js 0.4 has been merged in some relevant parts
basic and topic scripts added for test
color handling fixed, more scripts show up now
2010-02-12 18:36:54 +01:00

3138 lines
98 KiB
JavaScript

/*
P R O C E S S I N G - 0 . 4 . J S
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
*/
(function(){
// Attach Processing to the window
this.Processing = function Processing( processing_code ){
echo("initialising Processing interpreter");
// Build an Processing functions and env. vars into 'p'
var p = buildProcessing( );
// Send aCode Processing syntax to be converted to JavaScript
if( processing_code ){ p.init( processing_code ); }
return p;
};
// Parse Processing (Java-like) syntax to JavaScript syntax with Regex
var parse = Processing.parse = function parse( aCode, p ){
// Remove end-of-line comments
aCode = aCode.replace(/\/\/ .*\n/g, "\n");
// Weird parsing errors with %
aCode = aCode.replace(/([^\s])%([^\s])/g, "$1 % $2");
// Since frameRate() and frameRate are different things,
// we need to differentiate them somehow. So when we parse
// the Processing.js source, replace frameRate so it isn't
// confused with frameRate().
aCode = aCode.replace(/(\s*=\s*|\(*\s*)frameRate(\s*\)+?|\s*;)/, "$1p.FRAME_RATE$2");
// Simple convert a function-like thing to function
aCode = aCode.replace(/(?:static )?(\w+(?:\[\])* )(\w+)\s*(\([^\)]*\)\s*\{)/g, function (all, type, name, args) {
if (name === "if" || name === "for" || name === "while") {
return all;
} else {
return "Processing." + name + " = function " + name + args;
}
});
// Attach import() to p{} bypassing JS command, allowing for extrernal library loading
aCode = aCode.replace(/import \(|import\(/g, "p.Import(");
// Force .length() to be .length
aCode = aCode.replace(/\.length\(\)/g, ".length");
// foo( int foo, float bar )
aCode = aCode.replace(/([\(,]\s*)(\w+)((?:\[\])+| )\s*(\w+\s*[\),])/g, "$1$4");
aCode = aCode.replace(/([\(,]\s*)(\w+)((?:\[\])+| )\s*(\w+\s*[\),])/g, "$1$4");
// float[] foo = new float[5];
aCode = aCode.replace(/new (\w+)((?:\[([^\]]*)\])+)/g, function (all, name, args) {
return "new ArrayList(" + args.replace(/\[\]/g, "[0]").slice(1, -1).split("][").join(", ") + ")";
//return "new ArrayList(" + args.slice(1, -1).split("][").join(", ") + ")";
});
// What does this do? This does the same thing as "Fix Array[] foo = {...} to [...]" below
aCode = aCode.replace(/(?:static )?\w+\[\]\s*(\w+)\[?\]?\s*=\s*\{.*?\};/g, function (all) {
return all.replace(/\{/g, "[").replace(/\}/g, "]");
});
// int|float foo;
var intFloat = /(\n\s*(?:int|float)(?!\[\])*(?:\s*|[^\(;]*?,\s*))([a-zA-Z]\w*)\s*(,|;)/i;
while (intFloat.test(aCode)) {
aCode = aCode.replace(new RegExp(intFloat), function (all, type, name, sep) {
return type + " " + name + " = 0" + sep;
});
}
// float foo = 5;
aCode = aCode.replace(/(?:static\s+)?(?:final\s+)?(\w+)((?:\[\])+| ) *(\w+)\[?\]?(\s*[=,;])/g, function (all, type, arr, name, sep) {
if (type === "return") {
return all;
} else {
return "var " + name + sep;
}
});
// Fix Array[] foo = {...} to [...]
aCode = aCode.replace(/\=\s*\{((.|\s)*?)\};/g, function (all, data) {
return "= [" + data.replace(/\{/g, "[").replace(/\}/g, "]") + "]";
});
// super() is a reserved word
aCode = aCode.replace(/super\(/g, "superMethod(");
var classes = ["int", "float", "boolean", "String", "byte", "double", "long"];
var classReplace = function (all, name, extend, vars, last) {
classes.push(name);
var staticVar = "";
vars = vars.replace(/final\s+var\s+(\w+\s*=\s*.*?;)/g, function (all, set) {
staticVar += " " + name + "." + set;
return "";
});
// Move arguments up from constructor and wrap contents with
// a with(this), and unwrap constructor
return "function " + name + "() {with(this){\n " + (extend ? "var __self=this;function superMethod(){extendClass(__self,arguments," + extend + ");}\n" : "") +
// Replace var foo = 0; with this.foo = 0;
// and force var foo; to become this.foo = null;
vars.replace(/\s*,\s*/g, ";\n this.").replace(/\b(var |final |public )+\s*/g, "this.").replace(/\b(var |final |public )+\s*/g, "this.").replace(/this\.(\w+);/g, "this.$1 = null;") + (extend ? "extendClass(this, " + extend + ");\n" : "") + "<CLASS " + name + " " + staticVar + ">" + (typeof last === "string" ? last : name + "(");
};
var nextBrace = function (right) {
var rest = right,
position = 0,
leftCount = 1,
rightCount = 0;
while (leftCount !== rightCount) {
var nextLeft = rest.indexOf("{"),
nextRight = rest.indexOf("}");
if (nextLeft < nextRight && nextLeft !== -1) {
leftCount++;
rest = rest.slice(nextLeft + 1);
position += nextLeft + 1;
} else {
rightCount++;
rest = rest.slice(nextRight + 1);
position += nextRight + 1;
}
}
return right.slice(0, position - 1);
};
var matchClasses = /(?:public |abstract |static )*class (\w+)\s*(?:extends\s*(\w+)\s*)?\{\s*((?:.|\n)*?)\b\1\s*\(/g;
var matchNoCon = /(?:public |abstract |static )*class (\w+)\s*(?:extends\s*(\w+)\s*)?\{\s*((?:.|\n)*?)(Processing)/g;
aCode = aCode.replace(matchClasses, classReplace);
aCode = aCode.replace(matchNoCon, classReplace);
var matchClass = /<CLASS (\w+) (.*?)>/,
m;
while ((m = aCode.match(matchClass))) {
var left = RegExp.leftContext,
allRest = RegExp.rightContext,
rest = nextBrace(allRest),
className = m[1],
staticVars = m[2] || "";
allRest = allRest.slice(rest.length + 1);
rest = rest.replace(new RegExp("\\b" + className + "\\(([^\\)]*?)\\)\\s*{", "g"), function (all, args) {
args = args.split(/,\s*?/);
if (args[0].match(/^\s*$/)) {
args.shift();
}
var fn = "if ( arguments.length === " + args.length + " ) {\n";
for (var i = 0; i < args.length; i++) {
fn += " var " + args[i] + " = arguments[" + i + "];\n";
}
return fn;
});
// Fix class method names
// this.collide = function() { ... }
// and add closing } for with(this) ...
rest = rest.replace(/(?:public )?Processing.\w+ = function (\w+)\((.*?)\)/g, function (all, name, args) {
return "ADDMETHOD(this, '" + name + "', function(" + args + ")";
});
var matchMethod = /ADDMETHOD([\s\S]*?\{)/,
mc;
var methods = "";
while ((mc = rest.match(matchMethod))) {
var prev = RegExp.leftContext,
allNext = RegExp.rightContext,
next = nextBrace(allNext);
methods += "addMethod" + mc[1] + next + "});";
rest = prev + allNext.slice(next.length + 1);
}
rest = methods + rest;
aCode = left + rest + "\n}}" + staticVars + allRest;
}
// Do some tidying up, where necessary
aCode = aCode.replace(/Processing.\w+ = function addMethod/g, "addMethod");
// Check if 3D context is invoked -- this is not the best way to do this.
if (aCode.match(/size\((?:.+),(?:.+),\s*OPENGL\);/)) {
p.use3DContext = true;
}
// Handle (int) Casting
aCode = aCode.replace(/\(int\)/g, "0|");
// Remove Casting
aCode = aCode.replace(new RegExp("\\((" + classes.join("|") + ")(\\[\\])*\\)", "g"), "");
// Force numbers to exist //
//aCode = aCode.replace(/([^.])(\w+)\s*\+=/g, "$1$2 = ($2||0) +");
//! // Force characters-as-bytes to work --> Ping: Andor
aCode = aCode.replace(/('[a-zA-Z0-9]')/g, "$1.charCodeAt(0)");
var toNumbers = function (str) {
var ret = [];
str.replace(/(..)/g, function (str) {
ret.push(parseInt(str, 16));
});
return ret;
};
// Convert #aaaaaa into color
aCode = aCode.replace(/#([a-f0-9]{6})/ig, function (m, hex) {
var num = toNumbers(hex);
return "DefaultColor(" + num[0] + "," + num[1] + "," + num[2] + ")";
});
// Convert 3.0f to just 3.0
aCode = aCode.replace(/(\d+)f/g, "$1");
return aCode;
};
// Attach Processing functions to 'p'
function buildProcessing( freej_screen ){
// Create the 'p' object
var p = {};
// Set Processing defaults / environment variables
p.name = 'Processing.js Instance';
p.PI = Math.PI;
p.TWO_PI = 2 * p.PI;
p.HALF_PI = p.PI / 2;
p.P3D = 3;
p.CORNER = 0;
p.RADIUS = 1;
p.CENTER_RADIUS = 1;
p.CENTER = 2;
p.POLYGON = 2;
p.QUADS = 5;
p.TRIANGLES = 6;
p.POINTS = 7;
p.LINES = 8;
p.TRIANGLE_STRIP = 9;
p.TRIANGLE_FAN = 4;
p.QUAD_STRIP = 3;
p.CORNERS = 10;
p.CLOSE = true;
p.RGB = 1;
p.HSB = 2;
p.focused = true;
// KeyCode table
p.CENTER = 88888880;
p.CODED = 88888888;
p.UP = 88888870;
p.RIGHT = 88888871;
p.DOWN = 88888872;
p.LEFT = 88888869;
//! // Description required...
p.codedKeys = [ 69, 70, 71, 72 ];
// "Private" variables used to maintain state
var online = true,
doFill = true,
doStroke = true,
loopStarted = false,
hasBackground = false,
doLoop = true,
looping = 0,
curRectMode = p.CORNER,
curEllipseMode = p.CENTER,
inSetup = false,
inDraw = false,
curBackground = "rgba( 204, 204, 204, 1 )",
curFrameRate = 1000,
curCursor = p.ARROW,
// oldCursor = document.body.style.cursor,
curMsPerFrame = 1,
curShape = p.POLYGON,
curShapeCount = 0,
curvePoints = [],
curTightness = 0,
opacityRange = 255,
redRange = 255,
greenRange = 255,
blueRange = 255,
pathOpen = false,
mousePressed = false,
keyPressed = false,
curColorMode = p.RGB,
curTint = -1,
curTextSize = 12,
curTextFont = "Arial",
getLoaded = false,
start = new Date().getTime(),
timeSinceLastFPS = start,
framesSinceLastFPS = 0;
var firstX, firstY, secondX, secondY, prevX, prevY;
// Stores states for pushStyle() and popStyle().
var styleArray = new Array(0);
// Store a line for println(), print() handline
p.ln = "";
// 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.mouseDown = false;
// Undefined event handlers to be replaced by user when needed
p.mouseClicked = undefined;
p.mouseDragged = undefined;
p.mouseMoved = undefined;
p.mousePressed = undefined;
p.mouseReleased = undefined;
p.keyPressed = undefined;
p.keyReleased = undefined;
p.draw = undefined;
p.setup = undefined;
// The height/width of the canvas
// p.width = curElement.width - 0;
// p.height = curElement.height - 0;
// no need to restrict the size to the screen in freej
// since a layer can be bigger than that...
// The current animation frame
p.frameCount = 0;
////////////////////////////////////////////////////////////////////////////
// Array handling
////////////////////////////////////////////////////////////////////////////
p.split = function (str, delim) {
return str.split(delim);
};
p.splitTokens = function (str, tokens) {
if (arguments.length === 1) {
tokens = "\n\t\r\f ";
}
tokens = "[" + tokens + "]";
var ary = new Array(0);
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 = undefined;
}
return ary;
};
p.append = function (array, element) {
array[array.length] = element;
return array;
};
p.concat = function concat(array1, array2) {
return array1.concat(array2);
};
p.splice = function (array, value, index) {
if (array.length === 0 && value.length === 0) {
return array;
}
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;
};
p.subset = function (array, offset, length) {
if (arguments.length === 2) {
return p.subset(array, offset, array.length - offset);
} else if (arguments.length === 3) {
return array.slice(offset, offset + length);
}
};
p.join = function join(array, seperator) {
return array.join(seperator);
};
p.shorten = function (ary) {
var newary = new Array(0);
// copy array into new array
var len = ary.length;
for (var i = 0; i < len; i++) {
newary[i] = ary[i];
}
newary.pop();
return newary;
};
p.expand = function (ary, newSize) {
var newary = new Array(0);
var len = ary.length;
for (var i = 0; i < len; i++) {
newary[i] = ary[i];
}
if (arguments.length === 1) {
// double size of array
newary.length *= 2;
} else if (arguments.length === 2) {
// size is newSize
newary.length = newSize;
}
return newary;
};
p.ArrayList = function ArrayList(size, size2, size3) {
var array = new Array(0 | size);
if (size2) {
for (var i = 0; i < size; i++) {
array[i] = [];
for (var j = 0; j < size2; j++) {
var a = array[i][j] = size3 ? new Array(size3) : 0;
for (var k = 0; k < size3; k++) {
a[k] = 0;
}
}
}
} else {
for (var l = 0; l < size; l++) {
array[l] = 0;
}
}
array.get = function( i ){ return this[ i ]; };
array.add = function( item ){ return this.push( item ); };
array.size = function( ){ return this.length; };
array.clear = function( ){ this.length = 0; };
array.remove = function( i ){ return this.splice( i, 1 ); };
array.isEmpty = function( ){ return !this.length; };
array.clone = function( ){
var a = new ArrayList( size );
for( var i = 0; i < size; i++ ){
a[ i ] = this[ i ];
}
return a;
};
return array;
};
p.reverse = function (array) {
return array.reverse();
};
////////////////////////////////////////////////////////////////////////////
// Color functions
////////////////////////////////////////////////////////////////////////////
// convert rgba color strings to integer
p.rgbaToInt = function (color) {
var rgbaAry = /\(([^\)]+)\)/.exec(color).slice(1, 2)[0].split(',');
return ((rgbaAry[3] * 255) << 24) | (rgbaAry[0] << 16) | (rgbaAry[1] << 8) | (rgbaAry[2]);
};
// helper functions for internal blending modes
p.mix = function (a, b, f) {
return a + (((b - a) * f) >> 8);
};
p.peg = function (n) {
return (n < 0) ? 0 : ((n > 255) ? 255 : n);
};
// blending modes
p.modes = {
replace: function (a, b) {
return p.rgbaToInt(b);
},
blend: function (a, b) {
var c1 = p.rgbaToInt(a);
var c2 = p.rgbaToInt(b);
var f = (c2 & p.ALPHA_MASK) >>> 24;
return (Math.min(((c1 & p.ALPHA_MASK) >>> 24) + f, 0xff) << 24 | p.mix(c1 & p.RED_MASK, c2 & p.RED_MASK, f) & p.RED_MASK | p.mix(c1 & p.GREEN_MASK, c2 & p.GREEN_MASK, f) & p.GREEN_MASK | p.mix(c1 & p.BLUE_MASK, c2 & p.BLUE_MASK, f));
},
add: function (a, b) {
var c1 = p.rgbaToInt(a);
var c2 = p.rgbaToInt(b);
var f = (c2 & p.ALPHA_MASK) >>> 24;
return (Math.min(((c1 & p.ALPHA_MASK) >>> 24) + f, 0xff) << 24 | Math.min(((c1 & p.RED_MASK) + ((c2 & p.RED_MASK) >> 8) * f), p.RED_MASK) & p.RED_MASK | Math.min(((c1 & p.GREEN_MASK) + ((c2 & p.GREEN_MASK) >> 8) * f), p.GREEN_MASK) & p.GREEN_MASK | Math.min((c1 & p.BLUE_MASK) + (((c2 & p.BLUE_MASK) * f) >> 8), p.BLUE_MASK));
},
subtract: function (a, b) {
var c1 = p.rgbaToInt(a);
var c2 = p.rgbaToInt(b);
var f = (c2 & p.ALPHA_MASK) >>> 24;
return (Math.min(((c1 & p.ALPHA_MASK) >>> 24) + f, 0xff) << 24 | Math.max(((c1 & p.RED_MASK) - ((c2 & p.RED_MASK) >> 8) * f), p.GREEN_MASK) & p.RED_MASK | Math.max(((c1 & p.GREEN_MASK) - ((c2 & p.GREEN_MASK) >> 8) * f), p.BLUE_MASK) & p.GREEN_MASK | Math.max((c1 & p.BLUE_MASK) - (((c2 & p.BLUE_MASK) * f) >> 8), 0));
},
lightest: function (a, b) {
var c1 = p.rgbaToInt(a);
var c2 = p.rgbaToInt(b);
var f = (c2 & p.ALPHA_MASK) >>> 24;
return (Math.min(((c1 & p.ALPHA_MASK) >>> 24) + f, 0xff) << 24 | Math.max(c1 & p.RED_MASK, ((c2 & p.RED_MASK) >> 8) * f) & p.RED_MASK | Math.max(c1 & p.GREEN_MASK, ((c2 & p.GREEN_MASK) >> 8) * f) & p.GREEN_MASK | Math.max(c1 & p.BLUE_MASK, ((c2 & p.BLUE_MASK) * f) >> 8));
},
darkest: function (a, b) {
var c1 = p.rgbaToInt(a);
var c2 = p.rgbaToInt(b);
var f = (c2 & p.ALPHA_MASK) >>> 24;
return (Math.min(((c1 & p.ALPHA_MASK) >>> 24) + f, 0xff) << 24 | p.mix(c1 & p.RED_MASK, Math.min(c1 & p.RED_MASK, ((c2 & p.RED_MASK) >> 8) * f), f) & p.RED_MASK | p.mix(c1 & p.GREEN_MASK, Math.min(c1 & p.GREEN_MASK, ((c2 & p.GREEN_MASK) >> 8) * f), f) & p.GREEN_MASK | p.mix(c1 & p.BLUE_MASK, Math.min(c1 & p.BLUE_MASK, ((c2 & p.BLUE_MASK) * f) >> 8), f));
},
difference: function (a, b) {
var c1 = p.rgbaToInt(a);
var c2 = p.rgbaToInt(b);
var f = (c2 & p.ALPHA_MASK) >>> 24;
var ar = (c1 & p.RED_MASK) >> 16;
var ag = (c1 & p.GREEN_MASK) >> 8;
var ab = (c1 & p.BLUE_MASK);
var br = (c2 & p.RED_MASK) >> 16;
var bg = (c2 & p.GREEN_MASK) >> 8;
var bb = (c2 & p.BLUE_MASK);
// formula:
var cr = (ar > br) ? (ar - br) : (br - ar);
var cg = (ag > bg) ? (ag - bg) : (bg - ag);
var cb = (ab > bb) ? (ab - bb) : (bb - ab);
// alpha blend (this portion will always be the same)
return (Math.min(((c1 & p.ALPHA_MASK) >>> 24) + f, 0xff) << 24 | (p.peg(ar + (((cr - ar) * f) >> 8)) << 16) | (p.peg(ag + (((cg - ag) * f) >> 8)) << 8) | (p.peg(ab + (((cb - ab) * f) >> 8))));
},
exclusion: function (a, b) {
var c1 = p.rgbaToInt(a);
var c2 = p.rgbaToInt(b);
var f = (c2 & p.ALPHA_MASK) >>> 24;
var ar = (c1 & p.RED_MASK) >> 16;
var ag = (c1 & p.GREEN_MASK) >> 8;
var ab = (c1 & p.BLUE_MASK);
var br = (c2 & p.RED_MASK) >> 16;
var bg = (c2 & p.GREEN_MASK) >> 8;
var bb = (c2 & p.BLUE_MASK);
// formula:
var cr = ar + br - ((ar * br) >> 7);
var cg = ag + bg - ((ag * bg) >> 7);
var cb = ab + bb - ((ab * bb) >> 7);
// alpha blend (this portion will always be the same)
return (Math.min(((c1 & p.ALPHA_MASK) >>> 24) + f, 0xff) << 24 | (p.peg(ar + (((cr - ar) * f) >> 8)) << 16) | (p.peg(ag + (((cg - ag) * f) >> 8)) << 8) | (p.peg(ab + (((cb - ab) * f) >> 8))));
},
multiply: function (a, b) {
var c1 = p.rgbaToInt(a);
var c2 = p.rgbaToInt(b);
var f = (c2 & p.ALPHA_MASK) >>> 24;
var ar = (c1 & p.RED_MASK) >> 16;
var ag = (c1 & p.GREEN_MASK) >> 8;
var ab = (c1 & p.BLUE_MASK);
var br = (c2 & p.RED_MASK) >> 16;
var bg = (c2 & p.GREEN_MASK) >> 8;
var bb = (c2 & p.BLUE_MASK);
// formula:
var cr = (ar * br) >> 8;
var cg = (ag * bg) >> 8;
var cb = (ab * bb) >> 8;
// alpha blend (this portion will always be the same)
return (Math.min(((c1 & p.ALPHA_MASK) >>> 24) + f, 0xff) << 24 | (p.peg(ar + (((cr - ar) * f) >> 8)) << 16) | (p.peg(ag + (((cg - ag) * f) >> 8)) << 8) | (p.peg(ab + (((cb - ab) * f) >> 8))));
},
screen: function (a, b) {
var c1 = p.rgbaToInt(a);
var c2 = p.rgbaToInt(b);
var f = (c2 & p.ALPHA_MASK) >>> 24;
var ar = (c1 & p.RED_MASK) >> 16;
var ag = (c1 & p.GREEN_MASK) >> 8;
var ab = (c1 & p.BLUE_MASK);
var br = (c2 & p.RED_MASK) >> 16;
var bg = (c2 & p.GREEN_MASK) >> 8;
var bb = (c2 & p.BLUE_MASK);
// formula:
var cr = 255 - (((255 - ar) * (255 - br)) >> 8);
var cg = 255 - (((255 - ag) * (255 - bg)) >> 8);
var cb = 255 - (((255 - ab) * (255 - bb)) >> 8);
// alpha blend (this portion will always be the same)
return (Math.min(((c1 & p.ALPHA_MASK) >>> 24) + f, 0xff) << 24 | (p.peg(ar + (((cr - ar) * f) >> 8)) << 16) | (p.peg(ag + (((cg - ag) * f) >> 8)) << 8) | (p.peg(ab + (((cb - ab) * f) >> 8))));
},
hard_light: function (a, b) {
var c1 = p.rgbaToInt(a);
var c2 = p.rgbaToInt(b);
var f = (c2 & p.ALPHA_MASK) >>> 24;
var ar = (c1 & p.RED_MASK) >> 16;
var ag = (c1 & p.GREEN_MASK) >> 8;
var ab = (c1 & p.BLUE_MASK);
var br = (c2 & p.RED_MASK) >> 16;
var bg = (c2 & p.GREEN_MASK) >> 8;
var bb = (c2 & p.BLUE_MASK);
// formula:
var cr = (br < 128) ? ((ar * br) >> 7) : (255 - (((255 - ar) * (255 - br)) >> 7));
var cg = (bg < 128) ? ((ag * bg) >> 7) : (255 - (((255 - ag) * (255 - bg)) >> 7));
var cb = (bb < 128) ? ((ab * bb) >> 7) : (255 - (((255 - ab) * (255 - bb)) >> 7));
// alpha blend (this portion will always be the same)
return (Math.min(((c1 & p.ALPHA_MASK) >>> 24) + f, 0xff) << 24 | (p.peg(ar + (((cr - ar) * f) >> 8)) << 16) | (p.peg(ag + (((cg - ag) * f) >> 8)) << 8) | (p.peg(ab + (((cb - ab) * f) >> 8))));
},
soft_light: function (a, b) {
var c1 = p.rgbaToInt(a);
var c2 = p.rgbaToInt(b);
var f = (c2 & p.ALPHA_MASK) >>> 24;
var ar = (c1 & p.RED_MASK) >> 16;
var ag = (c1 & p.GREEN_MASK) >> 8;
var ab = (c1 & p.BLUE_MASK);
var br = (c2 & p.RED_MASK) >> 16;
var bg = (c2 & p.GREEN_MASK) >> 8;
var bb = (c2 & p.BLUE_MASK);
// formula:
var cr = ((ar * br) >> 7) + ((ar * ar) >> 8) - ((ar * ar * br) >> 15);
var cg = ((ag * bg) >> 7) + ((ag * ag) >> 8) - ((ag * ag * bg) >> 15);
var cb = ((ab * bb) >> 7) + ((ab * ab) >> 8) - ((ab * ab * bb) >> 15);
// alpha blend (this portion will always be the same)
return (Math.min(((c1 & p.ALPHA_MASK) >>> 24) + f, 0xff) << 24 | (p.peg(ar + (((cr - ar) * f) >> 8)) << 16) | (p.peg(ag + (((cg - ag) * f) >> 8)) << 8) | (p.peg(ab + (((cb - ab) * f) >> 8))));
},
overlay: function (a, b) {
var c1 = p.rgbaToInt(a);
var c2 = p.rgbaToInt(b);
var f = (c2 & p.ALPHA_MASK) >>> 24;
var ar = (c1 & p.RED_MASK) >> 16;
var ag = (c1 & p.GREEN_MASK) >> 8;
var ab = (c1 & p.BLUE_MASK);
var br = (c2 & p.RED_MASK) >> 16;
var bg = (c2 & p.GREEN_MASK) >> 8;
var bb = (c2 & p.BLUE_MASK);
// formula:
var cr = (ar < 128) ? ((ar * br) >> 7) : (255 - (((255 - ar) * (255 - br)) >> 7));
var cg = (ag < 128) ? ((ag * bg) >> 7) : (255 - (((255 - ag) * (255 - bg)) >> 7));
var cb = (ab < 128) ? ((ab * bb) >> 7) : (255 - (((255 - ab) * (255 - bb)) >> 7));
// alpha blend (this portion will always be the same)
return (Math.min(((c1 & p.ALPHA_MASK) >>> 24) + f, 0xff) << 24 | (p.peg(ar + (((cr - ar) * f) >> 8)) << 16) | (p.peg(ag + (((cg - ag) * f) >> 8)) << 8) | (p.peg(ab + (((cb - ab) * f) >> 8))));
},
dodge: function (a, b) {
var c1 = p.rgbaToInt(a);
var c2 = p.rgbaToInt(b);
var f = (c2 & p.ALPHA_MASK) >>> 24;
var ar = (c1 & p.RED_MASK) >> 16;
var ag = (c1 & p.GREEN_MASK) >> 8;
var ab = (c1 & p.BLUE_MASK);
var br = (c2 & p.RED_MASK) >> 16;
var bg = (c2 & p.GREEN_MASK) >> 8;
var bb = (c2 & p.BLUE_MASK);
// formula:
var cr = (br === 255) ? 255 : p.peg((ar << 8) / (255 - br)); // division requires pre-peg()-ing
var cg = (bg === 255) ? 255 : p.peg((ag << 8) / (255 - bg)); // "
var cb = (bb === 255) ? 255 : p.peg((ab << 8) / (255 - bb)); // "
// alpha blend (this portion will always be the same)
return (Math.min(((c1 & p.ALPHA_MASK) >>> 24) + f, 0xff) << 24 | (p.peg(ar + (((cr - ar) * f) >> 8)) << 16) | (p.peg(ag + (((cg - ag) * f) >> 8)) << 8) | (p.peg(ab + (((cb - ab) * f) >> 8))));
},
burn: function (a, b) {
var c1 = p.rgbaToInt(a);
var c2 = p.rgbaToInt(b);
var f = (c2 & p.ALPHA_MASK) >>> 24;
var ar = (c1 & p.RED_MASK) >> 16;
var ag = (c1 & p.GREEN_MASK) >> 8;
var ab = (c1 & p.BLUE_MASK);
var br = (c2 & p.RED_MASK) >> 16;
var bg = (c2 & p.GREEN_MASK) >> 8;
var bb = (c2 & p.BLUE_MASK);
// formula:
var cr = (br === 0) ? 0 : 255 - p.peg(((255 - ar) << 8) / br); // division requires pre-peg()-ing
var cg = (bg === 0) ? 0 : 255 - p.peg(((255 - ag) << 8) / bg); // "
var cb = (bb === 0) ? 0 : 255 - p.peg(((255 - ab) << 8) / bb); // "
// alpha blend (this portion will always be the same)
return (Math.min(((c1 & p.ALPHA_MASK) >>> 24) + f, 0xff) << 24 | (p.peg(ar + (((cr - ar) * f) >> 8)) << 16) | (p.peg(ag + (((cg - ag) * f) >> 8)) << 8) | (p.peg(ab + (((cb - ab) * f) >> 8))));
}
};
// our own freej color function -jrml
p.color = function() { p.context.set_color.apply(p.context, arguments); }
// !! WARNING: brightness() & saturation() not working with HSB colors
p.brightness = function brightness(){
return p.color( redRange, greenRange, blueRange );
}
p.saturation = function saturation( color ){
return p.color( ( 126 / 255 ) * redRange, ( 126 / 255 ) * greenRange, ( 126 / 255 ) * blueRange );
}
p.hue = function hue(){
return p.color( 0 * redRange, 0 * greenRange, 0 * blueRange );
}
// In case I ever need to do HSV conversion:
// http://srufaculty.sru.edu/david.dailey/javascript/js/5rml.js
p.color_old = function color( aValue1, aValue2, aValue3, aValue4 ) {
var aColor = "";
if ( arguments.length == 3 ) {
aColor = p.color( aValue1, aValue2, aValue3, opacityRange );
} else if ( arguments.length == 4 ) {
var a = aValue4 / opacityRange;
a = isNaN(a) ? 1 : a;
if ( curColorMode == p.HSB ) {
var rgb = HSBtoRGB(aValue1, aValue2, aValue3);
var r = rgb[0], g = rgb[1], b = rgb[2];
} else {
var r = getColor(aValue1, redRange);
var g = getColor(aValue2, greenRange);
var b = getColor(aValue3, blueRange);
}
aColor = "rgba(" + r + "," + g + "," + b + "," + a + ")";
} else if ( typeof aValue1 == "string" ) {
aColor = aValue1;
if ( arguments.length == 2 ) {
var c = aColor.split(",");
c[3] = (aValue2 / opacityRange) + ")";
aColor = c.join(",");
}
} else if ( arguments.length == 2 ) {
aColor = p.color( aValue1, aValue1, aValue1, aValue2 );
} else if ( typeof aValue1 == "number" && aValue1 < 256 && aValue1 >= 0) {
aColor = p.color( aValue1, aValue1, aValue1, opacityRange );
} else if ( typeof aValue1 == "number" ) {
var intcolor = 0;
if( aValue1 < 0 ){
intcolor = 4294967296 - ( aValue1 * -1 );
}else{
intcolor = aValue1;
}
var ac = Math.floor((intcolor % 4294967296) / 16777216);
var rc = Math.floor((intcolor % 16777216) / 65536);
var gc = Math.floor((intcolor % 65536) / 256);
var bc = intcolor % 256;
aColor = p.color( rc, gc, bc, ac );
} else {
aColor = p.color( redRange, greenRange, blueRange, opacityRange );
}
// HSB conversion function from Mootools, MIT Licensed
function HSBtoRGB(h, s, b) {
h = (h / redRange) * 360;
s = (s / greenRange) * 100;
b = (b / blueRange) * 100;
var br = Math.round(b / 100 * 255);
if (s == 0){
return [br, br, br];
} else {
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];
}
}
}
function getColor( aValue, range ) {
return Math.round(255 * (aValue / range));
}
return aColor;
}
p.red = function( aColor ){
return parseInt( verifyChannel( aColor ).slice( 5 ) ); };
p.green = function( aColor ){
return parseInt( verifyChannel( aColor ).split( "," )[ 1 ] ); };
p.blue = function( aColor ){
return parseInt( verifyChannel( aColor ).split( "," )[ 2 ] ); };
p.alpha = function( aColor ){
return parseInt( parseFloat( verifyChannel( aColor ).split( "," )[ 3 ] ) * 255 ); };
function verifyChannel( aColor ){
if( aColor.constructor == Array ){
return aColor;
} else {
return p.color( aColor );
}
}
p.lerpColor = function lerpColor( c1, c2, amt ){
// Get RGBA values for Color 1 to floats
var colors1 = p.color( c1 ).split( "," );
var r1 = parseInt( colors1[ 0 ].split( "(" )[ 1 ] );
var g1 = parseInt( colors1[ 1 ] );
var b1 = parseInt( colors1[ 2 ] );
var a1 = parseFloat( colors1[ 3 ].split( ")" )[ 0 ] );
// Get RGBA values for Color 2 to floats
var colors2 = p.color( c2 ).split( "," );
var r2 = parseInt( colors2[ 0 ].split( "(" )[ 1 ] );
var g2 = parseInt( colors2[ 1 ] );
var b2 = parseInt( colors2[ 2 ] );
var a2 = parseFloat( colors2[ 3 ].split( ")" )[ 0 ] );
// Return lerp value for each channel, INT for color, Float for Alpha-range
var r = parseInt( p.lerp( r1, r2, amt ) );
var g = parseInt( p.lerp( g1, g2, amt ) );
var b = parseInt( p.lerp( b1, b2, amt ) );
var a = parseFloat( p.lerp( a1, a2, amt ) );
return aColor = "rgba("+ r +","+ g +","+ b +","+ a +")";
}
// Forced default color mode for #aaaaaa style
p.DefaultColor = function( aValue1, aValue2, aValue3 ){
var tmpColorMode = curColorMode;
curColorMode = p.RGB;
var c = p.color(aValue1 / 255 * redRange,
aValue2 / 255 * greenRange,
aValue3 / 255 * blueRange );
curColorMode = tmpColorMode;
return c;
}
p.colorMode = function colorMode( mode, range1, range2, range3, range4 ){
curColorMode = mode;
if( arguments.length >= 4 ){
redRange = range1;
greenRange = range2;
blueRange = range3;
}
if( arguments.length == 5 ){ opacityRange = range4; }
if( arguments.length == 2 ){
p.colorMode( mode, range1, range1, range1, range1 );
}
};
////////////////////////////////////////////////////////////////////////////
// Canvas-Matrix manipulation
////////////////////////////////////////////////////////////////////////////
p.translate = function translate( x, y ){ p.context.translate( x, y ); };
p.scale = function scale( x, y ) { p.context.scale( x, y || x ); };
p.rotate = function rotate( aAngle ) { p.context.rotate( aAngle ); };
p.pushMatrix = function pushMatrix() { p.context.save(); };
p.popMatrix = function popMatrix() { p.context.restore(); };
p.ortho = function ortho(){};
////////////////////////////////////////////////////////////////////////////
//Time based functions
////////////////////////////////////////////////////////////////////////////
p.year = function year() { return ( new Date ).getYear() + 1900; };
p.month = function month() { return ( new Date ).getMonth(); };
p.day = function day() { return ( new Date ).getDay(); };
p.hour = function hour() { return ( new Date ).getHours(); };
p.minute = function minute(){ return ( new Date ).getMinutes(); };
p.second = function second(){ return ( new Date ).getSeconds(); };
p.millis = function millis(){ return ( new Date ) .getTime() - start; };
p.noLoop = function noLoop(){ doLoop = false; };
p.redraw = function redraw(){
if( hasBackground ){ p.background(); }
p.frameCount++;
inDraw = true;
p.pushMatrix();
p.draw();
p.popMatrix();
inDraw = false;
};
p.loop = function loop(){
if( loopStarted ){ return; }
looping = new TriggerController();
register_controller(looping)
looping.frame = function() {
p.redraw();
}
loopStarter = true;
};
p.frameRate = function frameRate( aRate ){
// curFrameRate = aRate;
// curMsPerFrame = 1000 / curFrameRate;
set_fps( aRate );
};
p.exit = function exit(){
clearInterval( looping );
};
////////////////////////////////////////////////////////////////////////////
// MISC functions
////////////////////////////////////////////////////////////////////////////
p.cursor = function(mode){ document.body.style.cursor=mode; }
p.link = function( href, target ) { window.location = href; };
p.beginDraw = function beginDraw(){};
p.endDraw = function endDraw(){};
// p.disableContextMenu = function disableContextMenu(){
// curElement.addEventListener( 'contextmenu', function( e ){
// e.preventDefault();
// e.stopPropagation();
// }, false );
// }
////////////////////////////////////////////////////////////////////////////
// Binary Functions
////////////////////////////////////////////////////////////////////////////
p.unbinary = function unbinary(binaryString) {
var binaryPattern = new RegExp("^[0|1]{8}$");
var addUp = 0;
if (isNaN(binaryString)) {
throw "NaN_Err";
} else {
if (arguments.length === 1 || binaryString.length === 8) {
if (binaryPattern.test(binaryString)) {
for (i = 0; i < 8; i++) {
addUp += (Math.pow(2, i) * parseInt(binaryString.charAt(7 - i), 10));
}
return addUp + "";
} else {
throw "notBinary: the value passed into unbinary was not an 8 bit binary number";
}
} else {
throw "longErr";
}
}
return addUp;
};
p.nfs = function (num, left, right) {
var str, len;
// array handling
if (typeof num === "object") {
str = new Array(0);
len = num.length;
for (var i = 0; i < len; i++) {
str[i] = p.nfs(num[i], left, right);
}
} else if (arguments.length === 3) {
var negative = false;
if (num < 0) {
negative = true;
}
str = "" + Math.abs(num);
var digits = ("" + Math.floor(Math.abs(num))).length;
var count = left - digits;
while (count > 0) {
str = "0" + str;
count--;
}
// get the number of decimal places, if none will be -1
var decimals = ("" + Math.abs(num)).length - digits - 1;
if (decimals === -1 && right > 0) {
str = str + ".";
}
if (decimals !== -1) {
count = right - decimals;
} else if (decimals === -1 && right > 0) {
count = right;
} else {
count = 0;
}
while (count > 0) {
str = str + "0";
count--;
}
str = (negative ? "-" : " ") + str;
} else if (arguments.length === 2) {
str = p.nfs(num, left, 0);
}
return str;
};
p.nfc = function (num, right) {
var str;
var decimals = right >= 0 ? right : 0;
if (typeof num === "object") {
str = new Array(0);
for (var i = 0; i < num.length; i++) {
str[i] = p.nfc(num[i], decimals);
}
} else if (arguments.length === 2) {
var rawStr = p.nfs(num, 0, decimals);
var ary = new Array(0);
ary = rawStr.split('.');
// ary[0] contains left of decimal, ary[1] contains decimal places if they exist
// insert commas now, then append ary[1] if it exists
var leftStr = ary[0];
var rightStr = ary.length > 1 ? '.' + ary[1] : '';
var commas = /(\d+)(\d{3})/;
while (commas.test(leftStr)) {
leftStr = leftStr.replace(commas, '$1' + ',' + '$2');
}
str = leftStr + rightStr;
} else if (arguments.length === 1) {
str = p.nfc(num, 0);
}
return str;
};
//function i use to convert decimals to a padded hex value
p.decimalToHex = function decimalToHex(d, padding) {
//if there is no padding value added, default padding to 8 else go into while statement.
padding = typeof(padding) === "undefined" || padding === null ? padding = 8 : padding;
var hex = Number(d).toString(16);
while (hex.length < padding) {
hex = "0" + hex;
}
return hex;
};
//regExp i made to pattern match rgba and extract it's values
p.colorRGB = function colorRGB(col) {
var patt = /^rgba?\((\d{1,3}),(\d{1,3}),(\d{1,3}),?(\d{0,3})\)$/i; //grouped \d{1,3} with ( ) so they can be referenced w\ $1-$4
// What's up with the crazy variable names? -F1LT3R
var al = col.replace(patt, "$4");
var reD = col.replace(patt, "$1");
var gree = col.replace(patt, "$2");
var blu = col.replace(patt, "$3");
return ("" + Number(al).toString(16) + Number(reD).toString(16) + Number(gree).toString(16) + Number(blu).toString(16)).toUpperCase();
};
p.hex = function hex(decimal, len) {
var hexadecimal = "";
var patternRGBa = /^rgba?\((\d{1,3}),(\d{1,3}),(\d{1,3}),?(\d{0,3})\)$/i; //match rgba(20,20,20,0) or rgba(20,20,20)
var patternDigits = /^\d+$/;
//************************** dealing with 2 parameters *************************************************
if (arguments.length === 2) {
if (patternDigits.test(decimal)) {
hexadecimal = p.decimalToHex(decimal, len);
} else if (patternRGBa.test(decimal)) //check to see if it's an rgba color
{
hexadecimal = p.colorRGB(decimal);
hexadecimal = hexadecimal.substring(hexadecimal.length - len, hexadecimal.length);
}
} else if (arguments.length === 1) //**************** dealing with 1 parameter ********************************
{
if (patternDigits.test(decimal)) { //check to see if it's a decimal
hexadecimal = p.decimalToHex(decimal);
} else if (patternRGBa.test(decimal)) //check to see if it's an rgba color
{
hexadecimal = p.colorRGB(decimal);
}
else if (decimal.indexOf("#") === 0) //check to see if it's hex color in format #ffffff
{
if (decimal.length < 7) {
throw "Not Hex format: the value passed into hex was not in the format #FFFFFF";
} else {
decimal = (decimal.slice(1)).toUpperCase();
while (decimal.length < 8) {
decimal = "FF" + decimal;
}
hexadecimal = decimal;
}
}
}
return hexadecimal;
};
p.unhex = function (str) {
var value = 0,
multiplier = 1,
num = 0;
var len = str.length - 1;
for (var i = len ; i >= 0; i--){
switch(str[i]){
case "0": num = 0; break;
case "1": num = 1; break;
case "2": num = 2; break;
case "3": num = 3; break;
case "4": num = 4; break;
case "5": num = 5; break;
case "6": num = 6; break;
case "7": num = 7; break;
case "8": num = 8; break;
case "9": num = 9; break;
case "A":
case "a": num = 10; break;
case "B":
case "b": num = 11; break;
case "C":
case "c": num = 12; break;
case "D":
case "d": num = 13; break;
case "E":
case "e": num = 14; break;
case "F":
case "f": num = 15; break;
default:return 0; break;
}
value += num * multiplier;
multiplier *= 16;
// correct for int overflow java expectation
if (value > 2147483647)
{
value -= 4294967296;
}
}
return value;
};
// Load a file or URL into strings
p.loadStrings = function loadStrings( url ){
return p.ajax( url ).split( "\n" );
};
// nf() should return an array when being called on an array, at the moment it only returns strings. -F1LT3R
// This breaks the join() ref-test. The Processing.org documentation says String or String[].
p.nf = function (num, pad) {
var str = "" + num;
for (var i = pad - str.length; i > 0; i--) {
str = "0" + str;
}
return str;
};
p.nfp = function nfp(Value, pad, right) {
var str = String(Value);
if (arguments.length < 3) { //check if it's 2 arguments
if (Value > 0) {
while (str.length < pad) {
str = "0" + str;
}
str = "+" + str;
return str;
} else {
str = str.slice(1); //used to remove the '-' infront of the original number.
while (str.length < pad) {
str = "0" + str;
}
str = "-" + str;
return str;
}
} else if (arguments.length === 3) { //check if it's 3 arguments
var decimalPos = str.indexOf('.'),
strL, strR;
if (Value > 0) {
strL = str.slice(0, decimalPos); //store #'s to left of decimal into strL
strR = str.slice(decimalPos + 1, str.length); //store #'s to right of decimal into strR
while (strL.length < pad) { //pad to left of decimal on positive #'s
strL = "0" + strL;
}
strL = "+" + strL;
while (strR.length < right) { //pad to right of decimal on positive #'s
strR = strR + "0";
}
return strL + "." + strR;
} else {
strL = str.slice(1, decimalPos); //store #'s to left of decimal into strL
strR = str.slice(decimalPos + 1, str.length); //store #'s to right of decimal into strR
while (strL.length < pad) { //pad to left of decimal on negative #'s
strL = "0" + strL;
}
strL = "-" + strL;
while (strR.length < right) { //pad to right of decimal on negative #'s
strR = strR + "0";
}
return strL + "." + strR;
}
}
};
////////////////////////////////////////////////////////////////////////////
// String Functions
////////////////////////////////////////////////////////////////////////////
// I have updated this to lint, we should check it still performs faster than the other option -F1LT3R
p.matchAll = function matchAll(aString, aRegExp) {
var i = 0,
results = [],
latest, regexp = new RegExp(aRegExp, "g");
latest = results[i] = regexp.exec(aString);
while (latest) {
i++;
latest = results[i] = regexp.exec(aString);
}
return results.slice(0, i);
};
String.prototype.replaceAll = function (re, replace) {
return this.replace(new RegExp(re, "g"), replace);
};
p.match = function (str, regexp) {
return str.match(regexp);
};
// Returns a line to lnPrinted() for user handling
p.lnPrinted = function lnPrinted() {};
p.printed = function printed() {};
// Event to send output to user control function print()/println()
p.println = function println() {
// Not working on Safari :( find work around!
if (arguments.callee.caller) {
var Caller = arguments.callee.caller.name.toString();
if (arguments.length > 1) {
p.ln = Caller !== "print" ? arguments : arguments[0];
} else {
p.ln = arguments[0];
}
//Returns a line to lnPrinted() for user error handling/debugging
if (Caller === "print") {
p.printed(arguments);
} else {
p.lnPrinted();
}
}
};
p.str = function str(aNumber) {
return aNumber + '';
};
p.print = function print() {
p.println(arguments[0]);
};
p.char = function (key) {
return key;
};
////////////////////////////////////////////////////////////////////////////
// Math functions
////////////////////////////////////////////////////////////////////////////
p.sq = function sq(aNumber) {
return aNumber * aNumber;
};
p.sqrt = function sqrt(aNumber) {
return Math.sqrt(aNumber);
};
p.int = function (aNumber) {
return Math.floor(aNumber);
};
p.min = function min(aNumber, aNumber2) {
return Math.min(aNumber, aNumber2);
};
p.max = function max(aNumber, aNumber2) {
return Math.max(aNumber, aNumber2);
};
p.floor = function floor(aNumber) {
return Math.floor(aNumber);
};
p.float = function (aNumber) {
return parseFloat(aNumber);
};
p.ceil = function ceil(aNumber) {
return Math.ceil(aNumber);
};
p.round = function round(aNumber) {
return Math.round(aNumber);
};
p.lerp = function lerp(value1, value2, amt) {
return ((value2 - value1) * amt) + value1;
};
p.abs = function abs(aNumber) {
return Math.abs(aNumber);
};
p.cos = function cos(aNumber) {
return Math.cos(aNumber);
};
p.sin = function sin(aNumber) {
return Math.sin(aNumber);
};
p.pow = function pow(aNumber, aExponent) {
return Math.pow(aNumber, aExponent);
};
p.sqrt = function sqrt(aNumber) {
return Math.sqrt(aNumber);
};
p.tan = function tan(aNumber) {
return Math.tan(aNumber);
};
p.atan = function atan(aNumber) {
return Math.atan(aNumber);
};
p.atan2 = function atan2(aNumber, aNumber2) {
return Math.atan2(aNumber, aNumber2);
};
p.radians = function radians(aAngle) {
return (aAngle / 180) * p.PI;
};
p.log = function log(aNumber) {
return Math.log(aNumber);
};
p.exp = function exp(aNumber) {
return Math.exp(aNumber);
};
p.asin = function asin(aNumber) {
return Math.asin(aNumber);
};
p.acos = function acos(aNumber) {
return Math.acos(aNumber);
};
p.boolean = function (val) {
var ret = false;
if (val && typeof val === 'number' && val !== 0) {
ret = true;
} else if (val && typeof val === 'boolean' && val === true) {
ret = true;
} else if (val && typeof val === 'string' && val.toLowerCase() === 'true') {
ret = true;
} else if (val && typeof val === 'object' && val.constructor === Array) {
ret = new Array(val.length);
for (var i = 0; i < val.length; i++) {
ret[i] = p.boolean(val[i]);
}
}
return ret;
};
p.dist = function dist(x1, y1, x2, y2) {
return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
};
p.map = function map(value, istart, istop, ostart, ostop) {
return ostart + (ostop - ostart) * ((value - istart) / (istop - istart));
};
p.mag = function (a, b, c) {
if (arguments.length === 2) {
return Math.sqrt(a * a + b * b);
} else if (arguments.length === 3) {
return Math.sqrt(a * a + b * b + c * c);
}
};
p.Random = function () {
var haveNextNextGaussian = false,
nextNextGaussian;
this.nextGaussian = function () {
if (haveNextNextGaussian) {
haveNextNextGaussian = false;
return nextNextGaussian;
} else {
var v1, v2, s;
do {
v1 = 2 * p.random(1) - 1; // between -1.0 and 1.0
v2 = 2 * p.random(1) - 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;
}
};
};
//! This can't be right... right?
p.byte = function (aNumber) {
return aNumber || 0;
};
p.norm = function norm(aNumber, low, high) {
var range = high - low;
return ((1 / range) * aNumber) - ((1 / range) * low);
};
p.random = function random(aMin, aMax) {
return arguments.length === 2 ? aMin + (Math.random() * (aMax - aMin)) : Math.random() * aMin;
};
var noiseGen = function noiseGen(x, y) {
var n = x + y * 57;
n = (n << 13) ^ n;
return Math.abs(1.0 - (((n * ((n * n * 15731) + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0));
};
var smoothedNoise = function smoothedNoise(x, y) {
var corners = (noiseGen(x - 1, y - 1) + noiseGen(x + 1, y - 1) + noiseGen(x - 1, y + 1) + noiseGen(x + 1, y + 1)) / 16,
sides = (noiseGen(x - 1, y) + noiseGen(x + 1, y) + noiseGen(x, y - 1) + noiseGen(x, y + 1)) / 8,
center = noiseGen(x, y) / 4;
return corners + sides + center;
};
var interpolate = function interpolate(a, b, x) {
var ft = x * p.PI;
var f = (1 - Math.cos(ft)) * 0.5;
return a * (1 - f) + b * f;
};
var interpolatedNoise = function interpolatedNoise(x, y) {
var integer_X = Math.floor(x);
var fractional_X = x - integer_X;
var integer_Y = Math.floor(y);
var fractional_Y = y - integer_Y;
var v1 = smoothedNoise(integer_X, integer_Y),
v2 = smoothedNoise(integer_X + 1, integer_Y),
v3 = smoothedNoise(integer_X, integer_Y + 1),
v4 = smoothedNoise(integer_X + 1, integer_Y + 1);
var i1 = interpolate(v1, v2, fractional_X),
i2 = interpolate(v3, v4, fractional_X);
return interpolate(i1, i2, fractional_Y);
};
var perlinNoise_2D = function perlinNoise_2D(x, y) {
var total = 0,
p = 0.25,
n = 3;
for (var i = 0; i <= n; i++) {
var frequency = Math.pow(2, i);
var amplitude = Math.pow(p, i);
total += interpolatedNoise(x * frequency, y * frequency) * amplitude;
}
return total;
};
// Add Thomas Saunders 3D noiseGen code here....
var perlinNoise_3D = function perlinNoise_3D() {
return 0;
};
// From: http://freespace.virgin.net/hugo.elias/models/m_perlin.htm
p.noise = function (x, y, z) {
switch (arguments.length) {
case 2:
return perlinNoise_2D(x, y);
case 3:
return perlinNoise_3D(x, y, z);
case 1:
return perlinNoise_2D(x, x);
}
};
p.constrain = function constrain(aNumber, aMin, aMax) {
return Math.min(Math.max(aNumber, aMin), aMax);
};
p.degrees = function degrees(aAngle) {
aAngle = (aAngle * 180) / p.PI;
if (aAngle < 0) {
aAngle = 360 + aAngle;
}
return aAngle;
};
function PerlinNoise_2D( x, y ){
var total = 0,
p = 0.25,
n = 3;
for( var i = 0; i <= n; i++ ){
var frequency = Math.pow( 2, i );
var amplitude = Math.pow( p, i );
total += InterpolatedNoise( x * frequency, y * frequency ) * amplitude;
}
return total;
}
function Interpolate( a, b, x ){
var ft = x * p.PI;
var f = (1 - Math.cos( ft ) ) * .5;
return a * ( 1 - f ) + b * f;
}
p.constrain = function constrain( aNumber, aMin, aMax ){
return Math.min( Math.max( aNumber, aMin ), aMax );
};
p.degrees = function degrees( aAngle ){
aAngle = ( aAngle * 180 ) / p.PI;
if (aAngle < 0) {aAngle = 360 + aAngle}
return aAngle;
};
// Changes the size of the Canvas ( this resets context properties like 'lineCap', etc.
p.size = function size( aWidth, aHeight ){
p.context = new VectorLayer(aWidth, aHeight);
// default white on black
p.background(0,0,0,0);
p.context.set_color(255);
p.context.start();
add_layer(p.context);
p.width = aWidth;
p.height = aHeight;
srand();
var props = { fillStyle : p.context.fillStyle,
strokeStyle : p.context.strokeStyle,
lineCap : p.context.lineCap
} // More to be added...
// curElement.width = p.width = aWidth;
// curElement.height = p.height = aHeight;
// for( var i in props ){
// p.context[ i ] = props[ i ]
// };
};
// PVector instantiation
p.PVector = function PVector(){ return new vectorOps( arguments ) };
// PVector method translation
p.PVector.add = PVectorAdd;
p.PVector.sub = PVectorSub;
p.PVector.mult = PVectorMult;
p.PVector.div = PVectorDiv;
p.PVector.dist = PVectorDist;
p.PVector.angleBetween = PVectorAngle;
// PVector methods
function PVectorAdd(){
var a = arguments;
return p.PVector( a[0].x + a[1].x, a[0].y + a[1].y, a[0].z + a[1].z );
}
function PVectorSub(){
var a = arguments;
return p.PVector( a[0].x - a[1].x, a[0].y - a[1].y,a[0].z - a[1].z );
}
function PVectorMult(){
if( typeof arguments[1] == 'number' ){
var a = arguments;
return p.PVector( a[0].x * a[1], a[0].y * a[1], a[0].z * a[1] );
}else if( typeof arguments[1] == 'object' ){
var a = arguments;
return p.PVector( a[0].x * a[1].x, a[0].y * a[1].y, a[0].z * a[1].z );
}
}
function PVectorDiv(){
if( typeof arguments[1] == 'number' ){
var a = arguments;
return p.PVector( a[0].x / a[1], a[0].y / a[1], a[0].z / a[1] );
}
else if ( typeof (arguments[1]) == 'object' ){
var a = arguments;
return p.PVector( a[0].x / a[1].x, a[0].y / a[1].y, a[0].z / a[1].z );
}
}
function PVectorDist(){
var v1 = new p.PVector();
var v2 = new p.PVector();
v1 = arguments[0];
v2 = arguments[1];
return v1.dist(v2);
}
function PVectorAngle(v1, v2){
return Math.acos( v1.dot(v2) / (v1.mag()*v2.mag()) );
}
// Common vector operations for PVector
function vectorOps(){
arguments = arguments[0];
this.x = arguments[ 0 ] || 0;
this.y = arguments[ 1 ] || 0;
this.z = arguments[ 2 ] || 0;
this.set = function(){
if (arguments.length == 1){
var vArr = arguments[0];
this.set(arguments[0].x || vArr[0], arguments[0].y || vArr[1], arguments[0].z || vArr[2]);
}else{
this.x = arguments[0];
this.y = arguments[1];
this.z = arguments[2];
};
};
this.get = function get(){ return p.PVector( this.x, this.y, this.z ) };
this.mag = function mag(){ return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z ) };
this.add = function(){
if( arguments.length == 3 ){
this.x += arguments[0];
this.y += arguments[1];
this.z += arguments[2];
}else if( arguments.length == 1 ){
this.x += arguments[0].x;
this.y += arguments[0].y;
this.z += arguments[0].z;
};
};
this.sub = function(){
if( arguments.length == 3 ){
this.x -= arguments[0];
this.y -= arguments[1];
this.z -= arguments[2];
}else if( arguments.length == 1 ){
this.x -= arguments[0].x;
this.y -= arguments[0].y;
this.z -= arguments[0].z;
};
};
this.mult = function(){
if( typeof arguments[0] == 'number' ){
this.x *= arguments[0];
this.y *= arguments[0];
this.z *= arguments[0];
}else if( typeof arguments[0] == 'object' ){
this.x *= arguments[0].x;
this.y *= arguments[0].y;
this.z *= arguments[0].z;
};
};
this.div = function(){
if( typeof arguments[0] == 'number' ){
this.x /= arguments[0];
this.y /= arguments[0];
this.z /= arguments[0];
}else if( typeof arguments[0] == 'object' ){
this.x /= arguments[0].x;
this.y /= arguments[0].y;
this.z /= arguments[0].z;
};
};
this.dist = function(){
var v = new p.PVector();
v = arguments[0];
var dx = this.x - v.x;
var dy = this.y - v.y;
var dz = this.z - v.z;
return Math.sqrt( dx*dx + dy*dy + dz*dz );
};
this.dot = function dot(){
var num;
if( arguments.length == 3 ){
num = this.x * arguments[0] + this.y * arguments[1] + this.z * arguments[2];
}else if( arguments.length == 2 ){
var v1 = new p.PVector();
var v2 = new p.PVector();
v1 = arguments[0];
v2 = arguments[1];
num = v1.dot(v2);
}else if( arguments.length == 1 ){
var v = new p.PVector();
v = arguments[0];
num = this.x * v.x + this.y * v.y + this.z * v.z;
};
return num;
};
this.cross = function cross(){
var crossX, crossY, crossZ;
var v = new p.PVector(); // Will implement at later date - aSydiK :: What do you mean? - F1LT3R
v = arguments[0];
crossX = this.y * v.z - v.y * this.z;
crossY = this.z * v.x - v.z * this.x;
crossZ = this.x * v.y - v.x * this.y;
return p.PVector( crossX, crossY, crossZ );
};
this.normalize = function normalize(){
var m = this.mag();
console.log( m );
if( m > 0 ){ this.div( m ) };
};
this.limit = function limit( high ){
if( this.mag() > high ){
this.normalize();
this.mult( high );
};
};
this.angleBetween = function(){
PVectorAngle.call( this, arguments );
};
this.array = function array(){
var vArray = new Array( 3 );
vArray[0] = this.x;
vArray[1] = this.y;
vArray[2] = this.z;
return vArray;
};
}
// End of PVector operations
////////////////////////////////////////////////////////////////////////////
// Style functions
////////////////////////////////////////////////////////////////////////////
p.noStroke = function noStroke() { doStroke = false; };
p.noFill = function noFill() { doFill = false; };
p.smooth = function smooth() {};
p.noSmooth = function noSmooth() {};
p.fill = function(){
doFill = true;
p.context.set_color.apply(p.context, arguments);
};
p.stroke = function() {
doStroke = true;
p.context.set_color.apply(p.context, arguments);
}
// echo ("STROKE args: " + arguments);
// doStroke = true;
// doStrokeArgs = arguments
// // our own freej color function -jrml
// // p.context.color.apply(p.context, arguments);
// };
p.strokeWeight = function strokeWeight( w ){
p.context.lineWidth = w;
};
////////////////////////////////////////////////////////////////////////////
// Vector drawing functions
////////////////////////////////////////////////////////////////////////////
p.Point = function Point( x, y ){
this.x = x;
this.y = y;
this.copy = function(){
return new Point( x, y );
}
};
p.point = function point( x, y ){
var oldFill = p.context.fillStyle;
p.context.fillStyle = p.context.strokeStyle;
p.context.fillRect( Math.round( x ), Math.round( y ), 1, 1 );
p.context.fillStyle = oldFill;
};
p.beginShape = function beginShape( type ){
curShape = type;
curShapeCount = 0;
curvePoints = [];
};
p.endShape = function endShape( close ){
if( curShapeCount != 0 ){
if( close || doFill ){ p.context.lineTo( firstX, firstY ); }
if( doFill ){ p.context.fill(); }
if( doStroke ){ p.context.stroke(); }
p.context.closePath();
curShapeCount = 0;
pathOpen = false;
}
if( pathOpen ){
if ( doFill ){ p.context.fill(); }
if ( doStroke ){ p.context.stroke(); }
p.context.closePath();
curShapeCount = 0;
pathOpen = false;
}
};
p.vertex = function vertex( x, y, x2, y2, x3, y3 ){
if( curShapeCount == 0 && curShape != p.POINTS ){
pathOpen = true;
p.context.beginPath();
p.context.moveTo( x, y );
firstX = x;
firstY = y;
}else{
if( curShape == p.POINTS ){
p.point( x, y );
}else if( arguments.length == 2 ){
if( curShape != p.QUAD_STRIP || curShapeCount != 2 ){
p.context.lineTo( x, y );
}
if( curShape == p.TRIANGLE_STRIP ){
if( curShapeCount == 2 ){
// finish shape
p.endShape( p.CLOSE );
pathOpen = true;
p.context.beginPath();
// redraw last line to start next shape
p.context.moveTo( prevX, prevY );
p.context.lineTo( x, y );
curShapeCount = 1;
}
firstX = prevX;
firstY = prevY;
}
if( curShape == p.TRIANGLE_FAN && curShapeCount == 2 ){
// finish shape
p.endShape( p.CLOSE) ;
pathOpen = true;
p.context.beginPath();
// redraw last line to start next shape
p.context.moveTo( firstX, firstY );
p.context.lineTo( x, y );
curShapeCount = 1;
}
if( curShape == p.QUAD_STRIP && curShapeCount == 3 ){
// finish shape
p.context.lineTo( prevX, prevY );
p.endShape(p.CLOSE);
pathOpen = true;
p.context.beginPath();
// redraw lines to start next shape
p.context.moveTo( prevX, prevY );
p.context.lineTo( x, y );
curShapeCount = 1;
}
if( curShape == p.QUAD_STRIP ){
firstX = secondX;
firstY = secondY;
secondX = prevX;
secondY = prevY;
}
}else if( arguments.length == 4 ){
if( curShapeCount > 1 ){
p.context.moveTo( prevX, prevY );
p.context.quadraticCurveTo( firstX, firstY, x, y );
curShapeCount = 1;
}
}else if( arguments.length == 6 ){
p.context.bezierCurveTo( x, y, x2, y2, x3, y3 );
}
}
prevX = x;
prevY = y;
curShapeCount ++;
if( curShape == p.LINES && curShapeCount == 2 ||
( curShape == p.TRIANGLES ) && curShapeCount == 3 ||
( curShape == p.QUADS ) && curShapeCount == 4
){
p.endShape( p.CLOSE );
}
};
p.curveVertex = function( x, y, x2, y2 ){
if( curvePoints.length < 3 ){
curvePoints.push( [ x, y ] );
}else{
var b = [], s = 1 - curTightness;
/*
* 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 |
*/
curvePoints.push( [ x, y ] );
b[ 0 ] = [ curvePoints[ 1 ][ 0 ], curvePoints[ 1 ][ 1 ] ];
b[ 1 ] = [ curvePoints[ 1 ][ 0 ] + ( s * curvePoints[ 2 ][ 0 ] - s * curvePoints[ 0 ][ 0 ] ) / 6,
curvePoints[ 1 ][ 1 ] + ( s * curvePoints[ 2 ][ 1 ] - s * curvePoints[ 0 ][ 1 ] ) / 6 ];
b[ 2 ] = [ curvePoints[ 2 ][ 0 ] + ( s * curvePoints[ 1 ][ 0 ] - s * curvePoints[ 3 ][ 0 ] ) / 6,
curvePoints[ 2 ][ 1 ] + ( s * curvePoints[ 1 ][ 1 ] - s * curvePoints[ 3 ][ 1 ] ) / 6 ];
b[ 3 ] = [ curvePoints[ 2 ][ 0 ], curvePoints[ 2 ][ 1 ] ];
if( !pathOpen ){
p.vertex( b[ 0 ][ 0 ], b[ 0 ][ 1 ] );
}else{
curShapeCount = 1;
}
p.vertex(
b[ 1 ][ 0 ],
b[ 1 ][ 1 ],
b[ 2 ][ 0 ],
b[ 2 ][ 1 ],
b[ 3 ][ 0 ],
b[ 3 ][ 1 ]
);
curvePoints.shift();
}
};
p.curveTightness = function( tightness ){ curTightness = tightness; };
p.bezierVertex = p.vertex;
p.rectMode = function rectMode( aRectMode ){ curRectMode = aRectMode; };
p.imageMode = function (){};
p.ellipseMode = function ellipseMode( aEllipseMode ) { curEllipseMode = aEllipseMode; };
p.arc = function arc( x, y, width, height, start, stop ){
if( width <= 0 ){ return; }
if( curEllipseMode == p.CORNER ){
x += width / 2;
y += height / 2;
}
p.context.moveTo( x, y );
p.context.beginPath();
p.context.arc( x, y, curEllipseMode == p.CENTER_RADIUS ? width : width/2, start, stop, false );
if( doStroke ){ p.context.stroke(); }
p.context.lineTo( x, y );
if( doFill ){ p.context.fill(); }
p.context.closePath();
};
// QUAAA optimize by implementing these iterations in C++
p.line = function line( x1, y1, x2, y2 ){
p.context.lineCap = "round";
p.context.beginPath();
p.context.moveTo( x1, y1 );
p.context.lineTo( x2, y2 );
p.context.stroke();
p.context.closePath();
};
p.bezier = function bezier( x1, y1, x2, y2, x3, y3, x4, y4 ){
p.context.lineCap = "butt";
p.context.beginPath();
p.context.moveTo( x1, y1 );
p.context.bezierCurveTo( x2, y2, x3, y3, x4, y4 );
p.context.stroke();
p.context.closePath();
};
p.triangle = function triangle( x1, y1, x2, y2, x3, y3 ){
p.beginShape();
p.vertex( x1, y1 );
p.vertex( x2, y2 );
p.vertex( x3, y3 );
p.endShape();
};
p.quad = function quad( x1, y1, x2, y2, x3, y3, x4, y4 ){
p.context.lineCap = "square";
p.beginShape();
p.vertex( x1, y1 );
p.vertex( x2, y2 );
p.vertex( x3, y3 );
p.vertex( x4, y4 );
p.endShape();
};
p.rect = function rect( x, y, width, height ){
if( !( width + height ) ){ return; }
p.context.beginPath();
var offsetStart = 0;
var offsetEnd = 0;
if( curRectMode == p.CORNERS ){
width -= x;
height -= y;
}
if( curRectMode == p.RADIUS ){
width *= 2;
height *= 2;
}
if( curRectMode == p.CENTER || curRectMode == p.RADIUS ){
x -= width / 2;
y -= height / 2;
}
p.context.rect(
Math.round( x ) - offsetStart,
Math.round( y ) - offsetStart,
Math.round( width ) + offsetEnd,
Math.round( height ) + offsetEnd
);
if( doFill ){ p.context.fill(); }
if( doStroke ){ p.context.stroke() };
p.context.closePath();
};
p.ellipse = function ellipse( x, y, width, height ){
x = x || 0;
y = y || 0;
if( width <= 0 && height <= 0 ){ return; }
p.context.beginPath();
if( curEllipseMode == p.RADIUS ){
width *= 2;
height *= 2;
}
var offsetStart = 0;
// Shortcut for drawing a circle
if( width == height ){
p.context.arc( x - offsetStart, y - offsetStart, width / 2, 0, p.TWO_PI, false );
}else{
var w = width/2,
h = height/2,
C = 0.5522847498307933;
var c_x = C * w,
c_y = C * h;
//! Do we still need this? I hope the Canvas arc() more capable by now?
p.context.moveTo( x + w, y );
p.context.bezierCurveTo( x+w , y-c_y , x+c_x , y-h , x , y-h );
p.context.bezierCurveTo( x-c_x , y-h , x-w , y-c_y , x-w , y );
p.context.bezierCurveTo( x-w , y+c_y , x-c_x , y+h, x, y+h );
p.context.bezierCurveTo( x+c_x , y+h , x+w , y+c_y , x+w , y );
}
if( doFill ){ p.context.fill(); }
if( doStroke ){ p.context.stroke(); }
p.context.closePath();
};
////////////////////////////////////////////////////////////////////////////
// Raster drawing functions
////////////////////////////////////////////////////////////////////////////
p.save = function save( file ){};
// Loads an image for display. Type is unused. Callback is fired on load.
p.loadImage = function loadImage( file, type, callback ){
var img = document.createElement( 'img' );
img.src = file;
img.onload = function(){
var h = this.height,
w = this.width;
var canvas = document.createElement( "canvas" );
canvas.width = w;
canvas.height = h;
var context = canvas.getContext( "2d" );
context.drawImage( this, 0, 0 );
this.data = buildImageObject( context.getImageData( 0, 0, w, h ) );
this.data.img = img;
callback?callback():0;
}
return img;
};
// Gets a single pixel or block of pixels from the current Canvas Context
// p.get = function get( x, y ){
// if( !arguments.length ){
// var c = p.createGraphics( p.width, p.height );
// c.image( p.context, 0, 0 );
// return c;
// }
// if( !getLoaded ){
// getLoaded = buildImageObject( p.context.getImageData( 0, 0, p.width, p.height ) );
// }
// return getLoaded.get( x, y );
// };
// // Creates a new Processing instance and passes it back for... processing
// p.createGraphics = function createGraphics( w, h ){
// var canvas = document.createElement( "canvas" );
// var ret = buildProcessing( canvas );
// ret.size( w, h );
// ret.canvas = canvas;
// return ret;
// };
// Paints a pixel array into the canvas
p.set = function set( x, y, obj ){
if( obj && obj.img ){
p.image( obj, x, y );
}else{
var oldFill = p.context.fillStyle,
color = obj;
p.context.fillStyle = color;
p.context.fillRect( Math.round( x ), Math.round( y ), 1, 1 );
p.context.fillStyle = oldFill;
}
};
// Gets a 1-Dimensional pixel array from Canvas
p.loadPixels = function(){
p.pixels = buildImageObject( p.context.getImageData(0, 0, p.width, p.height) ).pixels;
};
// Draws a 1-Dimensional pixel array to Canvas
p.updatePixels = function() {
var colors = /(\d+),(\d+),(\d+),(\d+)/,
pixels = {};
pixels.width = p.width;
pixels.height = p.height;
pixels.data = [];
if( p.context.createImageData ){
pixels = p.context.createImageData( p.width, p.height );
}
var data = pixels.data,
pos = 0;
for( var i = 0, l = p.pixels.length; i < l; i++ ){
var c = ( p.pixels[i] || "rgba(0,0,0,1)" ).match( colors );
data[ pos + 0 ] = parseInt( c[ 1 ] );
data[ pos + 1 ] = parseInt( c[ 2 ] );
data[ pos + 2 ] = parseInt( c[ 3 ] );
data[ pos + 3 ] = parseFloat( c[ 4 ] ) * 255;
pos += 4;
}
p.context.putImageData( pixels, 0, 0 );
};
// Draw an image or a color to the background
p.background = function background( img ) {
if( arguments.length ){
if( img.data && img.data.img ){
curBackground = img.data;
}else{
p.context.push_color();
p.context.set_color.apply(p.context, arguments);
// curBackground = p.color.apply( this, arguments );
}
}
if( curBackground.img ){
p.image( img, 0, 0 );
}else{
// var oldFill = p.context.fillStyle;
// p.context.fillStyle = curBackground + "";
// p.context.set_color(0);
// echo("background fill");
p.context.fillRect( 0, 0, p.width, p.height );
p.context.pop_color();
}
};
p.AniSprite = function( prefix, frames ){
this.images = [];
this.pos = 0;
for( var i = 0; i < frames; i++ ){
this.images.push( prefix + p.nf( i, ( "" + frames ).length ) + ".gif" );
}
this.display = function( x, y ){
p.image_old( this.images[ this.pos ], x, y );
if( ++this.pos >= frames ){
this.pos = 0;
}
};
this.getWidth = function(){ return getImage_old( this.images[ 0 ] ).width; };
this.getHeight = function(){ return getImage_old( this.images[ 0 ] ).height; };
};
function buildImageObject( obj ){
var pixels = obj.data;
var data = p.createImage( obj.width, obj.height );
if( data.__defineGetter__ && data.__lookupGetter__ && !data.__lookupGetter__( "pixels" ) ){
var pixelsDone;
data.__defineGetter__( "pixels", function(){
if( pixelsDone ){
return pixelsDone;
}
pixelsDone = [];
for( var i = 0; i < pixels.length; i += 4 ){
pixelsDone.push(
p.color(
pixels[ i ],
pixels[ i + 1 ],
pixels[ i + 2 ],
pixels[ i + 3 ])
);
}
return pixelsDone;
});
}else{
data.pixels = [];
for ( var i = 0; i < pixels.length; i += 4 ){
data.pixels.push( p.color(
pixels[ i ],
pixels[ i + 1 ],
pixels[ i + 2 ],
pixels[ i + 3 ]
));
}
}
return data;
}
p.createImage = function createImage( w, h, mode ){
var data = {};
data.width = w;
data.height = h;
data.data = [];
if( p.context.createImageData ) {
data = p.context.createImageData( w, h );
}
data.pixels = new Array( w * h );
data.get = function( x, y ){
return this.pixels[ w * y + x ];
};
data._mask = null;
data.mask = function( img ){
this._mask = img;
};
data.loadPixels = function(){};
data.updatePixels = function(){};
return data;
};
function getImage( img ){
if( typeof img == "string" ){
return document.getElementById( img );
}
if( img.img ){
return img.img;
}else if( img.getContext || img.canvas ){
img.pixels = img.getContext( '2d' ).createImageData( img.width, img.height );
}
for( var i = 0, l = img.pixels.length; i < l; i++ ){
var pos = i * 4;
var c = ( img.pixels[ i ] || "rgba(0,0,0,1)" ).slice( 5, - 1 ).split( "," );
img.data[ pos + 0 ] = parseInt( c[ 0 ] );
img.data[ pos + 1 ] = parseInt( c[ 1 ] );
img.data[ pos + 2 ] = parseInt( c[ 2 ] );
img.data[ pos + 3 ] = parseFloat( c[ 3 ] ) * 100;
}
var canvas = document.createElement( "canvas" );
canvas.width = img.width;
canvas.height = img.height;
var context = canvas.getContext( "2d" );
context.putImageData( img.pixels, 0, 0 );
img.canvas = canvas;
return img;
}
// Depreciating "getImage_old" from PJS - currently here to support AniSprite
function getImage_old( img ){
if( typeof img == "string" ){
return document.getElementById( img );
}
if( img.img || img.canvas ){
return img.img || img.canvas;
}
for( var i = 0, l = img.pixels.length; i < l; i++ ){
var pos = i * 4;
var c = ( img.pixels[ i ] || "rgba(0,0,0,1)" ).slice( 5, - 1 ).split( "," );
img.data[ pos + 0 ] = parseInt( c[ 0 ] );
img.data[ pos + 1 ] = parseInt( c[ 1 ] );
img.data[ pos + 2 ] = parseInt( c[ 2 ] );
img.data[ pos + 3 ] = parseFloat( c[ 3 ] ) * 100;
}
var canvas = document.createElement( "canvas" );
canvas.width = img.width;
canvas.height = img.height;
var context = canvas.getContext( "2d" );
context.putImageData( img, 0, 0 );
img.canvas = canvas;
return canvas;
}
// Depreciating "getImage_old" from PJS - currently here to support AniSprite
p.image_old=function image_old(img,x,y,w,h){
x = x || 0;
y = y || 0;
var obj = getImage( img );
if( curTint >= 0 ){
var oldAlpha = p.context.globalAlpha;
p.context.globalAlpha = curTint / opacityRange;
}
if( arguments.length == 3 ){
p.context.drawImage( obj, x, y );
}else{
p.context.drawImage( obj, x, y, w, h );
}
if( curTint >= 0 ){
p.context.globalAlpha = oldAlpha;
}
if( img._mask ){
var oldComposite = p.context.globalCompositeOperation;
p.context.globalCompositeOperation = "darker";
p.image( img._mask, x, y );
p.context.globalCompositeOperation = oldComposite;
}
};
// Draws an image to the Canvas
p.image = function image( img, x, y, w, h ){
if( img.data || img.canvas ){
x = x || 0;
y = y || 0;
var obj = getImage( img.data || img.canvas );
if( curTint >= 0 ){
var oldAlpha = p.context.globalAlpha;
p.context.globalAlpha = curTint / opacityRange;
}
if( arguments.length == 3 ){
p.context.drawImage( obj, x, y );
}else{
p.context.drawImage( obj, x, y, w, h );
}
if( curTint >= 0 ){
p.context.globalAlpha = oldAlpha;
}
if( img._mask ){
var oldComposite = p.context.globalCompositeOperation;
p.context.globalCompositeOperation = "darker";
p.image( img._mask, x, y );
p.context.globalCompositeOperation = oldComposite;
}
}
if( typeof img == 'string' ){
}
};
// Clears a rectangle in the Canvas element or the whole Canvas
p.clear = function clear ( x, y, width, height ) {
if( arguments.length == 0 ){
p.context.clearRect( 0, 0, p.width, p.height );
}else{
p.context.clearRect( x, y, width, height );
}
}
p.tint = function tint( rgb, a ){
curTint = a;
};
////////////////////////////////////////////////////////////////////////////
// Font handling
////////////////////////////////////////////////////////////////////////////
// Loads a font from an SVG or Canvas API
p.loadFont = function loadFont( name ){
if( name.indexOf( ".svg" ) == - 1 ){
return {
name: name,
width: function( str ){
if( p.context.mozMeasureText ){
return p.context.mozMeasureText(
typeof str == "number" ?
String.fromCharCode( str ) :
str
) / curTextSize;
}else{
return 0;
}
}
};
}else{
// If the font is a glyph, calculate by SVG table
var font = p.loadGlyphs( name );
return {
name : name,
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( p.glyphLook( p.glyphTable[ name ], str[ i ] ).horiz_adv_x ); }
catch( e ){ ; }
}
return width / p.glyphTable[ name ].units_per_em;
}
}
}
};
// Sets a 'current font' for use
p.textFont = function textFont( name, size ){
curTextFont = name;
p.textSize( size );
};
// Sets the font size
p.textSize = function textSize( size ){
//! Was this meant to return textSize value if no arguments were passed?
if( size ){
curTextSize = size;
}
};
p.textAlign = function textAlign(){};
// A lookup table for characters that can not be referenced by Object
p.glyphLook = function glyphLook( font, chr ){
try{
switch( chr ){
case "1" : return font[ "one" ]; break;
case "2" : return font[ "two" ]; break;
case "3" : return font[ "three" ]; break;
case "4" : return font[ "four" ]; break;
case "5" : return font[ "five" ]; break;
case "6" : return font[ "six" ]; break;
case "7" : return font[ "seven" ]; break;
case "8" : return font[ "eight" ]; break;
case "9" : return font[ "nine" ]; break;
case "0" : return font[ "zero" ]; break;
case " " : return font[ "space" ]; break;
case "$" : return font[ "dollar" ]; break;
case "!" : return font[ "exclam" ]; break;
case '"' : return font[ "quotedbl" ]; break;
case "#" : return font[ "numbersign" ]; break;
case "%" : return font[ "percent" ]; break;
case "&" : return font[ "ampersand" ]; break;
case "'" : return font[ "quotesingle" ]; break;
case "(" : return font[ "parenleft" ]; break;
case ")" : return font[ "parenright" ]; break;
case "*" : return font[ "asterisk" ]; break;
case "+" : return font[ "plus" ]; break;
case "," : return font[ "comma" ]; break;
case "-" : return font[ "hyphen" ]; break;
case "." : return font[ "period" ]; break;
case "/" : return font[ "slash" ]; break;
case "_" : return font[ "underscore" ]; break;
case ":" : return font[ "colon" ]; break;
case ";" : return font[ "semicolon" ]; break;
case "<" : return font[ "less" ]; break;
case "=" : return font[ "equal" ]; break;
case ">" : return font[ "greater" ]; break;
case "?" : return font[ "question" ]; break;
case "@" : return font[ "at" ]; break;
case "[" : return font[ "bracketleft" ]; break;
case "\\" : return font[ "backslash" ]; break;
case "]" : return font[ "bracketright" ]; break;
case "^" : return font[ "asciicircum" ]; break;
case "`" : return font[ "grave" ]; break;
case "{" : return font[ "braceleft" ]; break;
case "|" : return font[ "bar" ]; break;
case "}" : return font[ "braceright" ]; break;
case "~" : return font[ "asciitilde" ]; break;
// If the character is not 'special', access it by object reference
default : return font[ chr ]; break;
}
}catch( e ){ ; }
}
// Print some text to the Canvas
p.text = function text( str, x, y ){
// If the font is a standard Canvas font...
if( !curTextFont.glyph ){
if( str && p.context.mozDrawText ){
p.context.save();
p.context.mozTextStyle = curTextSize + "px " + curTextFont.name;
p.context.translate( x, y );
p.context.mozDrawText(
typeof str == "number" ?
String.fromCharCode( str ) :
str ) ;
p.context.restore();
}
}else{
// If the font is a Batik SVG font...
var font = p.glyphTable[ curTextFont.name ];
p.context.save();
p.context.translate( x, y + curTextSize );
var upem = font[ "units_per_em" ],
newScale = 1 / upem * curTextSize;
p.context.scale( newScale, newScale );
var len = str.length;
for(var i = 0; i < len; i++ ){
// Test character against glyph table
try{ p.glyphLook( font, str[ i ] ).draw(); }
catch( e ){ ; }
}
p.context.restore();
}
};
// Load Batik SVG Fonts and parse to pre-def objects for quick rendering
p.loadGlyphs = function loadGlyph( url ){
// Load and parse Batik SVG font as XML into a Processing Glyph object
var loadXML = function loadXML(){
try{
var xmlDoc = new ActiveXObject( "Microsoft.XMLDOM" );
}
catch( e ){
try{
xmlDoc=document.implementation.createDocument( "", "", null );
}
catch( e ){
p.println( e.message );
return;
}
};
try{
xmlDoc.async = false;
xmlDoc.load( url );
parse( xmlDoc.getElementsByTagName( "svg" )[ 0 ] );
}
catch( e ){
// Google Chrome, Safari etc.
try{
p.println( e.message );
var xmlhttp = new window.XMLHttpRequest();
xmlhttp.open( "GET", url, false );
xmlhttp.send( null );
parse( xmlhttp.responseXML.documentElement );
}
catch( e ){ ; }
}
};
// Return arrays of SVG commands and coords
var regex = function regex( needle, hay ){
var regexp = new RegExp( needle, "g" ),
results = [],
i = 0;
while( results[ i ] = regexp.exec( hay ) ){ i++; }
return results;
}
// Parse SVG font-file into block of Canvas commands
var parse = function parse( svg ){
// Store font attributes
var font = svg.getElementsByTagName( "font" );
p.glyphTable[ url ][ "horiz_adv_x" ] = font[ 0 ].getAttribute( "horiz-adv-x" );
var font_face = svg.getElementsByTagName( "font-face" )[ 0 ];
p.glyphTable[ url ][ "units_per_em" ] = parseFloat( font_face.getAttribute( "units-per-em") );
p.glyphTable[ url ][ "ascent" ] = parseFloat( font_face.getAttribute( "ascent" ) );
p.glyphTable[ url ][ "descent" ] = parseFloat( font_face.getAttribute( "descent" ) );
var getXY = "[0-9\-]+",
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" );
var horiz_adv_x = glyph[ i ].getAttribute( "horiz-adv-x" );
if( horiz_adv_x == null ){ var horiz_adv_x = p.glyphTable[ url ][ 'horiz_adv_x' ]; }
var buildPath = function buildPath( d ){
var c = regex( "[A-Za-z][0-9\- ]+|Z", d );
// Begin storing path object
var path = "var path={draw:function(){p.context.beginPath();p.context.save();";
var 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": //p.context.moveTo(x,-y);
x = parseFloat( xy[ 0 ][ 0 ] );
y = parseFloat( xy[ 1 ][ 0 ] );
//! Brackets needed on (-y)?
path += "p.context.moveTo("+ x +","+ (-y) +");";
break;
case "L": //p.context.lineTo(x,-y);
x = parseFloat( xy[ 0 ][ 0 ] );
y = parseFloat( xy[ 1 ][ 0 ] );
path += "p.context.lineTo("+ x +","+ (-y) +");";
break;
case "H"://p.context.lineTo(x,-y)
x = parseFloat( xy[ 0 ][ 0 ] );
path += "p.context.lineTo("+ x +","+ (-y) +");";
break;
case "V"://p.context.lineTo(x,-y);
y = parseFloat( xy[ 0 ][ 0 ] );
path += "p.context.lineTo("+ x +","+ (-y) +");";
break;
case "T"://p.context.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 ) );
a = Math.PI+Math.atan2( cx - x, cy - y );
cx = x + ( Math.sin( a ) * ( d ) );
cy = y + ( Math.cos( a ) * ( d ) );
}else{
cx = x;
cy = y;
}
path += "p.context.quadraticCurveTo("+ cx +","+ (-cy) +","+ nx +","+ (-ny) +");";
x = nx;
y = ny;
break;
case "Q"://p.context.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 += "p.context.quadraticCurveTo("+ cx +","+ (-cy) +","+ nx +","+ (-ny) +");";
x = nx;
y = ny;
break;
case "Z"://p.context.closePath();
path += "p.context.closePath();";
break;
}
lastCom = com[ 0 ];
}
path += "doStroke?p.context.stroke():0;";
path += "doFill?p.context.fill():0;";
path += "p.context.restore();";
path += "p.context.translate("+ horiz_adv_x +",0);";
path += "}}";
return path;
}
var d = glyph[ i ].getAttribute( "d" );
// Split path commands in glpyh
if( d !== undefined ){
var path = buildPath( d );
eval( path );
// Store glyph data to table object
p.glyphTable[ url ][ name ] = {
name : name,
unicode : unicode,
horiz_adv_x : horiz_adv_x,
draw : path.draw
}
}
} // finished adding glyphs to table
}
// Create a new object in glyphTable to store this font
p.glyphTable[ url ] = {};
// Begin loading the Batik SVG font...
loadXML( url );
// Return the loaded font for attribute grabbing
return p.glyphTable[ url ];
}
////////////////////////////////////////////////////////////////////////////
// Class methods
////////////////////////////////////////////////////////////////////////////
p.extendClass = function extendClass( obj, args, fn ){
if( arguments.length == 3 ){
fn.apply( obj, args );
}else{
args.call( obj );
}
};
p.addMethod = function addMethod( object, name, fn ){
if( object[ name ] ){
var args = fn.length,
oldfn = object[ name ];
object[ name ] = function(){
if( arguments.length == args ){
return fn.apply( this, arguments );
}else{
return oldfn.apply( this, arguments );
}
};
}else{
object[ name ] = fn;
}
};
////////////////////////////////////////////////////////////////////////////
// Set up environment
////////////////////////////////////////////////////////////////////////////
p.init = function init(code){
// p.stroke( 255 );
// p.fill( 255 );
// Canvas has trouble rendering single pixel stuff on whole-pixel
// counts, so we slightly offset it (this is super lame).
// p.context.translate( 0.5, 0.5 );
// luckily enough FreeJ is not-so-lame...
// The fun bit!
if( code ){
(function( Processing ){
with ( p ){
eval(parse(code, p));
}
})( p );
}
if( p.setup ){
inSetup = true;
p.setup();
}
inSetup = false;
if( p.draw ){
if( !doLoop ){
p.redraw();
} else {
p.loop();
}
}
//////////////////////////////////////////////////////////////////////////
// Event handling
//////////////////////////////////////////////////////////////////////////
/*
attach( curElement, "mousemove" , function(e){
var scrollX = window.scrollX != null ? window.scrollX : window.pageXOffset;
var scrollY = window.scrollY != null ? window.scrollY : window.pageYOffset;
p.pmouseX = p.mouseX;
p.pmouseY = p.mouseY;
p.mouseX = e.clientX - curElement.offsetLeft + scrollX;
p.mouseY = e.clientY - curElement.offsetTop + scrollY;
if( p.mouseMoved ){ p.mouseMoved(); }
if( mousePressed && p.mouseDragged ){ p.mouseDragged(); }
});
attach( curElement, "mouseout" , function( e ){ p.cursor("auto"); });
attach( curElement, "mousedown", function( e ){
mousePressed = true;
switch(e.which){
case 1: p.mouseButton = p.LEFT; break;
case 2: p.mouseButton = p.CENTER; break;
case 3: p.mouseButton = p.RIGHT; break;
}
p.mouseDown = true;
if( typeof p.mousePressed == "function" ){ p.mousePressed(); }
else{ p.mousePressed = true; }
});
attach( curElement, "mouseup", function( e ){
mousePressed = false;
if( p.mouseClicked ){ p.mouseClicked(); }
if( typeof p.mousePressed != "function" ){ p.mousePressed = false; }
if( p.mouseReleased ){ p.mouseReleased(); }
});
/*
attach( document, "keydown", function( e ){
keyPressed = true;
p.key = e.keyCode + 32;
var i, len = p.codedKeys.length;
for( i=0; i < len; i++ ){
if( p.key == p.codedKeys[ i ] ){
switch(p.key){
case 70: p.keyCode = p.UP ; break;
case 71: p.keyCode = p.RIGHT ; break;
case 72: p.keyCode = p.DOWN ; break;
case 69: p.keyCode = p.LEFT ; break;
}
p.key=p.CODED;
}
}
if( e.shiftKey ){ p.key = String.fromCharCode(p.key).toUpperCase().charCodeAt( 0 ); }
if( typeof p.keyPressed == "function" ){ p.keyPressed(); }
else{ p.keyPressed = true; }
});
attach( document, "keyup", function( e ){
keyPressed = false;
if( typeof p.keyPressed != "function" ){ p.keyPressed = false; }
if( p.keyReleased ){ p.keyReleased(); }
});
function attach(elem, type, fn) {
// if( elem.addEventListener ){ elem.addEventListener( type, fn, false ); }
// else{ elem.attachEvent( "on" + type, fn ); }
}
*/
};
return p;
}
})();