Examples using processing-js.
Created
October 25, 2013 20:57
-
-
Save mobeets/7161726 to your computer and use it in GitHub Desktop.
processing-js examples
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
int width = 300; | |
int height = 300; | |
int nBalls = 10; | |
Ball[] balls = new Ball[nBalls]; | |
Vect2D[] vels = new Vect2D[nBalls]; | |
void setup(){ | |
size(width, height); | |
smooth(); | |
noStroke(); | |
initBalls(); | |
} | |
void initBalls(){ | |
for (int i = 0; i < nBalls; i++) { | |
balls[i] = new Ball(30*i, 30*i, random(5, 15)); | |
vels[i] = new Vect2D(1, 1); | |
} | |
} | |
void draw(){ | |
background(51); | |
fill(204); | |
for (int i = 0; i < nBalls; i++){ | |
balls[i].x += vels[i].vx; | |
balls[i].y += vels[i].vy; | |
ellipse(balls[i].x, balls[i].y, balls[i].r*2, balls[i].r*2); | |
checkBoundaryCollision(balls[i], vels[i]); | |
// check all ball pairs for collisions | |
for (int j = i+1; j < nBalls; j++){ | |
checkObjectCollision(balls[i], vels[i], balls[j], vels[j]); | |
} | |
} | |
} | |
void checkObjectCollision(Ball b1, Vect2D v1, Ball b2, Vect2D v2){ | |
// get distances between the balls components | |
Vect2D bVect = new Vect2D(); | |
bVect.vx = b2.x - b1.x; | |
bVect.vy = b2.y - b1.y; | |
// calculate magnitude of the vector separating the balls | |
float bVectMag = sqrt(bVect.vx * bVect.vx + bVect.vy * bVect.vy); | |
if (bVectMag < b1.r + b2.r){ | |
// get angle of bVect | |
float theta = atan2(bVect.vy, bVect.vx); | |
// precalculate trig values | |
float sine = sin(theta); | |
float cosine = cos(theta); | |
/* bTemp will hold rotated ball positions. You | |
just need to worry about bTemp[1] position*/ | |
Ball[] bTemp = { | |
new Ball(), new Ball() }; | |
/* b2's position is relative to b1's | |
so you can use the vector between them (bVect) as the | |
reference point in the rotation expressions. | |
bTemp[0].x and bTemp[0].y will initialize | |
automatically to 0.0, which is what you want | |
since b2 will rotate around b1 */ | |
bTemp[1].x = cosine * bVect.vx + sine * bVect.vy; | |
bTemp[1].y = cosine * bVect.vy - sine * bVect.vx; | |
// rotate Temporary velocities | |
Vect2D[] vTemp = { | |
new Vect2D(), new Vect2D() }; | |
vTemp[0].vx = cosine * v1.vx + sine * v1.vy; | |
vTemp[0].vy = cosine * v1.vy - sine * v1.vx; | |
vTemp[1].vx = cosine * v2.vx + sine * v2.vy; | |
vTemp[1].vy = cosine * v2.vy - sine * v2.vx; | |
/* Now that velocities are rotated, you can use 1D | |
conservation of momentum equations to calculate | |
the final velocity along the x-axis. */ | |
Vect2D[] vFinal = { | |
new Vect2D(), new Vect2D() }; | |
// final rotated velocity for b1 | |
vFinal[0].vx = ((b1.m - b2.m) * vTemp[0].vx + 2 * b2.m * | |
vTemp[1].vx) / (b1.m + b2.m); | |
vFinal[0].vy = vTemp[0].vy; | |
// final rotated velocity for b1 | |
vFinal[1].vx = ((b2.m - b1.m) * vTemp[1].vx + 2 * b1.m * | |
vTemp[0].vx) / (b1.m + b2.m); | |
vFinal[1].vy = vTemp[1].vy; | |
// hack to avoid clumping | |
bTemp[0].x += vFinal[0].vx; | |
bTemp[1].x += vFinal[1].vx; | |
/* Rotate ball positions and velocities back | |
Reverse signs in trig expressions to rotate | |
in the opposite direction */ | |
// rotate balls | |
Ball[] bFinal = { | |
new Ball(), new Ball() }; | |
bFinal[0].x = cosine * bTemp[0].x - sine * bTemp[0].y; | |
bFinal[0].y = cosine * bTemp[0].y + sine * bTemp[0].x; | |
bFinal[1].x = cosine * bTemp[1].x - sine * bTemp[1].y; | |
bFinal[1].y = cosine * bTemp[1].y + sine * bTemp[1].x; | |
// update balls to screen position | |
b2.x = b1.x + bFinal[1].x; | |
b2.y = b1.y + bFinal[1].y; | |
b1.x = b1.x + bFinal[0].x; | |
b1.y = b1.y + bFinal[0].y; | |
// update velocities | |
v1.vx = cosine * vFinal[0].vx - sine * vFinal[0].vy; | |
v1.vy = cosine * vFinal[0].vy + sine * vFinal[0].vx; | |
v2.vx = cosine * vFinal[1].vx - sine * vFinal[1].vy; | |
v2.vy = cosine * vFinal[1].vy + sine * vFinal[1].vx; | |
} | |
} | |
class Ball{ | |
float x, y, r, m; | |
// default constructor | |
Ball() { | |
} | |
Ball(float x, float y, float r) { | |
this.x = x; | |
this.y = y; | |
this.r = r; | |
m = r*.1; | |
} | |
} | |
class Vect2D{ | |
float vx, vy; | |
// default constructor | |
Vect2D() { | |
} | |
Vect2D(float vx, float vy) { | |
this.vx = vx; | |
this.vy = vy; | |
} | |
} | |
// checkBoundaryCollision() function: | |
void checkBoundaryCollision(Ball ball, Vect2D vel){ | |
if (ball.x > width-ball.r){ | |
ball.x = width-ball.r; | |
vel.vx *= -1; | |
} | |
else if (ball.x < ball.r){ | |
ball.x = ball.r; | |
vel.vx *= -1; | |
} | |
else if (ball.y > height-ball.r){ | |
ball.y = height-ball.r; | |
vel.vy *= -1; | |
} | |
else if (ball.y < ball.r){ | |
ball.y = ball.r; | |
vel.vy *= -1; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>Processing.js</title> | |
<meta charset="UTF-8"> | |
<script type="text/javascript" src="processing-1.4.1.js"></script> | |
</head> | |
<body> | |
<canvas data-processing-sources="balls.pde"></canvas> | |
<canvas data-processing-sources="touch.pde"></canvas> | |
<canvas data-processing-sources="miley.pde"></canvas> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// convert -delay 20 -loop 0 miley-*.gif miley.gif | |
int X, Y; | |
int nX, nY; | |
int delay = 30; | |
float max_distance; | |
int X2, Y2; | |
float cur_deg = 0; | |
int radius = 50; | |
int count; | |
void setup(){ | |
size(300, 300); | |
frameRate(30); | |
strokeWeight(1); | |
X = 0, Y = 0; | |
nX = 0, nY = 0; | |
max_distance = dist(0, 0, width, height); | |
X2 = width/2 + radius; | |
Y2 = height/2; | |
count = 0; | |
} | |
void draw(){ | |
background(0); | |
mouseMoved(); | |
moveAroundArc(); | |
drawLines(); | |
drawCircles(); | |
if (count < 1){ | |
// save('tmp.tif'); | |
// saveFrame('tmp.tif'); | |
count += 1; | |
} | |
} | |
void mouseMoved(){ | |
nX = mouseX; | |
nY = mouseY; | |
X += (nX-X)/delay; | |
Y += (nY-Y)/delay; | |
} | |
void moveAroundArc(){ | |
cur_deg = (cur_deg + 0.2) % 360; | |
X2 = width/2 + radius*cos(cur_deg); | |
Y2 = height/2 + radius*sin(cur_deg); | |
} | |
void drawLines(){ | |
for(int i = 2; i < width-2; i += 2) { | |
if(i % 20 == 0) { | |
stroke(255); | |
line(i, 40, i, height); | |
} else if (i % 10 == 0) { | |
stroke(153); | |
line(i, 20, i, 180); | |
line(i, height/2, i, height-40); | |
} else { | |
stroke(102); | |
line(i, 0, i, height); | |
// line(i, height/2, i, height-40); | |
// line(i, height/2, X, Y-40); | |
} | |
} | |
} | |
void drawCircles(){ | |
for(int i = 0; i <= width; i += 20) { | |
for(int j = 0; j <= width; j += 20) { | |
float size = dist(X2, Y2, i, j); | |
size = size/max_distance * 60; | |
ellipse(i, j, size, size); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*** | |
P R O C E S S I N G . J S - 1.4.1 | |
a port of the Processing visualization language | |
Processing.js is licensed under the MIT License, see LICENSE. | |
For a list of copyright holders, please refer to AUTHORS. | |
http://processingjs.org | |
***/ | |
(function(window, document, Math, undef) { | |
var nop = function() {}; | |
var debug = function() { | |
if ("console" in window) return function(msg) { | |
window.console.log("Processing.js: " + msg) | |
}; | |
return nop | |
}(); | |
var ajax = function(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); | |
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); | |
document.head = document.head || document.getElementsByTagName("head")[0]; | |
function setupTypedArray(name, fallback) { | |
if (name in window) return window[name]; | |
if (typeof window[fallback] === "function") return window[fallback]; | |
return function(obj) { | |
if (obj instanceof Array) return obj; | |
if (typeof obj === "number") { | |
var arr = []; | |
arr.length = obj; | |
return arr | |
} | |
} | |
} | |
if (document.documentMode >= 9 && !document.doctype) throw "The doctype directive is missing. The recommended doctype in Internet Explorer is the HTML5 doctype: <!DOCTYPE html>"; | |
var Float32Array = setupTypedArray("Float32Array", "WebGLFloatArray"), | |
Int32Array = setupTypedArray("Int32Array", "WebGLIntArray"), | |
Uint16Array = setupTypedArray("Uint16Array", "WebGLUnsignedShortArray"), | |
Uint8Array = setupTypedArray("Uint8Array", "WebGLUnsignedByteArray"); | |
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, | |
SR: 13, | |
SG: 14, | |
SB: 15, | |
SA: 16, | |
SW: 17, | |
TX: 18, | |
TY: 19, | |
TZ: 20, | |
VX: 21, | |
VY: 22, | |
VZ: 23, | |
VW: 24, | |
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, | |
P2D: 1, | |
JAVA2D: 1, | |
WEBGL: 2, | |
P3D: 2, | |
OPENGL: 2, | |
PDF: 0, | |
DXF: 0, | |
OTHER: 0, | |
WINDOWS: 1, | |
MAXOSX: 2, | |
LINUX: 3, | |
EPSILON: 1.0E-4, | |
MAX_FLOAT: 3.4028235E38, | |
MIN_FLOAT: -3.4028235E38, | |
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\u000c\u00a0", | |
RGB: 1, | |
ARGB: 2, | |
HSB: 3, | |
ALPHA: 4, | |
CMYK: 5, | |
TIFF: 0, | |
TARGA: 1, | |
JPEG: 2, | |
GIF: 3, | |
BLUR: 11, | |
GRAY: 12, | |
INVERT: 13, | |
OPAQUE: 14, | |
POSTERIZE: 15, | |
THRESHOLD: 16, | |
ERODE: 17, | |
DILATE: 18, | |
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, | |
ALPHA_MASK: 4278190080, | |
RED_MASK: 16711680, | |
GREEN_MASK: 65280, | |
BLUE_MASK: 255, | |
CUSTOM: 0, | |
ORTHOGRAPHIC: 2, | |
PERSPECTIVE: 3, | |
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, | |
GEOMETRY: 3, | |
VERTEX: 0, | |
BEZIER_VERTEX: 1, | |
CURVE_VERTEX: 2, | |
BREAK: 3, | |
CLOSESHAPE: 4, | |
OPEN: 1, | |
CLOSE: 2, | |
CORNER: 0, | |
CORNERS: 1, | |
RADIUS: 2, | |
CENTER_RADIUS: 2, | |
CENTER: 3, | |
DIAMETER: 3, | |
CENTER_DIAMETER: 3, | |
BASELINE: 0, | |
TOP: 101, | |
BOTTOM: 102, | |
NORMAL: 1, | |
NORMALIZED: 1, | |
IMAGE: 2, | |
MODEL: 4, | |
SHAPE: 5, | |
SQUARE: "butt", | |
ROUND: "round", | |
PROJECT: "square", | |
MITER: "miter", | |
BEVEL: "bevel", | |
AMBIENT: 0, | |
DIRECTIONAL: 1, | |
SPOT: 3, | |
BACKSPACE: 8, | |
TAB: 9, | |
ENTER: 10, | |
RETURN: 13, | |
ESC: 27, | |
DELETE: 127, | |
CODED: 65535, | |
SHIFT: 16, | |
CONTROL: 17, | |
ALT: 18, | |
CAPSLK: 20, | |
PGUP: 33, | |
PGDN: 34, | |
END: 35, | |
HOME: 36, | |
LEFT: 37, | |
UP: 38, | |
RIGHT: 39, | |
DOWN: 40, | |
F1: 112, | |
F2: 113, | |
F3: 114, | |
F4: 115, | |
F5: 116, | |
F6: 117, | |
F7: 118, | |
F8: 119, | |
F9: 120, | |
F10: 121, | |
F11: 122, | |
F12: 123, | |
NUMLK: 144, | |
META: 157, | |
INSERT: 155, | |
ARROW: "default", | |
CROSS: "crosshair", | |
HAND: "pointer", | |
MOVE: "move", | |
TEXT: "text", | |
WAIT: "wait", | |
NOCURSOR: "url('data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=='), auto", | |
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, | |
SINCOS_LENGTH: 720, | |
PRECISIONB: 15, | |
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 | |
}; | |
function virtHashCode(obj) { | |
if (typeof obj === "string") { | |
var hash = 0; | |
for (var i = 0; i < obj.length; ++i) hash = hash * 31 + obj.charCodeAt(i) & 4294967295; | |
return hash | |
} | |
if (typeof obj !== "object") return obj & 4294967295; | |
if (obj.hashCode instanceof Function) return obj.hashCode(); | |
if (obj.$id === undef) obj.$id = Math.floor(Math.random() * 65536) - 32768 << 16 | Math.floor(Math.random() * 65536); | |
return obj.$id | |
} | |
function virtEquals(obj, other) { | |
if (obj === null || other === null) return obj === null && other === null; | |
if (typeof obj === "string") return obj === other; | |
if (typeof obj !== "object") return obj === other; | |
if (obj.equals instanceof Function) return obj.equals(other); | |
return obj === other | |
} | |
var ObjectIterator = function(obj) { | |
if (obj.iterator instanceof | |
Function) return obj.iterator(); | |
if (obj instanceof Array) { | |
var index = -1; | |
this.hasNext = function() { | |
return ++index < obj.length | |
}; | |
this.next = function() { | |
return obj[index] | |
} | |
} else throw "Unable to iterate: " + obj; | |
}; | |
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(a) { | |
var array; | |
if (a instanceof ArrayList) array = a.toArray(); | |
else { | |
array = []; | |
if (typeof a === "number") array.length = a > 0 ? a : 0 | |
} | |
this.get = function(i) { | |
return array[i] | |
}; | |
this.contains = function(item) { | |
return this.indexOf(item) > -1 | |
}; | |
this.indexOf = function(item) { | |
for (var i = 0, len = array.length; i < len; ++i) if (virtEquals(item, array[i])) return i; | |
return -1 | |
}; | |
this.lastIndexOf = function(item) { | |
for (var i = array.length - 1; i >= 0; --i) if (virtEquals(item, array[i])) return i; | |
return -1 | |
}; | |
this.add = function() { | |
if (arguments.length === 1) array.push(arguments[0]); | |
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]); | |
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."; | |
}; | |
this.addAll = function(arg1, arg2) { | |
var it; | |
if (typeof arg1 === "number") { | |
if (arg1 < 0 || arg1 > array.length) throw "Index out of bounds for addAll: " + arg1 + " greater or equal than " + array.length; | |
it = new ObjectIterator(arg2); | |
while (it.hasNext()) array.splice(arg1++, 0, it.next()) | |
} else { | |
it = new ObjectIterator(arg1); | |
while (it.hasNext()) array.push(it.next()) | |
} | |
}; | |
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."; | |
}; | |
this.size = function() { | |
return array.length | |
}; | |
this.clear = function() { | |
array.length = 0 | |
}; | |
this.remove = function(item) { | |
if (typeof item === "number") return array.splice(item, 1)[0]; | |
item = this.indexOf(item); | |
if (item > -1) { | |
array.splice(item, 1); | |
return true | |
} | |
return false | |
}; | |
this.removeAll = function(c) { | |
var i, x, item, newList = new ArrayList; | |
newList.addAll(this); | |
this.clear(); | |
for (i = 0, x = 0; i < newList.size(); i++) { | |
item = newList.get(i); | |
if (!c.contains(item)) this.add(x++, item) | |
} | |
if (this.size() < newList.size()) return true; | |
return false | |
}; | |
this.isEmpty = function() { | |
return !array.length | |
}; | |
this.clone = function() { | |
return new ArrayList(this) | |
}; | |
this.toArray = function() { | |
return array.slice(0) | |
}; | |
this.iterator = function() { | |
return new Iterator(array) | |
} | |
} | |
return ArrayList | |
}(); | |
var HashMap = function() { | |
function HashMap() { | |
if (arguments.length === 1 && arguments[0] instanceof HashMap) return arguments[0].clone(); | |
var initialCapacity = arguments.length > 0 ? arguments[0] : 16; | |
var loadFactor = arguments.length > 1 ? arguments[1] : 0.75; | |
var buckets = []; | |
buckets.length = initialCapacity; | |
var count = 0; | |
var hashMap = this; | |
function getBucketIndex(key) { | |
var index = virtHashCode(key) % buckets.length; | |
return index < 0 ? buckets.length + index : index | |
} | |
function ensureLoad() { | |
if (count <= loadFactor * buckets.length) return; | |
var allEntries = []; | |
for (var i = 0; i < buckets.length; ++i) if (buckets[i] !== undef) allEntries = allEntries.concat(buckets[i]); | |
var newBucketsLength = buckets.length * 2; | |
buckets = []; | |
buckets.length = newBucketsLength; | |
for (var j = 0; j < allEntries.length; ++j) { | |
var index = getBucketIndex(allEntries[j].key); | |
var bucket = buckets[index]; | |
if (bucket === undef) buckets[index] = bucket = []; | |
bucket.push(allEntries[j]) | |
} | |
} | |
function Iterator(conversion, removeItem) { | |
var bucketIndex = 0; | |
var itemIndex = -1; | |
var endOfBuckets = false; | |
var currentItem; | |
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 | |
} | |
} | |
this.hasNext = function() { | |
return !endOfBuckets | |
}; | |
this.next = function() { | |
currentItem = conversion(buckets[bucketIndex][itemIndex]); | |
findNext(); | |
return currentItem | |
}; | |
this.remove = function() { | |
if (currentItem !== undef) { | |
removeItem(currentItem); | |
--itemIndex; | |
findNext() | |
} | |
}; | |
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 = getBucketIndex(key); | |
var bucket = buckets[index]; | |
if (bucket === undef) return false; | |
for (var i = 0; i < bucket.length; ++i) if (virtEquals(bucket[i].key, key)) return true; | |
return false | |
}; | |
this.containsValue = function(value) { | |
for (var i = 0; i < buckets.length; ++i) { | |
var bucket = buckets[i]; | |
if (bucket === undef) continue; | |
for (var j = 0; j < bucket.length; ++j) if (virtEquals(bucket[j].value, value)) return true | |
} | |
return false | |
}; | |
this.entrySet = function() { | |
return new Set(function(pair) { | |
return new Entry(pair) | |
}, | |
function(pair) { | |
return pair instanceof Entry && pair._isIn(hashMap) | |
}, | |
function(pair) { | |
return hashMap.remove(pair.getKey()) | |
}) | |
}; | |
this.get = function(key) { | |
var index = getBucketIndex(key); | |
var bucket = buckets[index]; | |
if (bucket === undef) return null; | |
for (var i = 0; i < bucket.length; ++i) if (virtEquals(bucket[i].key, key)) return bucket[i].value; | |
return null | |
}; | |
this.isEmpty = function() { | |
return count === 0 | |
}; | |
this.keySet = function() { | |
return new Set(function(pair) { | |
return pair.key | |
}, | |
function(key) { | |
return hashMap.containsKey(key) | |
}, | |
function(key) { | |
return hashMap.remove(key) | |
}) | |
}; | |
this.values = function() { | |
return new Set(function(pair) { | |
return pair.value | |
}, | |
function(value) { | |
return hashMap.containsValue(value) | |
}, | |
function(value) { | |
return hashMap.removeByValue(value) | |
}) | |
}; | |
this.put = function(key, value) { | |
var index = getBucketIndex(key); | |
var bucket = buckets[index]; | |
if (bucket === undef) { | |
++count; | |
buckets[index] = [{ | |
key: key, | |
value: value | |
}]; | |
ensureLoad(); | |
return null | |
} | |
for (var i = 0; i < bucket.length; ++i) if (virtEquals(bucket[i].key, key)) { | |
var previous = bucket[i].value; | |
bucket[i].value = value; | |
return previous | |
}++count; | |
bucket.push({ | |
key: key, | |
value: value | |
}); | |
ensureLoad(); | |
return null | |
}; | |
this.putAll = function(m) { | |
var it = m.entrySet().iterator(); | |
while (it.hasNext()) { | |
var entry = it.next(); | |
this.put(entry.getKey(), entry.getValue()) | |
} | |
}; | |
this.remove = function(key) { | |
var index = getBucketIndex(key); | |
var bucket = buckets[index]; | |
if (bucket === undef) return null; | |
for (var i = 0; i < bucket.length; ++i) if (virtEquals(bucket[i].key, key)) { | |
--count; | |
var previous = bucket[i].value; | |
bucket[i].removed = true; | |
if (bucket.length > 1) bucket.splice(i, 1); | |
else buckets[index] = undef; | |
return previous | |
} | |
return null | |
}; | |
this.removeByValue = function(value) { | |
var bucket, i, ilen, pair; | |
for (bucket in buckets) if (buckets.hasOwnProperty(bucket)) for (i = 0, ilen = buckets[bucket].length; i < ilen; i++) { | |
pair = buckets[bucket][i]; | |
if (pair.value === value) { | |
buckets[bucket].splice(i, 1); | |
return true | |
} | |
} | |
return false | |
}; | |
this.size = function() { | |
return count | |
} | |
} | |
return HashMap | |
}(); | |
var PVector = function() { | |
function PVector(x, y, z) { | |
this.x = x || 0; | |
this.y = y || 0; | |
this.z = z || 0 | |
} | |
PVector.dist = function(v1, v2) { | |
return v1.dist(v2) | |
}; | |
PVector.dot = function(v1, v2) { | |
return v1.dot(v2) | |
}; | |
PVector.cross = function(v1, v2) { | |
return v1.cross(v2) | |
}; | |
PVector.angleBetween = function(v1, v2) { | |
return Math.acos(v1.dot(v2) / (v1.mag() * v2.mag())) | |
}; | |
PVector.prototype = { | |
set: function(v, y, z) { | |
if (arguments.length === 1) this.set(v.x || v[0] || 0, v.y || v[1] || 0, v.z || v[2] || 0); | |
else { | |
this.x = v; | |
this.y = y; | |
this.z = z | |
} | |
}, | |
get: function() { | |
return new PVector(this.x, this.y, this.z) | |
}, | |
mag: function() { | |
var x = this.x, | |
y = this.y, | |
z = this.z; | |
return Math.sqrt(x * x + y * y + z * z) | |
}, | |
add: function(v, y, z) { | |
if (arguments.length === 1) { | |
this.x += v.x; | |
this.y += v.y; | |
this.z += v.z | |
} else { | |
this.x += v; | |
this.y += y; | |
this.z += z | |
} | |
}, | |
sub: function(v, y, z) { | |
if (arguments.length === 1) { | |
this.x -= v.x; | |
this.y -= v.y; | |
this.z -= v.z | |
} else { | |
this.x -= v; | |
this.y -= y; | |
this.z -= z | |
} | |
}, | |
mult: function(v) { | |
if (typeof v === "number") { | |
this.x *= v; | |
this.y *= v; | |
this.z *= v | |
} else { | |
this.x *= v.x; | |
this.y *= v.y; | |
this.z *= v.z | |
} | |
}, | |
div: function(v) { | |
if (typeof v === "number") { | |
this.x /= v; | |
this.y /= v; | |
this.z /= v | |
} else { | |
this.x /= v.x; | |
this.y /= v.y; | |
this.z /= v.z | |
} | |
}, | |
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 === 1) return this.x * v.x + this.y * v.y + this.z * v.z; | |
return this.x * v + this.y * y + this.z * z | |
}, | |
cross: function(v) { | |
var x = this.x, | |
y = this.y, | |
z = this.z; | |
return new PVector(y * v.z - v.y * z, z * v.x - v.z * x, x * v.y - v.x * y) | |
}, | |
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] | |
} | |
}; | |
function createPVectorMethod(method) { | |
return function(v1, v2) { | |
var v = v1.get(); | |
v[method](v2); | |
return v | |
} | |
} | |
for (var method in PVector.prototype) if (PVector.prototype.hasOwnProperty(method) && !PVector.hasOwnProperty(method)) PVector[method] = createPVectorMethod(method); | |
return PVector | |
}(); | |
function DefaultScope() {} | |
DefaultScope.prototype = PConstants; | |
var defaultScope = new DefaultScope; | |
defaultScope.ArrayList = ArrayList; | |
defaultScope.HashMap = HashMap; | |
defaultScope.PVector = PVector; | |
defaultScope.ObjectIterator = ObjectIterator; | |
defaultScope.PConstants = PConstants; | |
defaultScope.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) | |
} | |
}; | |
function overloadBaseClassFunction(object, name, basefn) { | |
if (!object.hasOwnProperty(name) || typeof object[name] !== "function") { | |
object[name] = basefn; | |
return | |
} | |
var fn = object[name]; | |
if ("$overloads" in fn) { | |
fn.$defaultOverload = basefn; | |
return | |
} | |
if (! ("$overloads" in basefn) && fn.length === basefn.length) return; | |
var overloads, defaultOverload; | |
if ("$overloads" in basefn) { | |
overloads = basefn.$overloads.slice(0); | |
overloads[fn.length] = fn; | |
defaultOverload = basefn.$defaultOverload | |
} else { | |
overloads = []; | |
overloads[basefn.length] = basefn; | |
overloads[fn.length] = fn; | |
defaultOverload = fn | |
} | |
var hubfn = function() { | |
var fn = hubfn.$overloads[arguments.length] || ("$methodArgsIndex" in hubfn && arguments.length > hubfn.$methodArgsIndex ? hubfn.$overloads[hubfn.$methodArgsIndex] : null) || hubfn.$defaultOverload; | |
return fn.apply(this, arguments) | |
}; | |
hubfn.$overloads = overloads; | |
if ("$methodArgsIndex" in basefn) hubfn.$methodArgsIndex = basefn.$methodArgsIndex; | |
hubfn.$defaultOverload = defaultOverload; | |
hubfn.name = name; | |
object[name] = hubfn | |
} | |
function extendClass(subClass, baseClass) { | |
function extendGetterSetter(propertyName) { | |
defaultScope.defineProperty(subClass, propertyName, { | |
get: function() { | |
return baseClass[propertyName] | |
}, | |
set: function(v) { | |
baseClass[propertyName] = v | |
}, | |
enumerable: true | |
}) | |
} | |
var properties = []; | |
for (var propertyName in baseClass) if (typeof baseClass[propertyName] === "function") overloadBaseClassFunction(subClass, propertyName, baseClass[propertyName]); | |
else if (propertyName.charAt(0) !== "$" && !(propertyName in subClass)) properties.push(propertyName); | |
while (properties.length > 0) extendGetterSetter(properties.shift()); | |
subClass.$super = baseClass | |
} | |
defaultScope.extendClassChain = function(base) { | |
var path = [base]; | |
for (var self = base.$upcast; self; self = self.$upcast) { | |
extendClass(self, base); | |
path.push(self); | |
base = self | |
} | |
while (path.length > 0) path.pop().$self = base | |
}; | |
defaultScope.extendStaticMembers = function(derived, base) { | |
extendClass(derived, base) | |
}; | |
defaultScope.extendInterfaceMembers = function(derived, base) { | |
extendClass(derived, base) | |
}; | |
defaultScope.addMethod = function(object, name, fn, hasMethodArgs) { | |
var existingfn = object[name]; | |
if (existingfn || hasMethodArgs) { | |
var args = fn.length; | |
if ("$overloads" in existingfn) existingfn.$overloads[args] = fn; | |
else { | |
var hubfn = function() { | |
var fn = hubfn.$overloads[arguments.length] || ("$methodArgsIndex" in hubfn && arguments.length > hubfn.$methodArgsIndex ? hubfn.$overloads[hubfn.$methodArgsIndex] : null) || hubfn.$defaultOverload; | |
return fn.apply(this, arguments) | |
}; | |
var overloads = []; | |
if (existingfn) overloads[existingfn.length] = existingfn; | |
overloads[args] = fn; | |
hubfn.$overloads = overloads; | |
hubfn.$defaultOverload = existingfn || fn; | |
if (hasMethodArgs) hubfn.$methodArgsIndex = args; | |
hubfn.name = name; | |
object[name] = hubfn | |
} | |
} else object[name] = fn | |
}; | |
function isNumericalJavaType(type) { | |
if (typeof type !== "string") return false; | |
return ["byte", "int", "char", "color", "float", "long", "double"].indexOf(type) !== -1 | |
} | |
defaultScope.createJavaArray = function(type, bounds) { | |
var result = null, | |
defaultValue = null; | |
if (typeof type === "string") if (type === "boolean") defaultValue = false; | |
else if (isNumericalJavaType(type)) defaultValue = 0; | |
if (typeof bounds[0] === "number") { | |
var itemsCount = 0 | bounds[0]; | |
if (bounds.length <= 1) { | |
result = []; | |
result.length = itemsCount; | |
for (var i = 0; i < itemsCount; ++i) result[i] = defaultValue | |
} else { | |
result = []; | |
var newBounds = bounds.slice(1); | |
for (var j = 0; j < itemsCount; ++j) result.push(defaultScope.createJavaArray(type, newBounds)) | |
} | |
} | |
return result | |
}; | |
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" | |
}; | |
(function(Processing) { | |
var unsupportedP5 = ("open() createOutput() createInput() BufferedReader selectFolder() " + "dataPath() createWriter() selectOutput() beginRecord() " + "saveStream() endRecord() selectInput() saveBytes() createReader() " + "beginRaw() endRaw() PrintWriter delay()").split(" "), | |
count = unsupportedP5.length, | |
prettyName, p5Name; | |
function createUnsupportedFunc(n) { | |
return function() { | |
throw "Processing.js does not support " + n + "."; | |
} | |
} | |
while (count--) { | |
prettyName = unsupportedP5[count]; | |
p5Name = prettyName.replace("()", ""); | |
Processing[p5Name] = createUnsupportedFunc(prettyName) | |
} | |
})(defaultScope); | |
defaultScope.defineProperty(defaultScope, "screenWidth", { | |
get: function() { | |
return window.innerWidth | |
} | |
}); | |
defaultScope.defineProperty(defaultScope, "screenHeight", { | |
get: function() { | |
return window.innerHeight | |
} | |
}); | |
defaultScope.defineProperty(defaultScope, "online", { | |
get: function() { | |
return true | |
} | |
}); | |
var processingInstances = []; | |
var processingInstanceIds = {}; | |
var removeInstance = function(id) { | |
processingInstances.splice(processingInstanceIds[id], 1); | |
delete processingInstanceIds[id] | |
}; | |
var addInstance = function(processing) { | |
if (processing.externals.canvas.id === undef || !processing.externals.canvas.id.length) processing.externals.canvas.id = "__processing" + processingInstances.length; | |
processingInstanceIds[processing.externals.canvas.id] = processingInstances.length; | |
processingInstances.push(processing) | |
}; | |
function computeFontMetrics(pfont) { | |
var emQuad = 250, | |
correctionFactor = pfont.size / emQuad, | |
canvas = document.createElement("canvas"); | |
canvas.width = 2 * emQuad; | |
canvas.height = 2 * emQuad; | |
canvas.style.opacity = 0; | |
var cfmFont = pfont.getCSSDefinition(emQuad + "px", "normal"), | |
ctx = canvas.getContext("2d"); | |
ctx.font = cfmFont; | |
var protrusions = "dbflkhyjqpg"; | |
canvas.width = ctx.measureText(protrusions).width; | |
ctx.font = cfmFont; | |
var leadDiv = document.createElement("div"); | |
leadDiv.style.position = "absolute"; | |
leadDiv.style.opacity = 0; | |
leadDiv.style.fontFamily = '"' + pfont.name + '"'; | |
leadDiv.style.fontSize = emQuad + "px"; | |
leadDiv.innerHTML = protrusions + "<br/>" + protrusions; | |
document.body.appendChild(leadDiv); | |
var w = canvas.width, | |
h = canvas.height, | |
baseline = h / 2; | |
ctx.fillStyle = "white"; | |
ctx.fillRect(0, 0, w, h); | |
ctx.fillStyle = "black"; | |
ctx.fillText(protrusions, 0, baseline); | |
var pixelData = ctx.getImageData(0, 0, w, h).data; | |
var i = 0, | |
w4 = w * 4, | |
len = pixelData.length; | |
while (++i < len && pixelData[i] === 255) nop(); | |
var ascent = Math.round(i / w4); | |
i = len - 1; | |
while (--i > 0 && pixelData[i] === 255) nop(); | |
var descent = Math.round(i / w4); | |
pfont.ascent = correctionFactor * (baseline - ascent); | |
pfont.descent = correctionFactor * (descent - baseline); | |
if (document.defaultView.getComputedStyle) { | |
var leadDivHeight = document.defaultView.getComputedStyle(leadDiv, null).getPropertyValue("height"); | |
leadDivHeight = correctionFactor * leadDivHeight.replace("px", ""); | |
if (leadDivHeight >= pfont.size * 2) pfont.leading = Math.round(leadDivHeight / 2) | |
} | |
document.body.removeChild(leadDiv); | |
if (pfont.caching) return ctx | |
} | |
function PFont(name, size) { | |
if (name === undef) name = ""; | |
this.name = name; | |
if (size === undef) size = 0; | |
this.size = size; | |
this.glyph = false; | |
this.ascent = 0; | |
this.descent = 0; | |
this.leading = 1.2 * size; | |
var illegalIndicator = name.indexOf(" Italic Bold"); | |
if (illegalIndicator !== -1) name = name.substring(0, illegalIndicator); | |
this.style = "normal"; | |
var italicsIndicator = name.indexOf(" Italic"); | |
if (italicsIndicator !== -1) { | |
name = name.substring(0, italicsIndicator); | |
this.style = "italic" | |
} | |
this.weight = "normal"; | |
var boldIndicator = name.indexOf(" Bold"); | |
if (boldIndicator !== -1) { | |
name = name.substring(0, boldIndicator); | |
this.weight = "bold" | |
} | |
this.family = "sans-serif"; | |
if (name !== undef) switch (name) { | |
case "sans-serif": | |
case "serif": | |
case "monospace": | |
case "fantasy": | |
case "cursive": | |
this.family = name; | |
break; | |
default: | |
this.family = '"' + name + '", sans-serif'; | |
break | |
} | |
this.context2d = computeFontMetrics(this); | |
this.css = this.getCSSDefinition(); | |
if (this.context2d) this.context2d.font = this.css | |
} | |
PFont.prototype.caching = true; | |
PFont.prototype.getCSSDefinition = function(fontSize, lineHeight) { | |
if (fontSize === undef) fontSize = this.size + "px"; | |
if (lineHeight === undef) lineHeight = this.leading + "px"; | |
var components = [this.style, "normal", this.weight, fontSize + "/" + lineHeight, this.family]; | |
return components.join(" ") | |
}; | |
PFont.prototype.measureTextWidth = function(string) { | |
return this.context2d.measureText(string).width | |
}; | |
PFont.prototype.measureTextWidthFallback = function(string) { | |
var canvas = document.createElement("canvas"), | |
ctx = canvas.getContext("2d"); | |
ctx.font = this.css; | |
return ctx.measureText(string).width | |
}; | |
PFont.PFontCache = { | |
length: 0 | |
}; | |
PFont.get = function(fontName, fontSize) { | |
fontSize = (fontSize * 10 + 0.5 | 0) / 10; | |
var cache = PFont.PFontCache, | |
idx = fontName + "/" + fontSize; | |
if (!cache[idx]) { | |
cache[idx] = new PFont(fontName, fontSize); | |
cache.length++; | |
if (cache.length === 50) { | |
PFont.prototype.measureTextWidth = PFont.prototype.measureTextWidthFallback; | |
PFont.prototype.caching = false; | |
var entry; | |
for (entry in cache) if (entry !== "length") cache[entry].context2d = null; | |
return new PFont(fontName, fontSize) | |
} | |
if (cache.length === 400) { | |
PFont.PFontCache = {}; | |
PFont.get = PFont.getFallback; | |
return new PFont(fontName, fontSize) | |
} | |
} | |
return cache[idx] | |
}; | |
PFont.getFallback = function(fontName, fontSize) { | |
return new PFont(fontName, fontSize) | |
}; | |
PFont.list = function() { | |
return ["sans-serif", "serif", "monospace", "fantasy", "cursive"] | |
}; | |
PFont.preloading = { | |
template: {}, | |
initialized: false, | |
initialize: function() { | |
var generateTinyFont = function() { | |
var encoded = "#E3KAI2wAgT1MvMg7Eo3VmNtYX7ABi3CxnbHlm" + "7Abw3kaGVhZ7ACs3OGhoZWE7A53CRobXR47AY3" + "AGbG9jYQ7G03Bm1heH7ABC3CBuYW1l7Ae3AgcG" + "9zd7AI3AE#B3AQ2kgTY18PPPUACwAg3ALSRoo3" + "#yld0xg32QAB77#E777773B#E3C#I#Q77773E#" + "Q7777777772CMAIw7AB77732B#M#Q3wAB#g3B#" + "E#E2BB//82BB////w#B7#gAEg3E77x2B32B#E#" + "Q#MTcBAQ32gAe#M#QQJ#E32M#QQJ#I#g32Q77#"; | |
var expand = function(input) { | |
return "AAAAAAAA".substr(~~input ? 7 - input : 6) | |
}; | |
return encoded.replace(/[#237]/g, expand) | |
}; | |
var fontface = document.createElement("style"); | |
fontface.setAttribute("type", "text/css"); | |
fontface.innerHTML = "@font-face {\n" + ' font-family: "PjsEmptyFont";' + "\n" + " src: url('data:application/x-font-ttf;base64," + generateTinyFont() + "')\n" + " format('truetype');\n" + "}"; | |
document.head.appendChild(fontface); | |
var element = document.createElement("span"); | |
element.style.cssText = 'position: absolute; top: 0; left: 0; opacity: 0; font-family: "PjsEmptyFont", fantasy;'; | |
element.innerHTML = "AAAAAAAA"; | |
document.body.appendChild(element); | |
this.template = element; | |
this.initialized = true | |
}, | |
getElementWidth: function(element) { | |
return document.defaultView.getComputedStyle(element, "").getPropertyValue("width") | |
}, | |
timeAttempted: 0, | |
pending: function(intervallength) { | |
if (!this.initialized) this.initialize(); | |
var element, computedWidthFont, computedWidthRef = this.getElementWidth(this.template); | |
for (var i = 0; i < this.fontList.length; i++) { | |
element = this.fontList[i]; | |
computedWidthFont = this.getElementWidth(element); | |
if (this.timeAttempted < 4E3 && computedWidthFont === computedWidthRef) { | |
this.timeAttempted += intervallength; | |
return true | |
} else { | |
document.body.removeChild(element); | |
this.fontList.splice(i--, 1); | |
this.timeAttempted = 0 | |
} | |
} | |
if (this.fontList.length === 0) return false; | |
return true | |
}, | |
fontList: [], | |
addedList: {}, | |
add: function(fontSrc) { | |
if (!this.initialized) this.initialize(); | |
var fontName = typeof fontSrc === "object" ? fontSrc.fontFace : fontSrc, | |
fontUrl = typeof fontSrc === "object" ? fontSrc.url : fontSrc; | |
if (this.addedList[fontName]) return; | |
var style = document.createElement("style"); | |
style.setAttribute("type", "text/css"); | |
style.innerHTML = "@font-face{\n font-family: '" + fontName + "';\n src: url('" + fontUrl + "');\n}\n"; | |
document.head.appendChild(style); | |
this.addedList[fontName] = true; | |
var element = document.createElement("span"); | |
element.style.cssText = "position: absolute; top: 0; left: 0; opacity: 0;"; | |
element.style.fontFamily = '"' + fontName + '", "PjsEmptyFont", fantasy'; | |
element.innerHTML = "AAAAAAAA"; | |
document.body.appendChild(element); | |
this.fontList.push(element) | |
} | |
}; | |
defaultScope.PFont = PFont; | |
var Processing = this.Processing = function(aCanvas, aCode) { | |
if (! (this instanceof | |
Processing)) throw "called Processing constructor as if it were a function: missing 'new'."; | |
var curElement, pgraphicsMode = aCanvas === undef && aCode === undef; | |
if (pgraphicsMode) curElement = document.createElement("canvas"); | |
else curElement = typeof aCanvas === "string" ? document.getElementById(aCanvas) : aCanvas; | |
if (! (curElement instanceof HTMLCanvasElement)) throw "called Processing constructor without passing canvas element reference or id."; | |
function unimplemented(s) { | |
Processing.debug("Unimplemented - " + s) | |
} | |
var p = this; | |
p.externals = { | |
canvas: curElement, | |
context: undef, | |
sketch: undef | |
}; | |
p.name = "Processing.js Instance"; | |
p.use3DContext = false; | |
p.focused = false; | |
p.breakShape = false; | |
p.glyphTable = {}; | |
p.pmouseX = 0; | |
p.pmouseY = 0; | |
p.mouseX = 0; | |
p.mouseY = 0; | |
p.mouseButton = 0; | |
p.mouseScroll = 0; | |
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 = nop; | |
p.keyReleased = nop; | |
p.keyTyped = nop; | |
p.draw = undef; | |
p.setup = undef; | |
p.__mousePressed = false; | |
p.__keyPressed = false; | |
p.__frameRate = 60; | |
p.frameCount = 0; | |
p.width = 100; | |
p.height = 100; | |
var curContext, curSketch, drawing, online = true, | |
doFill = true, | |
fillStyle = [1, 1, 1, 1], | |
currentFillColor = 4294967295, | |
isFillDirty = true, | |
doStroke = true, | |
strokeStyle = [0, 0, 0, 1], | |
currentStrokeColor = 4278190080, | |
isStrokeDirty = true, | |
lineWidth = 1, | |
loopStarted = false, | |
renderSmooth = false, | |
doLoop = true, | |
looping = 0, | |
curRectMode = 0, | |
curEllipseMode = 3, | |
normalX = 0, | |
normalY = 0, | |
normalZ = 0, | |
normalMode = 0, | |
curFrameRate = 60, | |
curMsPerFrame = 1E3 / curFrameRate, | |
curCursor = 'default', | |
oldCursor = curElement.style.cursor, | |
curShape = 20, | |
curShapeCount = 0, | |
curvePoints = [], | |
curTightness = 0, | |
curveDet = 20, | |
curveInited = false, | |
backgroundObj = -3355444, | |
bezDetail = 20, | |
colorModeA = 255, | |
colorModeX = 255, | |
colorModeY = 255, | |
colorModeZ = 255, | |
pathOpen = false, | |
mouseDragging = false, | |
pmouseXLastFrame = 0, | |
pmouseYLastFrame = 0, | |
curColorMode = 1, | |
curTint = null, | |
curTint3d = null, | |
getLoaded = false, | |
start = Date.now(), | |
timeSinceLastFPS = start, | |
framesSinceLastFPS = 0, | |
textcanvas, curveBasisMatrix, curveToBezierMatrix, curveDrawMatrix, bezierDrawMatrix, bezierBasisInverse, bezierBasisMatrix, curContextCache = { | |
attributes: {}, | |
locations: {} | |
}, | |
programObject3D, programObject2D, programObjectUnlitShape, boxBuffer, boxNormBuffer, boxOutlineBuffer, rectBuffer, rectNormBuffer, sphereBuffer, lineBuffer, fillBuffer, fillColorBuffer, strokeColorBuffer, pointBuffer, shapeTexVBO, canTex, textTex, curTexture = { | |
width: 0, | |
height: 0 | |
}, | |
curTextureMode = 2, | |
usingTexture = false, | |
textBuffer, textureBuffer, indexBuffer, horizontalTextAlignment = 37, | |
verticalTextAlignment = 0, | |
textMode = 4, | |
curFontName = "Arial", | |
curTextSize = 12, | |
curTextAscent = 9, | |
curTextDescent = 2, | |
curTextLeading = 14, | |
curTextFont = PFont.get(curFontName, curTextSize), | |
originalContext, proxyContext = null, | |
isContextReplaced = false, | |
setPixelsCached, maxPixelsCached = 1E3, | |
pressedKeysMap = [], | |
lastPressedKeyCode = null, | |
codedKeys = [16, | |
17, 18, 20, 33, 34, 35, 36, 37, 38, 39, 40, 144, 155, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 157]; | |
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 | |
} | |
var lightCount = 0; | |
var sphereDetailV = 0, | |
sphereDetailU = 0, | |
sphereX = [], | |
sphereY = [], | |
sphereZ = [], | |
sinLUT = new Float32Array(720), | |
cosLUT = new Float32Array(720), | |
sphereVerts, sphereNorms; | |
var cam, cameraInv, modelView, modelViewInv, userMatrixStack, userReverseMatrixStack, inverseCopy, projection, manipulatingCamera = false, | |
frustumMode = false, | |
cameraFOV = 60 * (Math.PI / 180), | |
cameraX = p.width / 2, | |
cameraY = p.height / 2, | |
cameraZ = cameraY / Math.tan(cameraFOV / 2), | |
cameraNear = cameraZ / 10, | |
cameraFar = cameraZ * 10, | |
cameraAspect = p.width / p.height; | |
var vertArray = [], | |
curveVertArray = [], | |
curveVertCount = 0, | |
isCurve = false, | |
isBezier = false, | |
firstVert = true; | |
var curShapeMode = 0; | |
var styleArray = []; | |
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]); | |
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]); | |
var vertexShaderSrcUnlitShape = "varying vec4 vFrontColor;" + "attribute vec3 aVertex;" + "attribute vec4 aColor;" + "uniform mat4 uView;" + "uniform mat4 uProjection;" + "uniform float uPointSize;" + "void main(void) {" + " vFrontColor = aColor;" + " gl_PointSize = uPointSize;" + " gl_Position = uProjection * uView * vec4(aVertex, 1.0);" + "}"; | |
var fragmentShaderSrcUnlitShape = "#ifdef GL_ES\n" + "precision highp float;\n" + "#endif\n" + "varying vec4 vFrontColor;" + "uniform bool uSmooth;" + "void main(void){" + " if(uSmooth == true){" + " float dist = distance(gl_PointCoord, vec2(0.5));" + " if(dist > 0.5){" + " discard;" + " }" + " }" + " gl_FragColor = vFrontColor;" + "}"; | |
var vertexShaderSrc2D = "varying vec4 vFrontColor;" + "attribute vec3 aVertex;" + "attribute vec2 aTextureCoord;" + "uniform vec4 uColor;" + "uniform mat4 uModel;" + "uniform mat4 uView;" + "uniform mat4 uProjection;" + "uniform float uPointSize;" + "varying vec2 vTextureCoord;" + "void main(void) {" + " gl_PointSize = uPointSize;" + " vFrontColor = uColor;" + " gl_Position = uProjection * uView * uModel * vec4(aVertex, 1.0);" + " vTextureCoord = aTextureCoord;" + "}"; | |
var fragmentShaderSrc2D = "#ifdef GL_ES\n" + "precision highp float;\n" + "#endif\n" + "varying vec4 vFrontColor;" + "varying vec2 vTextureCoord;" + "uniform sampler2D uSampler;" + "uniform int uIsDrawingText;" + "uniform bool uSmooth;" + "void main(void){" + " if(uSmooth == true){" + " float dist = distance(gl_PointCoord, vec2(0.5));" + " if(dist > 0.5){" + " discard;" + " }" + " }" + " if(uIsDrawingText == 1){" + " float alpha = texture2D(uSampler, vTextureCoord).a;" + " gl_FragColor = vec4(vFrontColor.rgb * alpha, alpha);" + " }" + " else{" + " gl_FragColor = vFrontColor;" + " }" + "}"; | |
var webglMaxTempsWorkaround = /Windows/.test(navigator.userAgent); | |
var vertexShaderSrc3D = "varying vec4 vFrontColor;" + "attribute vec3 aVertex;" + "attribute vec3 aNormal;" + "attribute vec4 aColor;" + "attribute vec2 aTexture;" + "varying vec2 vTexture;" + "uniform vec4 uColor;" + "uniform bool uUsingMat;" + "uniform vec3 uSpecular;" + "uniform vec3 uMaterialEmissive;" + "uniform vec3 uMaterialAmbient;" + "uniform vec3 uMaterialSpecular;" + "uniform float uShininess;" + "uniform mat4 uModel;" + "uniform mat4 uView;" + "uniform mat4 uProjection;" + "uniform mat4 uNormalTransform;" + "uniform int uLightCount;" + "uniform vec3 uFalloff;" + "struct Light {" + " int type;" + " vec3 color;" + " vec3 position;" + " vec3 direction;" + " float angle;" + " vec3 halfVector;" + " float concentration;" + "};" + "uniform Light uLights0;" + "uniform Light uLights1;" + "uniform Light uLights2;" + "uniform Light uLights3;" + "uniform Light uLights4;" + "uniform Light uLights5;" + "uniform Light uLights6;" + "uniform Light uLights7;" + "Light getLight(int index){" + " if(index == 0) return uLights0;" + " if(index == 1) return uLights1;" + " if(index == 2) return uLights2;" + " if(index == 3) return uLights3;" + " if(index == 4) return uLights4;" + " if(index == 5) return uLights5;" + " if(index == 6) return uLights6;" + " return uLights7;" + "}" + "void AmbientLight( inout vec3 totalAmbient, in vec3 ecPos, in Light light ) {" + " float d = length( light.position - ecPos );" + " float attenuation = 1.0 / ( uFalloff[0] + ( uFalloff[1] * d ) + ( uFalloff[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-normalize(ecPos) )));" + " if( nDotVP != 0.0 ){" + " powerFactor = pow( nDotVH, uShininess );" + " }" + " col += light.color * nDotVP;" + " spec += uSpecular * powerFactor;" + "}" + "void PointLight( inout vec3 col, inout vec3 spec, in vec3 vertNormal, in vec3 ecPos, in Light light ) {" + " float powerFactor;" + " vec3 VP = light.position - ecPos;" + " float d = length( VP ); " + " VP = normalize( VP );" + " float attenuation = 1.0 / ( uFalloff[0] + ( uFalloff[1] * d ) + ( uFalloff[2] * d * d ));" + " float nDotVP = max( 0.0, dot( vertNormal, VP ));" + " vec3 halfVector = normalize( VP - normalize(ecPos) );" + " float nDotHV = max( 0.0, dot( vertNormal, halfVector ));" + " if( nDotVP == 0.0 ) {" + " powerFactor = 0.0;" + " }" + " else {" + " powerFactor = pow( nDotHV, uShininess );" + " }" + " spec += uSpecular * powerFactor * attenuation;" + " col += light.color * nDotVP * attenuation;" + "}" + "void SpotLight( inout vec3 col, inout vec3 spec, in vec3 vertNormal, in vec3 ecPos, in Light light ) {" + " float spotAttenuation;" + " float powerFactor = 0.0;" + " vec3 VP = light.position - ecPos;" + " vec3 ldir = normalize( -light.direction );" + " float d = length( VP );" + " VP = normalize( VP );" + " float attenuation = 1.0 / ( uFalloff[0] + ( uFalloff[1] * d ) + ( uFalloff[2] * d * d ) );" + " float spotDot = dot( VP, ldir );" + (webglMaxTempsWorkaround ? " spotAttenuation = 1.0; " : " if( spotDot > cos( light.angle ) ) {" + " spotAttenuation = pow( spotDot, light.concentration );" + " }" + " else{" + " spotAttenuation = 0.0;" + " }" + " attenuation *= spotAttenuation;" + "") + " float nDotVP = max( 0.0, dot( vertNormal, VP ) );" + " vec3 halfVector = normalize( VP - normalize(ecPos) );" + " float nDotHV = max( 0.0, dot( vertNormal, halfVector ) );" + " if( nDotVP != 0.0 ) {" + " powerFactor = pow( nDotHV, uShininess );" + " }" + " spec += uSpecular * powerFactor * attenuation;" + " col += light.color * nDotVP * attenuation;" + "}" + "void main(void) {" + " vec3 finalAmbient = vec3( 0.0 );" + " vec3 finalDiffuse = vec3( 0.0 );" + " vec3 finalSpecular = vec3( 0.0 );" + " vec4 col = uColor;" + " if ( uColor[0] == -1.0 ){" + " col = aColor;" + " }" + " vec3 norm = normalize(vec3( uNormalTransform * vec4( aNormal, 0.0 ) ));" + " vec4 ecPos4 = uView * uModel * vec4(aVertex, 1.0);" + " vec3 ecPos = (vec3(ecPos4))/ecPos4.w;" + " if( uLightCount == 0 ) {" + " vFrontColor = col + vec4(uMaterialSpecular, 1.0);" + " }" + " else {" + " for( int i = 0; i < 8; i++ ) {" + " Light l = getLight(i);" + " if( i >= uLightCount ){" + " 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, l );" + " }" + " else {" + " SpotLight( finalDiffuse, finalSpecular, norm, ecPos, l );" + " }" + " }" + " if( uUsingMat == false ) {" + " vFrontColor = vec4(" + " vec3( col ) * finalAmbient +" + " vec3( col ) * finalDiffuse +" + " vec3( col ) * finalSpecular," + " col[3] );" + " }" + " else{" + " vFrontColor = vec4( " + " uMaterialEmissive + " + " (vec3(col) * uMaterialAmbient * finalAmbient ) + " + " (vec3(col) * finalDiffuse) + " + " (uMaterialSpecular * finalSpecular), " + " col[3] );" + " }" + " }" + " vTexture.xy = aTexture.xy;" + " gl_Position = uProjection * uView * uModel * vec4( aVertex, 1.0 );" + "}"; | |
var fragmentShaderSrc3D = "#ifdef GL_ES\n" + "precision highp float;\n" + "#endif\n" + "varying vec4 vFrontColor;" + "uniform sampler2D uSampler;" + "uniform bool uUsingTexture;" + "varying vec2 vTexture;" + "void main(void){" + " if( uUsingTexture ){" + " gl_FragColor = vec4(texture2D(uSampler, vTexture.xy)) * vFrontColor;" + " }" + " else{" + " gl_FragColor = vFrontColor;" + " }" + "}"; | |
function uniformf(cacheId, programObj, varName, varValue) { | |
var varLocation = curContextCache.locations[cacheId]; | |
if (varLocation === undef) { | |
varLocation = curContext.getUniformLocation(programObj, varName); | |
curContextCache.locations[cacheId] = varLocation | |
} | |
if (varLocation !== null) 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) | |
} | |
function uniformi(cacheId, programObj, varName, varValue) { | |
var varLocation = curContextCache.locations[cacheId]; | |
if (varLocation === undef) { | |
varLocation = curContext.getUniformLocation(programObj, varName); | |
curContextCache.locations[cacheId] = varLocation | |
} | |
if (varLocation !== null) 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) | |
} | |
function uniformMatrix(cacheId, programObj, varName, transpose, matrix) { | |
var varLocation = curContextCache.locations[cacheId]; | |
if (varLocation === undef) { | |
varLocation = curContext.getUniformLocation(programObj, varName); | |
curContextCache.locations[cacheId] = varLocation | |
} | |
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) | |
} | |
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) | |
} | |
} | |
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) | |
} | |
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 | |
}; | |
var imageModeCorner = function(x, y, w, h, whAreSizes) { | |
return { | |
x: x, | |
y: y, | |
w: w, | |
h: h | |
} | |
}; | |
var imageModeConvert = imageModeCorner; | |
var imageModeCorners = function(x, y, w, h, whAreSizes) { | |
return { | |
x: x, | |
y: y, | |
w: whAreSizes ? w : w - x, | |
h: whAreSizes ? h : h - y | |
} | |
}; | |
var imageModeCenter = function(x, y, w, h, whAreSizes) { | |
return { | |
x: x - w / 2, | |
y: y - h / 2, | |
w: w, | |
h: h | |
} | |
}; | |
var DrawingShared = function() {}; | |
var Drawing2D = function() {}; | |
var Drawing3D = function() {}; | |
var DrawingPre = function() {}; | |
Drawing2D.prototype = new DrawingShared; | |
Drawing2D.prototype.constructor = Drawing2D; | |
Drawing3D.prototype = new DrawingShared; | |
Drawing3D.prototype.constructor = Drawing3D; | |
DrawingPre.prototype = new DrawingShared; | |
DrawingPre.prototype.constructor = DrawingPre; | |
DrawingShared.prototype.a3DOnlyFunction = nop; | |
var charMap = {}; | |
var Char = p.Character = function(chr) { | |
if (typeof chr === "string" && chr.length === 1) this.code = chr.charCodeAt(0); | |
else if (typeof chr === "number") this.code = chr; | |
else if (chr instanceof Char) this.code = chr; | |
else this.code = NaN; | |
return charMap[this.code] === undef ? charMap[this.code] = this : charMap[this.code] | |
}; | |
Char.prototype.toString = function() { | |
return String.fromCharCode(this.code) | |
}; | |
Char.prototype.valueOf = function() { | |
return this.code | |
}; | |
var PShape = p.PShape = function(family) { | |
this.family = family || 0; | |
this.visible = true; | |
this.style = true; | |
this.children = []; | |
this.nameTable = []; | |
this.params = []; | |
this.name = ""; | |
this.image = null; | |
this.matrix = null; | |
this.kind = null; | |
this.close = null; | |
this.width = null; | |
this.height = null; | |
this.parent = null | |
}; | |
PShape.prototype = { | |
isVisible: function() { | |
return this.visible | |
}, | |
setVisible: function(visible) { | |
this.visible = visible | |
}, | |
disableStyle: function() { | |
this.style = false; | |
for (var i = 0, j = this.children.length; i < j; i++) this.children[i].disableStyle() | |
}, | |
enableStyle: function() { | |
this.style = true; | |
for (var i = 0, j = this.children.length; i < j; i++) this.children[i].enableStyle() | |
}, | |
getFamily: function() { | |
return this.family | |
}, | |
getWidth: function() { | |
return this.width | |
}, | |
getHeight: function() { | |
return this.height | |
}, | |
setName: function(name) { | |
this.name = name | |
}, | |
getName: function() { | |
return this.name | |
}, | |
draw: function(renderContext) { | |
renderContext = renderContext || p; | |
if (this.visible) { | |
this.pre(renderContext); | |
this.drawImpl(renderContext); | |
this.post(renderContext) | |
} | |
}, | |
drawImpl: function(renderContext) { | |
if (this.family === 0) this.drawGroup(renderContext); | |
else if (this.family === 1) this.drawPrimitive(renderContext); | |
else if (this.family === 3) this.drawGeometry(renderContext); | |
else if (this.family === 21) this.drawPath(renderContext) | |
}, | |
drawPath: function(renderContext) { | |
var i, j; | |
if (this.vertices.length === 0) return; | |
renderContext.beginShape(); | |
if (this.vertexCodes.length === 0) if (this.vertices[0].length === 2) for (i = 0, j = this.vertices.length; i < j; i++) renderContext.vertex(this.vertices[i][0], this.vertices[i][1]); | |
else for (i = 0, j = this.vertices.length; i < j; i++) renderContext.vertex(this.vertices[i][0], this.vertices[i][1], this.vertices[i][2]); | |
else { | |
var index = 0; | |
if (this.vertices[0].length === 2) for (i = 0, j = this.vertexCodes.length; i < j; i++) if (this.vertexCodes[i] === 0) { | |
renderContext.vertex(this.vertices[index][0], this.vertices[index][1], this.vertices[index]["moveTo"]); | |
renderContext.breakShape = false; | |
index++ | |
} else if (this.vertexCodes[i] === 1) { | |
renderContext.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] === 2) { | |
renderContext.curveVertex(this.vertices[index][0], this.vertices[index][1]); | |
index++ | |
} else { | |
if (this.vertexCodes[i] === 3) renderContext.breakShape = true | |
} else for (i = 0, j = this.vertexCodes.length; i < j; i++) if (this.vertexCodes[i] === 0) { | |
renderContext.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; | |
renderContext.breakShape = false | |
} else if (this.vertexCodes[i] === 1) { | |
renderContext.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] === 2) { | |
renderContext.curveVertex(this.vertices[index][0], this.vertices[index][1], this.vertices[index][2]); | |
index++ | |
} else if (this.vertexCodes[i] === 3) renderContext.breakShape = true | |
} | |
renderContext.endShape(this.close ? 2 : 1) | |
}, | |
drawGeometry: function(renderContext) { | |
var i, j; | |
renderContext.beginShape(this.kind); | |
if (this.style) for (i = 0, j = this.vertices.length; i < j; i++) renderContext.vertex(this.vertices[i]); | |
else for (i = 0, j = this.vertices.length; i < j; i++) { | |
var vert = this.vertices[i]; | |
if (vert[2] === 0) renderContext.vertex(vert[0], vert[1]); | |
else renderContext.vertex(vert[0], vert[1], vert[2]) | |
} | |
renderContext.endShape() | |
}, | |
drawGroup: function(renderContext) { | |
for (var i = 0, j = this.children.length; i < j; i++) this.children[i].draw(renderContext) | |
}, | |
drawPrimitive: function(renderContext) { | |
if (this.kind === 2) renderContext.point(this.params[0], this.params[1]); | |
else if (this.kind === 4) if (this.params.length === 4) renderContext.line(this.params[0], this.params[1], this.params[2], this.params[3]); | |
else renderContext.line(this.params[0], this.params[1], this.params[2], this.params[3], this.params[4], this.params[5]); | |
else if (this.kind === 8) renderContext.triangle(this.params[0], this.params[1], this.params[2], this.params[3], this.params[4], this.params[5]); | |
else if (this.kind === 16) renderContext.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 === 30) if (this.image !== null) { | |
var imMode = imageModeConvert; | |
renderContext.imageMode(0); | |
renderContext.image(this.image, this.params[0], this.params[1], this.params[2], this.params[3]); | |
imageModeConvert = imMode | |
} else { | |
var rcMode = curRectMode; | |
renderContext.rectMode(0); | |
renderContext.rect(this.params[0], this.params[1], this.params[2], this.params[3]); | |
curRectMode = rcMode | |
} else if (this.kind === 31) { | |
var elMode = curEllipseMode; | |
renderContext.ellipseMode(0); | |
renderContext.ellipse(this.params[0], this.params[1], this.params[2], this.params[3]); | |
curEllipseMode = elMode | |
} else if (this.kind === 32) { | |
var eMode = curEllipseMode; | |
renderContext.ellipseMode(0); | |
renderContext.arc(this.params[0], this.params[1], this.params[2], this.params[3], this.params[4], this.params[5]); | |
curEllipseMode = eMode | |
} else if (this.kind === 41) if (this.params.length === 1) renderContext.box(this.params[0]); | |
else renderContext.box(this.params[0], this.params[1], this.params[2]); | |
else if (this.kind === 40) renderContext.sphere(this.params[0]) | |
}, | |
pre: function(renderContext) { | |
if (this.matrix) { | |
renderContext.pushMatrix(); | |
renderContext.transform(this.matrix) | |
} | |
if (this.style) { | |
renderContext.pushStyle(); | |
this.styles(renderContext) | |
} | |
}, | |
post: function(renderContext) { | |
if (this.matrix) renderContext.popMatrix(); | |
if (this.style) renderContext.popStyle() | |
}, | |
styles: function(renderContext) { | |
if (this.stroke) { | |
renderContext.stroke(this.strokeColor); | |
renderContext.strokeWeight(this.strokeWeight); | |
renderContext.strokeCap(this.strokeCap); | |
renderContext.strokeJoin(this.strokeJoin) | |
} else renderContext.noStroke(); | |
if (this.fill) renderContext.fill(this.fillColor); | |
else renderContext.noFill() | |
}, | |
getChild: function(child) { | |
var i, j; | |
if (typeof child === "number") return this.children[child]; | |
var found; | |
if (child === "" || this.name === child) return this; | |
if (this.nameTable.length > 0) { | |
for (i = 0, j = this.nameTable.length; i < j || found; i++) if (this.nameTable[i].getName === child) { | |
found = this.nameTable[i]; | |
break | |
} | |
if (found) return found | |
} | |
for (i = 0, j = this.children.length; i < j; i++) { | |
found = this.children[i].getChild(child); | |
if (found) return found | |
} | |
return null | |
}, | |
getChildCount: function() { | |
return this.children.length | |
}, | |
addChild: function(child) { | |
this.children.push(child); | |
child.parent = this; | |
if (child.getName() !== null) this.addName(child.getName(), child) | |
}, | |
addName: function(name, shape) { | |
if (this.parent !== null) this.parent.addName(name, shape); | |
else this.nameTable.push([name, shape]) | |
}, | |
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) | |
} | |
}, | |
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 | |
}, | |
rotateX: function(angle) { | |
this.rotate(angle, 1, 0, 0) | |
}, | |
rotateY: function(angle) { | |
this.rotate(angle, 0, 1, 0) | |
}, | |
rotateZ: function(angle) { | |
this.rotate(angle, 0, 0, 1) | |
}, | |
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]) | |
} | |
}, | |
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]) | |
} | |
}, | |
resetMatrix: function() { | |
this.checkMatrix(2); | |
this.matrix.reset() | |
}, | |
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]) | |
} | |
} | |
}; | |
var PShapeSVG = p.PShapeSVG = function() { | |
p.PShape.call(this); | |
if (arguments.length === 1) { | |
this.element = arguments[0]; | |
this.vertexCodes = []; | |
this.vertices = []; | |
this.opacity = 1; | |
this.stroke = false; | |
this.strokeColor = 4278190080; | |
this.strokeWeight = 1; | |
this.strokeCap = 'butt'; | |
this.strokeJoin = 'miter'; | |
this.strokeGradient = null; | |
this.strokeGradientPaint = null; | |
this.strokeName = null; | |
this.strokeOpacity = 1; | |
this.fill = true; | |
this.fillColor = 4278190080; | |
this.fillGradient = null; | |
this.fillGradientPaint = null; | |
this.fillName = null; | |
this.fillOpacity = 1; | |
if (this.element.getName() !== "svg") throw "root is not <svg>, it's <" + this.element.getName() + ">"; | |
} else if (arguments.length === 2) if (typeof arguments[1] === "string") { | |
if (arguments[1].indexOf(".svg") > -1) { | |
this.element = new p.XMLElement(p, arguments[1]); | |
this.vertexCodes = []; | |
this.vertices = []; | |
this.opacity = 1; | |
this.stroke = false; | |
this.strokeColor = 4278190080; | |
this.strokeWeight = 1; | |
this.strokeCap = 'butt'; | |
this.strokeJoin = 'miter'; | |
this.strokeGradient = ""; | |
this.strokeGradientPaint = ""; | |
this.strokeName = ""; | |
this.strokeOpacity = 1; | |
this.fill = true; | |
this.fillColor = 4278190080; | |
this.fillGradient = null; | |
this.fillGradientPaint = null; | |
this.fillOpacity = 1 | |
} | |
} else if (arguments[0]) { | |
this.element = arguments[1]; | |
this.vertexCodes = arguments[0].vertexCodes.slice(); | |
this.vertices = arguments[0].vertices.slice(); | |
this.stroke = arguments[0].stroke; | |
this.strokeColor = arguments[0].strokeColor; | |
this.strokeWeight = arguments[0].strokeWeight; | |
this.strokeCap = arguments[0].strokeCap; | |
this.strokeJoin = arguments[0].strokeJoin; | |
this.strokeGradient = arguments[0].strokeGradient; | |
this.strokeGradientPaint = arguments[0].strokeGradientPaint; | |
this.strokeName = arguments[0].strokeName; | |
this.fill = arguments[0].fill; | |
this.fillColor = arguments[0].fillColor; | |
this.fillGradient = arguments[0].fillGradient; | |
this.fillGradientPaint = arguments[0].fillGradientPaint; | |
this.fillName = arguments[0].fillName; | |
this.strokeOpacity = arguments[0].strokeOpacity; | |
this.fillOpacity = arguments[0].fillOpacity; | |
this.opacity = arguments[0].opacity | |
} | |
this.name = this.element.getStringAttribute("id"); | |
var displayStr = this.element.getStringAttribute("display", "inline"); | |
this.visible = displayStr !== "none"; | |
var str = this.element.getAttribute("transform"); | |
if (str) this.matrix = this.parseMatrix(str); | |
var viewBoxStr = this.element.getStringAttribute("viewBox"); | |
if (viewBoxStr !== null) { | |
var viewBox = viewBoxStr.split(" "); | |
this.width = viewBox[2]; | |
this.height = viewBox[3] | |
} | |
var unitWidth = this.element.getStringAttribute("width"); | |
var unitHeight = this.element.getStringAttribute("height"); | |
if (unitWidth !== null) { | |
this.width = this.parseUnitSize(unitWidth); | |
this.height = this.parseUnitSize(unitHeight) | |
} else if (this.width === 0 || this.height === 0) { | |
this.width = 1; | |
this.height = 1; | |
throw "The width and/or height is not " + "readable in the <svg> tag of this file."; | |
} | |
this.parseColors(this.element); | |
this.parseChildren(this.element) | |
}; | |
PShapeSVG.prototype = new PShape; | |
PShapeSVG.prototype.parseMatrix = function() { | |
function getCoords(s) { | |
var m = []; | |
s.replace(/\((.*?)\)/, function() { | |
return function(all, params) { | |
m = params.replace(/,+/g, " ").split(/\s+/) | |
} | |
}()); | |
return m | |
} | |
return function(str) { | |
this.checkMatrix(2); | |
var pieces = []; | |
str.replace(/\s*(\w+)\((.*?)\)/g, function(all) { | |
pieces.push(p.trim(all)) | |
}); | |
if (pieces.length === 0) return null; | |
for (var i = 0, j = pieces.length; i < j; i++) { | |
var m = getCoords(pieces[i]); | |
if (pieces[i].indexOf("matrix") !== -1) this.matrix.set(m[0], m[2], m[4], m[1], m[3], m[5]); | |
else if (pieces[i].indexOf("translate") !== -1) { | |
var tx = m[0]; | |
var ty = m.length === 2 ? m[1] : 0; | |
this.matrix.translate(tx, ty) | |
} else if (pieces[i].indexOf("scale") !== -1) { | |
var sx = m[0]; | |
var sy = m.length === 2 ? m[1] : m[0]; | |
this.matrix.scale(sx, sy) | |
} else if (pieces[i].indexOf("rotate") !== -1) { | |
var angle = m[0]; | |
if (m.length === 1) this.matrix.rotate(p.radians(angle)); | |
else if (m.length === 3) { | |
this.matrix.translate(m[1], m[2]); | |
this.matrix.rotate(p.radians(m[0])); | |
this.matrix.translate(-m[1], -m[2]) | |
} | |
} else if (pieces[i].indexOf("skewX") !== -1) this.matrix.skewX(parseFloat(m[0])); | |
else if (pieces[i].indexOf("skewY") !== -1) this.matrix.skewY(m[0]); | |
else if (pieces[i].indexOf("shearX") !== -1) this.matrix.shearX(m[0]); | |
else if (pieces[i].indexOf("shearY") !== -1) this.matrix.shearY(m[0]) | |
} | |
return this.matrix | |
} | |
}(); | |
PShapeSVG.prototype.parseChildren = function(element) { | |
var newelement = element.getChildren(); | |
var children = new p.PShape; | |
for (var i = 0, j = newelement.length; i < j; i++) { | |
var kid = this.parseChild(newelement[i]); | |
if (kid) children.addChild(kid) | |
} | |
this.children.push(children) | |
}; | |
PShapeSVG.prototype.getName = function() { | |
return this.name | |
}; | |
PShapeSVG.prototype.parseChild = function(elem) { | |
var name = elem.getName(); | |
var shape; | |
if (name === "g") shape = new PShapeSVG(this, elem); | |
else if (name === "defs") shape = new PShapeSVG(this, elem); | |
else if (name === "line") { | |
shape = new PShapeSVG(this, elem); | |
shape.parseLine() | |
} else if (name === "circle") { | |
shape = new PShapeSVG(this, elem); | |
shape.parseEllipse(true) | |
} else if (name === "ellipse") { | |
shape = new PShapeSVG(this, elem); | |
shape.parseEllipse(false) | |
} else if (name === "rect") { | |
shape = new PShapeSVG(this, elem); | |
shape.parseRect() | |
} else if (name === "polygon") { | |
shape = new PShapeSVG(this, elem); | |
shape.parsePoly(true) | |
} else if (name === "polyline") { | |
shape = new PShapeSVG(this, elem); | |
shape.parsePoly(false) | |
} else if (name === "path") { | |
shape = new PShapeSVG(this, elem); | |
shape.parsePath() | |
} else if (name === "radialGradient") unimplemented("PShapeSVG.prototype.parseChild, name = radialGradient"); | |
else if (name === "linearGradient") unimplemented("PShapeSVG.prototype.parseChild, name = linearGradient"); | |
else if (name === "text") unimplemented("PShapeSVG.prototype.parseChild, name = text"); | |
else if (name === "filter") unimplemented("PShapeSVG.prototype.parseChild, name = filter"); | |
else if (name === "mask") unimplemented("PShapeSVG.prototype.parseChild, name = mask"); | |
else nop(); | |
return shape | |
}; | |
PShapeSVG.prototype.parsePath = function() { | |
this.family = 21; | |
this.kind = 0; | |
var pathDataChars = []; | |
var c; | |
var pathData = p.trim(this.element.getStringAttribute("d").replace(/[\s,]+/g, " ")); | |
if (pathData === null) return; | |
pathData = p.__toCharArray(pathData); | |
var cx = 0, | |
cy = 0, | |
ctrlX = 0, | |
ctrlY = 0, | |
ctrlX1 = 0, | |
ctrlX2 = 0, | |
ctrlY1 = 0, | |
ctrlY2 = 0, | |
endX = 0, | |
endY = 0, | |
ppx = 0, | |
ppy = 0, | |
px = 0, | |
py = 0, | |
i = 0, | |
valOf = 0; | |
var str = ""; | |
var tmpArray = []; | |
var flag = false; | |
var lastInstruction; | |
var command; | |
var j, k; | |
while (i < pathData.length) { | |
valOf = pathData[i].valueOf(); | |
if (valOf >= 65 && valOf <= 90 || valOf >= 97 && valOf <= 122) { | |
j = i; | |
i++; | |
if (i < pathData.length) { | |
tmpArray = []; | |
valOf = pathData[i].valueOf(); | |
while (! (valOf >= 65 && valOf <= 90 || valOf >= 97 && valOf <= 100 || valOf >= 102 && valOf <= 122) && flag === false) { | |
if (valOf === 32) { | |
if (str !== "") { | |
tmpArray.push(parseFloat(str)); | |
str = "" | |
} | |
i++ | |
} else if (valOf === 45) if (pathData[i - 1].valueOf() === 101) { | |
str += pathData[i].toString(); | |
i++ | |
} else { | |
if (str !== "") tmpArray.push(parseFloat(str)); | |
str = pathData[i].toString(); | |
i++ | |
} else { | |
str += pathData[i].toString(); | |
i++ | |
} | |
if (i === pathData.length) flag = true; | |
else valOf = pathData[i].valueOf() | |
} | |
} | |
if (str !== "") { | |
tmpArray.push(parseFloat(str)); | |
str = "" | |
} | |
command = pathData[j]; | |
valOf = command.valueOf(); | |
if (valOf === 77) { | |
if (tmpArray.length >= 2 && tmpArray.length % 2 === 0) { | |
cx = tmpArray[0]; | |
cy = tmpArray[1]; | |
this.parsePathMoveto(cx, cy); | |
if (tmpArray.length > 2) for (j = 2, k = tmpArray.length; j < k; j += 2) { | |
cx = tmpArray[j]; | |
cy = tmpArray[j + 1]; | |
this.parsePathLineto(cx, cy) | |
} | |
} | |
} else if (valOf === 109) { | |
if (tmpArray.length >= 2 && tmpArray.length % 2 === 0) { | |
cx += tmpArray[0]; | |
cy += tmpArray[1]; | |
this.parsePathMoveto(cx, cy); | |
if (tmpArray.length > 2) for (j = 2, k = tmpArray.length; j < k; j += 2) { | |
cx += tmpArray[j]; | |
cy += tmpArray[j + 1]; | |
this.parsePathLineto(cx, cy) | |
} | |
} | |
} else if (valOf === 76) { | |
if (tmpArray.length >= 2 && tmpArray.length % 2 === 0) for (j = 0, k = tmpArray.length; j < k; j += 2) { | |
cx = tmpArray[j]; | |
cy = tmpArray[j + 1]; | |
this.parsePathLineto(cx, cy) | |
} | |
} else if (valOf === 108) { | |
if (tmpArray.length >= 2 && tmpArray.length % 2 === 0) for (j = 0, k = tmpArray.length; j < k; j += 2) { | |
cx += tmpArray[j]; | |
cy += tmpArray[j + 1]; | |
this.parsePathLineto(cx, cy) | |
} | |
} else if (valOf === 72) for (j = 0, k = tmpArray.length; j < k; j++) { | |
cx = tmpArray[j]; | |
this.parsePathLineto(cx, cy) | |
} else if (valOf === 104) for (j = 0, k = tmpArray.length; j < k; j++) { | |
cx += tmpArray[j]; | |
this.parsePathLineto(cx, cy) | |
} else if (valOf === 86) for (j = 0, k = tmpArray.length; j < k; j++) { | |
cy = tmpArray[j]; | |
this.parsePathLineto(cx, cy) | |
} else if (valOf === 118) for (j = 0, k = tmpArray.length; j < k; j++) { | |
cy += tmpArray[j]; | |
this.parsePathLineto(cx, cy) | |
} else if (valOf === 67) { | |
if (tmpArray.length >= 6 && tmpArray.length % 6 === 0) for (j = 0, k = tmpArray.length; j < k; j += 6) { | |
ctrlX1 = tmpArray[j]; | |
ctrlY1 = tmpArray[j + 1]; | |
ctrlX2 = tmpArray[j + 2]; | |
ctrlY2 = tmpArray[j + 3]; | |
endX = tmpArray[j + 4]; | |
endY = tmpArray[j + 5]; | |
this.parsePathCurveto(ctrlX1, ctrlY1, ctrlX2, ctrlY2, endX, endY); | |
cx = endX; | |
cy = endY | |
} | |
} else if (valOf === 99) { | |
if (tmpArray.length >= 6 && tmpArray.length % 6 === 0) for (j = 0, k = tmpArray.length; j < k; j += 6) { | |
ctrlX1 = cx + tmpArray[j]; | |
ctrlY1 = cy + tmpArray[j + 1]; | |
ctrlX2 = cx + tmpArray[j + 2]; | |
ctrlY2 = cy + tmpArray[j + 3]; | |
endX = cx + tmpArray[j + 4]; | |
endY = cy + tmpArray[j + 5]; | |
this.parsePathCurveto(ctrlX1, ctrlY1, ctrlX2, ctrlY2, endX, endY); | |
cx = endX; | |
cy = endY | |
} | |
} else if (valOf === 83) { | |
if (tmpArray.length >= 4 && tmpArray.length % 4 === 0) for (j = 0, k = tmpArray.length; j < k; j += 4) { | |
if (lastInstruction.toLowerCase() === "c" || lastInstruction.toLowerCase() === "s") { | |
ppx = this.vertices[this.vertices.length - 2][0]; | |
ppy = this.vertices[this.vertices.length - 2][1]; | |
px = this.vertices[this.vertices.length - 1][0]; | |
py = this.vertices[this.vertices.length - 1][1]; | |
ctrlX1 = px + (px - ppx); | |
ctrlY1 = py + (py - ppy) | |
} else { | |
ctrlX1 = this.vertices[this.vertices.length - 1][0]; | |
ctrlY1 = this.vertices[this.vertices.length - 1][1] | |
} | |
ctrlX2 = tmpArray[j]; | |
ctrlY2 = tmpArray[j + 1]; | |
endX = tmpArray[j + 2]; | |
endY = tmpArray[j + 3]; | |
this.parsePathCurveto(ctrlX1, ctrlY1, ctrlX2, ctrlY2, endX, endY); | |
cx = endX; | |
cy = endY | |
} | |
} else if (valOf === 115) { | |
if (tmpArray.length >= 4 && tmpArray.length % 4 === 0) for (j = 0, k = tmpArray.length; j < k; j += 4) { | |
if (lastInstruction.toLowerCase() === "c" || lastInstruction.toLowerCase() === "s") { | |
ppx = this.vertices[this.vertices.length - 2][0]; | |
ppy = this.vertices[this.vertices.length - 2][1]; | |
px = this.vertices[this.vertices.length - 1][0]; | |
py = this.vertices[this.vertices.length - 1][1]; | |
ctrlX1 = px + (px - ppx); | |
ctrlY1 = py + (py - ppy) | |
} else { | |
ctrlX1 = this.vertices[this.vertices.length - 1][0]; | |
ctrlY1 = this.vertices[this.vertices.length - 1][1] | |
} | |
ctrlX2 = cx + tmpArray[j]; | |
ctrlY2 = cy + tmpArray[j + 1]; | |
endX = cx + tmpArray[j + 2]; | |
endY = cy + tmpArray[j + 3]; | |
this.parsePathCurveto(ctrlX1, ctrlY1, ctrlX2, ctrlY2, endX, endY); | |
cx = endX; | |
cy = endY | |
} | |
} else if (valOf === 81) { | |
if (tmpArray.length >= 4 && tmpArray.length % 4 === 0) for (j = 0, k = tmpArray.length; j < k; j += 4) { | |
ctrlX = tmpArray[j]; | |
ctrlY = tmpArray[j + 1]; | |
endX = tmpArray[j + 2]; | |
endY = tmpArray[j + 3]; | |
this.parsePathQuadto(cx, cy, ctrlX, ctrlY, endX, endY); | |
cx = endX; | |
cy = endY | |
} | |
} else if (valOf === 113) { | |
if (tmpArray.length >= 4 && tmpArray.length % 4 === 0) for (j = 0, k = tmpArray.length; j < k; j += 4) { | |
ctrlX = cx + tmpArray[j]; | |
ctrlY = cy + tmpArray[j + 1]; | |
endX = cx + tmpArray[j + 2]; | |
endY = cy + tmpArray[j + 3]; | |
this.parsePathQuadto(cx, cy, ctrlX, ctrlY, endX, endY); | |
cx = endX; | |
cy = endY | |
} | |
} else if (valOf === 84) { | |
if (tmpArray.length >= 2 && tmpArray.length % 2 === 0) for (j = 0, k = tmpArray.length; j < k; j += 2) { | |
if (lastInstruction.toLowerCase() === "q" || lastInstruction.toLowerCase() === "t") { | |
ppx = this.vertices[this.vertices.length - 2][0]; | |
ppy = this.vertices[this.vertices.length - 2][1]; | |
px = this.vertices[this.vertices.length - 1][0]; | |
py = this.vertices[this.vertices.length - 1][1]; | |
ctrlX = px + (px - ppx); | |
ctrlY = py + (py - ppy) | |
} else { | |
ctrlX = cx; | |
ctrlY = cy | |
} | |
endX = tmpArray[j]; | |
endY = tmpArray[j + 1]; | |
this.parsePathQuadto(cx, cy, ctrlX, ctrlY, endX, endY); | |
cx = endX; | |
cy = endY | |
} | |
} else if (valOf === 116) { | |
if (tmpArray.length >= 2 && tmpArray.length % 2 === 0) for (j = 0, k = tmpArray.length; j < k; j += 2) { | |
if (lastInstruction.toLowerCase() === "q" || lastInstruction.toLowerCase() === "t") { | |
ppx = this.vertices[this.vertices.length - 2][0]; | |
ppy = this.vertices[this.vertices.length - 2][1]; | |
px = this.vertices[this.vertices.length - 1][0]; | |
py = this.vertices[this.vertices.length - 1][1]; | |
ctrlX = px + (px - ppx); | |
ctrlY = py + (py - ppy) | |
} else { | |
ctrlX = cx; | |
ctrlY = cy | |
} | |
endX = cx + tmpArray[j]; | |
endY = cy + tmpArray[j + 1]; | |
this.parsePathQuadto(cx, cy, ctrlX, ctrlY, endX, endY); | |
cx = endX; | |
cy = endY | |
} | |
} else if (valOf === 90 || valOf === 122) this.close = true; | |
lastInstruction = command.toString() | |
} else i++ | |
} | |
}; | |
PShapeSVG.prototype.parsePathQuadto = function(x1, y1, cx, cy, x2, y2) { | |
if (this.vertices.length > 0) { | |
this.parsePathCode(1); | |
this.parsePathVertex(x1 + (cx - x1) * 2 / 3, y1 + (cy - y1) * 2 / 3); | |
this.parsePathVertex(x2 + (cx - x2) * 2 / 3, y2 + (cy - y2) * 2 / 3); | |
this.parsePathVertex(x2, y2) | |
} else throw "Path must start with M/m"; | |
}; | |
PShapeSVG.prototype.parsePathCurveto = function(x1, y1, x2, y2, x3, y3) { | |
if (this.vertices.length > 0) { | |
this.parsePathCode(1); | |
this.parsePathVertex(x1, y1); | |
this.parsePathVertex(x2, y2); | |
this.parsePathVertex(x3, y3) | |
} else throw "Path must start with M/m"; | |
}; | |
PShapeSVG.prototype.parsePathLineto = function(px, py) { | |
if (this.vertices.length > 0) { | |
this.parsePathCode(0); | |
this.parsePathVertex(px, py); | |
this.vertices[this.vertices.length - 1]["moveTo"] = false | |
} else throw "Path must start with M/m"; | |
}; | |
PShapeSVG.prototype.parsePathMoveto = function(px, py) { | |
if (this.vertices.length > 0) this.parsePathCode(3); | |
this.parsePathCode(0); | |
this.parsePathVertex(px, py); | |
this.vertices[this.vertices.length - 1]["moveTo"] = true | |
}; | |
PShapeSVG.prototype.parsePathVertex = function(x, y) { | |
var verts = []; | |
verts[0] = x; | |
verts[1] = y; | |
this.vertices.push(verts) | |
}; | |
PShapeSVG.prototype.parsePathCode = function(what) { | |
this.vertexCodes.push(what) | |
}; | |
PShapeSVG.prototype.parsePoly = function(val) { | |
this.family = 21; | |
this.close = val; | |
var pointsAttr = p.trim(this.element.getStringAttribute("points").replace(/[,\s]+/g, " ")); | |
if (pointsAttr !== null) { | |
var pointsBuffer = pointsAttr.split(" "); | |
if (pointsBuffer.length % 2 === 0) for (var i = 0, j = pointsBuffer.length; i < j; i++) { | |
var verts = []; | |
verts[0] = pointsBuffer[i]; | |
verts[1] = pointsBuffer[++i]; | |
this.vertices.push(verts) | |
} else throw "Error parsing polygon points: odd number of coordinates provided"; | |
} | |
}; | |
PShapeSVG.prototype.parseRect = function() { | |
this.kind = 30; | |
this.family = 1; | |
this.params = []; | |
this.params[0] = this.element.getFloatAttribute("x"); | |
this.params[1] = this.element.getFloatAttribute("y"); | |
this.params[2] = this.element.getFloatAttribute("width"); | |
this.params[3] = this.element.getFloatAttribute("height"); | |
if (this.params[2] < 0 || this.params[3] < 0) throw "svg error: negative width or height found while parsing <rect>"; | |
}; | |
PShapeSVG.prototype.parseEllipse = function(val) { | |
this.kind = 31; | |
this.family = 1; | |
this.params = []; | |
this.params[0] = this.element.getFloatAttribute("cx") | 0; | |
this.params[1] = this.element.getFloatAttribute("cy") | 0; | |
var rx, ry; | |
if (val) { | |
rx = ry = this.element.getFloatAttribute("r"); | |
if (rx < 0) throw "svg error: negative radius found while parsing <circle>"; | |
} else { | |
rx = this.element.getFloatAttribute("rx"); | |
ry = this.element.getFloatAttribute("ry"); | |
if (rx < 0 || ry < 0) throw "svg error: negative x-axis radius or y-axis radius found while parsing <ellipse>"; | |
} | |
this.params[0] -= rx; | |
this.params[1] -= ry; | |
this.params[2] = rx * 2; | |
this.params[3] = ry * 2 | |
}; | |
PShapeSVG.prototype.parseLine = function() { | |
this.kind = 4; | |
this.family = 1; | |
this.params = []; | |
this.params[0] = this.element.getFloatAttribute("x1"); | |
this.params[1] = this.element.getFloatAttribute("y1"); | |
this.params[2] = this.element.getFloatAttribute("x2"); | |
this.params[3] = this.element.getFloatAttribute("y2") | |
}; | |
PShapeSVG.prototype.parseColors = function(element) { | |
if (element.hasAttribute("opacity")) this.setOpacity(element.getAttribute("opacity")); | |
if (element.hasAttribute("stroke")) this.setStroke(element.getAttribute("stroke")); | |
if (element.hasAttribute("stroke-width")) this.setStrokeWeight(element.getAttribute("stroke-width")); | |
if (element.hasAttribute("stroke-linejoin")) this.setStrokeJoin(element.getAttribute("stroke-linejoin")); | |
if (element.hasAttribute("stroke-linecap")) this.setStrokeCap(element.getStringAttribute("stroke-linecap")); | |
if (element.hasAttribute("fill")) this.setFill(element.getStringAttribute("fill")); | |
if (element.hasAttribute("style")) { | |
var styleText = element.getStringAttribute("style"); | |
var styleTokens = styleText.toString().split(";"); | |
for (var i = 0, j = styleTokens.length; i < j; i++) { | |
var tokens = p.trim(styleTokens[i].split(":")); | |
if (tokens[0] === "fill") this.setFill(tokens[1]); | |
else if (tokens[0] === "fill-opacity") this.setFillOpacity(tokens[1]); | |
else if (tokens[0] === "stroke") this.setStroke(tokens[1]); | |
else if (tokens[0] === "stroke-width") this.setStrokeWeight(tokens[1]); | |
else if (tokens[0] === "stroke-linecap") this.setStrokeCap(tokens[1]); | |
else if (tokens[0] === "stroke-linejoin") this.setStrokeJoin(tokens[1]); | |
else if (tokens[0] === "stroke-opacity") this.setStrokeOpacity(tokens[1]); | |
else if (tokens[0] === "opacity") this.setOpacity(tokens[1]) | |
} | |
} | |
}; | |
PShapeSVG.prototype.setFillOpacity = function(opacityText) { | |
this.fillOpacity = parseFloat(opacityText); | |
this.fillColor = this.fillOpacity * 255 << 24 | this.fillColor & 16777215 | |
}; | |
PShapeSVG.prototype.setFill = function(fillText) { | |
var opacityMask = this.fillColor & 4278190080; | |
if (fillText === "none") this.fill = false; | |
else if (fillText.indexOf("#") === 0) { | |
this.fill = true; | |
if (fillText.length === 4) fillText = fillText.replace(/#(.)(.)(.)/, "#$1$1$2$2$3$3"); | |
this.fillColor = opacityMask | parseInt(fillText.substring(1), 16) & 16777215 | |
} else if (fillText.indexOf("rgb") === 0) { | |
this.fill = true; | |
this.fillColor = opacityMask | this.parseRGB(fillText) | |
} else if (fillText.indexOf("url(#") === 0) this.fillName = fillText.substring(5, fillText.length - 1); | |
else if (colors[fillText]) { | |
this.fill = true; | |
this.fillColor = opacityMask | parseInt(colors[fillText].substring(1), 16) & 16777215 | |
} | |
}; | |
PShapeSVG.prototype.setOpacity = function(opacity) { | |
this.strokeColor = parseFloat(opacity) * 255 << 24 | this.strokeColor & 16777215; | |
this.fillColor = parseFloat(opacity) * 255 << 24 | this.fillColor & 16777215 | |
}; | |
PShapeSVG.prototype.setStroke = function(strokeText) { | |
var opacityMask = this.strokeColor & 4278190080; | |
if (strokeText === "none") this.stroke = false; | |
else if (strokeText.charAt(0) === "#") { | |
this.stroke = true; | |
if (strokeText.length === 4) strokeText = strokeText.replace(/#(.)(.)(.)/, "#$1$1$2$2$3$3"); | |
this.strokeColor = opacityMask | parseInt(strokeText.substring(1), 16) & 16777215 | |
} else if (strokeText.indexOf("rgb") === 0) { | |
this.stroke = true; | |
this.strokeColor = opacityMask | this.parseRGB(strokeText) | |
} else if (strokeText.indexOf("url(#") === 0) this.strokeName = strokeText.substring(5, strokeText.length - 1); | |
else if (colors[strokeText]) { | |
this.stroke = true; | |
this.strokeColor = opacityMask | parseInt(colors[strokeText].substring(1), 16) & 16777215 | |
} | |
}; | |
PShapeSVG.prototype.setStrokeWeight = function(weight) { | |
this.strokeWeight = this.parseUnitSize(weight) | |
}; | |
PShapeSVG.prototype.setStrokeJoin = function(linejoin) { | |
if (linejoin === "miter") this.strokeJoin = 'miter'; | |
else if (linejoin === "round") this.strokeJoin = 'round'; | |
else if (linejoin === "bevel") this.strokeJoin = 'bevel' | |
}; | |
PShapeSVG.prototype.setStrokeCap = function(linecap) { | |
if (linecap === "butt") this.strokeCap = 'butt'; | |
else if (linecap === "round") this.strokeCap = 'round'; | |
else if (linecap === "square") this.strokeCap = 'square' | |
}; | |
PShapeSVG.prototype.setStrokeOpacity = function(opacityText) { | |
this.strokeOpacity = parseFloat(opacityText); | |
this.strokeColor = this.strokeOpacity * 255 << 24 | this.strokeColor & 16777215 | |
}; | |
PShapeSVG.prototype.parseRGB = function(color) { | |
var sub = color.substring(color.indexOf("(") + 1, color.indexOf(")")); | |
var values = sub.split(", "); | |
return values[0] << 16 | values[1] << 8 | values[2] | |
}; | |
PShapeSVG.prototype.parseUnitSize = function(text) { | |
var len = text.length - 2; | |
if (len < 0) return text; | |
if (text.indexOf("pt") === len) return parseFloat(text.substring(0, len)) * 1.25; | |
if (text.indexOf("pc") === len) return parseFloat(text.substring(0, len)) * 15; | |
if (text.indexOf("mm") === len) return parseFloat(text.substring(0, len)) * 3.543307; | |
if (text.indexOf("cm") === len) return parseFloat(text.substring(0, len)) * 35.43307; | |
if (text.indexOf("in") === len) return parseFloat(text.substring(0, len)) * 90; | |
if (text.indexOf("px") === len) return parseFloat(text.substring(0, len)); | |
return parseFloat(text) | |
}; | |
p.shape = function(shape, x, y, width, height) { | |
if (arguments.length >= 1 && arguments[0] !== null) if (shape.isVisible()) { | |
p.pushMatrix(); | |
if (curShapeMode === 3) if (arguments.length === 5) { | |
p.translate(x - width / 2, y - height / 2); | |
p.scale(width / shape.getWidth(), height / shape.getHeight()) | |
} else if (arguments.length === 3) p.translate(x - shape.getWidth() / 2, -shape.getHeight() / 2); | |
else p.translate(-shape.getWidth() / 2, -shape.getHeight() / 2); | |
else if (curShapeMode === 0) if (arguments.length === 5) { | |
p.translate(x, y); | |
p.scale(width / shape.getWidth(), height / shape.getHeight()) | |
} else { | |
if (arguments.length === 3) p.translate(x, y) | |
} else if (curShapeMode === 1) if (arguments.length === 5) { | |
width -= x; | |
height -= y; | |
p.translate(x, y); | |
p.scale(width / shape.getWidth(), height / shape.getHeight()) | |
} else if (arguments.length === 3) p.translate(x, y); | |
shape.draw(p); | |
if (arguments.length === 1 && curShapeMode === 3 || arguments.length > 1) p.popMatrix() | |
} | |
}; | |
p.shapeMode = function(mode) { | |
curShapeMode = mode | |
}; | |
p.loadShape = function(filename) { | |
if (arguments.length === 1) if (filename.indexOf(".svg") > -1) return new PShapeSVG(null, filename); | |
return null | |
}; | |
var XMLAttribute = function(fname, n, nameSpace, v, t) { | |
this.fullName = fname || ""; | |
this.name = n || ""; | |
this.namespace = nameSpace || ""; | |
this.value = v; | |
this.type = t | |
}; | |
XMLAttribute.prototype = { | |
getName: function() { | |
return this.name | |
}, | |
getFullName: function() { | |
return this.fullName | |
}, | |
getNamespace: function() { | |
return this.namespace | |
}, | |
getValue: function() { | |
return this.value | |
}, | |
getType: function() { | |
return this.type | |
}, | |
setValue: function(newval) { | |
this.value = newval | |
} | |
}; | |
var XMLElement = p.XMLElement = function(selector, uri, sysid, line) { | |
this.attributes = []; | |
this.children = []; | |
this.fullName = null; | |
this.name = null; | |
this.namespace = ""; | |
this.content = null; | |
this.parent = null; | |
this.lineNr = ""; | |
this.systemID = ""; | |
this.type = "ELEMENT"; | |
if (selector) if (typeof selector === "string") if (uri === undef && selector.indexOf("<") > -1) this.parse(selector); | |
else { | |
this.fullName = selector; | |
this.namespace = uri; | |
this.systemId = sysid; | |
this.lineNr = line | |
} else this.parse(uri) | |
}; | |
XMLElement.prototype = { | |
parse: function(textstring) { | |
var xmlDoc; | |
try { | |
var extension = textstring.substring(textstring.length - 4); | |
if (extension === ".xml" || extension === ".svg") textstring = ajax(textstring); | |
xmlDoc = (new DOMParser).parseFromString(textstring, "text/xml"); | |
var elements = xmlDoc.documentElement; | |
if (elements) this.parseChildrenRecursive(null, elements); | |
else throw "Error loading document"; | |
return this | |
} catch(e) { | |
throw e; | |
} | |
}, | |
parseChildrenRecursive: function(parent, elementpath) { | |
var xmlelement, xmlattribute, tmpattrib, l, m, child; | |
if (!parent) { | |
this.fullName = elementpath.localName; | |
this.name = elementpath.nodeName; | |
xmlelement = this | |
} else { | |
xmlelement = new XMLElement(elementpath.nodeName); | |
xmlelement.parent = parent | |
} | |
if (elementpath.nodeType === 3 && elementpath.textContent !== "") return this.createPCDataElement(elementpath.textContent); | |
if (elementpath.nodeType === 4) return this.createCDataElement(elementpath.textContent); | |
if (elementpath.attributes) for (l = 0, m = elementpath.attributes.length; l < m; l++) { | |
tmpattrib = elementpath.attributes[l]; | |
xmlattribute = new XMLAttribute(tmpattrib.getname, tmpattrib.nodeName, tmpattrib.namespaceURI, tmpattrib.nodeValue, tmpattrib.nodeType); | |
xmlelement.attributes.push(xmlattribute) | |
} | |
if (elementpath.childNodes) for (l = 0, m = elementpath.childNodes.length; l < m; l++) { | |
var node = elementpath.childNodes[l]; | |
child = xmlelement.parseChildrenRecursive(xmlelement, node); | |
if (child !== null) xmlelement.children.push(child) | |
} | |
return xmlelement | |
}, | |
createElement: function(fullname, namespaceuri, sysid, line) { | |
if (sysid === undef) return new XMLElement(fullname, namespaceuri); | |
return new XMLElement(fullname, namespaceuri, sysid, line) | |
}, | |
createPCDataElement: function(content, isCDATA) { | |
if (content.replace(/^\s+$/g, "") === "") return null; | |
var pcdata = new XMLElement; | |
pcdata.type = "TEXT"; | |
pcdata.content = content; | |
return pcdata | |
}, | |
createCDataElement: function(content) { | |
var cdata = this.createPCDataElement(content); | |
if (cdata === null) return null; | |
cdata.type = "CDATA"; | |
var htmlentities = { | |
"<": "<", | |
">": ">", | |
"'": "'", | |
'"': """ | |
}, | |
entity; | |
for (entity in htmlentities) if (!Object.hasOwnProperty(htmlentities, entity)) content = content.replace(new RegExp(entity, "g"), htmlentities[entity]); | |
cdata.cdata = content; | |
return cdata | |
}, | |
hasAttribute: function() { | |
if (arguments.length === 1) return this.getAttribute(arguments[0]) !== null; | |
if (arguments.length === 2) return this.getAttribute(arguments[0], arguments[1]) !== null | |
}, | |
equals: function(other) { | |
if (! (other instanceof XMLElement)) return false; | |
var i, j; | |
if (this.fullName !== other.fullName) return false; | |
if (this.attributes.length !== other.getAttributeCount()) return false; | |
if (this.attributes.length !== other.attributes.length) return false; | |
var attr_name, attr_ns, attr_value, attr_type, attr_other; | |
for (i = 0, j = this.attributes.length; i < j; i++) { | |
attr_name = this.attributes[i].getName(); | |
attr_ns = this.attributes[i].getNamespace(); | |
attr_other = other.findAttribute(attr_name, attr_ns); | |
if (attr_other === null) return false; | |
if (this.attributes[i].getValue() !== attr_other.getValue()) return false; | |
if (this.attributes[i].getType() !== attr_other.getType()) return false | |
} | |
if (this.children.length !== other.getChildCount()) return false; | |
if (this.children.length > 0) { | |
var child1, child2; | |
for (i = 0, j = this.children.length; i < j; i++) { | |
child1 = this.getChild(i); | |
child2 = other.getChild(i); | |
if (!child1.equals(child2)) return false | |
} | |
return true | |
} | |
return this.content === other.content | |
}, | |
getContent: function() { | |
if (this.type === "TEXT" || this.type === "CDATA") return this.content; | |
var children = this.children; | |
if (children.length === 1 && (children[0].type === "TEXT" || children[0].type === "CDATA")) return children[0].content; | |
return null | |
}, | |
getAttribute: function() { | |
var attribute; | |
if (arguments.length === 2) { | |
attribute = this.findAttribute(arguments[0]); | |
if (attribute) return attribute.getValue(); | |
return arguments[1] | |
} else if (arguments.length === 1) { | |
attribute = this.findAttribute(arguments[0]); | |
if (attribute) return attribute.getValue(); | |
return null | |
} else if (arguments.length === 3) { | |
attribute = this.findAttribute(arguments[0], arguments[1]); | |
if (attribute) return attribute.getValue(); | |
return arguments[2] | |
} | |
}, | |
getStringAttribute: function() { | |
if (arguments.length === 1) return this.getAttribute(arguments[0]); | |
if (arguments.length === 2) return this.getAttribute(arguments[0], arguments[1]); | |
return this.getAttribute(arguments[0], arguments[1], arguments[2]) | |
}, | |
getString: function(attributeName) { | |
return this.getStringAttribute(attributeName) | |
}, | |
getFloatAttribute: function() { | |
if (arguments.length === 1) return parseFloat(this.getAttribute(arguments[0], 0)); | |
if (arguments.length === 2) return this.getAttribute(arguments[0], arguments[1]); | |
return this.getAttribute(arguments[0], arguments[1], arguments[2]) | |
}, | |
getFloat: function(attributeName) { | |
return this.getFloatAttribute(attributeName) | |
}, | |
getIntAttribute: function() { | |
if (arguments.length === 1) return this.getAttribute(arguments[0], 0); | |
if (arguments.length === 2) return this.getAttribute(arguments[0], arguments[1]); | |
return this.getAttribute(arguments[0], arguments[1], arguments[2]) | |
}, | |
getInt: function(attributeName) { | |
return this.getIntAttribute(attributeName) | |
}, | |
hasChildren: function() { | |
return this.children.length > 0 | |
}, | |
addChild: function(child) { | |
if (child !== null) { | |
child.parent = this; | |
this.children.push(child) | |
} | |
}, | |
insertChild: function(child, index) { | |
if (child) { | |
if (child.getLocalName() === null && !this.hasChildren()) { | |
var lastChild = this.children[this.children.length - 1]; | |
if (lastChild.getLocalName() === null) { | |
lastChild.setContent(lastChild.getContent() + child.getContent()); | |
return | |
} | |
} | |
child.parent = this; | |
this.children.splice(index, 0, child) | |
} | |
}, | |
getChild: function(selector) { | |
if (typeof selector === "number") return this.children[selector]; | |
if (selector.indexOf("/") !== -1) return this.getChildRecursive(selector.split("/"), 0); | |
var kid, kidName; | |
for (var i = 0, j = this.getChildCount(); i < j; i++) { | |
kid = this.getChild(i); | |
kidName = kid.getName(); | |
if (kidName !== null && kidName === selector) return kid | |
} | |
return null | |
}, | |
getChildren: function() { | |
if (arguments.length === 1) { | |
if (typeof arguments[0] === "number") return this.getChild(arguments[0]); | |
if (arguments[0].indexOf("/") !== -1) return this.getChildrenRecursive(arguments[0].split("/"), 0); | |
var matches = []; | |
var kid, kidName; | |
for (var i = 0, j = this.getChildCount(); i < j; i++) { | |
kid = this.getChild(i); | |
kidName = kid.getName(); | |
if (kidName !== null && kidName === arguments[0]) matches.push(kid) | |
} | |
return matches | |
} | |
return this.children | |
}, | |
getChildCount: function() { | |
return this.children.length | |
}, | |
getChildRecursive: function(items, offset) { | |
if (offset === items.length) return this; | |
var kid, kidName, matchName = items[offset]; | |
for (var i = 0, j = this.getChildCount(); i < j; i++) { | |
kid = this.getChild(i); | |
kidName = kid.getName(); | |
if (kidName !== null && kidName === matchName) return kid.getChildRecursive(items, offset + 1) | |
} | |
return null | |
}, | |
getChildrenRecursive: function(items, offset) { | |
if (offset === items.length - 1) return this.getChildren(items[offset]); | |
var matches = this.getChildren(items[offset]); | |
var kidMatches = []; | |
for (var i = 0; i < matches.length; i++) kidMatches = kidMatches.concat(matches[i].getChildrenRecursive(items, offset + 1)); | |
return kidMatches | |
}, | |
isLeaf: function() { | |
return !this.hasChildren() | |
}, | |
listChildren: function() { | |
var arr = []; | |
for (var i = 0, j = this.children.length; i < j; i++) arr.push(this.getChild(i).getName()); | |
return arr | |
}, | |
removeAttribute: function(name, namespace) { | |
this.namespace = namespace || ""; | |
for (var i = 0, j = this.attributes.length; i < j; i++) if (this.attributes[i].getName() === name && this.attributes[i].getNamespace() === this.namespace) { | |
this.attributes.splice(i, 1); | |
break | |
} | |
}, | |
removeChild: function(child) { | |
if (child) for (var i = 0, j = this.children.length; i < j; i++) if (this.children[i].equals(child)) { | |
this.children.splice(i, 1); | |
break | |
} | |
}, | |
removeChildAtIndex: function(index) { | |
if (this.children.length > index) this.children.splice(index, 1) | |
}, | |
findAttribute: function(name, namespace) { | |
this.namespace = namespace || ""; | |
for (var i = 0, j = this.attributes.length; i < j; i++) if (this.attributes[i].getName() === name && this.attributes[i].getNamespace() === this.namespace) return this.attributes[i]; | |
return null | |
}, | |
setAttribute: function() { | |
var attr; | |
if (arguments.length === 3) { | |
var index = arguments[0].indexOf(":"); | |
var name = arguments[0].substring(index + 1); | |
attr = this.findAttribute(name, arguments[1]); | |
if (attr) attr.setValue(arguments[2]); | |
else { | |
attr = new XMLAttribute(arguments[0], name, arguments[1], arguments[2], "CDATA"); | |
this.attributes.push(attr) | |
} | |
} else { | |
attr = this.findAttribute(arguments[0]); | |
if (attr) attr.setValue(arguments[1]); | |
else { | |
attr = new XMLAttribute(arguments[0], arguments[0], null, arguments[1], "CDATA"); | |
this.attributes.push(attr) | |
} | |
} | |
}, | |
setString: function(attribute, value) { | |
this.setAttribute(attribute, value) | |
}, | |
setInt: function(attribute, value) { | |
this.setAttribute(attribute, value) | |
}, | |
setFloat: function(attribute, value) { | |
this.setAttribute(attribute, value) | |
}, | |
setContent: function(content) { | |
if (this.children.length > 0) Processing.debug("Tried to set content for XMLElement with children"); | |
this.content = content | |
}, | |
setName: function() { | |
if (arguments.length === 1) { | |
this.name = arguments[0]; | |
this.fullName = arguments[0]; | |
this.namespace = null | |
} else { | |
var index = arguments[0].indexOf(":"); | |
if (arguments[1] === null || index < 0) this.name = arguments[0]; | |
else this.name = arguments[0].substring(index + 1); | |
this.fullName = arguments[0]; | |
this.namespace = arguments[1] | |
} | |
}, | |
getName: function() { | |
return this.fullName | |
}, | |
getLocalName: function() { | |
return this.name | |
}, | |
getAttributeCount: function() { | |
return this.attributes.length | |
}, | |
toString: function() { | |
if (this.type === "TEXT") return this.content; | |
if (this.type === "CDATA") return this.cdata; | |
var tagstring = this.fullName; | |
var xmlstring = "<" + tagstring; | |
var a, c; | |
for (a = 0; a < this.attributes.length; a++) { | |
var attr = this.attributes[a]; | |
xmlstring += " " + attr.getName() + "=" + '"' + attr.getValue() + '"' | |
} | |
if (this.children.length === 0) if (this.content === "") xmlstring += "/>"; | |
else xmlstring += ">" + this.content + "</" + tagstring + ">"; | |
else { | |
xmlstring += ">"; | |
for (c = 0; c < this.children.length; c++) xmlstring += this.children[c].toString(); | |
xmlstring += "</" + tagstring + ">" | |
} | |
return xmlstring | |
} | |
}; | |
XMLElement.parse = function(xmlstring) { | |
var element = new XMLElement; | |
element.parse(xmlstring); | |
return element | |
}; | |
var XML = p.XML = p.XMLElement; | |
p.loadXML = function(uri) { | |
return new XML(p, uri) | |
}; | |
var printMatrixHelper = function(elements) { | |
var big = 0; | |
for (var i = 0; i < elements.length; i++) if (i !== 0) big = Math.max(big, Math.abs(elements[i])); | |
else big = Math.abs(elements[i]); | |
var digits = (big + "").indexOf("."); | |
if (digits === 0) digits = 1; | |
else if (digits === -1) digits = (big + "").length; | |
return digits | |
}; | |
var PMatrix2D = p.PMatrix2D = function() { | |
if (arguments.length === 0) this.reset(); | |
else if (arguments.length === 1 && arguments[0] instanceof PMatrix2D) this.set(arguments[0].array()); | |
else if (arguments.length === 6) this.set(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5]) | |
}; | |
PMatrix2D.prototype = { | |
set: function() { | |
if (arguments.length === 6) { | |
var a = arguments; | |
this.set([a[0], a[1], a[2], a[3], a[4], a[5]]) | |
} else if (arguments.length === 1 && arguments[0] instanceof PMatrix2D) this.elements = arguments[0].array(); | |
else if (arguments.length === 1 && arguments[0] instanceof Array) this.elements = arguments[0].slice() | |
}, | |
get: function() { | |
var outgoing = new PMatrix2D; | |
outgoing.set(this.elements); | |
return outgoing | |
}, | |
reset: function() { | |
this.set([1, 0, 0, 0, 1, 0]) | |
}, | |
array: function array() { | |
return this.elements.slice() | |
}, | |
translate: function(tx, ty) { | |
this.elements[2] = tx * this.elements[0] + ty * this.elements[1] + this.elements[2]; | |
this.elements[5] = tx * this.elements[3] + ty * this.elements[4] + this.elements[5] | |
}, | |
invTranslate: function(tx, ty) { | |
this.translate(-tx, -ty) | |
}, | |
transpose: function() {}, | |
mult: function(source, target) { | |
var x, y; | |
if (source instanceof | |
PVector) { | |
x = source.x; | |
y = source.y; | |
if (!target) target = new PVector | |
} else if (source instanceof Array) { | |
x = source[0]; | |
y = source[1]; | |
if (!target) target = [] | |
} | |
if (target instanceof Array) { | |
target[0] = this.elements[0] * x + this.elements[1] * y + this.elements[2]; | |
target[1] = this.elements[3] * x + this.elements[4] * y + this.elements[5] | |
} else if (target instanceof PVector) { | |
target.x = this.elements[0] * x + this.elements[1] * y + this.elements[2]; | |
target.y = this.elements[3] * x + this.elements[4] * y + this.elements[5]; | |
target.z = 0 | |
} | |
return target | |
}, | |
multX: function(x, y) { | |
return x * this.elements[0] + y * this.elements[1] + this.elements[2] | |
}, | |
multY: function(x, y) { | |
return x * this.elements[3] + y * this.elements[4] + this.elements[5] | |
}, | |
skewX: function(angle) { | |
this.apply(1, 0, 1, angle, 0, 0) | |
}, | |
skewY: function(angle) { | |
this.apply(1, 0, 1, 0, angle, 0) | |
}, | |
shearX: function(angle) { | |
this.apply(1, 0, 1, Math.tan(angle), 0, 0) | |
}, | |
shearY: function(angle) { | |
this.apply(1, 0, 1, 0, Math.tan(angle), 0) | |
}, | |
determinant: function() { | |
return this.elements[0] * this.elements[4] - this.elements[1] * this.elements[3] | |
}, | |
invert: function() { | |
var d = this.determinant(); | |
if (Math.abs(d) > -2147483648) { | |
var old00 = this.elements[0]; | |
var old01 = this.elements[1]; | |
var old02 = this.elements[2]; | |
var old10 = this.elements[3]; | |
var old11 = this.elements[4]; | |
var old12 = this.elements[5]; | |
this.elements[0] = old11 / d; | |
this.elements[3] = -old10 / d; | |
this.elements[1] = -old01 / d; | |
this.elements[4] = old00 / d; | |
this.elements[2] = (old01 * old12 - old11 * old02) / d; | |
this.elements[5] = (old10 * old02 - old00 * old12) / d; | |
return true | |
} | |
return false | |
}, | |
scale: function(sx, sy) { | |
if (sx && !sy) sy = sx; | |
if (sx && sy) { | |
this.elements[0] *= sx; | |
this.elements[1] *= sy; | |
this.elements[3] *= sx; | |
this.elements[4] *= sy | |
} | |
}, | |
invScale: function(sx, sy) { | |
if (sx && !sy) sy = sx; | |
this.scale(1 / sx, 1 / sy) | |
}, | |
apply: function() { | |
var source; | |
if (arguments.length === 1 && arguments[0] instanceof PMatrix2D) source = arguments[0].array(); | |
else if (arguments.length === 6) source = Array.prototype.slice.call(arguments); | |
else if (arguments.length === 1 && arguments[0] instanceof Array) source = arguments[0]; | |
var result = [0, 0, this.elements[2], 0, 0, this.elements[5]]; | |
var e = 0; | |
for (var row = 0; row < 2; row++) for (var col = 0; col < 3; col++, e++) result[e] += this.elements[row * 3 + 0] * source[col + 0] + this.elements[row * 3 + 1] * source[col + 3]; | |
this.elements = result.slice() | |
}, | |
preApply: function() { | |
var source; | |
if (arguments.length === 1 && arguments[0] instanceof PMatrix2D) source = arguments[0].array(); | |
else if (arguments.length === 6) source = Array.prototype.slice.call(arguments); | |
else if (arguments.length === 1 && arguments[0] instanceof Array) source = arguments[0]; | |
var result = [0, 0, source[2], 0, 0, source[5]]; | |
result[2] = source[2] + this.elements[2] * source[0] + this.elements[5] * source[1]; | |
result[5] = source[5] + this.elements[2] * source[3] + this.elements[5] * source[4]; | |
result[0] = this.elements[0] * source[0] + this.elements[3] * source[1]; | |
result[3] = this.elements[0] * source[3] + this.elements[3] * source[4]; | |
result[1] = this.elements[1] * source[0] + this.elements[4] * source[1]; | |
result[4] = this.elements[1] * source[3] + this.elements[4] * source[4]; | |
this.elements = result.slice() | |
}, | |
rotate: function(angle) { | |
var c = Math.cos(angle); | |
var s = Math.sin(angle); | |
var temp1 = this.elements[0]; | |
var temp2 = this.elements[1]; | |
this.elements[0] = c * temp1 + s * temp2; | |
this.elements[1] = -s * temp1 + c * temp2; | |
temp1 = this.elements[3]; | |
temp2 = this.elements[4]; | |
this.elements[3] = c * temp1 + s * temp2; | |
this.elements[4] = -s * temp1 + c * temp2 | |
}, | |
rotateZ: function(angle) { | |
this.rotate(angle) | |
}, | |
invRotateZ: function(angle) { | |
this.rotateZ(angle - Math.PI) | |
}, | |
print: function() { | |
var digits = printMatrixHelper(this.elements); | |
var output = "" + p.nfs(this.elements[0], digits, 4) + " " + p.nfs(this.elements[1], digits, 4) + " " + p.nfs(this.elements[2], digits, 4) + "\n" + p.nfs(this.elements[3], digits, 4) + " " + p.nfs(this.elements[4], digits, 4) + " " + p.nfs(this.elements[5], digits, 4) + "\n\n"; | |
p.println(output) | |
} | |
}; | |
var PMatrix3D = p.PMatrix3D = function() { | |
this.reset() | |
}; | |
PMatrix3D.prototype = { | |
set: function() { | |
if (arguments.length === 16) this.elements = Array.prototype.slice.call(arguments); | |
else if (arguments.length === 1 && arguments[0] instanceof PMatrix3D) this.elements = arguments[0].array(); | |
else if (arguments.length === 1 && arguments[0] instanceof Array) this.elements = arguments[0].slice() | |
}, | |
get: function() { | |
var outgoing = new PMatrix3D; | |
outgoing.set(this.elements); | |
return outgoing | |
}, | |
reset: function() { | |
this.elements = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] | |
}, | |
array: function array() { | |
return this.elements.slice() | |
}, | |
translate: function(tx, ty, tz) { | |
if (tz === undef) tz = 0; | |
this.elements[3] += tx * this.elements[0] + ty * this.elements[1] + tz * this.elements[2]; | |
this.elements[7] += tx * this.elements[4] + ty * this.elements[5] + tz * this.elements[6]; | |
this.elements[11] += tx * this.elements[8] + ty * this.elements[9] + tz * this.elements[10]; | |
this.elements[15] += tx * this.elements[12] + ty * this.elements[13] + tz * this.elements[14] | |
}, | |
transpose: function() { | |
var temp = this.elements[4]; | |
this.elements[4] = this.elements[1]; | |
this.elements[1] = temp; | |
temp = this.elements[8]; | |
this.elements[8] = this.elements[2]; | |
this.elements[2] = temp; | |
temp = this.elements[6]; | |
this.elements[6] = this.elements[9]; | |
this.elements[9] = temp; | |
temp = this.elements[3]; | |
this.elements[3] = this.elements[12]; | |
this.elements[12] = temp; | |
temp = this.elements[7]; | |
this.elements[7] = this.elements[13]; | |
this.elements[13] = temp; | |
temp = this.elements[11]; | |
this.elements[11] = this.elements[14]; | |
this.elements[14] = temp | |
}, | |
mult: function(source, target) { | |
var x, y, z, w; | |
if (source instanceof | |
PVector) { | |
x = source.x; | |
y = source.y; | |
z = source.z; | |
w = 1; | |
if (!target) target = new PVector | |
} else if (source instanceof Array) { | |
x = source[0]; | |
y = source[1]; | |
z = source[2]; | |
w = source[3] || 1; | |
if (!target || target.length !== 3 && target.length !== 4) target = [0, 0, 0] | |
} | |
if (target instanceof Array) if (target.length === 3) { | |
target[0] = this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3]; | |
target[1] = this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7]; | |
target[2] = this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11] | |
} else if (target.length === 4) { | |
target[0] = this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3] * w; | |
target[1] = this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7] * w; | |
target[2] = this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11] * w; | |
target[3] = this.elements[12] * x + this.elements[13] * y + this.elements[14] * z + this.elements[15] * w | |
} | |
if (target instanceof PVector) { | |
target.x = this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3]; | |
target.y = this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7]; | |
target.z = this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11] | |
} | |
return target | |
}, | |
preApply: function() { | |
var source; | |
if (arguments.length === 1 && arguments[0] instanceof PMatrix3D) source = arguments[0].array(); | |
else if (arguments.length === 16) source = Array.prototype.slice.call(arguments); | |
else if (arguments.length === 1 && arguments[0] instanceof Array) source = arguments[0]; | |
var result = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; | |
var e = 0; | |
for (var row = 0; row < 4; row++) for (var col = 0; col < 4; col++, e++) result[e] += this.elements[col + 0] * source[row * 4 + 0] + this.elements[col + 4] * source[row * 4 + 1] + this.elements[col + 8] * source[row * 4 + 2] + this.elements[col + 12] * source[row * 4 + 3]; | |
this.elements = result.slice() | |
}, | |
apply: function() { | |
var source; | |
if (arguments.length === 1 && arguments[0] instanceof PMatrix3D) source = arguments[0].array(); | |
else if (arguments.length === 16) source = Array.prototype.slice.call(arguments); | |
else if (arguments.length === 1 && arguments[0] instanceof Array) source = arguments[0]; | |
var result = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; | |
var e = 0; | |
for (var row = 0; row < 4; row++) for (var col = 0; col < 4; col++, e++) result[e] += this.elements[row * 4 + 0] * source[col + 0] + this.elements[row * 4 + 1] * source[col + 4] + this.elements[row * 4 + 2] * source[col + 8] + this.elements[row * 4 + 3] * source[col + 12]; | |
this.elements = result.slice() | |
}, | |
rotate: function(angle, v0, v1, v2) { | |
if (!v1) this.rotateZ(angle); | |
else { | |
var c = p.cos(angle); | |
var s = p.sin(angle); | |
var t = 1 - c; | |
this.apply(t * v0 * v0 + c, t * v0 * v1 - s * v2, t * v0 * v2 + s * v1, 0, t * v0 * v1 + s * v2, t * v1 * v1 + c, t * v1 * v2 - s * v0, 0, t * v0 * v2 - s * v1, t * v1 * v2 + s * v0, t * v2 * v2 + c, 0, 0, 0, 0, 1) | |
} | |
}, | |
invApply: function() { | |
if (inverseCopy === undef) inverseCopy = new PMatrix3D; | |
var a = arguments; | |
inverseCopy.set(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15]); | |
if (!inverseCopy.invert()) return false; | |
this.preApply(inverseCopy); | |
return true | |
}, | |
rotateX: function(angle) { | |
var c = p.cos(angle); | |
var s = p.sin(angle); | |
this.apply([1, 0, 0, 0, 0, c, -s, 0, 0, s, c, 0, 0, 0, 0, 1]) | |
}, | |
rotateY: function(angle) { | |
var c = p.cos(angle); | |
var s = p.sin(angle); | |
this.apply([c, | |
0, s, 0, 0, 1, 0, 0, -s, 0, c, 0, 0, 0, 0, 1]) | |
}, | |
rotateZ: function(angle) { | |
var c = Math.cos(angle); | |
var s = Math.sin(angle); | |
this.apply([c, -s, 0, 0, s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]) | |
}, | |
scale: function(sx, sy, sz) { | |
if (sx && !sy && !sz) sy = sz = sx; | |
else if (sx && sy && !sz) sz = 1; | |
if (sx && sy && sz) { | |
this.elements[0] *= sx; | |
this.elements[1] *= sy; | |
this.elements[2] *= sz; | |
this.elements[4] *= sx; | |
this.elements[5] *= sy; | |
this.elements[6] *= sz; | |
this.elements[8] *= sx; | |
this.elements[9] *= sy; | |
this.elements[10] *= sz; | |
this.elements[12] *= sx; | |
this.elements[13] *= sy; | |
this.elements[14] *= sz | |
} | |
}, | |
skewX: function(angle) { | |
var t = Math.tan(angle); | |
this.apply(1, t, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1) | |
}, | |
skewY: function(angle) { | |
var t = Math.tan(angle); | |
this.apply(1, 0, 0, 0, t, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1) | |
}, | |
shearX: function(angle) { | |
var t = Math.tan(angle); | |
this.apply(1, t, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1) | |
}, | |
shearY: function(angle) { | |
var t = Math.tan(angle); | |
this.apply(1, 0, 0, 0, t, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1) | |
}, | |
multX: function(x, y, z, w) { | |
if (!z) return this.elements[0] * x + this.elements[1] * y + this.elements[3]; | |
if (!w) return this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3]; | |
return this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3] * w | |
}, | |
multY: function(x, y, z, w) { | |
if (!z) return this.elements[4] * x + this.elements[5] * y + this.elements[7]; | |
if (!w) return this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7]; | |
return this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7] * w | |
}, | |
multZ: function(x, y, z, w) { | |
if (!w) return this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11]; | |
return this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11] * w | |
}, | |
multW: function(x, y, z, w) { | |
if (!w) return this.elements[12] * x + this.elements[13] * y + this.elements[14] * z + this.elements[15]; | |
return this.elements[12] * x + this.elements[13] * y + this.elements[14] * z + this.elements[15] * w | |
}, | |
invert: function() { | |
var fA0 = this.elements[0] * this.elements[5] - this.elements[1] * this.elements[4]; | |
var fA1 = this.elements[0] * this.elements[6] - this.elements[2] * this.elements[4]; | |
var fA2 = this.elements[0] * this.elements[7] - this.elements[3] * this.elements[4]; | |
var fA3 = this.elements[1] * this.elements[6] - this.elements[2] * this.elements[5]; | |
var fA4 = this.elements[1] * this.elements[7] - this.elements[3] * this.elements[5]; | |
var fA5 = this.elements[2] * this.elements[7] - this.elements[3] * this.elements[6]; | |
var fB0 = this.elements[8] * this.elements[13] - this.elements[9] * this.elements[12]; | |
var fB1 = this.elements[8] * this.elements[14] - this.elements[10] * this.elements[12]; | |
var fB2 = this.elements[8] * this.elements[15] - this.elements[11] * this.elements[12]; | |
var fB3 = this.elements[9] * this.elements[14] - this.elements[10] * this.elements[13]; | |
var fB4 = this.elements[9] * this.elements[15] - this.elements[11] * this.elements[13]; | |
var fB5 = this.elements[10] * this.elements[15] - this.elements[11] * this.elements[14]; | |
var fDet = fA0 * fB5 - fA1 * fB4 + fA2 * fB3 + fA3 * fB2 - fA4 * fB1 + fA5 * fB0; | |
if (Math.abs(fDet) <= 1.0E-9) return false; | |
var kInv = []; | |
kInv[0] = +this.elements[5] * fB5 - this.elements[6] * fB4 + this.elements[7] * fB3; | |
kInv[4] = -this.elements[4] * fB5 + this.elements[6] * fB2 - this.elements[7] * fB1; | |
kInv[8] = +this.elements[4] * fB4 - this.elements[5] * fB2 + this.elements[7] * fB0; | |
kInv[12] = -this.elements[4] * fB3 + this.elements[5] * fB1 - this.elements[6] * fB0; | |
kInv[1] = -this.elements[1] * fB5 + this.elements[2] * fB4 - this.elements[3] * fB3; | |
kInv[5] = +this.elements[0] * fB5 - this.elements[2] * fB2 + this.elements[3] * fB1; | |
kInv[9] = -this.elements[0] * fB4 + this.elements[1] * fB2 - this.elements[3] * fB0; | |
kInv[13] = +this.elements[0] * fB3 - this.elements[1] * fB1 + this.elements[2] * fB0; | |
kInv[2] = +this.elements[13] * fA5 - this.elements[14] * fA4 + this.elements[15] * fA3; | |
kInv[6] = -this.elements[12] * fA5 + this.elements[14] * fA2 - this.elements[15] * fA1; | |
kInv[10] = +this.elements[12] * fA4 - this.elements[13] * fA2 + this.elements[15] * fA0; | |
kInv[14] = -this.elements[12] * fA3 + this.elements[13] * fA1 - this.elements[14] * fA0; | |
kInv[3] = -this.elements[9] * fA5 + this.elements[10] * fA4 - this.elements[11] * fA3; | |
kInv[7] = +this.elements[8] * fA5 - this.elements[10] * fA2 + this.elements[11] * fA1; | |
kInv[11] = -this.elements[8] * fA4 + this.elements[9] * fA2 - this.elements[11] * fA0; | |
kInv[15] = +this.elements[8] * fA3 - this.elements[9] * fA1 + this.elements[10] * fA0; | |
var fInvDet = 1 / fDet; | |
kInv[0] *= fInvDet; | |
kInv[1] *= fInvDet; | |
kInv[2] *= fInvDet; | |
kInv[3] *= fInvDet; | |
kInv[4] *= fInvDet; | |
kInv[5] *= fInvDet; | |
kInv[6] *= fInvDet; | |
kInv[7] *= fInvDet; | |
kInv[8] *= fInvDet; | |
kInv[9] *= fInvDet; | |
kInv[10] *= fInvDet; | |
kInv[11] *= fInvDet; | |
kInv[12] *= fInvDet; | |
kInv[13] *= fInvDet; | |
kInv[14] *= fInvDet; | |
kInv[15] *= fInvDet; | |
this.elements = kInv.slice(); | |
return true | |
}, | |
toString: function() { | |
var str = ""; | |
for (var i = 0; i < 15; i++) str += this.elements[i] + ", "; | |
str += this.elements[15]; | |
return str | |
}, | |
print: function() { | |
var digits = printMatrixHelper(this.elements); | |
var output = "" + p.nfs(this.elements[0], digits, 4) + " " + p.nfs(this.elements[1], digits, 4) + " " + p.nfs(this.elements[2], digits, 4) + " " + p.nfs(this.elements[3], digits, 4) + "\n" + p.nfs(this.elements[4], digits, 4) + " " + p.nfs(this.elements[5], digits, 4) + " " + p.nfs(this.elements[6], digits, 4) + " " + p.nfs(this.elements[7], digits, 4) + "\n" + p.nfs(this.elements[8], digits, 4) + " " + p.nfs(this.elements[9], digits, 4) + " " + p.nfs(this.elements[10], digits, 4) + " " + p.nfs(this.elements[11], digits, 4) + "\n" + p.nfs(this.elements[12], digits, 4) + " " + p.nfs(this.elements[13], digits, 4) + " " + p.nfs(this.elements[14], digits, 4) + " " + p.nfs(this.elements[15], digits, 4) + "\n\n"; | |
p.println(output) | |
}, | |
invTranslate: function(tx, ty, tz) { | |
this.preApply(1, 0, 0, -tx, 0, 1, 0, -ty, 0, 0, 1, -tz, 0, 0, 0, 1) | |
}, | |
invRotateX: function(angle) { | |
var c = Math.cos(-angle); | |
var s = Math.sin(-angle); | |
this.preApply([1, 0, 0, 0, 0, c, -s, 0, 0, s, c, 0, 0, 0, 0, 1]) | |
}, | |
invRotateY: function(angle) { | |
var c = Math.cos(-angle); | |
var s = Math.sin(-angle); | |
this.preApply([c, 0, s, 0, 0, 1, 0, 0, -s, 0, c, 0, 0, 0, 0, 1]) | |
}, | |
invRotateZ: function(angle) { | |
var c = Math.cos(-angle); | |
var s = Math.sin(-angle); | |
this.preApply([c, -s, 0, 0, s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]) | |
}, | |
invScale: function(x, y, z) { | |
this.preApply([1 / x, 0, 0, 0, 0, 1 / y, 0, 0, 0, 0, 1 / z, 0, 0, 0, 0, 1]) | |
} | |
}; | |
var PMatrixStack = p.PMatrixStack = function() { | |
this.matrixStack = [] | |
}; | |
PMatrixStack.prototype.load = function() { | |
var tmpMatrix = drawing.$newPMatrix(); | |
if (arguments.length === 1) tmpMatrix.set(arguments[0]); | |
else tmpMatrix.set(arguments); | |
this.matrixStack.push(tmpMatrix) | |
}; | |
Drawing2D.prototype.$newPMatrix = function() { | |
return new PMatrix2D | |
}; | |
Drawing3D.prototype.$newPMatrix = function() { | |
return new PMatrix3D | |
}; | |
PMatrixStack.prototype.push = function() { | |
this.matrixStack.push(this.peek()) | |
}; | |
PMatrixStack.prototype.pop = function() { | |
return this.matrixStack.pop() | |
}; | |
PMatrixStack.prototype.peek = function() { | |
var tmpMatrix = drawing.$newPMatrix(); | |
tmpMatrix.set(this.matrixStack[this.matrixStack.length - 1]); | |
return tmpMatrix | |
}; | |
PMatrixStack.prototype.mult = function(matrix) { | |
this.matrixStack[this.matrixStack.length - 1].apply(matrix) | |
}; | |
p.split = function(str, delim) { | |
return str.split(delim) | |
}; | |
p.splitTokens = function(str, tokens) { | |
if (tokens === undef) return str.split(/\s+/g); | |
var chars = tokens.split(/()/g), | |
buffer = "", | |
len = str.length, | |
i, c, tokenized = []; | |
for (i = 0; i < len; i++) { | |
c = str[i]; | |
if (chars.indexOf(c) > -1) { | |
if (buffer !== "") tokenized.push(buffer); | |
buffer = "" | |
} else buffer += c | |
} | |
if (buffer !== "") tokenized.push(buffer); | |
return tokenized | |
}; | |
p.append = function(array, element) { | |
array[array.length] = element; | |
return array | |
}; | |
p.concat = function(array1, array2) { | |
return array1.concat(array2) | |
}; | |
p.sort = function(array, numElem) { | |
var ret = []; | |
if (array.length > 0) { | |
var elemsToCopy = numElem > 0 ? numElem : array.length; | |
for (var i = 0; i < elemsToCopy; i++) ret.push(array[i]); | |
if (typeof array[0] === "string") ret.sort(); | |
else ret.sort(function(a, b) { | |
return a - b | |
}); | |
if (numElem > 0) for (var j = ret.length; j < array.length; j++) ret.push(array[j]) | |
} | |
return ret | |
}; | |
p.splice = function(array, value, index) { | |
if (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) { | |
var end = length !== undef ? offset + length : array.length; | |
return array.slice(offset, end) | |
}; | |
p.join = function(array, seperator) { | |
return array.join(seperator) | |
}; | |
p.shorten = function(ary) { | |
var newary = []; | |
var len = ary.length; | |
for (var i = 0; i < len; i++) newary[i] = ary[i]; | |
newary.pop(); | |
return newary | |
}; | |
p.expand = function(ary, targetSize) { | |
var temp = ary.slice(0), | |
newSize = targetSize || ary.length * 2; | |
temp.length = newSize; | |
return temp | |
}; | |
p.arrayCopy = function() { | |
var src, srcPos = 0, | |
dest, destPos = 0, | |
length; | |
if (arguments.length === 2) { | |
src = arguments[0]; | |
dest = arguments[1]; | |
length = src.length | |
} else if (arguments.length === 3) { | |
src = arguments[0]; | |
dest = arguments[1]; | |
length = arguments[2] | |
} else if (arguments.length === 5) { | |
src = arguments[0]; | |
srcPos = arguments[1]; | |
dest = arguments[2]; | |
destPos = arguments[3]; | |
length = arguments[4] | |
} | |
for (var i = srcPos, j = destPos; i < length + srcPos; i++, j++) if (dest[j] !== undef) dest[j] = src[i]; | |
else throw "array index out of bounds exception"; | |
}; | |
p.reverse = function(array) { | |
return array.reverse() | |
}; | |
p.mix = function(a, b, f) { | |
return a + ((b - a) * f >> 8) | |
}; | |
p.peg = function(n) { | |
return n < 0 ? 0 : n > 255 ? 255 : n | |
}; | |
p.modes = function() { | |
var ALPHA_MASK = 4278190080, | |
RED_MASK = 16711680, | |
GREEN_MASK = 65280, | |
BLUE_MASK = 255, | |
min = Math.min, | |
max = Math.max; | |
function applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb) { | |
var a = min(((c1 & 4278190080) >>> 24) + f, 255) << 24; | |
var r = ar + ((cr - ar) * f >> 8); | |
r = (r < 0 ? 0 : r > 255 ? 255 : r) << 16; | |
var g = ag + ((cg - ag) * f >> 8); | |
g = (g < 0 ? 0 : g > 255 ? 255 : g) << 8; | |
var b = ab + ((cb - ab) * f >> 8); | |
b = b < 0 ? 0 : b > 255 ? 255 : b; | |
return a | r | g | b | |
} | |
return { | |
replace: function(c1, c2) { | |
return c2 | |
}, | |
blend: function(c1, c2) { | |
var f = (c2 & ALPHA_MASK) >>> 24, | |
ar = c1 & RED_MASK, | |
ag = c1 & GREEN_MASK, | |
ab = c1 & BLUE_MASK, | |
br = c2 & RED_MASK, | |
bg = c2 & GREEN_MASK, | |
bb = c2 & BLUE_MASK; | |
return min(((c1 & ALPHA_MASK) >>> 24) + f, 255) << 24 | ar + ((br - ar) * f >> 8) & RED_MASK | ag + ((bg - ag) * f >> 8) & GREEN_MASK | ab + ((bb - ab) * f >> 8) & BLUE_MASK | |
}, | |
add: function(c1, c2) { | |
var f = (c2 & ALPHA_MASK) >>> 24; | |
return min(((c1 & ALPHA_MASK) >>> 24) + f, 255) << 24 | min((c1 & RED_MASK) + ((c2 & RED_MASK) >> 8) * f, RED_MASK) & RED_MASK | min((c1 & GREEN_MASK) + ((c2 & GREEN_MASK) >> 8) * f, GREEN_MASK) & GREEN_MASK | min((c1 & BLUE_MASK) + ((c2 & BLUE_MASK) * f >> 8), BLUE_MASK) | |
}, | |
subtract: function(c1, c2) { | |
var f = (c2 & ALPHA_MASK) >>> 24; | |
return min(((c1 & ALPHA_MASK) >>> 24) + f, 255) << 24 | max((c1 & RED_MASK) - ((c2 & RED_MASK) >> 8) * f, GREEN_MASK) & RED_MASK | max((c1 & GREEN_MASK) - ((c2 & GREEN_MASK) >> 8) * f, BLUE_MASK) & GREEN_MASK | max((c1 & BLUE_MASK) - ((c2 & BLUE_MASK) * f >> 8), 0) | |
}, | |
lightest: function(c1, c2) { | |
var f = (c2 & ALPHA_MASK) >>> 24; | |
return min(((c1 & ALPHA_MASK) >>> 24) + f, 255) << 24 | max(c1 & RED_MASK, ((c2 & RED_MASK) >> 8) * f) & RED_MASK | max(c1 & GREEN_MASK, ((c2 & GREEN_MASK) >> 8) * f) & GREEN_MASK | max(c1 & BLUE_MASK, (c2 & BLUE_MASK) * f >> 8) | |
}, | |
darkest: function(c1, c2) { | |
var f = (c2 & ALPHA_MASK) >>> 24, | |
ar = c1 & RED_MASK, | |
ag = c1 & GREEN_MASK, | |
ab = c1 & BLUE_MASK, | |
br = min(c1 & RED_MASK, ((c2 & RED_MASK) >> 8) * f), | |
bg = min(c1 & GREEN_MASK, ((c2 & GREEN_MASK) >> 8) * f), | |
bb = min(c1 & BLUE_MASK, (c2 & BLUE_MASK) * f >> 8); | |
return min(((c1 & ALPHA_MASK) >>> 24) + f, 255) << 24 | ar + ((br - ar) * f >> 8) & RED_MASK | ag + ((bg - ag) * f >> 8) & GREEN_MASK | ab + ((bb - ab) * f >> 8) & BLUE_MASK | |
}, | |
difference: function(c1, c2) { | |
var f = (c2 & ALPHA_MASK) >>> 24, | |
ar = (c1 & RED_MASK) >> 16, | |
ag = (c1 & GREEN_MASK) >> 8, | |
ab = c1 & BLUE_MASK, | |
br = (c2 & RED_MASK) >> 16, | |
bg = (c2 & GREEN_MASK) >> 8, | |
bb = c2 & BLUE_MASK, | |
cr = ar > br ? ar - br : br - ar, | |
cg = ag > bg ? ag - bg : bg - ag, | |
cb = ab > bb ? ab - bb : bb - ab; | |
return applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb) | |
}, | |
exclusion: function(c1, c2) { | |
var f = (c2 & ALPHA_MASK) >>> 24, | |
ar = (c1 & RED_MASK) >> 16, | |
ag = (c1 & GREEN_MASK) >> 8, | |
ab = c1 & BLUE_MASK, | |
br = (c2 & RED_MASK) >> 16, | |
bg = (c2 & GREEN_MASK) >> 8, | |
bb = c2 & BLUE_MASK, | |
cr = ar + br - (ar * br >> 7), | |
cg = ag + bg - (ag * bg >> 7), | |
cb = ab + bb - (ab * bb >> 7); | |
return applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb) | |
}, | |
multiply: function(c1, c2) { | |
var f = (c2 & ALPHA_MASK) >>> 24, | |
ar = (c1 & RED_MASK) >> 16, | |
ag = (c1 & GREEN_MASK) >> 8, | |
ab = c1 & BLUE_MASK, | |
br = (c2 & RED_MASK) >> 16, | |
bg = (c2 & GREEN_MASK) >> 8, | |
bb = c2 & BLUE_MASK, | |
cr = ar * br >> 8, | |
cg = ag * bg >> 8, | |
cb = ab * bb >> 8; | |
return applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb) | |
}, | |
screen: function(c1, c2) { | |
var f = (c2 & ALPHA_MASK) >>> 24, | |
ar = (c1 & RED_MASK) >> 16, | |
ag = (c1 & GREEN_MASK) >> 8, | |
ab = c1 & BLUE_MASK, | |
br = (c2 & RED_MASK) >> 16, | |
bg = (c2 & GREEN_MASK) >> 8, | |
bb = c2 & BLUE_MASK, | |
cr = 255 - ((255 - ar) * (255 - br) >> 8), | |
cg = 255 - ((255 - ag) * (255 - bg) >> 8), | |
cb = 255 - ((255 - ab) * (255 - bb) >> 8); | |
return applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb) | |
}, | |
hard_light: function(c1, c2) { | |
var f = (c2 & ALPHA_MASK) >>> 24, | |
ar = (c1 & RED_MASK) >> 16, | |
ag = (c1 & GREEN_MASK) >> 8, | |
ab = c1 & BLUE_MASK, | |
br = (c2 & RED_MASK) >> 16, | |
bg = (c2 & GREEN_MASK) >> 8, | |
bb = c2 & BLUE_MASK, | |
cr = br < 128 ? ar * br >> 7 : 255 - ((255 - ar) * (255 - br) >> 7), | |
cg = bg < 128 ? ag * bg >> 7 : 255 - ((255 - ag) * (255 - bg) >> 7), | |
cb = bb < 128 ? ab * bb >> 7 : 255 - ((255 - ab) * (255 - bb) >> 7); | |
return applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb) | |
}, | |
soft_light: function(c1, c2) { | |
var f = (c2 & ALPHA_MASK) >>> 24, | |
ar = (c1 & RED_MASK) >> 16, | |
ag = (c1 & GREEN_MASK) >> 8, | |
ab = c1 & BLUE_MASK, | |
br = (c2 & RED_MASK) >> 16, | |
bg = (c2 & GREEN_MASK) >> 8, | |
bb = c2 & BLUE_MASK, | |
cr = (ar * br >> 7) + (ar * ar >> 8) - (ar * ar * br >> 15), | |
cg = (ag * bg >> 7) + (ag * ag >> 8) - (ag * ag * bg >> 15), | |
cb = (ab * bb >> 7) + (ab * ab >> 8) - (ab * ab * bb >> 15); | |
return applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb) | |
}, | |
overlay: function(c1, c2) { | |
var f = (c2 & ALPHA_MASK) >>> 24, | |
ar = (c1 & RED_MASK) >> 16, | |
ag = (c1 & GREEN_MASK) >> 8, | |
ab = c1 & BLUE_MASK, | |
br = (c2 & RED_MASK) >> 16, | |
bg = (c2 & GREEN_MASK) >> 8, | |
bb = c2 & BLUE_MASK, | |
cr = ar < 128 ? ar * br >> 7 : 255 - ((255 - ar) * (255 - br) >> 7), | |
cg = ag < 128 ? ag * bg >> 7 : 255 - ((255 - ag) * (255 - bg) >> 7), | |
cb = ab < 128 ? ab * bb >> 7 : 255 - ((255 - ab) * (255 - bb) >> 7); | |
return applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb) | |
}, | |
dodge: function(c1, c2) { | |
var f = (c2 & ALPHA_MASK) >>> 24, | |
ar = (c1 & RED_MASK) >> 16, | |
ag = (c1 & GREEN_MASK) >> 8, | |
ab = c1 & BLUE_MASK, | |
br = (c2 & RED_MASK) >> 16, | |
bg = (c2 & GREEN_MASK) >> 8, | |
bb = c2 & BLUE_MASK; | |
var cr = 255; | |
if (br !== 255) { | |
cr = (ar << 8) / (255 - br); | |
cr = cr < 0 ? 0 : cr > 255 ? 255 : cr | |
} | |
var cg = 255; | |
if (bg !== 255) { | |
cg = (ag << 8) / (255 - bg); | |
cg = cg < 0 ? 0 : cg > 255 ? 255 : cg | |
} | |
var cb = 255; | |
if (bb !== 255) { | |
cb = (ab << 8) / (255 - bb); | |
cb = cb < 0 ? 0 : cb > 255 ? 255 : cb | |
} | |
return applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb) | |
}, | |
burn: function(c1, c2) { | |
var f = (c2 & ALPHA_MASK) >>> 24, | |
ar = (c1 & RED_MASK) >> 16, | |
ag = (c1 & GREEN_MASK) >> 8, | |
ab = c1 & BLUE_MASK, | |
br = (c2 & RED_MASK) >> 16, | |
bg = (c2 & GREEN_MASK) >> 8, | |
bb = c2 & BLUE_MASK; | |
var cr = 0; | |
if (br !== 0) { | |
cr = (255 - ar << 8) / br; | |
cr = 255 - (cr < 0 ? 0 : cr > 255 ? 255 : cr) | |
} | |
var cg = 0; | |
if (bg !== 0) { | |
cg = (255 - ag << 8) / bg; | |
cg = 255 - (cg < 0 ? 0 : cg > 255 ? 255 : cg) | |
} | |
var cb = 0; | |
if (bb !== 0) { | |
cb = (255 - ab << 8) / bb; | |
cb = 255 - (cb < 0 ? 0 : cb > 255 ? 255 : cb) | |
} | |
return applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb) | |
} | |
} | |
}(); | |
function color$4(aValue1, aValue2, aValue3, aValue4) { | |
var r, g, b, a; | |
if (curColorMode === 3) { | |
var rgb = p.color.toRGB(aValue1, aValue2, aValue3); | |
r = rgb[0]; | |
g = rgb[1]; | |
b = rgb[2] | |
} else { | |
r = Math.round(255 * (aValue1 / colorModeX)); | |
g = Math.round(255 * (aValue2 / colorModeY)); | |
b = Math.round(255 * (aValue3 / colorModeZ)) | |
} | |
a = Math.round(255 * (aValue4 / colorModeA)); | |
r = r < 0 ? 0 : r; | |
g = g < 0 ? 0 : g; | |
b = b < 0 ? 0 : b; | |
a = a < 0 ? 0 : a; | |
r = r > 255 ? 255 : r; | |
g = g > 255 ? 255 : g; | |
b = b > 255 ? 255 : b; | |
a = a > 255 ? 255 : a; | |
return a << 24 & 4278190080 | r << 16 & 16711680 | g << 8 & 65280 | b & 255 | |
} | |
function color$2(aValue1, aValue2) { | |
var a; | |
if (aValue1 & 4278190080) { | |
a = Math.round(255 * (aValue2 / colorModeA)); | |
a = a > 255 ? 255 : a; | |
a = a < 0 ? 0 : a; | |
return aValue1 - (aValue1 & 4278190080) + (a << 24 & 4278190080) | |
} | |
if (curColorMode === 1) return color$4(aValue1, aValue1, aValue1, aValue2); | |
if (curColorMode === 3) return color$4(0, 0, aValue1 / colorModeX * colorModeZ, aValue2) | |
} | |
function color$1(aValue1) { | |
if (aValue1 <= colorModeX && aValue1 >= 0) { | |
if (curColorMode === 1) return color$4(aValue1, aValue1, aValue1, colorModeA); | |
if (curColorMode === 3) return color$4(0, 0, aValue1 / colorModeX * colorModeZ, colorModeA) | |
} | |
if (aValue1) { | |
if (aValue1 > 2147483647) aValue1 -= 4294967296; | |
return aValue1 | |
} | |
} | |
p.color = function(aValue1, aValue2, aValue3, aValue4) { | |
if (aValue1 !== undef && aValue2 !== undef && aValue3 !== undef && aValue4 !== undef) return color$4(aValue1, aValue2, aValue3, aValue4); | |
if (aValue1 !== undef && aValue2 !== undef && aValue3 !== undef) return color$4(aValue1, aValue2, aValue3, colorModeA); | |
if (aValue1 !== undef && aValue2 !== undef) return color$2(aValue1, aValue2); | |
if (typeof aValue1 === "number") return color$1(aValue1); | |
return color$4(colorModeX, colorModeY, colorModeZ, colorModeA) | |
}; | |
p.color.toString = function(colorInt) { | |
return "rgba(" + ((colorInt >> 16) & 255) + "," + ((colorInt >> 8) & 255) + "," + (colorInt & 255) + "," + ((colorInt >> 24) & 255) / 255 + ")" | |
}; | |
p.color.toInt = function(r, g, b, a) { | |
return a << 24 & 4278190080 | r << 16 & 16711680 | g << 8 & 65280 | b & 255 | |
}; | |
p.color.toArray = function(colorInt) { | |
return [(colorInt >> 16) & 255, (colorInt >> 8) & 255, colorInt & 255, (colorInt >> 24) & 255] | |
}; | |
p.color.toGLArray = function(colorInt) { | |
return [((colorInt & 16711680) >>> 16) / 255, ((colorInt >> 8) & 255) / 255, (colorInt & 255) / 255, ((colorInt >> 24) & 255) / 255] | |
}; | |
p.color.toRGB = function(h, s, b) { | |
h = h > colorModeX ? colorModeX : h; | |
s = s > colorModeY ? colorModeY : s; | |
b = b > colorModeZ ? colorModeZ : b; | |
h = h / colorModeX * 360; | |
s = s / colorModeY * 100; | |
b = b / colorModeZ * 100; | |
var br = Math.round(b / 100 * 255); | |
if (s === 0) return [br, br, br]; | |
var hue = h % 360; | |
var f = hue % 60; | |
var p = Math.round(b * (100 - s) / 1E4 * 255); | |
var q = Math.round(b * (6E3 - s * f) / 6E5 * 255); | |
var t = Math.round(b * (6E3 - s * (60 - f)) / 6E5 * 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 colorToHSB(colorInt) { | |
var red, green, blue; | |
red = ((colorInt >> 16) & 255) / 255; | |
green = ((colorInt >> 8) & 255) / 255; | |
blue = (colorInt & 255) / 255; | |
var max = p.max(p.max(red, green), blue), | |
min = p.min(p.min(red, green), blue), | |
hue, saturation; | |
if (min === max) return [0, 0, max * colorModeZ]; | |
saturation = (max - min) / max; | |
if (red === max) hue = (green - blue) / (max - min); | |
else if (green === max) hue = 2 + (blue - red) / (max - min); | |
else hue = 4 + (red - green) / (max - min); | |
hue /= 6; | |
if (hue < 0) hue += 1; | |
else if (hue > 1) hue -= 1; | |
return [hue * colorModeX, saturation * colorModeY, max * colorModeZ] | |
} | |
p.brightness = function(colInt) { | |
return colorToHSB(colInt)[2] | |
}; | |
p.saturation = function(colInt) { | |
return colorToHSB(colInt)[1] | |
}; | |
p.hue = function(colInt) { | |
return colorToHSB(colInt)[0] | |
}; | |
p.red = function(aColor) { | |
return ((aColor >> 16) & 255) / 255 * colorModeX | |
}; | |
p.green = function(aColor) { | |
return ((aColor & 65280) >>> 8) / 255 * colorModeY | |
}; | |
p.blue = function(aColor) { | |
return (aColor & 255) / 255 * colorModeZ | |
}; | |
p.alpha = function(aColor) { | |
return ((aColor >> 24) & 255) / 255 * colorModeA | |
}; | |
p.lerpColor = function(c1, c2, amt) { | |
var r, g, b, a, r1, g1, b1, a1, r2, g2, b2, a2; | |
var hsb1, hsb2, rgb, h, s; | |
var colorBits1 = p.color(c1); | |
var colorBits2 = p.color(c2); | |
if (curColorMode === 3) { | |
hsb1 = colorToHSB(colorBits1); | |
a1 = ((colorBits1 >> 24) & 255) / colorModeA; | |
hsb2 = colorToHSB(colorBits2); | |
a2 = ((colorBits2 & 4278190080) >>> 24) / colorModeA; | |
h = p.lerp(hsb1[0], hsb2[0], amt); | |
s = p.lerp(hsb1[1], hsb2[1], amt); | |
b = p.lerp(hsb1[2], hsb2[2], amt); | |
rgb = p.color.toRGB(h, s, b); | |
a = p.lerp(a1, a2, amt) * colorModeA; | |
return a << 24 & 4278190080 | (rgb[0] & 255) << 16 | (rgb[1] & 255) << 8 | rgb[2] & 255 | |
} | |
r1 = (colorBits1 >> 16) & 255; | |
g1 = (colorBits1 >> 8) & 255; | |
b1 = colorBits1 & 255; | |
a1 = ((colorBits1 >> 24) & 255) / colorModeA; | |
r2 = (colorBits2 & 16711680) >>> 16; | |
g2 = (colorBits2 >> 8) & 255; | |
b2 = colorBits2 & 255; | |
a2 = ((colorBits2 >> 24) & 255) / colorModeA; | |
r = p.lerp(r1, r2, amt) | 0; | |
g = p.lerp(g1, g2, amt) | 0; | |
b = p.lerp(b1, b2, amt) | 0; | |
a = p.lerp(a1, a2, amt) * colorModeA; | |
return a << 24 & 4278190080 | r << 16 & 16711680 | g << 8 & 65280 | b & 255 | |
}; | |
p.colorMode = function() { | |
curColorMode = arguments[0]; | |
if (arguments.length > 1) { | |
colorModeX = arguments[1]; | |
colorModeY = arguments[2] || arguments[1]; | |
colorModeZ = arguments[3] || arguments[1]; | |
colorModeA = arguments[4] || arguments[1] | |
} | |
}; | |
p.blendColor = function(c1, c2, mode) { | |
if (mode === 0) return p.modes.replace(c1, c2); | |
else if (mode === 1) return p.modes.blend(c1, c2); | |
else if (mode === 2) return p.modes.add(c1, c2); | |
else if (mode === 4) return p.modes.subtract(c1, c2); | |
else if (mode === 8) return p.modes.lightest(c1, c2); | |
else if (mode === 16) return p.modes.darkest(c1, c2); | |
else if (mode === 32) return p.modes.difference(c1, c2); | |
else if (mode === 64) return p.modes.exclusion(c1, c2); | |
else if (mode === 128) return p.modes.multiply(c1, c2); | |
else if (mode === 256) return p.modes.screen(c1, c2); | |
else if (mode === 1024) return p.modes.hard_light(c1, c2); | |
else if (mode === 2048) return p.modes.soft_light(c1, c2); | |
else if (mode === 512) return p.modes.overlay(c1, c2); | |
else if (mode === 4096) return p.modes.dodge(c1, c2); | |
else if (mode === 8192) return p.modes.burn(c1, c2) | |
}; | |
function saveContext() { | |
curContext.save() | |
} | |
function restoreContext() { | |
curContext.restore(); | |
isStrokeDirty = true; | |
isFillDirty = true | |
} | |
p.printMatrix = function() { | |
modelView.print() | |
}; | |
Drawing2D.prototype.translate = function(x, y) { | |
modelView.translate(x, y); | |
modelViewInv.invTranslate(x, y); | |
curContext.translate(x, y) | |
}; | |
Drawing3D.prototype.translate = function(x, y, z) { | |
modelView.translate(x, y, z); | |
modelViewInv.invTranslate(x, y, z) | |
}; | |
Drawing2D.prototype.scale = function(x, y) { | |
modelView.scale(x, y); | |
modelViewInv.invScale(x, y); | |
curContext.scale(x, y || x) | |
}; | |
Drawing3D.prototype.scale = function(x, y, z) { | |
modelView.scale(x, y, z); | |
modelViewInv.invScale(x, y, z) | |
}; | |
Drawing2D.prototype.transform = function(pmatrix) { | |
var e = pmatrix.array(); | |
curContext.transform(e[0], e[3], e[1], e[4], e[2], e[5]) | |
}; | |
Drawing3D.prototype.transformm = function(pmatrix3d) { | |
throw "p.transform is currently not supported in 3D mode"; | |
}; | |
Drawing2D.prototype.pushMatrix = function() { | |
userMatrixStack.load(modelView); | |
userReverseMatrixStack.load(modelViewInv); | |
saveContext() | |
}; | |
Drawing3D.prototype.pushMatrix = function() { | |
userMatrixStack.load(modelView); | |
userReverseMatrixStack.load(modelViewInv) | |
}; | |
Drawing2D.prototype.popMatrix = function() { | |
modelView.set(userMatrixStack.pop()); | |
modelViewInv.set(userReverseMatrixStack.pop()); | |
restoreContext() | |
}; | |
Drawing3D.prototype.popMatrix = function() { | |
modelView.set(userMatrixStack.pop()); | |
modelViewInv.set(userReverseMatrixStack.pop()) | |
}; | |
Drawing2D.prototype.resetMatrix = function() { | |
modelView.reset(); | |
modelViewInv.reset(); | |
curContext.setTransform(1, 0, 0, 1, 0, 0) | |
}; | |
Drawing3D.prototype.resetMatrix = function() { | |
modelView.reset(); | |
modelViewInv.reset() | |
}; | |
DrawingShared.prototype.applyMatrix = function() { | |
var a = arguments; | |
modelView.apply(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15]); | |
modelViewInv.invApply(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15]) | |
}; | |
Drawing2D.prototype.applyMatrix = function() { | |
var a = arguments; | |
for (var cnt = a.length; cnt < 16; cnt++) a[cnt] = 0; | |
a[10] = a[15] = 1; | |
DrawingShared.prototype.applyMatrix.apply(this, a) | |
}; | |
p.rotateX = function(angleInRadians) { | |
modelView.rotateX(angleInRadians); | |
modelViewInv.invRotateX(angleInRadians) | |
}; | |
Drawing2D.prototype.rotateZ = function() { | |
throw "rotateZ() is not supported in 2D mode. Use rotate(float) instead."; | |
}; | |
Drawing3D.prototype.rotateZ = function(angleInRadians) { | |
modelView.rotateZ(angleInRadians); | |
modelViewInv.invRotateZ(angleInRadians) | |
}; | |
p.rotateY = function(angleInRadians) { | |
modelView.rotateY(angleInRadians); | |
modelViewInv.invRotateY(angleInRadians) | |
}; | |
Drawing2D.prototype.rotate = function(angleInRadians) { | |
modelView.rotateZ(angleInRadians); | |
modelViewInv.invRotateZ(angleInRadians); | |
curContext.rotate(angleInRadians) | |
}; | |
Drawing3D.prototype.rotate = function(angleInRadians) { | |
p.rotateZ(angleInRadians) | |
}; | |
Drawing2D.prototype.shearX = function(angleInRadians) { | |
modelView.shearX(angleInRadians); | |
curContext.transform(1, 0, angleInRadians, 1, 0, 0) | |
}; | |
Drawing3D.prototype.shearX = function(angleInRadians) { | |
modelView.shearX(angleInRadians) | |
}; | |
Drawing2D.prototype.shearY = function(angleInRadians) { | |
modelView.shearY(angleInRadians); | |
curContext.transform(1, angleInRadians, 0, 1, 0, 0) | |
}; | |
Drawing3D.prototype.shearY = function(angleInRadians) { | |
modelView.shearY(angleInRadians) | |
}; | |
p.pushStyle = function() { | |
saveContext(); | |
p.pushMatrix(); | |
var newState = { | |
"doFill": doFill, | |
"currentFillColor": currentFillColor, | |
"doStroke": doStroke, | |
"currentStrokeColor": currentStrokeColor, | |
"curTint": curTint, | |
"curRectMode": curRectMode, | |
"curColorMode": curColorMode, | |
"colorModeX": colorModeX, | |
"colorModeZ": colorModeZ, | |
"colorModeY": colorModeY, | |
"colorModeA": colorModeA, | |
"curTextFont": curTextFont, | |
"horizontalTextAlignment": horizontalTextAlignment, | |
"verticalTextAlignment": verticalTextAlignment, | |
"textMode": textMode, | |
"curFontName": curFontName, | |
"curTextSize": curTextSize, | |
"curTextAscent": curTextAscent, | |
"curTextDescent": curTextDescent, | |
"curTextLeading": curTextLeading | |
}; | |
styleArray.push(newState) | |
}; | |
p.popStyle = function() { | |
var oldState = styleArray.pop(); | |
if (oldState) { | |
restoreContext(); | |
p.popMatrix(); | |
doFill = oldState.doFill; | |
currentFillColor = oldState.currentFillColor; | |
doStroke = oldState.doStroke; | |
currentStrokeColor = oldState.currentStrokeColor; | |
curTint = oldState.curTint; | |
curRectMode = oldState.curRectMode; | |
curColorMode = oldState.curColorMode; | |
colorModeX = oldState.colorModeX; | |
colorModeZ = oldState.colorModeZ; | |
colorModeY = oldState.colorModeY; | |
colorModeA = oldState.colorModeA; | |
curTextFont = oldState.curTextFont; | |
curFontName = oldState.curFontName; | |
curTextSize = oldState.curTextSize; | |
horizontalTextAlignment = oldState.horizontalTextAlignment; | |
verticalTextAlignment = oldState.verticalTextAlignment; | |
textMode = oldState.textMode; | |
curTextAscent = oldState.curTextAscent; | |
curTextDescent = oldState.curTextDescent; | |
curTextLeading = oldState.curTextLeading | |
} else throw "Too many popStyle() without enough pushStyle()"; | |
}; | |
p.year = function() { | |
return (new Date).getFullYear() | |
}; | |
p.month = function() { | |
return (new Date).getMonth() + 1 | |
}; | |
p.day = function() { | |
return (new Date).getDate() | |
}; | |
p.hour = function() { | |
return (new Date).getHours() | |
}; | |
p.minute = function() { | |
return (new Date).getMinutes() | |
}; | |
p.second = function() { | |
return (new Date).getSeconds() | |
}; | |
p.millis = function() { | |
return Date.now() - start | |
}; | |
function redrawHelper() { | |
var sec = (Date.now() - timeSinceLastFPS) / 1E3; | |
framesSinceLastFPS++; | |
var fps = framesSinceLastFPS / sec; | |
if (sec > 0.5) { | |
timeSinceLastFPS = Date.now(); | |
framesSinceLastFPS = 0; | |
p.__frameRate = fps | |
} | |
p.frameCount++ | |
} | |
Drawing2D.prototype.redraw = function() { | |
redrawHelper(); | |
curContext.lineWidth = lineWidth; | |
var pmouseXLastEvent = p.pmouseX, | |
pmouseYLastEvent = p.pmouseY; | |
p.pmouseX = pmouseXLastFrame; | |
p.pmouseY = pmouseYLastFrame; | |
saveContext(); | |
p.draw(); | |
restoreContext(); | |
pmouseXLastFrame = p.mouseX; | |
pmouseYLastFrame = p.mouseY; | |
p.pmouseX = pmouseXLastEvent; | |
p.pmouseY = pmouseYLastEvent | |
}; | |
Drawing3D.prototype.redraw = function() { | |
redrawHelper(); | |
var pmouseXLastEvent = p.pmouseX, | |
pmouseYLastEvent = p.pmouseY; | |
p.pmouseX = pmouseXLastFrame; | |
p.pmouseY = pmouseYLastFrame; | |
curContext.clear(curContext.DEPTH_BUFFER_BIT); | |
curContextCache = { | |
attributes: {}, | |
locations: {} | |
}; | |
p.noLights(); | |
p.lightFalloff(1, 0, 0); | |
p.shininess(1); | |
p.ambient(255, 255, 255); | |
p.specular(0, 0, 0); | |
p.emissive(0, 0, 0); | |
p.camera(); | |
p.draw(); | |
pmouseXLastFrame = p.mouseX; | |
pmouseYLastFrame = p.mouseY; | |
p.pmouseX = pmouseXLastEvent; | |
p.pmouseY = pmouseYLastEvent | |
}; | |
p.noLoop = function() { | |
doLoop = false; | |
loopStarted = false; | |
clearInterval(looping); | |
curSketch.onPause() | |
}; | |
p.loop = function() { | |
if (loopStarted) return; | |
timeSinceLastFPS = Date.now(); | |
framesSinceLastFPS = 0; | |
looping = window.setInterval(function() { | |
try { | |
curSketch.onFrameStart(); | |
p.redraw(); | |
curSketch.onFrameEnd() | |
} catch(e_loop) { | |
window.clearInterval(looping); | |
throw e_loop; | |
} | |
}, | |
curMsPerFrame); | |
doLoop = true; | |
loopStarted = true; | |
curSketch.onLoop() | |
}; | |
p.frameRate = function(aRate) { | |
curFrameRate = aRate; | |
curMsPerFrame = 1E3 / curFrameRate; | |
if (doLoop) { | |
p.noLoop(); | |
p.loop() | |
} | |
}; | |
var eventHandlers = []; | |
function attachEventHandler(elem, type, fn) { | |
if (elem.addEventListener) elem.addEventListener(type, fn, false); | |
else elem.attachEvent("on" + type, fn); | |
eventHandlers.push({ | |
elem: elem, | |
type: type, | |
fn: fn | |
}) | |
} | |
function detachEventHandler(eventHandler) { | |
var elem = eventHandler.elem, | |
type = eventHandler.type, | |
fn = eventHandler.fn; | |
if (elem.removeEventListener) elem.removeEventListener(type, fn, false); | |
else if (elem.detachEvent) elem.detachEvent("on" + type, fn) | |
} | |
p.exit = function() { | |
window.clearInterval(looping); | |
removeInstance(p.externals.canvas.id); | |
delete curElement.onmousedown; | |
for (var lib in Processing.lib) if (Processing.lib.hasOwnProperty(lib)) if (Processing.lib[lib].hasOwnProperty("detach")) Processing.lib[lib].detach(p); | |
var i = eventHandlers.length; | |
while (i--) detachEventHandler(eventHandlers[i]); | |
curSketch.onExit() | |
}; | |
p.cursor = function() { | |
if (arguments.length > 1 || arguments.length === 1 && arguments[0] instanceof p.PImage) { | |
var image = arguments[0], | |
x, y; | |
if (arguments.length >= 3) { | |
x = arguments[1]; | |
y = arguments[2]; | |
if (x < 0 || y < 0 || y >= image.height || x >= image.width) throw "x and y must be non-negative and less than the dimensions of the image"; | |
} else { | |
x = image.width >>> 1; | |
y = image.height >>> 1 | |
} | |
var imageDataURL = image.toDataURL(); | |
var style = 'url("' + imageDataURL + '") ' + x + " " + y + ", default"; | |
curCursor = curElement.style.cursor = style | |
} else if (arguments.length === 1) { | |
var mode = arguments[0]; | |
curCursor = curElement.style.cursor = mode | |
} else curCursor = curElement.style.cursor = oldCursor | |
}; | |
p.noCursor = function() { | |
curCursor = curElement.style.cursor = PConstants.NOCURSOR | |
}; | |
p.link = function(href, target) { | |
if (target !== undef) window.open(href, target); | |
else window.location = href | |
}; | |
p.beginDraw = nop; | |
p.endDraw = nop; | |
Drawing2D.prototype.toImageData = function(x, y, w, h) { | |
x = x !== undef ? x : 0; | |
y = y !== undef ? y : 0; | |
w = w !== undef ? w : p.width; | |
h = h !== undef ? h : p.height; | |
return curContext.getImageData(x, y, w, h) | |
}; | |
Drawing3D.prototype.toImageData = function(x, y, w, h) { | |
x = x !== undef ? x : 0; | |
y = y !== undef ? y : 0; | |
w = w !== undef ? w : p.width; | |
h = h !== undef ? h : p.height; | |
var c = document.createElement("canvas"), | |
ctx = c.getContext("2d"), | |
obj = ctx.createImageData(w, h), | |
uBuff = new Uint8Array(w * h * 4); | |
curContext.readPixels(x, y, w, h, curContext.RGBA, curContext.UNSIGNED_BYTE, uBuff); | |
for (var i = 0, ul = uBuff.length, obj_data = obj.data; i < ul; i++) obj_data[i] = uBuff[(h - 1 - Math.floor(i / 4 / w)) * w * 4 + i % (w * 4)]; | |
return obj | |
}; | |
p.status = function(text) { | |
window.status = text | |
}; | |
p.binary = function(num, numBits) { | |
var bit; | |
if (numBits > 0) bit = numBits; | |
else if (num instanceof Char) { | |
bit = 16; | |
num |= 0 | |
} else { | |
bit = 32; | |
while (bit > 1 && !(num >>> bit - 1 & 1)) bit-- | |
} | |
var result = ""; | |
while (bit > 0) result += num >>> --bit & 1 ? "1" : "0"; | |
return result | |
}; | |
p.unbinary = function(binaryString) { | |
var i = binaryString.length - 1, | |
mask = 1, | |
result = 0; | |
while (i >= 0) { | |
var ch = binaryString[i--]; | |
if (ch !== "0" && ch !== "1") throw "the value passed into unbinary was not an 8 bit binary number"; | |
if (ch === "1") result += mask; | |
mask <<= 1 | |
} | |
return result | |
}; | |
function nfCoreScalar(value, plus, minus, leftDigits, rightDigits, group) { | |
var sign = value < 0 ? minus : plus; | |
var autoDetectDecimals = rightDigits === 0; | |
var rightDigitsOfDefault = rightDigits === undef || rightDigits < 0 ? 0 : rightDigits; | |
var absValue = Math.abs(value); | |
if (autoDetectDecimals) { | |
rightDigitsOfDefault = 1; | |
absValue *= 10; | |
while (Math.abs(Math.round(absValue) - absValue) > 1.0E-6 && rightDigitsOfDefault < 7) { | |
++rightDigitsOfDefault; | |
absValue *= 10 | |
} | |
} else if (rightDigitsOfDefault !== 0) absValue *= Math.pow(10, rightDigitsOfDefault); | |
var number, doubled = absValue * 2; | |
if (Math.floor(absValue) === absValue) number = absValue; | |
else if (Math.floor(doubled) === doubled) { | |
var floored = Math.floor(absValue); | |
number = floored + floored % 2 | |
} else number = Math.round(absValue); | |
var buffer = ""; | |
var totalDigits = leftDigits + rightDigitsOfDefault; | |
while (totalDigits > 0 || number > 0) { | |
totalDigits--; | |
buffer = "" + number % 10 + buffer; | |
number = Math.floor(number / 10) | |
} | |
if (group !== undef) { | |
var i = buffer.length - 3 - rightDigitsOfDefault; | |
while (i > 0) { | |
buffer = buffer.substring(0, i) + group + buffer.substring(i); | |
i -= 3 | |
} | |
} | |
if (rightDigitsOfDefault > 0) return sign + buffer.substring(0, buffer.length - rightDigitsOfDefault) + "." + buffer.substring(buffer.length - rightDigitsOfDefault, buffer.length); | |
return sign + buffer | |
} | |
function nfCore(value, plus, minus, leftDigits, rightDigits, group) { | |
if (value instanceof Array) { | |
var arr = []; | |
for (var i = 0, len = value.length; i < len; i++) arr.push(nfCoreScalar(value[i], plus, minus, leftDigits, rightDigits, group)); | |
return arr | |
} | |
return nfCoreScalar(value, plus, minus, leftDigits, rightDigits, group) | |
} | |
p.nf = function(value, leftDigits, rightDigits) { | |
return nfCore(value, "", "-", leftDigits, rightDigits) | |
}; | |
p.nfs = function(value, leftDigits, rightDigits) { | |
return nfCore(value, " ", "-", leftDigits, rightDigits) | |
}; | |
p.nfp = function(value, leftDigits, rightDigits) { | |
return nfCore(value, "+", "-", leftDigits, rightDigits) | |
}; | |
p.nfc = function(value, leftDigits, rightDigits) { | |
return nfCore(value, "", "-", leftDigits, rightDigits, ",") | |
}; | |
var decimalToHex = function(d, padding) { | |
padding = padding === undef || padding === null ? padding = 8 : padding; | |
if (d < 0) d = 4294967295 + d + 1; | |
var hex = Number(d).toString(16).toUpperCase(); | |
while (hex.length < padding) hex = "0" + hex; | |
if (hex.length >= padding) hex = hex.substring(hex.length - padding, hex.length); | |
return hex | |
}; | |
p.hex = function(value, len) { | |
if (arguments.length === 1) if (value instanceof Char) len = 4; | |
else len = 8; | |
return decimalToHex(value, len) | |
}; | |
function unhexScalar(hex) { | |
var value = parseInt("0x" + hex, 16); | |
if (value > 2147483647) value -= 4294967296; | |
return value | |
} | |
p.unhex = function(hex) { | |
if (hex instanceof Array) { | |
var arr = []; | |
for (var i = 0; i < hex.length; i++) arr.push(unhexScalar(hex[i])); | |
return arr | |
} | |
return unhexScalar(hex) | |
}; | |
p.loadStrings = function(filename) { | |
if (localStorage[filename]) return localStorage[filename].split("\n"); | |
var filecontent = ajax(filename); | |
if (typeof filecontent !== "string" || filecontent === "") return []; | |
filecontent = filecontent.replace(/(\r\n?)/g, "\n").replace(/\n$/, ""); | |
return filecontent.split("\n") | |
}; | |
p.saveStrings = function(filename, strings) { | |
localStorage[filename] = strings.join("\n") | |
}; | |
p.loadBytes = function(url) { | |
var string = ajax(url); | |
var ret = []; | |
for (var i = 0; i < string.length; i++) ret.push(string.charCodeAt(i)); | |
return ret | |
}; | |
function removeFirstArgument(args) { | |
return Array.prototype.slice.call(args, 1) | |
} | |
p.matchAll = function(aString, aRegExp) { | |
var results = [], | |
latest; | |
var regexp = new RegExp(aRegExp, "g"); | |
while ((latest = regexp.exec(aString)) !== null) { | |
results.push(latest); | |
if (latest[0].length === 0)++regexp.lastIndex | |
} | |
return results.length > 0 ? results : null | |
}; | |
p.__contains = function(subject, subStr) { | |
if (typeof subject !== "string") return subject.contains.apply(subject, removeFirstArgument(arguments)); | |
return subject !== null && subStr !== null && typeof subStr === "string" && subject.indexOf(subStr) > -1 | |
}; | |
p.__replaceAll = function(subject, regex, replacement) { | |
if (typeof subject !== "string") return subject.replaceAll.apply(subject, removeFirstArgument(arguments)); | |
return subject.replace(new RegExp(regex, "g"), replacement) | |
}; | |
p.__replaceFirst = function(subject, regex, replacement) { | |
if (typeof subject !== "string") return subject.replaceFirst.apply(subject, removeFirstArgument(arguments)); | |
return subject.replace(new RegExp(regex, ""), replacement) | |
}; | |
p.__replace = function(subject, what, replacement) { | |
if (typeof subject !== "string") return subject.replace.apply(subject, removeFirstArgument(arguments)); | |
if (what instanceof RegExp) return subject.replace(what, replacement); | |
if (typeof what !== "string") what = what.toString(); | |
if (what === "") return subject; | |
var i = subject.indexOf(what); | |
if (i < 0) return subject; | |
var j = 0, | |
result = ""; | |
do { | |
result += subject.substring(j, i) + replacement; | |
j = i + what.length | |
} while ((i = subject.indexOf(what, j)) >= 0); | |
return result + subject.substring(j) | |
}; | |
p.__equals = function(subject, other) { | |
if (subject.equals instanceof | |
Function) return subject.equals.apply(subject, removeFirstArgument(arguments)); | |
return subject.valueOf() === other.valueOf() | |
}; | |
p.__equalsIgnoreCase = function(subject, other) { | |
if (typeof subject !== "string") return subject.equalsIgnoreCase.apply(subject, removeFirstArgument(arguments)); | |
return subject.toLowerCase() === other.toLowerCase() | |
}; | |
p.__toCharArray = function(subject) { | |
if (typeof subject !== "string") return subject.toCharArray.apply(subject, removeFirstArgument(arguments)); | |
var chars = []; | |
for (var i = 0, len = subject.length; i < len; ++i) chars[i] = new Char(subject.charAt(i)); | |
return chars | |
}; | |
p.__split = function(subject, regex, limit) { | |
if (typeof subject !== "string") return subject.split.apply(subject, removeFirstArgument(arguments)); | |
var pattern = new RegExp(regex); | |
if (limit === undef || limit < 1) return subject.split(pattern); | |
var result = [], | |
currSubject = subject, | |
pos; | |
while ((pos = currSubject.search(pattern)) !== -1 && result.length < limit - 1) { | |
var match = pattern.exec(currSubject).toString(); | |
result.push(currSubject.substring(0, pos)); | |
currSubject = currSubject.substring(pos + match.length) | |
} | |
if (pos !== -1 || currSubject !== "") result.push(currSubject); | |
return result | |
}; | |
p.__codePointAt = function(subject, idx) { | |
var code = subject.charCodeAt(idx), | |
hi, low; | |
if (55296 <= code && code <= 56319) { | |
hi = code; | |
low = subject.charCodeAt(idx + 1); | |
return (hi - 55296) * 1024 + (low - 56320) + 65536 | |
} | |
return code | |
}; | |
p.match = function(str, regexp) { | |
return str.match(regexp) | |
}; | |
p.__matches = function(str, regexp) { | |
return (new RegExp(regexp)).test(str) | |
}; | |
p.__startsWith = function(subject, prefix, toffset) { | |
if (typeof subject !== "string") return subject.startsWith.apply(subject, removeFirstArgument(arguments)); | |
toffset = toffset || 0; | |
if (toffset < 0 || toffset > subject.length) return false; | |
return prefix === "" || prefix === subject ? true : subject.indexOf(prefix) === toffset | |
}; | |
p.__endsWith = function(subject, suffix) { | |
if (typeof subject !== "string") return subject.endsWith.apply(subject, removeFirstArgument(arguments)); | |
var suffixLen = suffix ? suffix.length : 0; | |
return suffix === "" || suffix === subject ? true : subject.indexOf(suffix) === subject.length - suffixLen | |
}; | |
p.__hashCode = function(subject) { | |
if (subject.hashCode instanceof | |
Function) return subject.hashCode.apply(subject, removeFirstArgument(arguments)); | |
return virtHashCode(subject) | |
}; | |
p.__printStackTrace = function(subject) { | |
p.println("Exception: " + subject.toString()) | |
}; | |
var logBuffer = []; | |
p.println = function(message) { | |
var bufferLen = logBuffer.length; | |
if (bufferLen) { | |
Processing.logger.log(logBuffer.join("")); | |
logBuffer.length = 0 | |
} | |
if (arguments.length === 0 && bufferLen === 0) Processing.logger.log(""); | |
else if (arguments.length !== 0) Processing.logger.log(message) | |
}; | |
p.print = function(message) { | |
logBuffer.push(message) | |
}; | |
p.str = function(val) { | |
if (val instanceof Array) { | |
var arr = []; | |
for (var i = 0; i < val.length; i++) arr.push(val[i].toString() + ""); | |
return arr | |
} | |
return val.toString() + "" | |
}; | |
p.trim = function(str) { | |
if (str instanceof Array) { | |
var arr = []; | |
for (var i = 0; i < str.length; i++) arr.push(str[i].replace(/^\s*/, "").replace(/\s*$/, "").replace(/\r*$/, "")); | |
return arr | |
} | |
return str.replace(/^\s*/, "").replace(/\s*$/, "").replace(/\r*$/, "") | |
}; | |
function booleanScalar(val) { | |
if (typeof val === "number") return val !== 0; | |
if (typeof val === "boolean") return val; | |
if (typeof val === "string") return val.toLowerCase() === "true"; | |
if (val instanceof Char) return val.code === 49 || val.code === 84 || val.code === 116 | |
} | |
p.parseBoolean = function(val) { | |
if (val instanceof Array) { | |
var ret = []; | |
for (var i = 0; i < val.length; i++) ret.push(booleanScalar(val[i])); | |
return ret | |
} | |
return booleanScalar(val) | |
}; | |
p.parseByte = function(what) { | |
if (what instanceof Array) { | |
var bytes = []; | |
for (var i = 0; i < what.length; i++) bytes.push(0 - (what[i] & 128) | what[i] & 127); | |
return bytes | |
} | |
return 0 - (what & 128) | what & 127 | |
}; | |
p.parseChar = function(key) { | |
if (typeof key === "number") return new Char(String.fromCharCode(key & 65535)); | |
if (key instanceof Array) { | |
var ret = []; | |
for (var i = 0; i < key.length; i++) ret.push(new Char(String.fromCharCode(key[i] & 65535))); | |
return ret | |
} | |
throw "char() may receive only one argument of type int, byte, int[], or byte[]."; | |
}; | |
function floatScalar(val) { | |
if (typeof val === "number") return val; | |
if (typeof val === "boolean") return val ? 1 : 0; | |
if (typeof val === "string") return parseFloat(val); | |
if (val instanceof Char) return val.code | |
} | |
p.parseFloat = function(val) { | |
if (val instanceof | |
Array) { | |
var ret = []; | |
for (var i = 0; i < val.length; i++) ret.push(floatScalar(val[i])); | |
return ret | |
} | |
return floatScalar(val) | |
}; | |
function intScalar(val, radix) { | |
if (typeof val === "number") return val & 4294967295; | |
if (typeof val === "boolean") return val ? 1 : 0; | |
if (typeof val === "string") { | |
var number = parseInt(val, radix || 10); | |
return number & 4294967295 | |
} | |
if (val instanceof Char) return val.code | |
} | |
p.parseInt = function(val, radix) { | |
if (val instanceof Array) { | |
var ret = []; | |
for (var i = 0; i < val.length; i++) if (typeof val[i] === "string" && !/^\s*[+\-]?\d+\s*$/.test(val[i])) ret.push(0); | |
else ret.push(intScalar(val[i], radix)); | |
return ret | |
} | |
return intScalar(val, radix) | |
}; | |
p.__int_cast = function(val) { | |
return 0 | val | |
}; | |
p.__instanceof = function(obj, type) { | |
if (typeof type !== "function") throw "Function is expected as type argument for instanceof operator"; | |
if (typeof obj === "string") return type === Object || type === String; | |
if (obj instanceof type) return true; | |
if (typeof obj !== "object" || obj === null) return false; | |
var objType = obj.constructor; | |
if (type.$isInterface) { | |
var interfaces = []; | |
while (objType) { | |
if (objType.$interfaces) interfaces = interfaces.concat(objType.$interfaces); | |
objType = objType.$base | |
} | |
while (interfaces.length > 0) { | |
var i = interfaces.shift(); | |
if (i === type) return true; | |
if (i.$interfaces) interfaces = interfaces.concat(i.$interfaces) | |
} | |
return false | |
} | |
while (objType.hasOwnProperty("$base")) { | |
objType = objType.$base; | |
if (objType === type) return true | |
} | |
return false | |
}; | |
p.abs = Math.abs; | |
p.ceil = Math.ceil; | |
p.constrain = function(aNumber, aMin, aMax) { | |
return aNumber > aMax ? aMax : aNumber < aMin ? aMin : aNumber | |
}; | |
p.dist = function() { | |
var dx, dy, dz; | |
if (arguments.length === 4) { | |
dx = arguments[0] - arguments[2]; | |
dy = arguments[1] - arguments[3]; | |
return Math.sqrt(dx * dx + dy * dy) | |
} | |
if (arguments.length === 6) { | |
dx = arguments[0] - arguments[3]; | |
dy = arguments[1] - arguments[4]; | |
dz = arguments[2] - arguments[5]; | |
return Math.sqrt(dx * dx + dy * dy + dz * dz) | |
} | |
}; | |
p.exp = Math.exp; | |
p.floor = Math.floor; | |
p.lerp = function(value1, value2, amt) { | |
return (value2 - value1) * amt + value1 | |
}; | |
p.log = Math.log; | |
p.mag = function(a, b, c) { | |
if (c) return Math.sqrt(a * a + b * b + c * c); | |
return Math.sqrt(a * a + b * b) | |
}; | |
p.map = function(value, istart, istop, ostart, ostop) { | |
return ostart + (ostop - ostart) * ((value - istart) / (istop - istart)) | |
}; | |
p.max = function() { | |
if (arguments.length === 2) return arguments[0] < arguments[1] ? arguments[1] : arguments[0]; | |
var numbers = arguments.length === 1 ? arguments[0] : arguments; | |
if (! ("length" in numbers && numbers.length > 0)) throw "Non-empty array is expected"; | |
var max = numbers[0], | |
count = numbers.length; | |
for (var i = 1; i < count; ++i) if (max < numbers[i]) max = numbers[i]; | |
return max | |
}; | |
p.min = function() { | |
if (arguments.length === 2) return arguments[0] < arguments[1] ? arguments[0] : arguments[1]; | |
var numbers = arguments.length === 1 ? arguments[0] : arguments; | |
if (! ("length" in numbers && numbers.length > 0)) throw "Non-empty array is expected"; | |
var min = numbers[0], | |
count = numbers.length; | |
for (var i = 1; i < count; ++i) if (min > numbers[i]) min = numbers[i]; | |
return min | |
}; | |
p.norm = function(aNumber, low, high) { | |
return (aNumber - low) / (high - low) | |
}; | |
p.pow = Math.pow; | |
p.round = Math.round; | |
p.sq = function(aNumber) { | |
return aNumber * aNumber | |
}; | |
p.sqrt = Math.sqrt; | |
p.acos = Math.acos; | |
p.asin = Math.asin; | |
p.atan = Math.atan; | |
p.atan2 = Math.atan2; | |
p.cos = Math.cos; | |
p.degrees = function(aAngle) { | |
return aAngle * 180 / Math.PI | |
}; | |
p.radians = function(aAngle) { | |
return aAngle / 180 * Math.PI | |
}; | |
p.sin = Math.sin; | |
p.tan = Math.tan; | |
var currentRandom = Math.random; | |
p.random = function() { | |
if (arguments.length === 0) return currentRandom(); | |
if (arguments.length === 1) return currentRandom() * arguments[0]; | |
var aMin = arguments[0], | |
aMax = arguments[1]; | |
return currentRandom() * (aMax - aMin) + aMin | |
}; | |
function Marsaglia(i1, i2) { | |
var z = i1 || 362436069, | |
w = i2 || 521288629; | |
var nextInt = function() { | |
z = 36969 * (z & 65535) + (z >>> 16) & 4294967295; | |
w = 18E3 * (w & 65535) + (w >>> 16) & 4294967295; | |
return ((z & 65535) << 16 | w & 65535) & 4294967295 | |
}; | |
this.nextDouble = function() { | |
var i = nextInt() / 4294967296; | |
return i < 0 ? 1 + i : i | |
}; | |
this.nextInt = nextInt | |
} | |
Marsaglia.createRandomized = function() { | |
var now = new Date; | |
return new Marsaglia(now / 6E4 & 4294967295, now & 4294967295) | |
}; | |
p.randomSeed = function(seed) { | |
currentRandom = (new Marsaglia(seed)).nextDouble | |
}; | |
p.Random = function(seed) { | |
var haveNextNextGaussian = false, | |
nextNextGaussian, random; | |
this.nextGaussian = function() { | |
if (haveNextNextGaussian) { | |
haveNextNextGaussian = false; | |
return nextNextGaussian | |
} | |
var v1, v2, s; | |
do { | |
v1 = 2 * random() - 1; | |
v2 = 2 * random() - 1; | |
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 | |
}; | |
random = seed === undef ? Math.random : (new Marsaglia(seed)).nextDouble | |
}; | |
function PerlinNoise(seed) { | |
var rnd = seed !== undef ? new Marsaglia(seed) : Marsaglia.createRandomized(); | |
var i, j; | |
var perm = new Uint8Array(512); | |
for (i = 0; i < 256; ++i) perm[i] = i; | |
for (i = 0; i < 256; ++i) { | |
var t = perm[j = rnd.nextInt() & 255]; | |
perm[j] = perm[i]; | |
perm[i] = t | |
} | |
for (i = 0; i < 256; ++i) perm[i + 256] = perm[i]; | |
function grad3d(i, x, y, z) { | |
var h = i & 15; | |
var u = h < 8 ? x : y, | |
v = h < 4 ? y : h === 12 || h === 14 ? x : z; | |
return ((h & 1) === 0 ? u : -u) + ((h & 2) === 0 ? v : -v) | |
} | |
function grad2d(i, x, y) { | |
var v = (i & 1) === 0 ? x : y; | |
return (i & 2) === 0 ? -v : v | |
} | |
function grad1d(i, x) { | |
return (i & 1) === 0 ? -x : x | |
} | |
function lerp(t, a, b) { | |
return a + t * (b - a) | |
} | |
this.noise3d = function(x, y, z) { | |
var X = Math.floor(x) & 255, | |
Y = Math.floor(y) & 255, | |
Z = Math.floor(z) & 255; | |
x -= Math.floor(x); | |
y -= Math.floor(y); | |
z -= Math.floor(z); | |
var fx = (3 - 2 * x) * x * x, | |
fy = (3 - 2 * y) * y * y, | |
fz = (3 - 2 * z) * z * z; | |
var p0 = perm[X] + Y, | |
p00 = perm[p0] + Z, | |
p01 = perm[p0 + 1] + Z, | |
p1 = perm[X + 1] + Y, | |
p10 = perm[p1] + Z, | |
p11 = perm[p1 + 1] + Z; | |
return lerp(fz, lerp(fy, lerp(fx, grad3d(perm[p00], x, y, z), grad3d(perm[p10], x - 1, y, z)), lerp(fx, grad3d(perm[p01], x, y - 1, z), grad3d(perm[p11], x - 1, y - 1, z))), lerp(fy, lerp(fx, grad3d(perm[p00 + 1], x, y, z - 1), grad3d(perm[p10 + 1], x - 1, y, z - 1)), lerp(fx, grad3d(perm[p01 + 1], x, y - 1, z - 1), grad3d(perm[p11 + 1], x - 1, y - 1, z - 1)))) | |
}; | |
this.noise2d = function(x, y) { | |
var X = Math.floor(x) & 255, | |
Y = Math.floor(y) & 255; | |
x -= Math.floor(x); | |
y -= Math.floor(y); | |
var fx = (3 - 2 * x) * x * x, | |
fy = (3 - 2 * y) * y * y; | |
var p0 = perm[X] + Y, | |
p1 = perm[X + 1] + Y; | |
return lerp(fy, lerp(fx, grad2d(perm[p0], x, y), grad2d(perm[p1], x - 1, y)), lerp(fx, grad2d(perm[p0 + 1], x, y - 1), grad2d(perm[p1 + 1], x - 1, y - 1))) | |
}; | |
this.noise1d = function(x) { | |
var X = Math.floor(x) & 255; | |
x -= Math.floor(x); | |
var fx = (3 - 2 * x) * x * x; | |
return lerp(fx, grad1d(perm[X], x), grad1d(perm[X + 1], x - 1)) | |
} | |
} | |
var noiseProfile = { | |
generator: undef, | |
octaves: 4, | |
fallout: 0.5, | |
seed: undef | |
}; | |
p.noise = function(x, y, z) { | |
if (noiseProfile.generator === undef) noiseProfile.generator = new PerlinNoise(noiseProfile.seed); | |
var generator = noiseProfile.generator; | |
var effect = 1, | |
k = 1, | |
sum = 0; | |
for (var i = 0; i < noiseProfile.octaves; ++i) { | |
effect *= noiseProfile.fallout; | |
switch (arguments.length) { | |
case 1: | |
sum += effect * (1 + generator.noise1d(k * x)) / 2; | |
break; | |
case 2: | |
sum += effect * (1 + generator.noise2d(k * x, k * y)) / 2; | |
break; | |
case 3: | |
sum += effect * (1 + generator.noise3d(k * x, k * y, k * z)) / 2; | |
break | |
} | |
k *= 2 | |
} | |
return sum | |
}; | |
p.noiseDetail = function(octaves, fallout) { | |
noiseProfile.octaves = octaves; | |
if (fallout !== undef) noiseProfile.fallout = fallout | |
}; | |
p.noiseSeed = function(seed) { | |
noiseProfile.seed = seed; | |
noiseProfile.generator = undef | |
}; | |
DrawingShared.prototype.size = function(aWidth, aHeight, aMode) { | |
if (doStroke) p.stroke(0); | |
if (doFill) p.fill(255); | |
var savedProperties = { | |
fillStyle: curContext.fillStyle, | |
strokeStyle: curContext.strokeStyle, | |
lineCap: curContext.lineCap, | |
lineJoin: curContext.lineJoin | |
}; | |
if (curElement.style.length > 0) { | |
curElement.style.removeProperty("width"); | |
curElement.style.removeProperty("height") | |
} | |
curElement.width = p.width = aWidth || 100; | |
curElement.height = p.height = aHeight || 100; | |
for (var prop in savedProperties) if (savedProperties.hasOwnProperty(prop)) curContext[prop] = savedProperties[prop]; | |
p.textFont(curTextFont); | |
p.background(); | |
maxPixelsCached = Math.max(1E3, aWidth * aHeight * 0.05); | |
p.externals.context = curContext; | |
for (var i = 0; i < 720; i++) { | |
sinLUT[i] = p.sin(i * (Math.PI / 180) * 0.5); | |
cosLUT[i] = p.cos(i * (Math.PI / 180) * 0.5) | |
} | |
}; | |
Drawing2D.prototype.size = function(aWidth, aHeight, aMode) { | |
if (curContext === undef) { | |
curContext = curElement.getContext("2d"); | |
userMatrixStack = new PMatrixStack; | |
userReverseMatrixStack = new PMatrixStack; | |
modelView = new PMatrix2D; | |
modelViewInv = new PMatrix2D | |
} | |
DrawingShared.prototype.size.apply(this, arguments) | |
}; | |
Drawing3D.prototype.size = function() { | |
var size3DCalled = false; | |
return function size(aWidth, aHeight, aMode) { | |
if (size3DCalled) throw "Multiple calls to size() for 3D renders are not allowed."; | |
size3DCalled = true; | |
function getGLContext(canvas) { | |
var ctxNames = ["experimental-webgl", "webgl", "webkit-3d"], | |
gl; | |
for (var i = 0, l = ctxNames.length; i < l; i++) { | |
gl = canvas.getContext(ctxNames[i], { | |
antialias: false, | |
preserveDrawingBuffer: true | |
}); | |
if (gl) break | |
} | |
return gl | |
} | |
try { | |
curElement.width = p.width = aWidth || 100; | |
curElement.height = p.height = aHeight || 100; | |
curContext = getGLContext(curElement); | |
canTex = curContext.createTexture(); | |
textTex = curContext.createTexture() | |
} catch(e_size) { | |
Processing.debug(e_size) | |
} | |
if (!curContext) throw "WebGL context is not supported on this browser."; | |
curContext.viewport(0, 0, curElement.width, curElement.height); | |
curContext.enable(curContext.DEPTH_TEST); | |
curContext.enable(curContext.BLEND); | |
curContext.blendFunc(curContext.SRC_ALPHA, curContext.ONE_MINUS_SRC_ALPHA); | |
programObject2D = createProgramObject(curContext, vertexShaderSrc2D, fragmentShaderSrc2D); | |
programObjectUnlitShape = createProgramObject(curContext, vertexShaderSrcUnlitShape, fragmentShaderSrcUnlitShape); | |
p.strokeWeight(1); | |
programObject3D = createProgramObject(curContext, vertexShaderSrc3D, fragmentShaderSrc3D); | |
curContext.useProgram(programObject3D); | |
uniformi("usingTexture3d", programObject3D, "usingTexture", usingTexture); | |
p.lightFalloff(1, 0, 0); | |
p.shininess(1); | |
p.ambient(255, 255, 255); | |
p.specular(0, 0, 0); | |
p.emissive(0, 0, 0); | |
boxBuffer = curContext.createBuffer(); | |
curContext.bindBuffer(curContext.ARRAY_BUFFER, boxBuffer); | |
curContext.bufferData(curContext.ARRAY_BUFFER, boxVerts, curContext.STATIC_DRAW); | |
boxNormBuffer = curContext.createBuffer(); | |
curContext.bindBuffer(curContext.ARRAY_BUFFER, boxNormBuffer); | |
curContext.bufferData(curContext.ARRAY_BUFFER, boxNorms, curContext.STATIC_DRAW); | |
boxOutlineBuffer = curContext.createBuffer(); | |
curContext.bindBuffer(curContext.ARRAY_BUFFER, boxOutlineBuffer); | |
curContext.bufferData(curContext.ARRAY_BUFFER, boxOutlineVerts, curContext.STATIC_DRAW); | |
rectBuffer = curContext.createBuffer(); | |
curContext.bindBuffer(curContext.ARRAY_BUFFER, rectBuffer); | |
curContext.bufferData(curContext.ARRAY_BUFFER, rectVerts, curContext.STATIC_DRAW); | |
rectNormBuffer = curContext.createBuffer(); | |
curContext.bindBuffer(curContext.ARRAY_BUFFER, rectNormBuffer); | |
curContext.bufferData(curContext.ARRAY_BUFFER, rectNorms, curContext.STATIC_DRAW); | |
sphereBuffer = curContext.createBuffer(); | |
lineBuffer = curContext.createBuffer(); | |
fillBuffer = curContext.createBuffer(); | |
fillColorBuffer = curContext.createBuffer(); | |
strokeColorBuffer = curContext.createBuffer(); | |
shapeTexVBO = curContext.createBuffer(); | |
pointBuffer = curContext.createBuffer(); | |
curContext.bindBuffer(curContext.ARRAY_BUFFER, pointBuffer); | |
curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array([0, 0, 0]), curContext.STATIC_DRAW); | |
textBuffer = curContext.createBuffer(); | |
curContext.bindBuffer(curContext.ARRAY_BUFFER, textBuffer); | |
curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array([1, 1, 0, -1, 1, 0, -1, -1, 0, 1, -1, 0]), curContext.STATIC_DRAW); | |
textureBuffer = curContext.createBuffer(); | |
curContext.bindBuffer(curContext.ARRAY_BUFFER, textureBuffer); | |
curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array([0, 0, 1, 0, 1, 1, 0, 1]), curContext.STATIC_DRAW); | |
indexBuffer = curContext.createBuffer(); | |
curContext.bindBuffer(curContext.ELEMENT_ARRAY_BUFFER, indexBuffer); | |
curContext.bufferData(curContext.ELEMENT_ARRAY_BUFFER, new Uint16Array([0, 1, 2, 2, 3, 0]), curContext.STATIC_DRAW); | |
cam = new PMatrix3D; | |
cameraInv = new PMatrix3D; | |
modelView = new PMatrix3D; | |
modelViewInv = new PMatrix3D; | |
projection = new PMatrix3D; | |
p.camera(); | |
p.perspective(); | |
userMatrixStack = new PMatrixStack; | |
userReverseMatrixStack = new PMatrixStack; | |
curveBasisMatrix = new PMatrix3D; | |
curveToBezierMatrix = new PMatrix3D; | |
curveDrawMatrix = new PMatrix3D; | |
bezierDrawMatrix = new PMatrix3D; | |
bezierBasisInverse = new PMatrix3D; | |
bezierBasisMatrix = new PMatrix3D; | |
bezierBasisMatrix.set(-1, 3, -3, 1, 3, -6, 3, 0, -3, 3, 0, 0, 1, 0, 0, 0); | |
DrawingShared.prototype.size.apply(this, arguments) | |
} | |
}(); | |
Drawing2D.prototype.ambientLight = DrawingShared.prototype.a3DOnlyFunction; | |
Drawing3D.prototype.ambientLight = function(r, g, b, x, y, z) { | |
if (lightCount === 8) throw "can only create " + 8 + " lights"; | |
var pos = new PVector(x, y, z); | |
var view = new PMatrix3D; | |
view.scale(1, -1, 1); | |
view.apply(modelView.array()); | |
view.mult(pos, pos); | |
var col = color$4(r, g, b, 0); | |
var normalizedCol = [((col >> 16) & 255) / 255, ((col >> 8) & 255) / 255, (col & 255) / 255]; | |
curContext.useProgram(programObject3D); | |
uniformf("uLights.color.3d." + lightCount, programObject3D, "uLights" + lightCount + ".color", normalizedCol); | |
uniformf("uLights.position.3d." + lightCount, programObject3D, "uLights" + lightCount + ".position", pos.array()); | |
uniformi("uLights.type.3d." + lightCount, programObject3D, "uLights" + lightCount + ".type", 0); | |
uniformi("uLightCount3d", programObject3D, "uLightCount", ++lightCount) | |
}; | |
Drawing2D.prototype.directionalLight = DrawingShared.prototype.a3DOnlyFunction; | |
Drawing3D.prototype.directionalLight = function(r, g, b, nx, ny, nz) { | |
if (lightCount === 8) throw "can only create " + 8 + " lights"; | |
curContext.useProgram(programObject3D); | |
var mvm = new PMatrix3D; | |
mvm.scale(1, -1, 1); | |
mvm.apply(modelView.array()); | |
mvm = mvm.array(); | |
var dir = [mvm[0] * nx + mvm[4] * ny + mvm[8] * nz, mvm[1] * nx + mvm[5] * ny + mvm[9] * nz, mvm[2] * nx + mvm[6] * ny + mvm[10] * nz]; | |
var col = color$4(r, g, b, 0); | |
var normalizedCol = [((col >> 16) & 255) / 255, ((col >> 8) & 255) / 255, (col & 255) / 255]; | |
uniformf("uLights.color.3d." + lightCount, programObject3D, "uLights" + lightCount + ".color", normalizedCol); | |
uniformf("uLights.position.3d." + lightCount, programObject3D, "uLights" + lightCount + ".position", dir); | |
uniformi("uLights.type.3d." + lightCount, programObject3D, "uLights" + lightCount + ".type", 1); | |
uniformi("uLightCount3d", programObject3D, "uLightCount", ++lightCount) | |
}; | |
Drawing2D.prototype.lightFalloff = DrawingShared.prototype.a3DOnlyFunction; | |
Drawing3D.prototype.lightFalloff = function(constant, linear, quadratic) { | |
curContext.useProgram(programObject3D); | |
uniformf("uFalloff3d", programObject3D, "uFalloff", [constant, linear, quadratic]) | |
}; | |
Drawing2D.prototype.lightSpecular = DrawingShared.prototype.a3DOnlyFunction; | |
Drawing3D.prototype.lightSpecular = function(r, g, b) { | |
var col = color$4(r, g, b, 0); | |
var normalizedCol = [((col >> 16) & 255) / 255, ((col >> 8) & 255) / 255, (col & 255) / 255]; | |
curContext.useProgram(programObject3D); | |
uniformf("uSpecular3d", programObject3D, "uSpecular", normalizedCol) | |
}; | |
p.lights = function() { | |
p.ambientLight(128, 128, 128); | |
p.directionalLight(128, 128, 128, 0, 0, -1); | |
p.lightFalloff(1, 0, 0); | |
p.lightSpecular(0, 0, 0) | |
}; | |
Drawing2D.prototype.pointLight = DrawingShared.prototype.a3DOnlyFunction; | |
Drawing3D.prototype.pointLight = function(r, g, b, x, y, z) { | |
if (lightCount === 8) throw "can only create " + 8 + " lights"; | |
var pos = new PVector(x, y, z); | |
var view = new PMatrix3D; | |
view.scale(1, -1, 1); | |
view.apply(modelView.array()); | |
view.mult(pos, pos); | |
var col = color$4(r, g, b, 0); | |
var normalizedCol = [((col >> 16) & 255) / 255, ((col >> 8) & 255) / 255, (col & 255) / 255]; | |
curContext.useProgram(programObject3D); | |
uniformf("uLights.color.3d." + lightCount, programObject3D, "uLights" + lightCount + ".color", normalizedCol); | |
uniformf("uLights.position.3d." + lightCount, programObject3D, "uLights" + lightCount + ".position", pos.array()); | |
uniformi("uLights.type.3d." + lightCount, programObject3D, "uLights" + lightCount + ".type", 2); | |
uniformi("uLightCount3d", programObject3D, "uLightCount", ++lightCount) | |
}; | |
Drawing2D.prototype.noLights = DrawingShared.prototype.a3DOnlyFunction; | |
Drawing3D.prototype.noLights = function() { | |
lightCount = 0; | |
curContext.useProgram(programObject3D); | |
uniformi("uLightCount3d", programObject3D, "uLightCount", lightCount) | |
}; | |
Drawing2D.prototype.spotLight = DrawingShared.prototype.a3DOnlyFunction; | |
Drawing3D.prototype.spotLight = function(r, g, b, x, y, z, nx, ny, nz, angle, concentration) { | |
if (lightCount === 8) throw "can only create " + 8 + " lights"; | |
curContext.useProgram(programObject3D); | |
var pos = new PVector(x, y, z); | |
var mvm = new PMatrix3D; | |
mvm.scale(1, -1, 1); | |
mvm.apply(modelView.array()); | |
mvm.mult(pos, pos); | |
mvm = mvm.array(); | |
var dir = [mvm[0] * nx + mvm[4] * ny + mvm[8] * nz, mvm[1] * | |
nx + mvm[5] * ny + mvm[9] * nz, mvm[2] * nx + mvm[6] * ny + mvm[10] * nz]; | |
var col = color$4(r, g, b, 0); | |
var normalizedCol = [((col >> 16) & 255) / 255, ((col >> 8) & 255) / 255, (col & 255) / 255]; | |
uniformf("uLights.color.3d." + lightCount, programObject3D, "uLights" + lightCount + ".color", normalizedCol); | |
uniformf("uLights.position.3d." + lightCount, programObject3D, "uLights" + lightCount + ".position", pos.array()); | |
uniformf("uLights.direction.3d." + lightCount, programObject3D, "uLights" + lightCount + ".direction", dir); | |
uniformf("uLights.concentration.3d." + lightCount, programObject3D, "uLights" + lightCount + ".concentration", concentration); | |
uniformf("uLights.angle.3d." + lightCount, programObject3D, "uLights" + lightCount + ".angle", angle); | |
uniformi("uLights.type.3d." + lightCount, programObject3D, "uLights" + lightCount + ".type", 3); | |
uniformi("uLightCount3d", programObject3D, "uLightCount", ++lightCount) | |
}; | |
Drawing2D.prototype.beginCamera = function() { | |
throw "beginCamera() is not available in 2D mode"; | |
}; | |
Drawing3D.prototype.beginCamera = function() { | |
if (manipulatingCamera) throw "You cannot call beginCamera() again before calling endCamera()"; | |
manipulatingCamera = true; | |
modelView = cameraInv; | |
modelViewInv = cam | |
}; | |
Drawing2D.prototype.endCamera = function() { | |
throw "endCamera() is not available in 2D mode"; | |
}; | |
Drawing3D.prototype.endCamera = function() { | |
if (!manipulatingCamera) throw "You cannot call endCamera() before calling beginCamera()"; | |
modelView.set(cam); | |
modelViewInv.set(cameraInv); | |
manipulatingCamera = false | |
}; | |
p.camera = function(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ) { | |
if (eyeX === undef) { | |
cameraX = p.width / 2; | |
cameraY = p.height / 2; | |
cameraZ = cameraY / Math.tan(cameraFOV / 2); | |
eyeX = cameraX; | |
eyeY = cameraY; | |
eyeZ = cameraZ; | |
centerX = cameraX; | |
centerY = cameraY; | |
centerZ = 0; | |
upX = 0; | |
upY = 1; | |
upZ = 0 | |
} | |
var z = new PVector(eyeX - centerX, eyeY - centerY, eyeZ - centerZ); | |
var y = new PVector(upX, upY, upZ); | |
z.normalize(); | |
var x = PVector.cross(y, z); | |
y = PVector.cross(z, x); | |
x.normalize(); | |
y.normalize(); | |
var xX = x.x, | |
xY = x.y, | |
xZ = x.z; | |
var yX = y.x, | |
yY = y.y, | |
yZ = y.z; | |
var zX = z.x, | |
zY = z.y, | |
zZ = z.z; | |
cam.set(xX, xY, xZ, 0, yX, yY, yZ, 0, zX, zY, zZ, 0, 0, 0, 0, 1); | |
cam.translate(-eyeX, -eyeY, -eyeZ); | |
cameraInv.reset(); | |
cameraInv.invApply(xX, xY, xZ, 0, yX, yY, yZ, 0, zX, zY, zZ, 0, 0, 0, 0, 1); | |
cameraInv.translate(eyeX, eyeY, eyeZ); | |
modelView.set(cam); | |
modelViewInv.set(cameraInv) | |
}; | |
p.perspective = function(fov, aspect, near, far) { | |
if (arguments.length === 0) { | |
cameraY = curElement.height / 2; | |
cameraZ = cameraY / Math.tan(cameraFOV / 2); | |
cameraNear = cameraZ / 10; | |
cameraFar = cameraZ * 10; | |
cameraAspect = p.width / p.height; | |
fov = cameraFOV; | |
aspect = cameraAspect; | |
near = cameraNear; | |
far = cameraFar | |
} | |
var yMax, yMin, xMax, xMin; | |
yMax = near * Math.tan(fov / 2); | |
yMin = -yMax; | |
xMax = yMax * aspect; | |
xMin = yMin * aspect; | |
p.frustum(xMin, xMax, yMin, yMax, near, far) | |
}; | |
Drawing2D.prototype.frustum = function() { | |
throw "Processing.js: frustum() is not supported in 2D mode"; | |
}; | |
Drawing3D.prototype.frustum = function(left, right, bottom, top, near, far) { | |
frustumMode = true; | |
projection = new PMatrix3D; | |
projection.set(2 * near / (right - left), 0, (right + left) / (right - left), 0, 0, 2 * near / (top - bottom), (top + bottom) / (top - bottom), 0, 0, 0, -(far + near) / (far - near), -(2 * far * near) / (far - near), 0, 0, -1, 0); | |
var proj = new PMatrix3D; | |
proj.set(projection); | |
proj.transpose(); | |
curContext.useProgram(programObject2D); | |
uniformMatrix("projection2d", programObject2D, "uProjection", false, proj.array()); | |
curContext.useProgram(programObject3D); | |
uniformMatrix("projection3d", programObject3D, "uProjection", false, proj.array()); | |
curContext.useProgram(programObjectUnlitShape); | |
uniformMatrix("uProjectionUS", programObjectUnlitShape, "uProjection", false, proj.array()) | |
}; | |
p.ortho = function(left, right, bottom, top, near, far) { | |
if (arguments.length === 0) { | |
left = 0; | |
right = p.width; | |
bottom = 0; | |
top = p.height; | |
near = -10; | |
far = 10 | |
} | |
var x = 2 / (right - left); | |
var y = 2 / (top - bottom); | |
var z = -2 / (far - near); | |
var tx = -(right + left) / (right - left); | |
var ty = -(top + bottom) / (top - bottom); | |
var tz = -(far + near) / (far - near); | |
projection = new PMatrix3D; | |
projection.set(x, 0, 0, tx, 0, y, 0, ty, 0, 0, z, tz, 0, 0, 0, 1); | |
var proj = new PMatrix3D; | |
proj.set(projection); | |
proj.transpose(); | |
curContext.useProgram(programObject2D); | |
uniformMatrix("projection2d", programObject2D, "uProjection", false, proj.array()); | |
curContext.useProgram(programObject3D); | |
uniformMatrix("projection3d", programObject3D, "uProjection", false, proj.array()); | |
curContext.useProgram(programObjectUnlitShape); | |
uniformMatrix("uProjectionUS", programObjectUnlitShape, "uProjection", false, proj.array()); | |
frustumMode = false | |
}; | |
p.printProjection = function() { | |
projection.print() | |
}; | |
p.printCamera = function() { | |
cam.print() | |
}; | |
Drawing2D.prototype.box = DrawingShared.prototype.a3DOnlyFunction; | |
Drawing3D.prototype.box = function(w, h, d) { | |
if (!h || !d) h = d = w; | |
var model = new PMatrix3D; | |
model.scale(w, h, d); | |
var view = new PMatrix3D; | |
view.scale(1, -1, 1); | |
view.apply(modelView.array()); | |
view.transpose(); | |
if (doFill) { | |
curContext.useProgram(programObject3D); | |
uniformMatrix("model3d", programObject3D, "uModel", false, model.array()); | |
uniformMatrix("view3d", programObject3D, "uView", false, view.array()); | |
curContext.enable(curContext.POLYGON_OFFSET_FILL); | |
curContext.polygonOffset(1, 1); | |
uniformf("color3d", programObject3D, "uColor", fillStyle); | |
if (lightCount > 0) { | |
var v = new PMatrix3D; | |
v.set(view); | |
var m = new PMatrix3D; | |
m.set(model); | |
v.mult(m); | |
var normalMatrix = new PMatrix3D; | |
normalMatrix.set(v); | |
normalMatrix.invert(); | |
normalMatrix.transpose(); | |
uniformMatrix("uNormalTransform3d", programObject3D, "uNormalTransform", false, normalMatrix.array()); | |
vertexAttribPointer("aNormal3d", programObject3D, "aNormal", 3, boxNormBuffer) | |
} else disableVertexAttribPointer("aNormal3d", programObject3D, "aNormal"); | |
vertexAttribPointer("aVertex3d", programObject3D, "aVertex", 3, boxBuffer); | |
disableVertexAttribPointer("aColor3d", programObject3D, "aColor"); | |
disableVertexAttribPointer("aTexture3d", programObject3D, "aTexture"); | |
curContext.drawArrays(curContext.TRIANGLES, 0, boxVerts.length / 3); | |
curContext.disable(curContext.POLYGON_OFFSET_FILL) | |
} | |
if (lineWidth > 0 && doStroke) { | |
curContext.useProgram(programObject2D); | |
uniformMatrix("uModel2d", programObject2D, "uModel", false, model.array()); | |
uniformMatrix("uView2d", programObject2D, "uView", false, view.array()); | |
uniformf("uColor2d", programObject2D, "uColor", strokeStyle); | |
uniformi("uIsDrawingText2d", programObject2D, "uIsDrawingText", false); | |
vertexAttribPointer("vertex2d", programObject2D, "aVertex", 3, boxOutlineBuffer); | |
disableVertexAttribPointer("aTextureCoord2d", programObject2D, "aTextureCoord"); | |
curContext.drawArrays(curContext.LINES, 0, boxOutlineVerts.length / 3) | |
} | |
}; | |
var initSphere = function() { | |
var i; | |
sphereVerts = []; | |
for (i = 0; i < sphereDetailU; i++) { | |
sphereVerts.push(0); | |
sphereVerts.push(-1); | |
sphereVerts.push(0); | |
sphereVerts.push(sphereX[i]); | |
sphereVerts.push(sphereY[i]); | |
sphereVerts.push(sphereZ[i]) | |
} | |
sphereVerts.push(0); | |
sphereVerts.push(-1); | |
sphereVerts.push(0); | |
sphereVerts.push(sphereX[0]); | |
sphereVerts.push(sphereY[0]); | |
sphereVerts.push(sphereZ[0]); | |
var v1, v11, v2; | |
var voff = 0; | |
for (i = 2; i < sphereDetailV; i++) { | |
v1 = v11 = voff; | |
voff += sphereDetailU; | |
v2 = voff; | |
for (var j = 0; j < sphereDetailU; j++) { | |
sphereVerts.push(sphereX[v1]); | |
sphereVerts.push(sphereY[v1]); | |
sphereVerts.push(sphereZ[v1++]); | |
sphereVerts.push(sphereX[v2]); | |
sphereVerts.push(sphereY[v2]); | |
sphereVerts.push(sphereZ[v2++]) | |
} | |
v1 = v11; | |
v2 = voff; | |
sphereVerts.push(sphereX[v1]); | |
sphereVerts.push(sphereY[v1]); | |
sphereVerts.push(sphereZ[v1]); | |
sphereVerts.push(sphereX[v2]); | |
sphereVerts.push(sphereY[v2]); | |
sphereVerts.push(sphereZ[v2]) | |
} | |
for (i = 0; i < sphereDetailU; i++) { | |
v2 = voff + i; | |
sphereVerts.push(sphereX[v2]); | |
sphereVerts.push(sphereY[v2]); | |
sphereVerts.push(sphereZ[v2]); | |
sphereVerts.push(0); | |
sphereVerts.push(1); | |
sphereVerts.push(0) | |
} | |
sphereVerts.push(sphereX[voff]); | |
sphereVerts.push(sphereY[voff]); | |
sphereVerts.push(sphereZ[voff]); | |
sphereVerts.push(0); | |
sphereVerts.push(1); | |
sphereVerts.push(0); | |
curContext.bindBuffer(curContext.ARRAY_BUFFER, sphereBuffer); | |
curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array(sphereVerts), curContext.STATIC_DRAW) | |
}; | |
p.sphereDetail = function(ures, vres) { | |
var i; | |
if (arguments.length === 1) ures = vres = arguments[0]; | |
if (ures < 3) ures = 3; | |
if (vres < 2) vres = 2; | |
if (ures === sphereDetailU && vres === sphereDetailV) return; | |
var delta = 720 / ures; | |
var cx = new Float32Array(ures); | |
var cz = new Float32Array(ures); | |
for (i = 0; i < ures; i++) { | |
cx[i] = cosLUT[i * delta % 720 | 0]; | |
cz[i] = sinLUT[i * delta % 720 | 0] | |
} | |
var vertCount = ures * (vres - 1) + 2; | |
var currVert = 0; | |
sphereX = new Float32Array(vertCount); | |
sphereY = new Float32Array(vertCount); | |
sphereZ = new Float32Array(vertCount); | |
var angle_step = 720 * 0.5 / vres; | |
var angle = angle_step; | |
for (i = 1; i < vres; i++) { | |
var curradius = sinLUT[angle % 720 | 0]; | |
var currY = -cosLUT[angle % 720 | 0]; | |
for (var j = 0; j < ures; j++) { | |
sphereX[currVert] = cx[j] * curradius; | |
sphereY[currVert] = currY; | |
sphereZ[currVert++] = cz[j] * curradius | |
} | |
angle += angle_step | |
} | |
sphereDetailU = ures; | |
sphereDetailV = vres; | |
initSphere() | |
}; | |
Drawing2D.prototype.sphere = DrawingShared.prototype.a3DOnlyFunction; | |
Drawing3D.prototype.sphere = function() { | |
var sRad = arguments[0]; | |
if (sphereDetailU < 3 || sphereDetailV < 2) p.sphereDetail(30); | |
var model = new PMatrix3D; | |
model.scale(sRad, sRad, sRad); | |
var view = new PMatrix3D; | |
view.scale(1, -1, 1); | |
view.apply(modelView.array()); | |
view.transpose(); | |
if (doFill) { | |
if (lightCount > 0) { | |
var v = new PMatrix3D; | |
v.set(view); | |
var m = new PMatrix3D; | |
m.set(model); | |
v.mult(m); | |
var normalMatrix = new PMatrix3D; | |
normalMatrix.set(v); | |
normalMatrix.invert(); | |
normalMatrix.transpose(); | |
uniformMatrix("uNormalTransform3d", programObject3D, "uNormalTransform", false, normalMatrix.array()); | |
vertexAttribPointer("aNormal3d", programObject3D, "aNormal", 3, sphereBuffer) | |
} else disableVertexAttribPointer("aNormal3d", programObject3D, "aNormal"); | |
curContext.useProgram(programObject3D); | |
disableVertexAttribPointer("aTexture3d", programObject3D, "aTexture"); | |
uniformMatrix("uModel3d", programObject3D, "uModel", false, model.array()); | |
uniformMatrix("uView3d", programObject3D, "uView", false, view.array()); | |
vertexAttribPointer("aVertex3d", programObject3D, "aVertex", 3, sphereBuffer); | |
disableVertexAttribPointer("aColor3d", programObject3D, "aColor"); | |
curContext.enable(curContext.POLYGON_OFFSET_FILL); | |
curContext.polygonOffset(1, 1); | |
uniformf("uColor3d", programObject3D, "uColor", fillStyle); | |
curContext.drawArrays(curContext.TRIANGLE_STRIP, 0, sphereVerts.length / 3); | |
curContext.disable(curContext.POLYGON_OFFSET_FILL) | |
} | |
if (lineWidth > 0 && doStroke) { | |
curContext.useProgram(programObject2D); | |
uniformMatrix("uModel2d", programObject2D, "uModel", false, model.array()); | |
uniformMatrix("uView2d", programObject2D, "uView", false, view.array()); | |
vertexAttribPointer("aVertex2d", programObject2D, "aVertex", 3, sphereBuffer); | |
disableVertexAttribPointer("aTextureCoord2d", programObject2D, "aTextureCoord"); | |
uniformf("uColor2d", programObject2D, "uColor", strokeStyle); | |
uniformi("uIsDrawingText", programObject2D, "uIsDrawingText", false); | |
curContext.drawArrays(curContext.LINE_STRIP, 0, sphereVerts.length / 3) | |
} | |
}; | |
p.modelX = function(x, y, z) { | |
var mv = modelView.array(); | |
var ci = cameraInv.array(); | |
var ax = mv[0] * x + mv[1] * y + mv[2] * z + mv[3]; | |
var ay = mv[4] * x + mv[5] * y + mv[6] * z + mv[7]; | |
var az = mv[8] * x + mv[9] * y + mv[10] * z + mv[11]; | |
var aw = mv[12] * x + mv[13] * y + mv[14] * z + mv[15]; | |
var ox = ci[0] * ax + ci[1] * ay + ci[2] * az + ci[3] * aw; | |
var ow = ci[12] * ax + ci[13] * ay + ci[14] * az + ci[15] * aw; | |
return ow !== 0 ? ox / ow : ox | |
}; | |
p.modelY = function(x, y, z) { | |
var mv = modelView.array(); | |
var ci = cameraInv.array(); | |
var ax = mv[0] * x + mv[1] * y + mv[2] * z + mv[3]; | |
var ay = mv[4] * x + mv[5] * y + mv[6] * z + mv[7]; | |
var az = mv[8] * x + mv[9] * y + mv[10] * z + mv[11]; | |
var aw = mv[12] * x + mv[13] * y + mv[14] * z + mv[15]; | |
var oy = ci[4] * ax + ci[5] * ay + ci[6] * az + ci[7] * aw; | |
var ow = ci[12] * ax + ci[13] * ay + ci[14] * az + ci[15] * aw; | |
return ow !== 0 ? oy / ow : oy | |
}; | |
p.modelZ = function(x, y, z) { | |
var mv = modelView.array(); | |
var ci = cameraInv.array(); | |
var ax = mv[0] * x + mv[1] * y + mv[2] * z + mv[3]; | |
var ay = mv[4] * x + mv[5] * y + mv[6] * z + mv[7]; | |
var az = mv[8] * x + mv[9] * y + mv[10] * z + mv[11]; | |
var aw = mv[12] * x + mv[13] * y + mv[14] * z + mv[15]; | |
var oz = ci[8] * ax + ci[9] * ay + ci[10] * az + ci[11] * aw; | |
var ow = ci[12] * ax + ci[13] * ay + ci[14] * az + ci[15] * aw; | |
return ow !== 0 ? oz / ow : oz | |
}; | |
Drawing2D.prototype.ambient = DrawingShared.prototype.a3DOnlyFunction; | |
Drawing3D.prototype.ambient = function(v1, v2, v3) { | |
curContext.useProgram(programObject3D); | |
uniformi("uUsingMat3d", programObject3D, "uUsingMat", true); | |
var col = p.color(v1, v2, v3); | |
uniformf("uMaterialAmbient3d", programObject3D, "uMaterialAmbient", p.color.toGLArray(col).slice(0, 3)) | |
}; | |
Drawing2D.prototype.emissive = DrawingShared.prototype.a3DOnlyFunction; | |
Drawing3D.prototype.emissive = function(v1, v2, v3) { | |
curContext.useProgram(programObject3D); | |
uniformi("uUsingMat3d", programObject3D, "uUsingMat", true); | |
var col = p.color(v1, v2, v3); | |
uniformf("uMaterialEmissive3d", programObject3D, "uMaterialEmissive", p.color.toGLArray(col).slice(0, 3)) | |
}; | |
Drawing2D.prototype.shininess = DrawingShared.prototype.a3DOnlyFunction; | |
Drawing3D.prototype.shininess = function(shine) { | |
curContext.useProgram(programObject3D); | |
uniformi("uUsingMat3d", programObject3D, "uUsingMat", true); | |
uniformf("uShininess3d", programObject3D, "uShininess", shine) | |
}; | |
Drawing2D.prototype.specular = DrawingShared.prototype.a3DOnlyFunction; | |
Drawing3D.prototype.specular = function(v1, v2, v3) { | |
curContext.useProgram(programObject3D); | |
uniformi("uUsingMat3d", programObject3D, "uUsingMat", true); | |
var col = p.color(v1, v2, v3); | |
uniformf("uMaterialSpecular3d", programObject3D, "uMaterialSpecular", p.color.toGLArray(col).slice(0, 3)) | |
}; | |
p.screenX = function(x, y, z) { | |
var mv = modelView.array(); | |
if (mv.length === 16) { | |
var ax = mv[0] * x + mv[1] * y + mv[2] * z + mv[3]; | |
var ay = mv[4] * x + mv[5] * y + mv[6] * z + mv[7]; | |
var az = mv[8] * x + mv[9] * y + mv[10] * z + mv[11]; | |
var aw = mv[12] * x + mv[13] * y + mv[14] * z + mv[15]; | |
var pj = projection.array(); | |
var ox = pj[0] * ax + pj[1] * ay + pj[2] * az + pj[3] * aw; | |
var ow = pj[12] * ax + pj[13] * ay + pj[14] * az + pj[15] * aw; | |
if (ow !== 0) ox /= ow; | |
return p.width * (1 + ox) / 2 | |
} | |
return modelView.multX(x, y) | |
}; | |
p.screenY = function screenY(x, y, z) { | |
var mv = modelView.array(); | |
if (mv.length === 16) { | |
var ax = mv[0] * x + mv[1] * y + mv[2] * z + mv[3]; | |
var ay = mv[4] * x + mv[5] * y + mv[6] * z + mv[7]; | |
var az = mv[8] * x + mv[9] * y + mv[10] * z + mv[11]; | |
var aw = mv[12] * x + mv[13] * y + mv[14] * z + mv[15]; | |
var pj = projection.array(); | |
var oy = pj[4] * ax + pj[5] * ay + pj[6] * az + pj[7] * aw; | |
var ow = pj[12] * ax + pj[13] * ay + pj[14] * az + pj[15] * aw; | |
if (ow !== 0) oy /= ow; | |
return p.height * (1 + oy) / 2 | |
} | |
return modelView.multY(x, y) | |
}; | |
p.screenZ = function screenZ(x, y, z) { | |
var mv = modelView.array(); | |
if (mv.length !== 16) return 0; | |
var pj = projection.array(); | |
var ax = mv[0] * x + mv[1] * y + mv[2] * z + mv[3]; | |
var ay = mv[4] * x + mv[5] * y + mv[6] * z + mv[7]; | |
var az = mv[8] * x + mv[9] * y + mv[10] * z + mv[11]; | |
var aw = mv[12] * x + mv[13] * y + mv[14] * z + mv[15]; | |
var oz = pj[8] * ax + pj[9] * ay + pj[10] * az + pj[11] * aw; | |
var ow = pj[12] * ax + pj[13] * ay + pj[14] * az + pj[15] * aw; | |
if (ow !== 0) oz /= ow; | |
return (oz + 1) / 2 | |
}; | |
DrawingShared.prototype.fill = function() { | |
var color = p.color(arguments[0], arguments[1], arguments[2], arguments[3]); | |
if (color === currentFillColor && doFill) return; | |
doFill = true; | |
currentFillColor = color | |
}; | |
Drawing2D.prototype.fill = function() { | |
DrawingShared.prototype.fill.apply(this, arguments); | |
isFillDirty = true | |
}; | |
Drawing3D.prototype.fill = function() { | |
DrawingShared.prototype.fill.apply(this, arguments); | |
fillStyle = p.color.toGLArray(currentFillColor) | |
}; | |
function executeContextFill() { | |
if (doFill) { | |
if (isFillDirty) { | |
curContext.fillStyle = p.color.toString(currentFillColor); | |
isFillDirty = false | |
} | |
curContext.fill() | |
} | |
} | |
p.noFill = function() { | |
doFill = false | |
}; | |
DrawingShared.prototype.stroke = function() { | |
var color = p.color(arguments[0], arguments[1], arguments[2], arguments[3]); | |
if (color === currentStrokeColor && doStroke) return; | |
doStroke = true; | |
currentStrokeColor = color | |
}; | |
Drawing2D.prototype.stroke = function() { | |
DrawingShared.prototype.stroke.apply(this, arguments); | |
isStrokeDirty = true | |
}; | |
Drawing3D.prototype.stroke = function() { | |
DrawingShared.prototype.stroke.apply(this, arguments); | |
strokeStyle = p.color.toGLArray(currentStrokeColor) | |
}; | |
function executeContextStroke() { | |
if (doStroke) { | |
if (isStrokeDirty) { | |
curContext.strokeStyle = p.color.toString(currentStrokeColor); | |
isStrokeDirty = false | |
} | |
curContext.stroke() | |
} | |
} | |
p.noStroke = function() { | |
doStroke = false | |
}; | |
DrawingShared.prototype.strokeWeight = function(w) { | |
lineWidth = w | |
}; | |
Drawing2D.prototype.strokeWeight = function(w) { | |
DrawingShared.prototype.strokeWeight.apply(this, arguments); | |
curContext.lineWidth = w | |
}; | |
Drawing3D.prototype.strokeWeight = function(w) { | |
DrawingShared.prototype.strokeWeight.apply(this, arguments); | |
curContext.useProgram(programObject2D); | |
uniformf("pointSize2d", programObject2D, "uPointSize", w); | |
curContext.useProgram(programObjectUnlitShape); | |
uniformf("pointSizeUnlitShape", programObjectUnlitShape, "uPointSize", w); | |
curContext.lineWidth(w) | |
}; | |
p.strokeCap = function(value) { | |
drawing.$ensureContext().lineCap = value | |
}; | |
p.strokeJoin = function(value) { | |
drawing.$ensureContext().lineJoin = value | |
}; | |
Drawing2D.prototype.smooth = function() { | |
renderSmooth = true; | |
var style = curElement.style; | |
style.setProperty("image-rendering", "optimizeQuality", "important"); | |
style.setProperty("-ms-interpolation-mode", "bicubic", "important"); | |
if (curContext.hasOwnProperty("mozImageSmoothingEnabled")) curContext.mozImageSmoothingEnabled = true | |
}; | |
Drawing3D.prototype.smooth = function() { | |
renderSmooth = true | |
}; | |
Drawing2D.prototype.noSmooth = function() { | |
renderSmooth = false; | |
var style = curElement.style; | |
style.setProperty("image-rendering", "optimizeSpeed", "important"); | |
style.setProperty("image-rendering", "-moz-crisp-edges", "important"); | |
style.setProperty("image-rendering", "-webkit-optimize-contrast", "important"); | |
style.setProperty("image-rendering", "optimize-contrast", "important"); | |
style.setProperty("-ms-interpolation-mode", "nearest-neighbor", "important"); | |
if (curContext.hasOwnProperty("mozImageSmoothingEnabled")) curContext.mozImageSmoothingEnabled = false | |
}; | |
Drawing3D.prototype.noSmooth = function() { | |
renderSmooth = false | |
}; | |
Drawing2D.prototype.point = function(x, y) { | |
if (!doStroke) return; | |
x = Math.round(x); | |
y = Math.round(y); | |
curContext.fillStyle = p.color.toString(currentStrokeColor); | |
isFillDirty = true; | |
if (lineWidth > 1) { | |
curContext.beginPath(); | |
curContext.arc(x, y, lineWidth / 2, 0, 6.283185307179586, false); | |
curContext.fill() | |
} else curContext.fillRect(x, y, 1, 1) | |
}; | |
Drawing3D.prototype.point = function(x, y, z) { | |
var model = new PMatrix3D; | |
model.translate(x, y, z || 0); | |
model.transpose(); | |
var view = new PMatrix3D; | |
view.scale(1, -1, 1); | |
view.apply(modelView.array()); | |
view.transpose(); | |
curContext.useProgram(programObject2D); | |
uniformMatrix("uModel2d", programObject2D, "uModel", false, model.array()); | |
uniformMatrix("uView2d", programObject2D, "uView", false, view.array()); | |
if (lineWidth > 0 && doStroke) { | |
uniformf("uColor2d", programObject2D, "uColor", strokeStyle); | |
uniformi("uIsDrawingText2d", programObject2D, "uIsDrawingText", false); | |
uniformi("uSmooth2d", programObject2D, "uSmooth", renderSmooth); | |
vertexAttribPointer("aVertex2d", programObject2D, "aVertex", 3, pointBuffer); | |
disableVertexAttribPointer("aTextureCoord2d", programObject2D, "aTextureCoord"); | |
curContext.drawArrays(curContext.POINTS, 0, 1) | |
} | |
}; | |
p.beginShape = function(type) { | |
curShape = type; | |
curvePoints = []; | |
vertArray = [] | |
}; | |
Drawing2D.prototype.vertex = function(x, y, moveTo) { | |
var vert = []; | |
if (firstVert) firstVert = false; | |
vert["isVert"] = true; | |
vert[0] = x; | |
vert[1] = y; | |
vert[2] = 0; | |
vert[3] = 0; | |
vert[4] = 0; | |
vert[5] = currentFillColor; | |
vert[6] = currentStrokeColor; | |
vertArray.push(vert); | |
if (moveTo) vertArray[vertArray.length - 1]["moveTo"] = moveTo | |
}; | |
Drawing3D.prototype.vertex = function(x, y, z, u, v) { | |
var vert = []; | |
if (firstVert) firstVert = false; | |
vert["isVert"] = true; | |
if (v === undef && usingTexture) { | |
v = u; | |
u = z; | |
z = 0 | |
} | |
if (u !== undef && v !== undef) { | |
if (curTextureMode === 2) { | |
u /= curTexture.width; | |
v /= curTexture.height | |
} | |
u = u > 1 ? 1 : u; | |
u = u < 0 ? 0 : u; | |
v = v > 1 ? 1 : v; | |
v = v < 0 ? 0 : v | |
} | |
vert[0] = x; | |
vert[1] = y; | |
vert[2] = z || 0; | |
vert[3] = u || 0; | |
vert[4] = v || 0; | |
vert[5] = fillStyle[0]; | |
vert[6] = fillStyle[1]; | |
vert[7] = fillStyle[2]; | |
vert[8] = fillStyle[3]; | |
vert[9] = strokeStyle[0]; | |
vert[10] = strokeStyle[1]; | |
vert[11] = strokeStyle[2]; | |
vert[12] = strokeStyle[3]; | |
vert[13] = normalX; | |
vert[14] = normalY; | |
vert[15] = normalZ; | |
vertArray.push(vert) | |
}; | |
var point3D = function(vArray, cArray) { | |
var view = new PMatrix3D; | |
view.scale(1, -1, 1); | |
view.apply(modelView.array()); | |
view.transpose(); | |
curContext.useProgram(programObjectUnlitShape); | |
uniformMatrix("uViewUS", programObjectUnlitShape, "uView", false, view.array()); | |
uniformi("uSmoothUS", programObjectUnlitShape, "uSmooth", renderSmooth); | |
vertexAttribPointer("aVertexUS", programObjectUnlitShape, "aVertex", 3, pointBuffer); | |
curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array(vArray), curContext.STREAM_DRAW); | |
vertexAttribPointer("aColorUS", programObjectUnlitShape, "aColor", 4, fillColorBuffer); | |
curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array(cArray), curContext.STREAM_DRAW); | |
curContext.drawArrays(curContext.POINTS, 0, vArray.length / 3) | |
}; | |
var line3D = function(vArray, mode, cArray) { | |
var ctxMode; | |
if (mode === "LINES") ctxMode = curContext.LINES; | |
else if (mode === "LINE_LOOP") ctxMode = curContext.LINE_LOOP; | |
else ctxMode = curContext.LINE_STRIP; | |
var view = new PMatrix3D; | |
view.scale(1, -1, 1); | |
view.apply(modelView.array()); | |
view.transpose(); | |
curContext.useProgram(programObjectUnlitShape); | |
uniformMatrix("uViewUS", programObjectUnlitShape, "uView", false, view.array()); | |
vertexAttribPointer("aVertexUS", programObjectUnlitShape, "aVertex", 3, lineBuffer); | |
curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array(vArray), curContext.STREAM_DRAW); | |
vertexAttribPointer("aColorUS", programObjectUnlitShape, "aColor", 4, strokeColorBuffer); | |
curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array(cArray), curContext.STREAM_DRAW); | |
curContext.drawArrays(ctxMode, 0, vArray.length / 3) | |
}; | |
var fill3D = function(vArray, mode, cArray, tArray) { | |
var ctxMode; | |
if (mode === "TRIANGLES") ctxMode = curContext.TRIANGLES; | |
else if (mode === "TRIANGLE_FAN") ctxMode = curContext.TRIANGLE_FAN; | |
else ctxMode = curContext.TRIANGLE_STRIP; | |
var view = new PMatrix3D; | |
view.scale(1, -1, 1); | |
view.apply(modelView.array()); | |
view.transpose(); | |
curContext.useProgram(programObject3D); | |
uniformMatrix("model3d", programObject3D, "uModel", false, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]); | |
uniformMatrix("view3d", programObject3D, "uView", false, view.array()); | |
curContext.enable(curContext.POLYGON_OFFSET_FILL); | |
curContext.polygonOffset(1, 1); | |
uniformf("color3d", programObject3D, "uColor", [-1, 0, 0, 0]); | |
vertexAttribPointer("vertex3d", programObject3D, "aVertex", 3, fillBuffer); | |
curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array(vArray), curContext.STREAM_DRAW); | |
if (usingTexture && curTint !== null) curTint3d(cArray); | |
vertexAttribPointer("aColor3d", programObject3D, "aColor", 4, fillColorBuffer); | |
curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array(cArray), curContext.STREAM_DRAW); | |
disableVertexAttribPointer("aNormal3d", programObject3D, "aNormal"); | |
if (usingTexture) { | |
uniformi("uUsingTexture3d", programObject3D, "uUsingTexture", usingTexture); | |
vertexAttribPointer("aTexture3d", programObject3D, "aTexture", 2, shapeTexVBO); | |
curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array(tArray), curContext.STREAM_DRAW) | |
} | |
curContext.drawArrays(ctxMode, 0, vArray.length / 3); | |
curContext.disable(curContext.POLYGON_OFFSET_FILL) | |
}; | |
function fillStrokeClose() { | |
executeContextFill(); | |
executeContextStroke(); | |
curContext.closePath() | |
} | |
Drawing2D.prototype.endShape = function(mode) { | |
if (vertArray.length === 0) return; | |
var closeShape = mode === 2; | |
if (closeShape) vertArray.push(vertArray[0]); | |
var lineVertArray = []; | |
var fillVertArray = []; | |
var colorVertArray = []; | |
var strokeVertArray = []; | |
var texVertArray = []; | |
var cachedVertArray; | |
firstVert = true; | |
var i, j, k; | |
var vertArrayLength = vertArray.length; | |
for (i = 0; i < vertArrayLength; i++) { | |
cachedVertArray = vertArray[i]; | |
for (j = 0; j < 3; j++) fillVertArray.push(cachedVertArray[j]) | |
} | |
for (i = 0; i < vertArrayLength; i++) { | |
cachedVertArray = vertArray[i]; | |
for (j = 5; j < 9; j++) colorVertArray.push(cachedVertArray[j]) | |
} | |
for (i = 0; i < vertArrayLength; i++) { | |
cachedVertArray = vertArray[i]; | |
for (j = 9; j < 13; j++) strokeVertArray.push(cachedVertArray[j]) | |
} | |
for (i = 0; i < vertArrayLength; i++) { | |
cachedVertArray = vertArray[i]; | |
texVertArray.push(cachedVertArray[3]); | |
texVertArray.push(cachedVertArray[4]) | |
} | |
if (isCurve && (curShape === 20 || curShape === undef)) { | |
if (vertArrayLength > 3) { | |
var b = [], | |
s = 1 - curTightness; | |
curContext.beginPath(); | |
curContext.moveTo(vertArray[1][0], vertArray[1][1]); | |
for (i = 1; i + 2 < vertArrayLength; i++) { | |
cachedVertArray = vertArray[i]; | |
b[0] = [cachedVertArray[0], cachedVertArray[1]]; | |
b[1] = [cachedVertArray[0] + (s * vertArray[i + 1][0] - s * vertArray[i - 1][0]) / 6, cachedVertArray[1] + (s * vertArray[i + 1][1] - s * vertArray[i - 1][1]) / 6]; | |
b[2] = [vertArray[i + 1][0] + (s * vertArray[i][0] - s * vertArray[i + 2][0]) / 6, vertArray[i + 1][1] + (s * vertArray[i][1] - s * vertArray[i + 2][1]) / 6]; | |
b[3] = [vertArray[i + 1][0], vertArray[i + 1][1]]; | |
curContext.bezierCurveTo(b[1][0], b[1][1], b[2][0], b[2][1], b[3][0], b[3][1]) | |
} | |
fillStrokeClose() | |
} | |
} else if (isBezier && (curShape === 20 || curShape === undef)) { | |
curContext.beginPath(); | |
for (i = 0; i < vertArrayLength; i++) { | |
cachedVertArray = vertArray[i]; | |
if (vertArray[i]["isVert"]) if (vertArray[i]["moveTo"]) curContext.moveTo(cachedVertArray[0], cachedVertArray[1]); | |
else curContext.lineTo(cachedVertArray[0], cachedVertArray[1]); | |
else curContext.bezierCurveTo(vertArray[i][0], vertArray[i][1], vertArray[i][2], vertArray[i][3], vertArray[i][4], vertArray[i][5]) | |
} | |
fillStrokeClose() | |
} else if (curShape === 2) for (i = 0; i < vertArrayLength; i++) { | |
cachedVertArray = vertArray[i]; | |
if (doStroke) p.stroke(cachedVertArray[6]); | |
p.point(cachedVertArray[0], cachedVertArray[1]) | |
} else if (curShape === 4) for (i = 0; i + 1 < vertArrayLength; i += 2) { | |
cachedVertArray = vertArray[i]; | |
if (doStroke) p.stroke(vertArray[i + 1][6]); | |
p.line(cachedVertArray[0], cachedVertArray[1], vertArray[i + 1][0], vertArray[i + 1][1]) | |
} else if (curShape === 9) for (i = 0; i + 2 < vertArrayLength; i += 3) { | |
cachedVertArray = vertArray[i]; | |
curContext.beginPath(); | |
curContext.moveTo(cachedVertArray[0], cachedVertArray[1]); | |
curContext.lineTo(vertArray[i + 1][0], vertArray[i + 1][1]); | |
curContext.lineTo(vertArray[i + 2][0], vertArray[i + 2][1]); | |
curContext.lineTo(cachedVertArray[0], cachedVertArray[1]); | |
if (doFill) { | |
p.fill(vertArray[i + 2][5]); | |
executeContextFill() | |
} | |
if (doStroke) { | |
p.stroke(vertArray[i + 2][6]); | |
executeContextStroke() | |
} | |
curContext.closePath() | |
} else if (curShape === 10) for (i = 0; i + 1 < vertArrayLength; i++) { | |
cachedVertArray = vertArray[i]; | |
curContext.beginPath(); | |
curContext.moveTo(vertArray[i + 1][0], vertArray[i + 1][1]); | |
curContext.lineTo(cachedVertArray[0], cachedVertArray[1]); | |
if (doStroke) p.stroke(vertArray[i + 1][6]); | |
if (doFill) p.fill(vertArray[i + 1][5]); | |
if (i + 2 < vertArrayLength) { | |
curContext.lineTo(vertArray[i + 2][0], vertArray[i + 2][1]); | |
if (doStroke) p.stroke(vertArray[i + 2][6]); | |
if (doFill) p.fill(vertArray[i + 2][5]) | |
} | |
fillStrokeClose() | |
} else if (curShape === 11) { | |
if (vertArrayLength > 2) { | |
curContext.beginPath(); | |
curContext.moveTo(vertArray[0][0], vertArray[0][1]); | |
curContext.lineTo(vertArray[1][0], vertArray[1][1]); | |
curContext.lineTo(vertArray[2][0], vertArray[2][1]); | |
if (doFill) { | |
p.fill(vertArray[2][5]); | |
executeContextFill() | |
} | |
if (doStroke) { | |
p.stroke(vertArray[2][6]); | |
executeContextStroke() | |
} | |
curContext.closePath(); | |
for (i = 3; i < vertArrayLength; i++) { | |
cachedVertArray = vertArray[i]; | |
curContext.beginPath(); | |
curContext.moveTo(vertArray[0][0], vertArray[0][1]); | |
curContext.lineTo(vertArray[i - 1][0], vertArray[i - 1][1]); | |
curContext.lineTo(cachedVertArray[0], cachedVertArray[1]); | |
if (doFill) { | |
p.fill(cachedVertArray[5]); | |
executeContextFill() | |
} | |
if (doStroke) { | |
p.stroke(cachedVertArray[6]); | |
executeContextStroke() | |
} | |
curContext.closePath() | |
} | |
} | |
} else if (curShape === 16) for (i = 0; i + 3 < vertArrayLength; i += 4) { | |
cachedVertArray = vertArray[i]; | |
curContext.beginPath(); | |
curContext.moveTo(cachedVertArray[0], cachedVertArray[1]); | |
for (j = 1; j < 4; j++) curContext.lineTo(vertArray[i + j][0], vertArray[i + j][1]); | |
curContext.lineTo(cachedVertArray[0], cachedVertArray[1]); | |
if (doFill) { | |
p.fill(vertArray[i + 3][5]); | |
executeContextFill() | |
} | |
if (doStroke) { | |
p.stroke(vertArray[i + 3][6]); | |
executeContextStroke() | |
} | |
curContext.closePath() | |
} else if (curShape === 17) { | |
if (vertArrayLength > 3) for (i = 0; i + 1 < vertArrayLength; i += 2) { | |
cachedVertArray = vertArray[i]; | |
curContext.beginPath(); | |
if (i + 3 < vertArrayLength) { | |
curContext.moveTo(vertArray[i + 2][0], vertArray[i + 2][1]); | |
curContext.lineTo(cachedVertArray[0], cachedVertArray[1]); | |
curContext.lineTo(vertArray[i + 1][0], vertArray[i + 1][1]); | |
curContext.lineTo(vertArray[i + 3][0], vertArray[i + 3][1]); | |
if (doFill) p.fill(vertArray[i + 3][5]); | |
if (doStroke) p.stroke(vertArray[i + 3][6]) | |
} else { | |
curContext.moveTo(cachedVertArray[0], cachedVertArray[1]); | |
curContext.lineTo(vertArray[i + 1][0], vertArray[i + 1][1]) | |
} | |
fillStrokeClose() | |
} | |
} else { | |
curContext.beginPath(); | |
curContext.moveTo(vertArray[0][0], vertArray[0][1]); | |
for (i = 1; i < vertArrayLength; i++) { | |
cachedVertArray = vertArray[i]; | |
if (cachedVertArray["isVert"]) if (cachedVertArray["moveTo"]) curContext.moveTo(cachedVertArray[0], cachedVertArray[1]); | |
else curContext.lineTo(cachedVertArray[0], cachedVertArray[1]) | |
} | |
fillStrokeClose() | |
} | |
isCurve = false; | |
isBezier = false; | |
curveVertArray = []; | |
curveVertCount = 0; | |
if (closeShape) vertArray.pop() | |
}; | |
Drawing3D.prototype.endShape = function(mode) { | |
if (vertArray.length === 0) return; | |
var closeShape = mode === 2; | |
var lineVertArray = []; | |
var fillVertArray = []; | |
var colorVertArray = []; | |
var strokeVertArray = []; | |
var texVertArray = []; | |
var cachedVertArray; | |
firstVert = true; | |
var i, j, k; | |
var vertArrayLength = vertArray.length; | |
for (i = 0; i < vertArrayLength; i++) { | |
cachedVertArray = vertArray[i]; | |
for (j = 0; j < 3; j++) fillVertArray.push(cachedVertArray[j]) | |
} | |
for (i = 0; i < vertArrayLength; i++) { | |
cachedVertArray = vertArray[i]; | |
for (j = 5; j < 9; j++) colorVertArray.push(cachedVertArray[j]) | |
} | |
for (i = 0; i < vertArrayLength; i++) { | |
cachedVertArray = vertArray[i]; | |
for (j = 9; j < 13; j++) strokeVertArray.push(cachedVertArray[j]) | |
} | |
for (i = 0; i < vertArrayLength; i++) { | |
cachedVertArray = vertArray[i]; | |
texVertArray.push(cachedVertArray[3]); | |
texVertArray.push(cachedVertArray[4]) | |
} | |
if (closeShape) { | |
fillVertArray.push(vertArray[0][0]); | |
fillVertArray.push(vertArray[0][1]); | |
fillVertArray.push(vertArray[0][2]); | |
for (i = 5; i < 9; i++) colorVertArray.push(vertArray[0][i]); | |
for (i = 9; i < 13; i++) strokeVertArray.push(vertArray[0][i]); | |
texVertArray.push(vertArray[0][3]); | |
texVertArray.push(vertArray[0][4]) | |
} | |
if (isCurve && (curShape === 20 || curShape === undef)) { | |
lineVertArray = fillVertArray; | |
if (doStroke) line3D(lineVertArray, null, strokeVertArray); | |
if (doFill) fill3D(fillVertArray, null, colorVertArray) | |
} else if (isBezier && (curShape === 20 || curShape === undef)) { | |
lineVertArray = fillVertArray; | |
lineVertArray.splice(lineVertArray.length - 3); | |
strokeVertArray.splice(strokeVertArray.length - 4); | |
if (doStroke) line3D(lineVertArray, null, strokeVertArray); | |
if (doFill) fill3D(fillVertArray, "TRIANGLES", colorVertArray) | |
} else { | |
if (curShape === 2) { | |
for (i = 0; i < vertArrayLength; i++) { | |
cachedVertArray = vertArray[i]; | |
for (j = 0; j < 3; j++) lineVertArray.push(cachedVertArray[j]) | |
} | |
point3D(lineVertArray, strokeVertArray) | |
} else if (curShape === 4) { | |
for (i = 0; i < vertArrayLength; i++) { | |
cachedVertArray = vertArray[i]; | |
for (j = 0; j < 3; j++) lineVertArray.push(cachedVertArray[j]) | |
} | |
for (i = 0; i < vertArrayLength; i++) { | |
cachedVertArray = vertArray[i]; | |
for (j = 5; j < 9; j++) colorVertArray.push(cachedVertArray[j]) | |
} | |
line3D(lineVertArray, "LINES", strokeVertArray) | |
} else if (curShape === 9) { | |
if (vertArrayLength > 2) for (i = 0; i + 2 < vertArrayLength; i += 3) { | |
fillVertArray = []; | |
texVertArray = []; | |
lineVertArray = []; | |
colorVertArray = []; | |
strokeVertArray = []; | |
for (j = 0; j < 3; j++) for (k = 0; k < 3; k++) { | |
lineVertArray.push(vertArray[i + j][k]); | |
fillVertArray.push(vertArray[i + j][k]) | |
} | |
for (j = 0; j < 3; j++) for (k = 3; k < 5; k++) texVertArray.push(vertArray[i + j][k]); | |
for (j = 0; j < 3; j++) for (k = 5; k < 9; k++) { | |
colorVertArray.push(vertArray[i + j][k]); | |
strokeVertArray.push(vertArray[i + j][k + 4]) | |
} | |
if (doStroke) line3D(lineVertArray, "LINE_LOOP", strokeVertArray); | |
if (doFill || usingTexture) fill3D(fillVertArray, "TRIANGLES", colorVertArray, texVertArray) | |
} | |
} else if (curShape === 10) { | |
if (vertArrayLength > 2) for (i = 0; i + 2 < vertArrayLength; i++) { | |
lineVertArray = []; | |
fillVertArray = []; | |
strokeVertArray = []; | |
colorVertArray = []; | |
texVertArray = []; | |
for (j = 0; j < 3; j++) for (k = 0; k < 3; k++) { | |
lineVertArray.push(vertArray[i + j][k]); | |
fillVertArray.push(vertArray[i + j][k]) | |
} | |
for (j = 0; j < 3; j++) for (k = 3; k < 5; k++) texVertArray.push(vertArray[i + j][k]); | |
for (j = 0; j < 3; j++) for (k = 5; k < 9; k++) { | |
strokeVertArray.push(vertArray[i + j][k + 4]); | |
colorVertArray.push(vertArray[i + j][k]) | |
} | |
if (doFill || usingTexture) fill3D(fillVertArray, "TRIANGLE_STRIP", colorVertArray, texVertArray); | |
if (doStroke) line3D(lineVertArray, "LINE_LOOP", strokeVertArray) | |
} | |
} else if (curShape === 11) { | |
if (vertArrayLength > 2) { | |
for (i = 0; i < 3; i++) { | |
cachedVertArray = vertArray[i]; | |
for (j = 0; j < 3; j++) lineVertArray.push(cachedVertArray[j]) | |
} | |
for (i = 0; i < 3; i++) { | |
cachedVertArray = vertArray[i]; | |
for (j = 9; j < 13; j++) strokeVertArray.push(cachedVertArray[j]) | |
} | |
if (doStroke) line3D(lineVertArray, "LINE_LOOP", strokeVertArray); | |
for (i = 2; i + 1 < vertArrayLength; i++) { | |
lineVertArray = []; | |
strokeVertArray = []; | |
lineVertArray.push(vertArray[0][0]); | |
lineVertArray.push(vertArray[0][1]); | |
lineVertArray.push(vertArray[0][2]); | |
strokeVertArray.push(vertArray[0][9]); | |
strokeVertArray.push(vertArray[0][10]); | |
strokeVertArray.push(vertArray[0][11]); | |
strokeVertArray.push(vertArray[0][12]); | |
for (j = 0; j < 2; j++) for (k = 0; k < 3; k++) lineVertArray.push(vertArray[i + j][k]); | |
for (j = 0; j < 2; j++) for (k = 9; k < 13; k++) strokeVertArray.push(vertArray[i + j][k]); | |
if (doStroke) line3D(lineVertArray, "LINE_STRIP", strokeVertArray) | |
} | |
if (doFill || usingTexture) fill3D(fillVertArray, "TRIANGLE_FAN", colorVertArray, texVertArray) | |
} | |
} else if (curShape === 16) for (i = 0; i + 3 < vertArrayLength; i += 4) { | |
lineVertArray = []; | |
for (j = 0; j < 4; j++) { | |
cachedVertArray = vertArray[i + j]; | |
for (k = 0; k < 3; k++) lineVertArray.push(cachedVertArray[k]) | |
} | |
if (doStroke) line3D(lineVertArray, "LINE_LOOP", strokeVertArray); | |
if (doFill) { | |
fillVertArray = []; | |
colorVertArray = []; | |
texVertArray = []; | |
for (j = 0; j < 3; j++) fillVertArray.push(vertArray[i][j]); | |
for (j = 5; j < 9; j++) colorVertArray.push(vertArray[i][j]); | |
for (j = 0; j < 3; j++) fillVertArray.push(vertArray[i + 1][j]); | |
for (j = 5; j < 9; j++) colorVertArray.push(vertArray[i + 1][j]); | |
for (j = 0; j < 3; j++) fillVertArray.push(vertArray[i + 3][j]); | |
for (j = 5; j < 9; j++) colorVertArray.push(vertArray[i + 3][j]); | |
for (j = 0; j < 3; j++) fillVertArray.push(vertArray[i + 2][j]); | |
for (j = 5; j < 9; j++) colorVertArray.push(vertArray[i + 2][j]); | |
if (usingTexture) { | |
texVertArray.push(vertArray[i + 0][3]); | |
texVertArray.push(vertArray[i + 0][4]); | |
texVertArray.push(vertArray[i + 1][3]); | |
texVertArray.push(vertArray[i + 1][4]); | |
texVertArray.push(vertArray[i + 3][3]); | |
texVertArray.push(vertArray[i + 3][4]); | |
texVertArray.push(vertArray[i + 2][3]); | |
texVertArray.push(vertArray[i + 2][4]) | |
} | |
fill3D(fillVertArray, "TRIANGLE_STRIP", colorVertArray, texVertArray) | |
} | |
} else if (curShape === 17) { | |
var tempArray = []; | |
if (vertArrayLength > 3) { | |
for (i = 0; i < 2; i++) { | |
cachedVertArray = vertArray[i]; | |
for (j = 0; j < 3; j++) lineVertArray.push(cachedVertArray[j]) | |
} | |
for (i = 0; i < 2; i++) { | |
cachedVertArray = vertArray[i]; | |
for (j = 9; j < 13; j++) strokeVertArray.push(cachedVertArray[j]) | |
} | |
line3D(lineVertArray, "LINE_STRIP", strokeVertArray); | |
if (vertArrayLength > 4 && vertArrayLength % 2 > 0) { | |
tempArray = fillVertArray.splice(fillVertArray.length - 3); | |
vertArray.pop() | |
} | |
for (i = 0; i + 3 < vertArrayLength; i += 2) { | |
lineVertArray = []; | |
strokeVertArray = []; | |
for (j = 0; j < 3; j++) lineVertArray.push(vertArray[i + 1][j]); | |
for (j = 0; j < 3; j++) lineVertArray.push(vertArray[i + 3][j]); | |
for (j = 0; j < 3; j++) lineVertArray.push(vertArray[i + 2][j]); | |
for (j = 0; j < 3; j++) lineVertArray.push(vertArray[i + 0][j]); | |
for (j = 9; j < 13; j++) strokeVertArray.push(vertArray[i + 1][j]); | |
for (j = 9; j < 13; j++) strokeVertArray.push(vertArray[i + 3][j]); | |
for (j = 9; j < 13; j++) strokeVertArray.push(vertArray[i + 2][j]); | |
for (j = 9; j < 13; j++) strokeVertArray.push(vertArray[i + 0][j]); | |
if (doStroke) line3D(lineVertArray, "LINE_STRIP", strokeVertArray) | |
} | |
if (doFill || usingTexture) fill3D(fillVertArray, "TRIANGLE_LIST", colorVertArray, texVertArray) | |
} | |
} else if (vertArrayLength === 1) { | |
for (j = 0; j < 3; j++) lineVertArray.push(vertArray[0][j]); | |
for (j = 9; j < 13; j++) strokeVertArray.push(vertArray[0][j]); | |
point3D(lineVertArray, strokeVertArray) | |
} else { | |
for (i = 0; i < vertArrayLength; i++) { | |
cachedVertArray = vertArray[i]; | |
for (j = 0; j < 3; j++) lineVertArray.push(cachedVertArray[j]); | |
for (j = 5; j < 9; j++) strokeVertArray.push(cachedVertArray[j]) | |
} | |
if (doStroke && closeShape) line3D(lineVertArray, "LINE_LOOP", strokeVertArray); | |
else if (doStroke && !closeShape) line3D(lineVertArray, "LINE_STRIP", strokeVertArray); | |
if (doFill || usingTexture) fill3D(fillVertArray, "TRIANGLE_FAN", colorVertArray, texVertArray) | |
} | |
usingTexture = false; | |
curContext.useProgram(programObject3D); | |
uniformi("usingTexture3d", programObject3D, "uUsingTexture", usingTexture) | |
} | |
isCurve = false; | |
isBezier = false; | |
curveVertArray = []; | |
curveVertCount = 0 | |
}; | |
var splineForward = function(segments, matrix) { | |
var f = 1 / segments; | |
var ff = f * f; | |
var fff = ff * f; | |
matrix.set(0, 0, 0, 1, fff, ff, f, 0, 6 * fff, 2 * ff, 0, 0, 6 * fff, 0, 0, 0) | |
}; | |
var curveInit = function() { | |
if (!curveDrawMatrix) { | |
curveBasisMatrix = new PMatrix3D; | |
curveDrawMatrix = new PMatrix3D; | |
curveInited = true | |
} | |
var s = curTightness; | |
curveBasisMatrix.set((s - 1) / 2, (s + 3) / 2, (-3 - s) / 2, (1 - s) / 2, 1 - s, (-5 - s) / 2, s + 2, (s - 1) / 2, (s - 1) / 2, 0, (1 - s) / 2, 0, 0, 1, 0, 0); | |
splineForward(curveDet, curveDrawMatrix); | |
if (!bezierBasisInverse) curveToBezierMatrix = new PMatrix3D; | |
curveToBezierMatrix.set(curveBasisMatrix); | |
curveToBezierMatrix.preApply(bezierBasisInverse); | |
curveDrawMatrix.apply(curveBasisMatrix) | |
}; | |
Drawing2D.prototype.bezierVertex = function() { | |
isBezier = true; | |
var vert = []; | |
if (firstVert) throw "vertex() must be used at least once before calling bezierVertex()"; | |
for (var i = 0; i < arguments.length; i++) vert[i] = arguments[i]; | |
vertArray.push(vert); | |
vertArray[vertArray.length - 1]["isVert"] = false | |
}; | |
Drawing3D.prototype.bezierVertex = function() { | |
isBezier = true; | |
var vert = []; | |
if (firstVert) throw "vertex() must be used at least once before calling bezierVertex()"; | |
if (arguments.length === 9) { | |
if (bezierDrawMatrix === undef) bezierDrawMatrix = new PMatrix3D; | |
var lastPoint = vertArray.length - 1; | |
splineForward(bezDetail, bezierDrawMatrix); | |
bezierDrawMatrix.apply(bezierBasisMatrix); | |
var draw = bezierDrawMatrix.array(); | |
var x1 = vertArray[lastPoint][0], | |
y1 = vertArray[lastPoint][1], | |
z1 = vertArray[lastPoint][2]; | |
var xplot1 = draw[4] * x1 + draw[5] * arguments[0] + draw[6] * arguments[3] + draw[7] * arguments[6]; | |
var xplot2 = draw[8] * x1 + draw[9] * arguments[0] + draw[10] * arguments[3] + draw[11] * arguments[6]; | |
var xplot3 = draw[12] * x1 + draw[13] * arguments[0] + draw[14] * arguments[3] + draw[15] * arguments[6]; | |
var yplot1 = draw[4] * y1 + draw[5] * arguments[1] + draw[6] * arguments[4] + draw[7] * arguments[7]; | |
var yplot2 = draw[8] * y1 + draw[9] * arguments[1] + draw[10] * arguments[4] + draw[11] * arguments[7]; | |
var yplot3 = draw[12] * y1 + draw[13] * arguments[1] + draw[14] * arguments[4] + draw[15] * arguments[7]; | |
var zplot1 = draw[4] * z1 + draw[5] * arguments[2] + draw[6] * arguments[5] + draw[7] * arguments[8]; | |
var zplot2 = draw[8] * z1 + draw[9] * arguments[2] + draw[10] * arguments[5] + draw[11] * arguments[8]; | |
var zplot3 = draw[12] * z1 + draw[13] * arguments[2] + draw[14] * arguments[5] + draw[15] * arguments[8]; | |
for (var j = 0; j < bezDetail; j++) { | |
x1 += xplot1; | |
xplot1 += xplot2; | |
xplot2 += xplot3; | |
y1 += yplot1; | |
yplot1 += yplot2; | |
yplot2 += yplot3; | |
z1 += zplot1; | |
zplot1 += zplot2; | |
zplot2 += zplot3; | |
p.vertex(x1, y1, z1) | |
} | |
p.vertex(arguments[6], arguments[7], arguments[8]) | |
} | |
}; | |
p.texture = function(pimage) { | |
var curContext = drawing.$ensureContext(); | |
if (pimage.__texture) curContext.bindTexture(curContext.TEXTURE_2D, pimage.__texture); | |
else if (pimage.localName === "canvas") { | |
curContext.bindTexture(curContext.TEXTURE_2D, canTex); | |
curContext.texImage2D(curContext.TEXTURE_2D, 0, curContext.RGBA, curContext.RGBA, curContext.UNSIGNED_BYTE, pimage); | |
curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_MAG_FILTER, curContext.LINEAR); | |
curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_MIN_FILTER, curContext.LINEAR); | |
curContext.generateMipmap(curContext.TEXTURE_2D); | |
curTexture.width = pimage.width; | |
curTexture.height = pimage.height | |
} else { | |
var texture = curContext.createTexture(), | |
cvs = document.createElement("canvas"), | |
cvsTextureCtx = cvs.getContext("2d"), | |
pot; | |
if (pimage.width & pimage.width - 1 === 0) cvs.width = pimage.width; | |
else { | |
pot = 1; | |
while (pot < pimage.width) pot *= 2; | |
cvs.width = pot | |
} | |
if (pimage.height & pimage.height - 1 === 0) cvs.height = pimage.height; | |
else { | |
pot = 1; | |
while (pot < pimage.height) pot *= 2; | |
cvs.height = pot | |
} | |
cvsTextureCtx.drawImage(pimage.sourceImg, 0, 0, pimage.width, pimage.height, 0, 0, cvs.width, cvs.height); | |
curContext.bindTexture(curContext.TEXTURE_2D, texture); | |
curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_MIN_FILTER, curContext.LINEAR_MIPMAP_LINEAR); | |
curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_MAG_FILTER, curContext.LINEAR); | |
curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_WRAP_T, curContext.CLAMP_TO_EDGE); | |
curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_WRAP_S, curContext.CLAMP_TO_EDGE); | |
curContext.texImage2D(curContext.TEXTURE_2D, 0, curContext.RGBA, curContext.RGBA, curContext.UNSIGNED_BYTE, cvs); | |
curContext.generateMipmap(curContext.TEXTURE_2D); | |
pimage.__texture = texture; | |
curTexture.width = pimage.width; | |
curTexture.height = pimage.height | |
} | |
usingTexture = true; | |
curContext.useProgram(programObject3D); | |
uniformi("usingTexture3d", programObject3D, "uUsingTexture", usingTexture) | |
}; | |
p.textureMode = function(mode) { | |
curTextureMode = mode | |
}; | |
var curveVertexSegment = function(x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4) { | |
var x0 = x2; | |
var y0 = y2; | |
var z0 = z2; | |
var draw = curveDrawMatrix.array(); | |
var xplot1 = draw[4] * x1 + draw[5] * x2 + draw[6] * x3 + draw[7] * x4; | |
var xplot2 = draw[8] * x1 + draw[9] * x2 + draw[10] * x3 + draw[11] * x4; | |
var xplot3 = draw[12] * x1 + draw[13] * x2 + draw[14] * x3 + draw[15] * x4; | |
var yplot1 = draw[4] * y1 + draw[5] * y2 + draw[6] * y3 + draw[7] * y4; | |
var yplot2 = draw[8] * y1 + draw[9] * y2 + draw[10] * y3 + draw[11] * y4; | |
var yplot3 = draw[12] * y1 + draw[13] * y2 + draw[14] * y3 + draw[15] * y4; | |
var zplot1 = draw[4] * z1 + draw[5] * z2 + draw[6] * z3 + draw[7] * z4; | |
var zplot2 = draw[8] * z1 + draw[9] * z2 + draw[10] * z3 + draw[11] * z4; | |
var zplot3 = draw[12] * z1 + draw[13] * z2 + draw[14] * z3 + draw[15] * z4; | |
p.vertex(x0, y0, z0); | |
for (var j = 0; j < curveDet; j++) { | |
x0 += xplot1; | |
xplot1 += xplot2; | |
xplot2 += xplot3; | |
y0 += yplot1; | |
yplot1 += yplot2; | |
yplot2 += yplot3; | |
z0 += zplot1; | |
zplot1 += zplot2; | |
zplot2 += zplot3; | |
p.vertex(x0, y0, z0) | |
} | |
}; | |
Drawing2D.prototype.curveVertex = function(x, y) { | |
isCurve = true; | |
p.vertex(x, y) | |
}; | |
Drawing3D.prototype.curveVertex = function(x, y, z) { | |
isCurve = true; | |
if (!curveInited) curveInit(); | |
var vert = []; | |
vert[0] = x; | |
vert[1] = y; | |
vert[2] = z; | |
curveVertArray.push(vert); | |
curveVertCount++; | |
if (curveVertCount > 3) curveVertexSegment(curveVertArray[curveVertCount - 4][0], curveVertArray[curveVertCount - 4][1], curveVertArray[curveVertCount - 4][2], curveVertArray[curveVertCount - 3][0], curveVertArray[curveVertCount - 3][1], curveVertArray[curveVertCount - 3][2], curveVertArray[curveVertCount - 2][0], curveVertArray[curveVertCount - 2][1], curveVertArray[curveVertCount - 2][2], curveVertArray[curveVertCount - 1][0], curveVertArray[curveVertCount - 1][1], curveVertArray[curveVertCount - 1][2]) | |
}; | |
Drawing2D.prototype.curve = function(x1, y1, x2, y2, x3, y3, x4, y4) { | |
p.beginShape(); | |
p.curveVertex(x1, y1); | |
p.curveVertex(x2, y2); | |
p.curveVertex(x3, y3); | |
p.curveVertex(x4, y4); | |
p.endShape() | |
}; | |
Drawing3D.prototype.curve = function(x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4) { | |
if (z4 !== undef) { | |
p.beginShape(); | |
p.curveVertex(x1, y1, z1); | |
p.curveVertex(x2, y2, z2); | |
p.curveVertex(x3, y3, z3); | |
p.curveVertex(x4, y4, z4); | |
p.endShape(); | |
return | |
} | |
p.beginShape(); | |
p.curveVertex(x1, y1); | |
p.curveVertex(z1, x2); | |
p.curveVertex(y2, z2); | |
p.curveVertex(x3, y3); | |
p.endShape() | |
}; | |
p.curveTightness = function(tightness) { | |
curTightness = tightness | |
}; | |
p.curveDetail = function(detail) { | |
curveDet = detail; | |
curveInit() | |
}; | |
p.rectMode = function(aRectMode) { | |
curRectMode = aRectMode | |
}; | |
p.imageMode = function(mode) { | |
switch (mode) { | |
case 0: | |
imageModeConvert = imageModeCorner; | |
break; | |
case 1: | |
imageModeConvert = imageModeCorners; | |
break; | |
case 3: | |
imageModeConvert = imageModeCenter; | |
break; | |
default: | |
throw "Invalid imageMode"; | |
} | |
}; | |
p.ellipseMode = function(aEllipseMode) { | |
curEllipseMode = aEllipseMode | |
}; | |
p.arc = function(x, y, width, height, start, stop) { | |
if (width <= 0 || stop < start) return; | |
if (curEllipseMode === 1) { | |
width = width - x; | |
height = height - y | |
} else if (curEllipseMode === 2) { | |
x = x - width; | |
y = y - height; | |
width = width * 2; | |
height = height * 2 | |
} else if (curEllipseMode === 3) { | |
x = x - width / 2; | |
y = y - height / 2 | |
} | |
while (start < 0) { | |
start += 6.283185307179586; | |
stop += 6.283185307179586 | |
} | |
if (stop - start > 6.283185307179586) { | |
start = 0; | |
stop = 6.283185307179586 | |
} | |
var hr = width / 2, | |
vr = height / 2, | |
centerX = x + hr, | |
centerY = y + vr, | |
startLUT = 0 | 0.5 + start * p.RAD_TO_DEG * 2, | |
stopLUT = 0 | 0.5 + stop * p.RAD_TO_DEG * 2, | |
i, j; | |
if (doFill) { | |
var savedStroke = doStroke; | |
doStroke = false; | |
p.beginShape(); | |
p.vertex(centerX, centerY); | |
for (i = startLUT; i <= stopLUT; i++) { | |
j = i % 720; | |
p.vertex(centerX + cosLUT[j] * hr, centerY + sinLUT[j] * vr) | |
} | |
p.endShape(2); | |
doStroke = savedStroke | |
} | |
if (doStroke) { | |
var savedFill = doFill; | |
doFill = false; | |
p.beginShape(); | |
for (i = startLUT; i <= stopLUT; i++) { | |
j = i % 720; | |
p.vertex(centerX + cosLUT[j] * hr, centerY + sinLUT[j] * vr) | |
} | |
p.endShape(); | |
doFill = savedFill | |
} | |
}; | |
Drawing2D.prototype.line = function(x1, y1, x2, y2) { | |
if (!doStroke) return; | |
x1 = Math.round(x1); | |
x2 = Math.round(x2); | |
y1 = Math.round(y1); | |
y2 = Math.round(y2); | |
if (x1 === x2 && y1 === y2) { | |
p.point(x1, y1); | |
return | |
} | |
var swap = undef, | |
lineCap = undef, | |
drawCrisp = true, | |
currentModelView = modelView.array(), | |
identityMatrix = [1, 0, 0, 0, 1, 0]; | |
for (var i = 0; i < 6 && drawCrisp; i++) drawCrisp = currentModelView[i] === identityMatrix[i]; | |
if (drawCrisp) { | |
if (x1 === x2) { | |
if (y1 > y2) { | |
swap = y1; | |
y1 = y2; | |
y2 = swap | |
} | |
y2++; | |
if (lineWidth % 2 === 1) curContext.translate(0.5, 0) | |
} else if (y1 === y2) { | |
if (x1 > x2) { | |
swap = x1; | |
x1 = x2; | |
x2 = swap | |
} | |
x2++; | |
if (lineWidth % 2 === 1) curContext.translate(0, 0.5) | |
} | |
if (lineWidth === 1) { | |
lineCap = curContext.lineCap; | |
curContext.lineCap = "butt" | |
} | |
} | |
curContext.beginPath(); | |
curContext.moveTo(x1 || 0, y1 || 0); | |
curContext.lineTo(x2 || 0, y2 || 0); | |
executeContextStroke(); | |
if (drawCrisp) { | |
if (x1 === x2 && lineWidth % 2 === 1) curContext.translate(-0.5, 0); | |
else if (y1 === y2 && lineWidth % 2 === 1) curContext.translate(0, -0.5); | |
if (lineWidth === 1) curContext.lineCap = lineCap | |
} | |
}; | |
Drawing3D.prototype.line = function(x1, y1, z1, x2, y2, z2) { | |
if (y2 === undef || z2 === undef) { | |
z2 = 0; | |
y2 = x2; | |
x2 = z1; | |
z1 = 0 | |
} | |
if (x1 === x2 && y1 === y2 && z1 === z2) { | |
p.point(x1, y1, z1); | |
return | |
} | |
var lineVerts = [x1, y1, z1, x2, y2, z2]; | |
var view = new PMatrix3D; | |
view.scale(1, -1, 1); | |
view.apply(modelView.array()); | |
view.transpose(); | |
if (lineWidth > 0 && doStroke) { | |
curContext.useProgram(programObject2D); | |
uniformMatrix("uModel2d", programObject2D, "uModel", false, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]); | |
uniformMatrix("uView2d", programObject2D, "uView", false, view.array()); | |
uniformf("uColor2d", programObject2D, "uColor", strokeStyle); | |
uniformi("uIsDrawingText", programObject2D, "uIsDrawingText", false); | |
vertexAttribPointer("aVertex2d", programObject2D, "aVertex", 3, lineBuffer); | |
disableVertexAttribPointer("aTextureCoord2d", programObject2D, "aTextureCoord"); | |
curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array(lineVerts), curContext.STREAM_DRAW); | |
curContext.drawArrays(curContext.LINES, 0, 2) | |
} | |
}; | |
Drawing2D.prototype.bezier = function() { | |
if (arguments.length !== 8) throw "You must use 8 parameters for bezier() in 2D mode"; | |
p.beginShape(); | |
p.vertex(arguments[0], arguments[1]); | |
p.bezierVertex(arguments[2], arguments[3], arguments[4], arguments[5], arguments[6], arguments[7]); | |
p.endShape() | |
}; | |
Drawing3D.prototype.bezier = function() { | |
if (arguments.length !== 12) throw "You must use 12 parameters for bezier() in 3D mode"; | |
p.beginShape(); | |
p.vertex(arguments[0], arguments[1], arguments[2]); | |
p.bezierVertex(arguments[3], arguments[4], arguments[5], arguments[6], arguments[7], arguments[8], arguments[9], arguments[10], arguments[11]); | |
p.endShape() | |
}; | |
p.bezierDetail = function(detail) { | |
bezDetail = detail | |
}; | |
p.bezierPoint = function(a, b, c, d, t) { | |
return (1 - t) * (1 - t) * (1 - t) * a + 3 * (1 - t) * (1 - t) * t * b + 3 * (1 - t) * t * t * c + t * t * t * d | |
}; | |
p.bezierTangent = function(a, b, c, d, t) { | |
return 3 * t * t * (-a + 3 * b - 3 * c + d) + 6 * t * (a - 2 * b + c) + 3 * (-a + b) | |
}; | |
p.curvePoint = function(a, b, c, d, t) { | |
return 0.5 * (2 * b + (-a + c) * t + (2 * a - 5 * b + 4 * c - d) * t * t + (-a + 3 * b - 3 * c + d) * t * t * t) | |
}; | |
p.curveTangent = function(a, b, c, d, t) { | |
return 0.5 * (-a + c + 2 * (2 * a - 5 * b + 4 * c - d) * t + 3 * (-a + 3 * b - 3 * c + d) * t * t) | |
}; | |
p.triangle = function(x1, y1, x2, y2, x3, y3) { | |
p.beginShape(9); | |
p.vertex(x1, y1, 0); | |
p.vertex(x2, y2, 0); | |
p.vertex(x3, y3, 0); | |
p.endShape() | |
}; | |
p.quad = function(x1, y1, x2, y2, x3, y3, x4, y4) { | |
p.beginShape(16); | |
p.vertex(x1, y1, 0); | |
p.vertex(x2, y2, 0); | |
p.vertex(x3, y3, 0); | |
p.vertex(x4, y4, 0); | |
p.endShape() | |
}; | |
var roundedRect$2d = function(x, y, width, height, tl, tr, br, bl) { | |
if (bl === undef) { | |
tr = tl; | |
br = tl; | |
bl = tl | |
} | |
var halfWidth = width / 2, | |
halfHeight = height / 2; | |
if (tl > halfWidth || tl > halfHeight) tl = Math.min(halfWidth, halfHeight); | |
if (tr > halfWidth || tr > halfHeight) tr = Math.min(halfWidth, halfHeight); | |
if (br > halfWidth || br > halfHeight) br = Math.min(halfWidth, halfHeight); | |
if (bl > halfWidth || bl > halfHeight) bl = Math.min(halfWidth, halfHeight); | |
if (!doFill || doStroke) curContext.translate(0.5, 0.5); | |
curContext.beginPath(); | |
curContext.moveTo(x + tl, y); | |
curContext.lineTo(x + width - tr, y); | |
curContext.quadraticCurveTo(x + width, y, x + width, y + tr); | |
curContext.lineTo(x + width, y + height - br); | |
curContext.quadraticCurveTo(x + width, y + height, x + width - br, y + height); | |
curContext.lineTo(x + bl, y + height); | |
curContext.quadraticCurveTo(x, y + height, x, y + height - bl); | |
curContext.lineTo(x, y + tl); | |
curContext.quadraticCurveTo(x, y, x + tl, y); | |
if (!doFill || doStroke) curContext.translate(-0.5, -0.5); | |
executeContextFill(); | |
executeContextStroke() | |
}; | |
Drawing2D.prototype.rect = function(x, y, width, height, tl, tr, br, bl) { | |
if (!width && !height) return; | |
if (curRectMode === 1) { | |
width -= x; | |
height -= y | |
} else if (curRectMode === 2) { | |
width *= 2; | |
height *= 2; | |
x -= width / 2; | |
y -= height / 2 | |
} else if (curRectMode === 3) { | |
x -= width / 2; | |
y -= height / 2 | |
} | |
x = Math.round(x); | |
y = Math.round(y); | |
width = Math.round(width); | |
height = Math.round(height); | |
if (tl !== undef) { | |
roundedRect$2d(x, y, width, height, tl, tr, br, bl); | |
return | |
} | |
if (doStroke && lineWidth % 2 === 1) curContext.translate(0.5, 0.5); | |
curContext.beginPath(); | |
curContext.rect(x, y, width, height); | |
executeContextFill(); | |
executeContextStroke(); | |
if (doStroke && lineWidth % 2 === 1) curContext.translate(-0.5, -0.5) | |
}; | |
Drawing3D.prototype.rect = function(x, y, width, height, tl, tr, br, bl) { | |
if (tl !== undef) throw "rect() with rounded corners is not supported in 3D mode"; | |
if (curRectMode === 1) { | |
width -= x; | |
height -= y | |
} else if (curRectMode === 2) { | |
width *= 2; | |
height *= 2; | |
x -= width / 2; | |
y -= height / 2 | |
} else if (curRectMode === 3) { | |
x -= width / 2; | |
y -= height / 2 | |
} | |
var model = new PMatrix3D; | |
model.translate(x, y, 0); | |
model.scale(width, height, 1); | |
model.transpose(); | |
var view = new PMatrix3D; | |
view.scale(1, -1, 1); | |
view.apply(modelView.array()); | |
view.transpose(); | |
if (lineWidth > 0 && doStroke) { | |
curContext.useProgram(programObject2D); | |
uniformMatrix("uModel2d", programObject2D, "uModel", false, model.array()); | |
uniformMatrix("uView2d", programObject2D, "uView", false, view.array()); | |
uniformf("uColor2d", programObject2D, "uColor", strokeStyle); | |
uniformi("uIsDrawingText2d", programObject2D, "uIsDrawingText", false); | |
vertexAttribPointer("aVertex2d", programObject2D, "aVertex", 3, rectBuffer); | |
disableVertexAttribPointer("aTextureCoord2d", programObject2D, "aTextureCoord"); | |
curContext.drawArrays(curContext.LINE_LOOP, 0, rectVerts.length / 3) | |
} | |
if (doFill) { | |
curContext.useProgram(programObject3D); | |
uniformMatrix("uModel3d", programObject3D, "uModel", false, model.array()); | |
uniformMatrix("uView3d", programObject3D, "uView", false, view.array()); | |
curContext.enable(curContext.POLYGON_OFFSET_FILL); | |
curContext.polygonOffset(1, 1); | |
uniformf("color3d", programObject3D, "uColor", fillStyle); | |
if (lightCount > 0) { | |
var v = new PMatrix3D; | |
v.set(view); | |
var m = new PMatrix3D; | |
m.set(model); | |
v.mult(m); | |
var normalMatrix = new PMatrix3D; | |
normalMatrix.set(v); | |
normalMatrix.invert(); | |
normalMatrix.transpose(); | |
uniformMatrix("uNormalTransform3d", programObject3D, "uNormalTransform", false, normalMatrix.array()); | |
vertexAttribPointer("aNormal3d", programObject3D, "aNormal", 3, rectNormBuffer) | |
} else disableVertexAttribPointer("normal3d", programObject3D, "aNormal"); | |
vertexAttribPointer("vertex3d", programObject3D, "aVertex", 3, rectBuffer); | |
curContext.drawArrays(curContext.TRIANGLE_FAN, 0, rectVerts.length / 3); | |
curContext.disable(curContext.POLYGON_OFFSET_FILL) | |
} | |
}; | |
Drawing2D.prototype.ellipse = function(x, y, width, height) { | |
x = x || 0; | |
y = y || 0; | |
if (width <= 0 && height <= 0) return; | |
if (curEllipseMode === 2) { | |
width *= 2; | |
height *= 2 | |
} else if (curEllipseMode === 1) { | |
width = width - x; | |
height = height - y; | |
x += width / 2; | |
y += height / 2 | |
} else if (curEllipseMode === 0) { | |
x += width / 2; | |
y += height / 2 | |
} | |
if (width === height) { | |
curContext.beginPath(); | |
curContext.arc(x, y, width / 2, 0, 6.283185307179586, false); | |
executeContextFill(); | |
executeContextStroke() | |
} else { | |
var w = width / 2, | |
h = height / 2, | |
C = 0.5522847498307933, | |
c_x = C * w, | |
c_y = C * h; | |
p.beginShape(); | |
p.vertex(x + w, y); | |
p.bezierVertex(x + w, y - c_y, x + c_x, y - h, x, y - h); | |
p.bezierVertex(x - c_x, y - h, x - w, y - c_y, x - w, y); | |
p.bezierVertex(x - w, y + c_y, x - c_x, y + h, x, y + h); | |
p.bezierVertex(x + c_x, y + h, x + w, y + c_y, x + w, y); | |
p.endShape() | |
} | |
}; | |
Drawing3D.prototype.ellipse = function(x, y, width, height) { | |
x = x || 0; | |
y = y || 0; | |
if (width <= 0 && height <= 0) return; | |
if (curEllipseMode === 2) { | |
width *= 2; | |
height *= 2 | |
} else if (curEllipseMode === 1) { | |
width = width - x; | |
height = height - y; | |
x += width / 2; | |
y += height / 2 | |
} else if (curEllipseMode === 0) { | |
x += width / 2; | |
y += height / 2 | |
} | |
var w = width / 2, | |
h = height / 2, | |
C = 0.5522847498307933, | |
c_x = C * w, | |
c_y = C * h; | |
p.beginShape(); | |
p.vertex(x + w, y); | |
p.bezierVertex(x + w, y - c_y, 0, x + c_x, y - h, 0, x, y - h, 0); | |
p.bezierVertex(x - c_x, y - h, 0, x - w, y - c_y, 0, x - w, y, 0); | |
p.bezierVertex(x - w, y + c_y, 0, x - c_x, y + h, 0, x, y + h, 0); | |
p.bezierVertex(x + c_x, y + h, 0, x + w, y + c_y, 0, x + w, y, 0); | |
p.endShape(); | |
if (doFill) { | |
var xAv = 0, | |
yAv = 0, | |
i, j; | |
for (i = 0; i < vertArray.length; i++) { | |
xAv += vertArray[i][0]; | |
yAv += vertArray[i][1] | |
} | |
xAv /= vertArray.length; | |
yAv /= vertArray.length; | |
var vert = [], | |
fillVertArray = [], | |
colorVertArray = []; | |
vert[0] = xAv; | |
vert[1] = yAv; | |
vert[2] = 0; | |
vert[3] = 0; | |
vert[4] = 0; | |
vert[5] = fillStyle[0]; | |
vert[6] = fillStyle[1]; | |
vert[7] = fillStyle[2]; | |
vert[8] = fillStyle[3]; | |
vert[9] = strokeStyle[0]; | |
vert[10] = strokeStyle[1]; | |
vert[11] = strokeStyle[2]; | |
vert[12] = strokeStyle[3]; | |
vert[13] = normalX; | |
vert[14] = normalY; | |
vert[15] = normalZ; | |
vertArray.unshift(vert); | |
for (i = 0; i < vertArray.length; i++) { | |
for (j = 0; j < 3; j++) fillVertArray.push(vertArray[i][j]); | |
for (j = 5; j < 9; j++) colorVertArray.push(vertArray[i][j]) | |
} | |
fill3D(fillVertArray, "TRIANGLE_FAN", colorVertArray) | |
} | |
}; | |
p.normal = function(nx, ny, nz) { | |
if (arguments.length !== 3 || !(typeof nx === "number" && typeof ny === "number" && typeof nz === "number")) throw "normal() requires three numeric arguments."; | |
normalX = nx; | |
normalY = ny; | |
normalZ = nz; | |
if (curShape !== 0) if (normalMode === 0) normalMode = 1; | |
else if (normalMode === 1) normalMode = 2 | |
}; | |
p.save = function(file, img) { | |
if (img !== undef) return window.open(img.toDataURL(), "_blank"); | |
return window.open(p.externals.canvas.toDataURL(), "_blank") | |
}; | |
var saveNumber = 0; | |
p.saveFrame = function(file) { | |
if (file === undef) file = "screen-####.png"; | |
var frameFilename = file.replace(/#+/, function(all) { | |
var s = "" + saveNumber++; | |
while (s.length < all.length) s = "0" + s; | |
return s | |
}); | |
p.save(frameFilename) | |
}; | |
var utilityContext2d = document.createElement("canvas").getContext("2d"); | |
var canvasDataCache = [undef, undef, undef]; | |
function getCanvasData(obj, w, h) { | |
var canvasData = canvasDataCache.shift(); | |
if (canvasData === undef) { | |
canvasData = {}; | |
canvasData.canvas = document.createElement("canvas"); | |
canvasData.context = canvasData.canvas.getContext("2d") | |
} | |
canvasDataCache.push(canvasData); | |
var canvas = canvasData.canvas, | |
context = canvasData.context, | |
width = w || obj.width, | |
height = h || obj.height; | |
canvas.width = width; | |
canvas.height = height; | |
if (!obj) context.clearRect(0, 0, width, height); | |
else if ("data" in obj) context.putImageData(obj, 0, 0); | |
else { | |
context.clearRect(0, 0, width, height); | |
context.drawImage(obj, 0, 0, width, height) | |
} | |
return canvasData | |
} | |
function buildPixelsObject(pImage) { | |
return { | |
getLength: function(aImg) { | |
return function() { | |
if (aImg.isRemote) throw "Image is loaded remotely. Cannot get length."; | |
else return aImg.imageData.data.length ? aImg.imageData.data.length / 4 : 0 | |
} | |
}(pImage), | |
getPixel: function(aImg) { | |
return function(i) { | |
var offset = i * 4, | |
data = aImg.imageData.data; | |
if (aImg.isRemote) throw "Image is loaded remotely. Cannot get pixels."; | |
return (data[offset + 3] & 255) << 24 | (data[offset] & 255) << 16 | (data[offset + 1] & 255) << 8 | data[offset + 2] & 255 | |
} | |
}(pImage), | |
setPixel: function(aImg) { | |
return function(i, c) { | |
var offset = i * 4, | |
data = aImg.imageData.data; | |
if (aImg.isRemote) throw "Image is loaded remotely. Cannot set pixel."; | |
data[offset + 0] = (c >> 16) & 255; | |
data[offset + 1] = (c >> 8) & 255; | |
data[offset + 2] = c & 255; | |
data[offset + 3] = (c >> 24) & 255; | |
aImg.__isDirty = true | |
} | |
}(pImage), | |
toArray: function(aImg) { | |
return function() { | |
var arr = [], | |
data = aImg.imageData.data, | |
length = aImg.width * aImg.height; | |
if (aImg.isRemote) throw "Image is loaded remotely. Cannot get pixels."; | |
for (var i = 0, offset = 0; i < length; i++, offset += 4) arr.push((data[offset + 3] & 255) << 24 | (data[offset] & 255) << 16 | (data[offset + 1] & 255) << 8 | data[offset + 2] & 255); | |
return arr | |
} | |
}(pImage), | |
set: function(aImg) { | |
return function(arr) { | |
var offset, data, c; | |
if (this.isRemote) throw "Image is loaded remotely. Cannot set pixels."; | |
data = aImg.imageData.data; | |
for (var i = 0, aL = arr.length; i < aL; i++) { | |
c = arr[i]; | |
offset = i * 4; | |
data[offset + 0] = (c >> 16) & 255; | |
data[offset + 1] = (c >> 8) & 255; | |
data[offset + 2] = c & 255; | |
data[offset + 3] = (c >> 24) & 255 | |
} | |
aImg.__isDirty = true | |
} | |
}(pImage) | |
} | |
} | |
var PImage = function(aWidth, aHeight, aFormat) { | |
this.__isDirty = false; | |
if (aWidth instanceof HTMLImageElement) this.fromHTMLImageData(aWidth); | |
else if (aHeight || aFormat) { | |
this.width = aWidth || 1; | |
this.height = aHeight || 1; | |
var canvas = this.sourceImg = document.createElement("canvas"); | |
canvas.width = this.width; | |
canvas.height = this.height; | |
var imageData = this.imageData = canvas.getContext("2d").createImageData(this.width, this.height); | |
this.format = aFormat === 2 || aFormat === 4 ? aFormat : 1; | |
if (this.format === 1) for (var i = 3, data = this.imageData.data, len = data.length; i < len; i += 4) data[i] = 255; | |
this.__isDirty = true; | |
this.updatePixels() | |
} else { | |
this.width = 0; | |
this.height = 0; | |
this.imageData = utilityContext2d.createImageData(1, 1); | |
this.format = 2 | |
} | |
this.pixels = buildPixelsObject(this) | |
}; | |
PImage.prototype = { | |
__isPImage: true, | |
updatePixels: function() { | |
var canvas = this.sourceImg; | |
if (canvas && canvas instanceof HTMLCanvasElement && this.__isDirty) canvas.getContext("2d").putImageData(this.imageData, 0, 0); | |
this.__isDirty = false | |
}, | |
fromHTMLImageData: function(htmlImg) { | |
var canvasData = getCanvasData(htmlImg); | |
try { | |
var imageData = canvasData.context.getImageData(0, 0, htmlImg.width, htmlImg.height); | |
this.fromImageData(imageData) | |
} catch(e) { | |
if (htmlImg.width && htmlImg.height) { | |
this.isRemote = true; | |
this.width = htmlImg.width; | |
this.height = htmlImg.height | |
} | |
} | |
this.sourceImg = htmlImg | |
}, | |
"get": function(x, y, w, h) { | |
if (!arguments.length) return p.get(this); | |
if (arguments.length === 2) return p.get(x, y, this); | |
if (arguments.length === 4) return p.get(x, y, w, h, this) | |
}, | |
"set": function(x, y, c) { | |
p.set(x, y, c, this); | |
this.__isDirty = true | |
}, | |
blend: function(srcImg, x, y, width, height, dx, dy, dwidth, dheight, MODE) { | |
if (arguments.length === 9) p.blend(this, srcImg, x, y, width, height, dx, dy, dwidth, dheight, this); | |
else if (arguments.length === 10) p.blend(srcImg, x, y, width, height, dx, dy, dwidth, dheight, MODE, this); | |
delete this.sourceImg | |
}, | |
copy: function(srcImg, sx, sy, swidth, sheight, dx, dy, dwidth, dheight) { | |
if (arguments.length === 8) p.blend(this, srcImg, sx, sy, swidth, sheight, dx, dy, dwidth, 0, this); | |
else if (arguments.length === 9) p.blend(srcImg, sx, sy, swidth, sheight, dx, dy, dwidth, dheight, 0, this); | |
delete this.sourceImg | |
}, | |
filter: function(mode, param) { | |
if (arguments.length === 2) p.filter(mode, param, this); | |
else if (arguments.length === 1) p.filter(mode, null, this); | |
delete this.sourceImg | |
}, | |
save: function(file) { | |
p.save(file, this) | |
}, | |
resize: function(w, h) { | |
if (this.isRemote) throw "Image is loaded remotely. Cannot resize."; | |
if (this.width !== 0 || this.height !== 0) { | |
if (w === 0 && h !== 0) w = Math.floor(this.width / this.height * h); | |
else if (h === 0 && w !== 0) h = Math.floor(this.height / this.width * w); | |
var canvas = getCanvasData(this.imageData).canvas; | |
var imageData = getCanvasData(canvas, w, h).context.getImageData(0, 0, w, h); | |
this.fromImageData(imageData) | |
} | |
}, | |
mask: function(mask) { | |
var obj = this.toImageData(), | |
i, size; | |
if (mask instanceof PImage || mask.__isPImage) if (mask.width === this.width && mask.height === this.height) { | |
mask = mask.toImageData(); | |
for (i = 2, size = this.width * this.height * 4; i < size; i += 4) obj.data[i + 1] = mask.data[i] | |
} else throw "mask must have the same dimensions as PImage."; | |
else if (mask instanceof | |
Array) if (this.width * this.height === mask.length) for (i = 0, size = mask.length; i < size; ++i) obj.data[i * 4 + 3] = mask[i]; | |
else throw "mask array must be the same length as PImage pixels array."; | |
this.fromImageData(obj) | |
}, | |
loadPixels: nop, | |
toImageData: function() { | |
if (this.isRemote) return this.sourceImg; | |
if (!this.__isDirty) return this.imageData; | |
var canvasData = getCanvasData(this.sourceImg); | |
return canvasData.context.getImageData(0, 0, this.width, this.height) | |
}, | |
toDataURL: function() { | |
if (this.isRemote) throw "Image is loaded remotely. Cannot create dataURI."; | |
var canvasData = getCanvasData(this.imageData); | |
return canvasData.canvas.toDataURL() | |
}, | |
fromImageData: function(canvasImg) { | |
var w = canvasImg.width, | |
h = canvasImg.height, | |
canvas = document.createElement("canvas"), | |
ctx = canvas.getContext("2d"); | |
this.width = canvas.width = w; | |
this.height = canvas.height = h; | |
ctx.putImageData(canvasImg, 0, 0); | |
this.format = 2; | |
this.imageData = canvasImg; | |
this.sourceImg = canvas | |
} | |
}; | |
p.PImage = PImage; | |
p.createImage = function(w, h, mode) { | |
return new PImage(w, h, mode) | |
}; | |
p.loadImage = function(file, type, callback) { | |
if (type) file = file + "." + type; | |
var pimg; | |
if (curSketch.imageCache.images[file]) { | |
pimg = new PImage(curSketch.imageCache.images[file]); | |
pimg.loaded = true; | |
return pimg | |
} | |
pimg = new PImage; | |
var img = document.createElement("img"); | |
pimg.sourceImg = img; | |
img.onload = function(aImage, aPImage, aCallback) { | |
var image = aImage; | |
var pimg = aPImage; | |
var callback = aCallback; | |
return function() { | |
pimg.fromHTMLImageData(image); | |
pimg.loaded = true; | |
if (callback) callback() | |
} | |
}(img, pimg, callback); | |
img.src = file; | |
return pimg | |
}; | |
p.requestImage = p.loadImage; | |
function get$2(x, y) { | |
var data; | |
if (x >= p.width || x < 0 || y < 0 || y >= p.height) return 0; | |
if (isContextReplaced) { | |
var offset = ((0 | x) + p.width * (0 | y)) * 4; | |
data = p.imageData.data; | |
return (data[offset + 3] & 255) << 24 | (data[offset] & 255) << 16 | (data[offset + 1] & 255) << 8 | data[offset + 2] & 255 | |
} | |
data = p.toImageData(0 | x, 0 | y, 1, 1).data; | |
return (data[3] & 255) << 24 | (data[0] & 255) << 16 | (data[1] & 255) << 8 | data[2] & 255 | |
} | |
function get$3(x, y, img) { | |
if (img.isRemote) throw "Image is loaded remotely. Cannot get x,y."; | |
var offset = y * img.width * 4 + x * 4, | |
data = img.imageData.data; | |
return (data[offset + 3] & 255) << 24 | (data[offset] & 255) << 16 | (data[offset + 1] & 255) << 8 | data[offset + 2] & 255 | |
} | |
function get$4(x, y, w, h) { | |
var c = new PImage(w, h, 2); | |
c.fromImageData(p.toImageData(x, y, w, h)); | |
return c | |
} | |
function get$5(x, y, w, h, img) { | |
if (img.isRemote) throw "Image is loaded remotely. Cannot get x,y,w,h."; | |
var c = new PImage(w, h, 2), | |
cData = c.imageData.data, | |
imgWidth = img.width, | |
imgHeight = img.height, | |
imgData = img.imageData.data; | |
var startRow = Math.max(0, -y), | |
startColumn = Math.max(0, -x), | |
stopRow = Math.min(h, imgHeight - y), | |
stopColumn = Math.min(w, imgWidth - x); | |
for (var i = startRow; i < stopRow; ++i) { | |
var sourceOffset = ((y + i) * imgWidth + (x + startColumn)) * 4; | |
var targetOffset = (i * w + startColumn) * 4; | |
for (var j = startColumn; j < stopColumn; ++j) { | |
cData[targetOffset++] = imgData[sourceOffset++]; | |
cData[targetOffset++] = imgData[sourceOffset++]; | |
cData[targetOffset++] = imgData[sourceOffset++]; | |
cData[targetOffset++] = imgData[sourceOffset++] | |
} | |
} | |
c.__isDirty = true; | |
return c | |
} | |
p.get = function(x, y, w, h, img) { | |
if (img !== undefined) return get$5(x, y, w, h, img); | |
if (h !== undefined) return get$4(x, y, w, h); | |
if (w !== undefined) return get$3(x, y, w); | |
if (y !== undefined) return get$2(x, y); | |
if (x !== undefined) return get$5(0, 0, x.width, x.height, x); | |
return get$4(0, 0, p.width, p.height) | |
}; | |
p.createGraphics = function(w, h, render) { | |
var pg = new Processing; | |
pg.size(w, h, render); | |
pg.background(0, 0); | |
return pg | |
}; | |
function resetContext() { | |
if (isContextReplaced) { | |
curContext = originalContext; | |
isContextReplaced = false; | |
p.updatePixels() | |
} | |
} | |
function SetPixelContextWrapper() { | |
function wrapFunction(newContext, name) { | |
function wrapper() { | |
resetContext(); | |
curContext[name].apply(curContext, arguments) | |
} | |
newContext[name] = wrapper | |
} | |
function wrapProperty(newContext, name) { | |
function getter() { | |
resetContext(); | |
return curContext[name] | |
} | |
function setter(value) { | |
resetContext(); | |
curContext[name] = value | |
} | |
p.defineProperty(newContext, name, { | |
get: getter, | |
set: setter | |
}) | |
} | |
for (var n in curContext) if (typeof curContext[n] === "function") wrapFunction(this, n); | |
else wrapProperty(this, n) | |
} | |
function replaceContext() { | |
if (isContextReplaced) return; | |
p.loadPixels(); | |
if (proxyContext === null) { | |
originalContext = curContext; | |
proxyContext = new SetPixelContextWrapper | |
} | |
isContextReplaced = true; | |
curContext = proxyContext; | |
setPixelsCached = 0 | |
} | |
function set$3(x, y, c) { | |
if (x < p.width && x >= 0 && y >= 0 && y < p.height) { | |
replaceContext(); | |
p.pixels.setPixel((0 | x) + p.width * (0 | y), c); | |
if (++setPixelsCached > maxPixelsCached) resetContext() | |
} | |
} | |
function set$4(x, y, obj, img) { | |
if (img.isRemote) throw "Image is loaded remotely. Cannot set x,y."; | |
var c = p.color.toArray(obj); | |
var offset = y * img.width * 4 + x * 4; | |
var data = img.imageData.data; | |
data[offset] = c[0]; | |
data[offset + 1] = c[1]; | |
data[offset + 2] = c[2]; | |
data[offset + 3] = c[3] | |
} | |
p.set = function(x, y, obj, img) { | |
var color, oldFill; | |
if (arguments.length === 3) if (typeof obj === "number") set$3(x, y, obj); | |
else { | |
if (obj instanceof PImage || obj.__isPImage) p.image(obj, x, y) | |
} else if (arguments.length === 4) set$4(x, y, obj, img) | |
}; | |
p.imageData = {}; | |
p.pixels = { | |
getLength: function() { | |
return p.imageData.data.length ? p.imageData.data.length / 4 : 0 | |
}, | |
getPixel: function(i) { | |
var offset = i * 4, | |
data = p.imageData.data; | |
return data[offset + 3] << 24 & 4278190080 | data[offset + 0] << 16 & 16711680 | data[offset + 1] << 8 & 65280 | data[offset + 2] & 255 | |
}, | |
setPixel: function(i, c) { | |
var offset = i * 4, | |
data = p.imageData.data; | |
data[offset + 0] = (c & 16711680) >>> 16; | |
data[offset + 1] = (c & 65280) >>> 8; | |
data[offset + 2] = c & 255; | |
data[offset + 3] = (c & 4278190080) >>> 24 | |
}, | |
toArray: function() { | |
var arr = [], | |
length = p.imageData.width * p.imageData.height, | |
data = p.imageData.data; | |
for (var i = 0, offset = 0; i < length; i++, offset += 4) arr.push(data[offset + 3] << 24 & 4278190080 | data[offset + 0] << 16 & 16711680 | data[offset + 1] << 8 & 65280 | data[offset + 2] & 255); | |
return arr | |
}, | |
set: function(arr) { | |
for (var i = 0, aL = arr.length; i < aL; i++) this.setPixel(i, arr[i]) | |
} | |
}; | |
p.loadPixels = function() { | |
p.imageData = drawing.$ensureContext().getImageData(0, 0, p.width, p.height) | |
}; | |
p.updatePixels = function() { | |
if (p.imageData) drawing.$ensureContext().putImageData(p.imageData, 0, 0) | |
}; | |
p.hint = function(which) { | |
var curContext = drawing.$ensureContext(); | |
if (which === 4) { | |
curContext.disable(curContext.DEPTH_TEST); | |
curContext.depthMask(false); | |
curContext.clear(curContext.DEPTH_BUFFER_BIT) | |
} else if (which === -4) { | |
curContext.enable(curContext.DEPTH_TEST); | |
curContext.depthMask(true) | |
} else if (which === -1 || which === 2) renderSmooth = true; | |
else if (which === 1) renderSmooth = false | |
}; | |
var backgroundHelper = function(arg1, arg2, arg3, arg4) { | |
var obj; | |
if (arg1 instanceof PImage || arg1.__isPImage) { | |
obj = arg1; | |
if (!obj.loaded) throw "Error using image in background(): PImage not loaded."; | |
if (obj.width !== p.width || obj.height !== p.height) throw "Background image must be the same dimensions as the canvas."; | |
} else obj = p.color(arg1, arg2, arg3, arg4); | |
backgroundObj = obj | |
}; | |
Drawing2D.prototype.background = function(arg1, arg2, arg3, arg4) { | |
if (arg1 !== undef) backgroundHelper(arg1, arg2, arg3, arg4); | |
if (backgroundObj instanceof PImage || backgroundObj.__isPImage) { | |
saveContext(); | |
curContext.setTransform(1, 0, 0, 1, 0, 0); | |
p.image(backgroundObj, 0, 0); | |
restoreContext() | |
} else { | |
saveContext(); | |
curContext.setTransform(1, 0, 0, 1, 0, 0); | |
if (p.alpha(backgroundObj) !== colorModeA) curContext.clearRect(0, 0, p.width, p.height); | |
curContext.fillStyle = p.color.toString(backgroundObj); | |
curContext.fillRect(0, 0, p.width, p.height); | |
isFillDirty = true; | |
restoreContext() | |
} | |
}; | |
Drawing3D.prototype.background = function(arg1, arg2, arg3, arg4) { | |
if (arguments.length > 0) backgroundHelper(arg1, arg2, arg3, arg4); | |
var c = p.color.toGLArray(backgroundObj); | |
curContext.clearColor(c[0], c[1], c[2], c[3]); | |
curContext.clear(curContext.COLOR_BUFFER_BIT | curContext.DEPTH_BUFFER_BIT) | |
}; | |
Drawing2D.prototype.image = function(img, x, y, w, h) { | |
x = Math.round(x); | |
y = Math.round(y); | |
if (img.width > 0) { | |
var wid = w || img.width; | |
var hgt = h || img.height; | |
var bounds = imageModeConvert(x || 0, y || 0, w || img.width, h || img.height, arguments.length < 4); | |
var fastImage = !!img.sourceImg && curTint === null; | |
if (fastImage) { | |
var htmlElement = img.sourceImg; | |
if (img.__isDirty) img.updatePixels(); | |
curContext.drawImage(htmlElement, 0, 0, htmlElement.width, htmlElement.height, bounds.x, bounds.y, bounds.w, bounds.h) | |
} else { | |
var obj = img.toImageData(); | |
if (curTint !== null) { | |
curTint(obj); | |
img.__isDirty = true | |
} | |
curContext.drawImage(getCanvasData(obj).canvas, 0, 0, img.width, img.height, bounds.x, bounds.y, bounds.w, bounds.h) | |
} | |
} | |
}; | |
Drawing3D.prototype.image = function(img, x, y, w, h) { | |
if (img.width > 0) { | |
x = Math.round(x); | |
y = Math.round(y); | |
w = w || img.width; | |
h = h || img.height; | |
p.beginShape(p.QUADS); | |
p.texture(img); | |
p.vertex(x, y, 0, 0, 0); | |
p.vertex(x, y + h, 0, 0, h); | |
p.vertex(x + w, y + h, 0, w, h); | |
p.vertex(x + w, y, 0, w, 0); | |
p.endShape() | |
} | |
}; | |
p.tint = function(a1, a2, a3, a4) { | |
var tintColor = p.color(a1, a2, a3, a4); | |
var r = p.red(tintColor) / colorModeX; | |
var g = p.green(tintColor) / colorModeY; | |
var b = p.blue(tintColor) / colorModeZ; | |
var a = p.alpha(tintColor) / colorModeA; | |
curTint = function(obj) { | |
var data = obj.data, | |
length = 4 * obj.width * obj.height; | |
for (var i = 0; i < length;) { | |
data[i++] *= r; | |
data[i++] *= g; | |
data[i++] *= b; | |
data[i++] *= a | |
} | |
}; | |
curTint3d = function(data) { | |
for (var i = 0; i < data.length;) { | |
data[i++] = r; | |
data[i++] = g; | |
data[i++] = b; | |
data[i++] = a | |
} | |
} | |
}; | |
p.noTint = function() { | |
curTint = null; | |
curTint3d = null | |
}; | |
p.copy = function(src, sx, sy, sw, sh, dx, dy, dw, dh) { | |
if (dh === undef) { | |
dh = dw; | |
dw = dy; | |
dy = dx; | |
dx = sh; | |
sh = sw; | |
sw = sy; | |
sy = sx; | |
sx = src; | |
src = p | |
} | |
p.blend(src, sx, sy, sw, sh, dx, dy, dw, dh, 0) | |
}; | |
p.blend = function(src, sx, sy, sw, sh, dx, dy, dw, dh, mode, pimgdest) { | |
if (src.isRemote) throw "Image is loaded remotely. Cannot blend image."; | |
if (mode === undef) { | |
mode = dh; | |
dh = dw; | |
dw = dy; | |
dy = dx; | |
dx = sh; | |
sh = sw; | |
sw = sy; | |
sy = sx; | |
sx = src; | |
src = p | |
} | |
var sx2 = sx + sw, | |
sy2 = sy + sh, | |
dx2 = dx + dw, | |
dy2 = dy + dh, | |
dest = pimgdest || p; | |
if (pimgdest === undef || mode === undef) p.loadPixels(); | |
src.loadPixels(); | |
if (src === p && p.intersect(sx, sy, sx2, sy2, dx, dy, dx2, dy2)) p.blit_resize(p.get(sx, sy, sx2 - sx, sy2 - sy), 0, 0, sx2 - sx - 1, sy2 - sy - 1, dest.imageData.data, dest.width, dest.height, dx, dy, dx2, dy2, mode); | |
else p.blit_resize(src, sx, sy, sx2, sy2, dest.imageData.data, dest.width, dest.height, dx, dy, dx2, dy2, mode); | |
if (pimgdest === undef) p.updatePixels() | |
}; | |
var buildBlurKernel = function(r) { | |
var radius = p.floor(r * 3.5), | |
i, radiusi; | |
radius = radius < 1 ? 1 : radius < 248 ? radius : 248; | |
if (p.shared.blurRadius !== radius) { | |
p.shared.blurRadius = radius; | |
p.shared.blurKernelSize = 1 + (p.shared.blurRadius << 1); | |
p.shared.blurKernel = new Float32Array(p.shared.blurKernelSize); | |
var sharedBlurKernal = p.shared.blurKernel; | |
var sharedBlurKernelSize = p.shared.blurKernelSize; | |
var sharedBlurRadius = p.shared.blurRadius; | |
for (i = 0; i < sharedBlurKernelSize; i++) sharedBlurKernal[i] = 0; | |
var radiusiSquared = (radius - 1) * (radius - 1); | |
for (i = 1; i < radius; i++) sharedBlurKernal[radius + i] = sharedBlurKernal[radiusi] = radiusiSquared; | |
sharedBlurKernal[radius] = radius * radius | |
} | |
}; | |
var blurARGB = function(r, aImg) { | |
var sum, cr, cg, cb, ca, c, m; | |
var read, ri, ym, ymi, bk0; | |
var wh = aImg.pixels.getLength(); | |
var r2 = new Float32Array(wh); | |
var g2 = new Float32Array(wh); | |
var b2 = new Float32Array(wh); | |
var a2 = new Float32Array(wh); | |
var yi = 0; | |
var x, y, i, offset; | |
buildBlurKernel(r); | |
var aImgHeight = aImg.height; | |
var aImgWidth = aImg.width; | |
var sharedBlurKernelSize = p.shared.blurKernelSize; | |
var sharedBlurRadius = p.shared.blurRadius; | |
var sharedBlurKernal = p.shared.blurKernel; | |
var pix = aImg.imageData.data; | |
for (y = 0; y < aImgHeight; y++) { | |
for (x = 0; x < aImgWidth; x++) { | |
cb = cg = cr = ca = sum = 0; | |
read = x - sharedBlurRadius; | |
if (read < 0) { | |
bk0 = -read; | |
read = 0 | |
} else { | |
if (read >= aImgWidth) break; | |
bk0 = 0 | |
} | |
for (i = bk0; i < sharedBlurKernelSize; i++) { | |
if (read >= aImgWidth) break; | |
offset = (read + yi) * 4; | |
m = sharedBlurKernal[i]; | |
ca += m * pix[offset + 3]; | |
cr += m * pix[offset]; | |
cg += m * pix[offset + 1]; | |
cb += m * pix[offset + 2]; | |
sum += m; | |
read++ | |
} | |
ri = yi + x; | |
a2[ri] = ca / sum; | |
r2[ri] = cr / sum; | |
g2[ri] = cg / sum; | |
b2[ri] = cb / sum | |
} | |
yi += aImgWidth | |
} | |
yi = 0; | |
ym = -sharedBlurRadius; | |
ymi = ym * aImgWidth; | |
for (y = 0; y < aImgHeight; y++) { | |
for (x = 0; x < aImgWidth; x++) { | |
cb = cg = cr = ca = sum = 0; | |
if (ym < 0) { | |
bk0 = ri = -ym; | |
read = x | |
} else { | |
if (ym >= aImgHeight) break; | |
bk0 = 0; | |
ri = ym; | |
read = x + ymi | |
} | |
for (i = bk0; i < sharedBlurKernelSize; i++) { | |
if (ri >= aImgHeight) break; | |
m = sharedBlurKernal[i]; | |
ca += m * a2[read]; | |
cr += m * r2[read]; | |
cg += m * g2[read]; | |
cb += m * b2[read]; | |
sum += m; | |
ri++; | |
read += aImgWidth | |
} | |
offset = (x + yi) * 4; | |
pix[offset] = cr / sum; | |
pix[offset + 1] = cg / sum; | |
pix[offset + 2] = cb / sum; | |
pix[offset + 3] = ca / sum | |
} | |
yi += aImgWidth; | |
ymi += aImgWidth; | |
ym++ | |
} | |
}; | |
var dilate = function(isInverted, aImg) { | |
var currIdx = 0; | |
var maxIdx = aImg.pixels.getLength(); | |
var out = new Int32Array(maxIdx); | |
var currRowIdx, maxRowIdx, colOrig, colOut, currLum; | |
var idxRight, idxLeft, idxUp, idxDown, colRight, colLeft, colUp, colDown, lumRight, lumLeft, lumUp, lumDown; | |
if (!isInverted) while (currIdx < maxIdx) { | |
currRowIdx = currIdx; | |
maxRowIdx = currIdx + aImg.width; | |
while (currIdx < maxRowIdx) { | |
colOrig = colOut = aImg.pixels.getPixel(currIdx); | |
idxLeft = currIdx - 1; | |
idxRight = currIdx + 1; | |
idxUp = currIdx - aImg.width; | |
idxDown = currIdx + aImg.width; | |
if (idxLeft < currRowIdx) idxLeft = currIdx; | |
if (idxRight >= maxRowIdx) idxRight = currIdx; | |
if (idxUp < 0) idxUp = 0; | |
if (idxDown >= maxIdx) idxDown = currIdx; | |
colUp = aImg.pixels.getPixel(idxUp); | |
colLeft = aImg.pixels.getPixel(idxLeft); | |
colDown = aImg.pixels.getPixel(idxDown); | |
colRight = aImg.pixels.getPixel(idxRight); | |
currLum = 77 * (colOrig >> 16 & 255) + 151 * (colOrig >> 8 & 255) + 28 * (colOrig & 255); | |
lumLeft = 77 * (colLeft >> 16 & 255) + 151 * (colLeft >> 8 & 255) + 28 * (colLeft & 255); | |
lumRight = 77 * (colRight >> 16 & 255) + 151 * (colRight >> 8 & 255) + 28 * (colRight & 255); | |
lumUp = 77 * (colUp >> 16 & 255) + 151 * (colUp >> 8 & 255) + 28 * (colUp & 255); | |
lumDown = 77 * (colDown >> 16 & 255) + 151 * (colDown >> 8 & 255) + 28 * (colDown & 255); | |
if (lumLeft > currLum) { | |
colOut = colLeft; | |
currLum = lumLeft | |
} | |
if (lumRight > currLum) { | |
colOut = colRight; | |
currLum = lumRight | |
} | |
if (lumUp > currLum) { | |
colOut = colUp; | |
currLum = lumUp | |
} | |
if (lumDown > currLum) { | |
colOut = colDown; | |
currLum = lumDown | |
} | |
out[currIdx++] = colOut | |
} | |
} else while (currIdx < maxIdx) { | |
currRowIdx = currIdx; | |
maxRowIdx = currIdx + aImg.width; | |
while (currIdx < maxRowIdx) { | |
colOrig = colOut = aImg.pixels.getPixel(currIdx); | |
idxLeft = currIdx - 1; | |
idxRight = currIdx + 1; | |
idxUp = currIdx - aImg.width; | |
idxDown = currIdx + aImg.width; | |
if (idxLeft < currRowIdx) idxLeft = currIdx; | |
if (idxRight >= maxRowIdx) idxRight = currIdx; | |
if (idxUp < 0) idxUp = 0; | |
if (idxDown >= maxIdx) idxDown = currIdx; | |
colUp = aImg.pixels.getPixel(idxUp); | |
colLeft = aImg.pixels.getPixel(idxLeft); | |
colDown = aImg.pixels.getPixel(idxDown); | |
colRight = aImg.pixels.getPixel(idxRight); | |
currLum = 77 * (colOrig >> 16 & 255) + 151 * (colOrig >> 8 & 255) + 28 * (colOrig & 255); | |
lumLeft = 77 * (colLeft >> 16 & 255) + 151 * (colLeft >> 8 & 255) + 28 * (colLeft & 255); | |
lumRight = 77 * (colRight >> 16 & 255) + 151 * (colRight >> 8 & 255) + 28 * (colRight & 255); | |
lumUp = 77 * (colUp >> 16 & 255) + 151 * (colUp >> 8 & 255) + 28 * (colUp & 255); | |
lumDown = 77 * (colDown >> 16 & 255) + 151 * (colDown >> 8 & 255) + 28 * (colDown & 255); | |
if (lumLeft < currLum) { | |
colOut = colLeft; | |
currLum = lumLeft | |
} | |
if (lumRight < currLum) { | |
colOut = colRight; | |
currLum = lumRight | |
} | |
if (lumUp < currLum) { | |
colOut = colUp; | |
currLum = lumUp | |
} | |
if (lumDown < currLum) { | |
colOut = colDown; | |
currLum = lumDown | |
} | |
out[currIdx++] = colOut | |
} | |
} | |
aImg.pixels.set(out) | |
}; | |
p.filter = function(kind, param, aImg) { | |
var img, col, lum, i; | |
if (arguments.length === 3) { | |
aImg.loadPixels(); | |
img = aImg | |
} else { | |
p.loadPixels(); | |
img = p | |
} | |
if (param === undef) param = null; | |
if (img.isRemote) throw "Image is loaded remotely. Cannot filter image."; | |
var imglen = img.pixels.getLength(); | |
switch (kind) { | |
case 11: | |
var radius = param || 1; | |
blurARGB(radius, img); | |
break; | |
case 12: | |
if (img.format === 4) { | |
for (i = 0; i < imglen; i++) { | |
col = 255 - img.pixels.getPixel(i); | |
img.pixels.setPixel(i, 4278190080 | col << 16 | col << 8 | col) | |
} | |
img.format = 1 | |
} else for (i = 0; i < imglen; i++) { | |
col = img.pixels.getPixel(i); | |
lum = 77 * (col >> 16 & 255) + 151 * (col >> 8 & 255) + 28 * (col & 255) >> 8; | |
img.pixels.setPixel(i, col & 4278190080 | lum << 16 | lum << 8 | lum) | |
} | |
break; | |
case 13: | |
for (i = 0; i < imglen; i++) img.pixels.setPixel(i, img.pixels.getPixel(i) ^ 16777215); | |
break; | |
case 15: | |
if (param === null) throw "Use filter(POSTERIZE, int levels) instead of filter(POSTERIZE)"; | |
var levels = p.floor(param); | |
if (levels < 2 || levels > 255) throw "Levels must be between 2 and 255 for filter(POSTERIZE, levels)"; | |
var levels1 = levels - 1; | |
for (i = 0; i < imglen; i++) { | |
var rlevel = img.pixels.getPixel(i) >> 16 & 255; | |
var glevel = img.pixels.getPixel(i) >> 8 & 255; | |
var blevel = img.pixels.getPixel(i) & 255; | |
rlevel = (rlevel * levels >> 8) * 255 / levels1; | |
glevel = (glevel * levels >> 8) * 255 / levels1; | |
blevel = (blevel * levels >> 8) * 255 / levels1; | |
img.pixels.setPixel(i, 4278190080 & img.pixels.getPixel(i) | rlevel << 16 | glevel << 8 | blevel) | |
} | |
break; | |
case 14: | |
for (i = 0; i < imglen; i++) img.pixels.setPixel(i, img.pixels.getPixel(i) | 4278190080); | |
img.format = 1; | |
break; | |
case 16: | |
if (param === null) param = 0.5; | |
if (param < 0 || param > 1) throw "Level must be between 0 and 1 for filter(THRESHOLD, level)"; | |
var thresh = p.floor(param * 255); | |
for (i = 0; i < imglen; i++) { | |
var max = p.max((img.pixels.getPixel(i) & 16711680) >> 16, p.max((img.pixels.getPixel(i) & 65280) >> 8, img.pixels.getPixel(i) & 255)); | |
img.pixels.setPixel(i, img.pixels.getPixel(i) & 4278190080 | (max < thresh ? 0 : 16777215)) | |
} | |
break; | |
case 17: | |
dilate(true, img); | |
break; | |
case 18: | |
dilate(false, img); | |
break | |
} | |
img.updatePixels() | |
}; | |
p.shared = { | |
fracU: 0, | |
ifU: 0, | |
fracV: 0, | |
ifV: 0, | |
u1: 0, | |
u2: 0, | |
v1: 0, | |
v2: 0, | |
sX: 0, | |
sY: 0, | |
iw: 0, | |
iw1: 0, | |
ih1: 0, | |
ul: 0, | |
ll: 0, | |
ur: 0, | |
lr: 0, | |
cUL: 0, | |
cLL: 0, | |
cUR: 0, | |
cLR: 0, | |
srcXOffset: 0, | |
srcYOffset: 0, | |
r: 0, | |
g: 0, | |
b: 0, | |
a: 0, | |
srcBuffer: null, | |
blurRadius: 0, | |
blurKernelSize: 0, | |
blurKernel: null | |
}; | |
p.intersect = function(sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2) { | |
var sw = sx2 - sx1 + 1; | |
var sh = sy2 - sy1 + 1; | |
var dw = dx2 - dx1 + 1; | |
var dh = dy2 - dy1 + 1; | |
if (dx1 < sx1) { | |
dw += dx1 - sx1; | |
if (dw > sw) dw = sw | |
} else { | |
var w = sw + sx1 - dx1; | |
if (dw > w) dw = w | |
} | |
if (dy1 < sy1) { | |
dh += dy1 - sy1; | |
if (dh > sh) dh = sh | |
} else { | |
var h = sh + sy1 - dy1; | |
if (dh > h) dh = h | |
} | |
return ! (dw <= 0 || dh <= 0) | |
}; | |
var blendFuncs = {}; | |
blendFuncs[1] = p.modes.blend; | |
blendFuncs[2] = p.modes.add; | |
blendFuncs[4] = p.modes.subtract; | |
blendFuncs[8] = p.modes.lightest; | |
blendFuncs[16] = p.modes.darkest; | |
blendFuncs[0] = p.modes.replace; | |
blendFuncs[32] = p.modes.difference; | |
blendFuncs[64] = p.modes.exclusion; | |
blendFuncs[128] = p.modes.multiply; | |
blendFuncs[256] = p.modes.screen; | |
blendFuncs[512] = p.modes.overlay; | |
blendFuncs[1024] = p.modes.hard_light; | |
blendFuncs[2048] = p.modes.soft_light; | |
blendFuncs[4096] = p.modes.dodge; | |
blendFuncs[8192] = p.modes.burn; | |
p.blit_resize = function(img, srcX1, srcY1, srcX2, srcY2, destPixels, screenW, screenH, destX1, destY1, destX2, destY2, mode) { | |
var x, y; | |
if (srcX1 < 0) srcX1 = 0; | |
if (srcY1 < 0) srcY1 = 0; | |
if (srcX2 >= img.width) srcX2 = img.width - 1; | |
if (srcY2 >= img.height) srcY2 = img.height - 1; | |
var srcW = srcX2 - srcX1; | |
var srcH = srcY2 - srcY1; | |
var destW = destX2 - destX1; | |
var destH = destY2 - destY1; | |
if (destW <= 0 || destH <= 0 || srcW <= 0 || srcH <= 0 || destX1 >= screenW || destY1 >= screenH || srcX1 >= img.width || srcY1 >= img.height) return; | |
var dx = Math.floor(srcW / destW * 32768); | |
var dy = Math.floor(srcH / destH * 32768); | |
var pshared = p.shared; | |
pshared.srcXOffset = Math.floor(destX1 < 0 ? -destX1 * dx : srcX1 * 32768); | |
pshared.srcYOffset = Math.floor(destY1 < 0 ? -destY1 * dy : srcY1 * 32768); | |
if (destX1 < 0) { | |
destW += destX1; | |
destX1 = 0 | |
} | |
if (destY1 < 0) { | |
destH += destY1; | |
destY1 = 0 | |
} | |
destW = Math.min(destW, screenW - destX1); | |
destH = Math.min(destH, screenH - destY1); | |
var destOffset = destY1 * screenW + destX1; | |
var destColor; | |
pshared.srcBuffer = img.imageData.data; | |
pshared.iw = img.width; | |
pshared.iw1 = img.width - 1; | |
pshared.ih1 = img.height - 1; | |
var filterBilinear = p.filter_bilinear, | |
filterNewScanline = p.filter_new_scanline, | |
blendFunc = blendFuncs[mode], | |
blendedColor, idx, cULoffset, cURoffset, cLLoffset, cLRoffset, ALPHA_MASK = 4278190080, | |
RED_MASK = 16711680, | |
GREEN_MASK = 65280, | |
BLUE_MASK = 255, | |
PREC_MAXVAL = 32767, | |
PRECISIONB = 15, | |
PREC_RED_SHIFT = 1, | |
PREC_ALPHA_SHIFT = 9, | |
srcBuffer = pshared.srcBuffer, | |
min = Math.min; | |
for (y = 0; y < destH; y++) { | |
pshared.sX = pshared.srcXOffset; | |
pshared.fracV = pshared.srcYOffset & PREC_MAXVAL; | |
pshared.ifV = PREC_MAXVAL - pshared.fracV; | |
pshared.v1 = (pshared.srcYOffset >> PRECISIONB) * pshared.iw; | |
pshared.v2 = min((pshared.srcYOffset >> PRECISIONB) + 1, pshared.ih1) * pshared.iw; | |
for (x = 0; x < destW; x++) { | |
idx = (destOffset + x) * 4; | |
destColor = destPixels[idx + 3] << 24 & ALPHA_MASK | destPixels[idx] << 16 & RED_MASK | destPixels[idx + 1] << 8 & GREEN_MASK | destPixels[idx + 2] & BLUE_MASK; | |
pshared.fracU = pshared.sX & PREC_MAXVAL; | |
pshared.ifU = PREC_MAXVAL - pshared.fracU; | |
pshared.ul = pshared.ifU * pshared.ifV >> PRECISIONB; | |
pshared.ll = pshared.ifU * pshared.fracV >> PRECISIONB; | |
pshared.ur = pshared.fracU * pshared.ifV >> PRECISIONB; | |
pshared.lr = pshared.fracU * pshared.fracV >> PRECISIONB; | |
pshared.u1 = pshared.sX >> PRECISIONB; | |
pshared.u2 = min(pshared.u1 + 1, pshared.iw1); | |
cULoffset = (pshared.v1 + pshared.u1) * 4; | |
cURoffset = (pshared.v1 + pshared.u2) * 4; | |
cLLoffset = (pshared.v2 + pshared.u1) * 4; | |
cLRoffset = (pshared.v2 + pshared.u2) * 4; | |
pshared.cUL = srcBuffer[cULoffset + 3] << 24 & ALPHA_MASK | srcBuffer[cULoffset] << 16 & RED_MASK | srcBuffer[cULoffset + 1] << 8 & GREEN_MASK | srcBuffer[cULoffset + 2] & BLUE_MASK; | |
pshared.cUR = srcBuffer[cURoffset + 3] << 24 & ALPHA_MASK | srcBuffer[cURoffset] << 16 & RED_MASK | srcBuffer[cURoffset + 1] << 8 & GREEN_MASK | srcBuffer[cURoffset + 2] & BLUE_MASK; | |
pshared.cLL = srcBuffer[cLLoffset + 3] << 24 & ALPHA_MASK | srcBuffer[cLLoffset] << 16 & RED_MASK | srcBuffer[cLLoffset + 1] << 8 & GREEN_MASK | srcBuffer[cLLoffset + 2] & BLUE_MASK; | |
pshared.cLR = srcBuffer[cLRoffset + 3] << 24 & ALPHA_MASK | srcBuffer[cLRoffset] << 16 & RED_MASK | srcBuffer[cLRoffset + 1] << 8 & GREEN_MASK | srcBuffer[cLRoffset + 2] & BLUE_MASK; | |
pshared.r = pshared.ul * ((pshared.cUL & RED_MASK) >> 16) + pshared.ll * ((pshared.cLL & RED_MASK) >> 16) + pshared.ur * ((pshared.cUR & RED_MASK) >> 16) + pshared.lr * ((pshared.cLR & RED_MASK) >> 16) << PREC_RED_SHIFT & RED_MASK; | |
pshared.g = pshared.ul * (pshared.cUL & GREEN_MASK) + pshared.ll * (pshared.cLL & GREEN_MASK) + pshared.ur * (pshared.cUR & GREEN_MASK) + pshared.lr * (pshared.cLR & GREEN_MASK) >>> PRECISIONB & GREEN_MASK; | |
pshared.b = pshared.ul * (pshared.cUL & BLUE_MASK) + pshared.ll * (pshared.cLL & BLUE_MASK) + pshared.ur * (pshared.cUR & BLUE_MASK) + pshared.lr * (pshared.cLR & BLUE_MASK) >>> PRECISIONB; | |
pshared.a = pshared.ul * ((pshared.cUL & ALPHA_MASK) >>> 24) + pshared.ll * ((pshared.cLL & ALPHA_MASK) >>> 24) + pshared.ur * ((pshared.cUR & ALPHA_MASK) >>> 24) + pshared.lr * ((pshared.cLR & ALPHA_MASK) >>> 24) << PREC_ALPHA_SHIFT & ALPHA_MASK; | |
blendedColor = blendFunc(destColor, pshared.a | pshared.r | pshared.g | pshared.b); | |
destPixels[idx] = (blendedColor & RED_MASK) >>> 16; | |
destPixels[idx + 1] = (blendedColor & GREEN_MASK) >>> 8; | |
destPixels[idx + 2] = blendedColor & BLUE_MASK; | |
destPixels[idx + 3] = (blendedColor & ALPHA_MASK) >>> 24; | |
pshared.sX += dx | |
} | |
destOffset += screenW; | |
pshared.srcYOffset += dy | |
} | |
}; | |
p.loadFont = function(name, size) { | |
if (name === undef) throw "font name required in loadFont."; | |
if (name.indexOf(".svg") === -1) { | |
if (size === undef) size = curTextFont.size; | |
return PFont.get(name, size) | |
} | |
var font = p.loadGlyphs(name); | |
return { | |
name: name, | |
css: "12px sans-serif", | |
glyph: true, | |
units_per_em: font.units_per_em, | |
horiz_adv_x: 1 / font.units_per_em * font.horiz_adv_x, | |
ascent: font.ascent, | |
descent: font.descent, | |
width: function(str) { | |
var width = 0; | |
var len = str.length; | |
for (var i = 0; i < len; i++) try { | |
width += parseFloat(p.glyphLook(p.glyphTable[name], str[i]).horiz_adv_x) | |
} catch(e) { | |
Processing.debug(e) | |
} | |
return width / p.glyphTable[name].units_per_em | |
} | |
} | |
}; | |
p.createFont = function(name, size) { | |
return p.loadFont(name, size) | |
}; | |
p.textFont = function(pfont, size) { | |
if (size !== undef) { | |
if (!pfont.glyph) pfont = PFont.get(pfont.name, size); | |
curTextSize = size | |
} | |
curTextFont = pfont; | |
curFontName = curTextFont.name; | |
curTextAscent = curTextFont.ascent; | |
curTextDescent = curTextFont.descent; | |
curTextLeading = curTextFont.leading; | |
var curContext = drawing.$ensureContext(); | |
curContext.font = curTextFont.css | |
}; | |
p.textSize = function(size) { | |
curTextFont = PFont.get(curFontName, size); | |
curTextSize = size; | |
curTextAscent = curTextFont.ascent; | |
curTextDescent = curTextFont.descent; | |
curTextLeading = curTextFont.leading; | |
var curContext = drawing.$ensureContext(); | |
curContext.font = curTextFont.css | |
}; | |
p.textAscent = function() { | |
return curTextAscent | |
}; | |
p.textDescent = function() { | |
return curTextDescent | |
}; | |
p.textLeading = function(leading) { | |
curTextLeading = leading | |
}; | |
p.textAlign = function(xalign, yalign) { | |
horizontalTextAlignment = xalign; | |
verticalTextAlignment = yalign || 0 | |
}; | |
function toP5String(obj) { | |
if (obj instanceof String) return obj; | |
if (typeof obj === "number") { | |
if (obj === (0 | obj)) return obj.toString(); | |
return p.nf(obj, 0, 3) | |
} | |
if (obj === null || obj === undef) return ""; | |
return obj.toString() | |
} | |
Drawing2D.prototype.textWidth = function(str) { | |
var lines = toP5String(str).split(/\r?\n/g), | |
width = 0; | |
var i, linesCount = lines.length; | |
curContext.font = curTextFont.css; | |
for (i = 0; i < linesCount; ++i) width = Math.max(width, curTextFont.measureTextWidth(lines[i])); | |
return width | 0 | |
}; | |
Drawing3D.prototype.textWidth = function(str) { | |
var lines = toP5String(str).split(/\r?\n/g), | |
width = 0; | |
var i, linesCount = lines.length; | |
if (textcanvas === undef) textcanvas = document.createElement("canvas"); | |
var textContext = textcanvas.getContext("2d"); | |
textContext.font = curTextFont.css; | |
for (i = 0; i < linesCount; ++i) width = Math.max(width, textContext.measureText(lines[i]).width); | |
return width | 0 | |
}; | |
p.glyphLook = function(font, chr) { | |
try { | |
switch (chr) { | |
case "1": | |
return font.one; | |
case "2": | |
return font.two; | |
case "3": | |
return font.three; | |
case "4": | |
return font.four; | |
case "5": | |
return font.five; | |
case "6": | |
return font.six; | |
case "7": | |
return font.seven; | |
case "8": | |
return font.eight; | |
case "9": | |
return font.nine; | |
case "0": | |
return font.zero; | |
case " ": | |
return font.space; | |
case "$": | |
return font.dollar; | |
case "!": | |
return font.exclam; | |
case '"': | |
return font.quotedbl; | |
case "#": | |
return font.numbersign; | |
case "%": | |
return font.percent; | |
case "&": | |
return font.ampersand; | |
case "'": | |
return font.quotesingle; | |
case "(": | |
return font.parenleft; | |
case ")": | |
return font.parenright; | |
case "*": | |
return font.asterisk; | |
case "+": | |
return font.plus; | |
case ",": | |
return font.comma; | |
case "-": | |
return font.hyphen; | |
case ".": | |
return font.period; | |
case "/": | |
return font.slash; | |
case "_": | |
return font.underscore; | |
case ":": | |
return font.colon; | |
case ";": | |
return font.semicolon; | |
case "<": | |
return font.less; | |
case "=": | |
return font.equal; | |
case ">": | |
return font.greater; | |
case "?": | |
return font.question; | |
case "@": | |
return font.at; | |
case "[": | |
return font.bracketleft; | |
case "\\": | |
return font.backslash; | |
case "]": | |
return font.bracketright; | |
case "^": | |
return font.asciicircum; | |
case "`": | |
return font.grave; | |
case "{": | |
return font.braceleft; | |
case "|": | |
return font.bar; | |
case "}": | |
return font.braceright; | |
case "~": | |
return font.asciitilde; | |
default: | |
return font[chr] | |
} | |
} catch(e) { | |
Processing.debug(e) | |
} | |
}; | |
Drawing2D.prototype.text$line = function(str, x, y, z, align) { | |
var textWidth = 0, | |
xOffset = 0; | |
if (!curTextFont.glyph) { | |
if (str && "fillText" in curContext) { | |
if (isFillDirty) { | |
curContext.fillStyle = p.color.toString(currentFillColor); | |
isFillDirty = false | |
} | |
if (align === 39 || align === 3) { | |
textWidth = curTextFont.measureTextWidth(str); | |
if (align === 39) xOffset = -textWidth; | |
else xOffset = -textWidth / 2 | |
} | |
curContext.fillText(str, x + xOffset, y) | |
} | |
} else { | |
var font = p.glyphTable[curFontName]; | |
saveContext(); | |
curContext.translate(x, y + curTextSize); | |
if (align === 39 || align === 3) { | |
textWidth = font.width(str); | |
if (align === 39) xOffset = -textWidth; | |
else xOffset = -textWidth / 2 | |
} | |
var upem = font.units_per_em, | |
newScale = 1 / upem * curTextSize; | |
curContext.scale(newScale, newScale); | |
for (var i = 0, len = str.length; i < len; i++) try { | |
p.glyphLook(font, str[i]).draw() | |
} catch(e) { | |
Processing.debug(e) | |
} | |
restoreContext() | |
} | |
}; | |
Drawing3D.prototype.text$line = function(str, x, y, z, align) { | |
if (textcanvas === undef) textcanvas = document.createElement("canvas"); | |
var oldContext = curContext; | |
curContext = textcanvas.getContext("2d"); | |
curContext.font = curTextFont.css; | |
var textWidth = curTextFont.measureTextWidth(str); | |
textcanvas.width = textWidth; | |
textcanvas.height = curTextSize; | |
curContext = textcanvas.getContext("2d"); | |
curContext.font = curTextFont.css; | |
curContext.textBaseline = "top"; | |
Drawing2D.prototype.text$line(str, 0, 0, 0, 37); | |
var aspect = textcanvas.width / textcanvas.height; | |
curContext = oldContext; | |
curContext.bindTexture(curContext.TEXTURE_2D, textTex); | |
curContext.texImage2D(curContext.TEXTURE_2D, 0, curContext.RGBA, curContext.RGBA, curContext.UNSIGNED_BYTE, textcanvas); | |
curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_MAG_FILTER, curContext.LINEAR); | |
curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_MIN_FILTER, curContext.LINEAR); | |
curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_WRAP_T, curContext.CLAMP_TO_EDGE); | |
curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_WRAP_S, curContext.CLAMP_TO_EDGE); | |
var xOffset = 0; | |
if (align === 39) xOffset = -textWidth; | |
else if (align === 3) xOffset = -textWidth / 2; | |
var model = new PMatrix3D; | |
var scalefactor = curTextSize * 0.5; | |
model.translate(x + xOffset - scalefactor / 2, y - scalefactor, z); | |
model.scale(-aspect * scalefactor, -scalefactor, scalefactor); | |
model.translate(-1, -1, -1); | |
model.transpose(); | |
var view = new PMatrix3D; | |
view.scale(1, -1, 1); | |
view.apply(modelView.array()); | |
view.transpose(); | |
curContext.useProgram(programObject2D); | |
vertexAttribPointer("aVertex2d", programObject2D, "aVertex", 3, textBuffer); | |
vertexAttribPointer("aTextureCoord2d", programObject2D, "aTextureCoord", 2, textureBuffer); | |
uniformi("uSampler2d", programObject2D, "uSampler", [0]); | |
uniformi("uIsDrawingText2d", programObject2D, "uIsDrawingText", true); | |
uniformMatrix("uModel2d", programObject2D, "uModel", false, model.array()); | |
uniformMatrix("uView2d", programObject2D, "uView", false, view.array()); | |
uniformf("uColor2d", programObject2D, "uColor", fillStyle); | |
curContext.bindBuffer(curContext.ELEMENT_ARRAY_BUFFER, indexBuffer); | |
curContext.drawElements(curContext.TRIANGLES, 6, curContext.UNSIGNED_SHORT, 0) | |
}; | |
function text$4(str, x, y, z) { | |
var lines, linesCount; | |
if (str.indexOf("\n") < 0) { | |
lines = [str]; | |
linesCount = 1 | |
} else { | |
lines = str.split(/\r?\n/g); | |
linesCount = lines.length | |
} | |
var yOffset = 0; | |
if (verticalTextAlignment === 101) yOffset = curTextAscent + curTextDescent; | |
else if (verticalTextAlignment === 3) yOffset = curTextAscent / 2 - (linesCount - 1) * curTextLeading / 2; | |
else if (verticalTextAlignment === 102) yOffset = -(curTextDescent + (linesCount - 1) * curTextLeading); | |
for (var i = 0; i < linesCount; ++i) { | |
var line = lines[i]; | |
drawing.text$line(line, x, y + yOffset, z, horizontalTextAlignment); | |
yOffset += curTextLeading | |
} | |
} | |
function text$6(str, x, y, width, height, z) { | |
if (str.length === 0 || width === 0 || height === 0) return; | |
if (curTextSize > height) return; | |
var spaceMark = -1; | |
var start = 0; | |
var lineWidth = 0; | |
var drawCommands = []; | |
for (var charPos = 0, len = str.length; charPos < len; charPos++) { | |
var currentChar = str[charPos]; | |
var spaceChar = currentChar === " "; | |
var letterWidth = curTextFont.measureTextWidth(currentChar); | |
if (currentChar !== "\n" && lineWidth + letterWidth <= width) { | |
if (spaceChar) spaceMark = charPos; | |
lineWidth += letterWidth | |
} else { | |
if (spaceMark + 1 === start) if (charPos > 0) spaceMark = charPos; | |
else return; | |
if (currentChar === "\n") { | |
drawCommands.push({ | |
text: str.substring(start, charPos), | |
width: lineWidth | |
}); | |
start = charPos + 1 | |
} else { | |
drawCommands.push({ | |
text: str.substring(start, spaceMark + 1), | |
width: lineWidth | |
}); | |
start = spaceMark + 1 | |
} | |
lineWidth = 0; | |
charPos = start - 1 | |
} | |
} | |
if (start < len) drawCommands.push({ | |
text: str.substring(start), | |
width: lineWidth | |
}); | |
var xOffset = 1, | |
yOffset = curTextAscent; | |
if (horizontalTextAlignment === 3) xOffset = width / 2; | |
else if (horizontalTextAlignment === 39) xOffset = width; | |
var linesCount = drawCommands.length, | |
visibleLines = Math.min(linesCount, Math.floor(height / curTextLeading)); | |
if (verticalTextAlignment === 101) yOffset = curTextAscent + curTextDescent; | |
else if (verticalTextAlignment === 3) yOffset = height / 2 - curTextLeading * (visibleLines / 2 - 1); | |
else if (verticalTextAlignment === 102) yOffset = curTextDescent + curTextLeading; | |
var command, drawCommand, leading; | |
for (command = 0; command < linesCount; command++) { | |
leading = command * curTextLeading; | |
if (yOffset + leading > height - curTextDescent) break; | |
drawCommand = drawCommands[command]; | |
drawing.text$line(drawCommand.text, x + xOffset, y + yOffset + leading, z, horizontalTextAlignment) | |
} | |
} | |
p.text = function() { | |
if (textMode === 5) return; | |
if (arguments.length === 3) text$4(toP5String(arguments[0]), arguments[1], arguments[2], 0); | |
else if (arguments.length === 4) text$4(toP5String(arguments[0]), arguments[1], arguments[2], arguments[3]); | |
else if (arguments.length === 5) text$6(toP5String(arguments[0]), arguments[1], arguments[2], arguments[3], arguments[4], 0); | |
else if (arguments.length === 6) text$6(toP5String(arguments[0]), arguments[1], arguments[2], arguments[3], arguments[4], arguments[5]) | |
}; | |
p.textMode = function(mode) { | |
textMode = mode | |
}; | |
p.loadGlyphs = function(url) { | |
var x, y, cx, cy, nx, ny, d, a, lastCom, lenC, horiz_adv_x, getXY = "[0-9\\-]+", | |
path; | |
var regex = function(needle, hay) { | |
var i = 0, | |
results = [], | |
latest, regexp = new RegExp(needle, "g"); | |
latest = results[i] = regexp.exec(hay); | |
while (latest) { | |
i++; | |
latest = results[i] = regexp.exec(hay) | |
} | |
return results | |
}; | |
var buildPath = function(d) { | |
var c = regex("[A-Za-z][0-9\\- ]+|Z", d); | |
var beforePathDraw = function() { | |
saveContext(); | |
return drawing.$ensureContext() | |
}; | |
var afterPathDraw = function() { | |
executeContextFill(); | |
executeContextStroke(); | |
restoreContext() | |
}; | |
path = "return {draw:function(){var curContext=beforePathDraw();curContext.beginPath();"; | |
x = 0; | |
y = 0; | |
cx = 0; | |
cy = 0; | |
nx = 0; | |
ny = 0; | |
d = 0; | |
a = 0; | |
lastCom = ""; | |
lenC = c.length - 1; | |
for (var j = 0; j < lenC; j++) { | |
var com = c[j][0], | |
xy = regex(getXY, com); | |
switch (com[0]) { | |
case "M": | |
x = parseFloat(xy[0][0]); | |
y = parseFloat(xy[1][0]); | |
path += "curContext.moveTo(" + x + "," + -y + ");"; | |
break; | |
case "L": | |
x = parseFloat(xy[0][0]); | |
y = parseFloat(xy[1][0]); | |
path += "curContext.lineTo(" + x + "," + -y + ");"; | |
break; | |
case "H": | |
x = parseFloat(xy[0][0]); | |
path += "curContext.lineTo(" + x + "," + -y + ");"; | |
break; | |
case "V": | |
y = parseFloat(xy[0][0]); | |
path += "curContext.lineTo(" + x + "," + -y + ");"; | |
break; | |
case "T": | |
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 += "curContext.quadraticCurveTo(" + cx + "," + -cy + "," + nx + "," + -ny + ");"; | |
x = nx; | |
y = ny; | |
break; | |
case "Q": | |
cx = parseFloat(xy[0][0]); | |
cy = parseFloat(xy[1][0]); | |
nx = parseFloat(xy[2][0]); | |
ny = parseFloat(xy[3][0]); | |
path += "curContext.quadraticCurveTo(" + cx + "," + -cy + "," + nx + "," + -ny + ");"; | |
x = nx; | |
y = ny; | |
break; | |
case "Z": | |
path += "curContext.closePath();"; | |
break | |
} | |
lastCom = com[0] | |
} | |
path += "afterPathDraw();"; | |
path += "curContext.translate(" + horiz_adv_x + ",0);"; | |
path += "}}"; | |
return (new Function("beforePathDraw", "afterPathDraw", path))(beforePathDraw, afterPathDraw) | |
}; | |
var parseSVGFont = function(svg) { | |
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 glyph = svg.getElementsByTagName("glyph"), | |
len = glyph.length; | |
for (var i = 0; i < len; i++) { | |
var unicode = glyph[i].getAttribute("unicode"); | |
var name = glyph[i].getAttribute("glyph-name"); | |
horiz_adv_x = glyph[i].getAttribute("horiz-adv-x"); | |
if (horiz_adv_x === null) horiz_adv_x = p.glyphTable[url].horiz_adv_x; | |
d = glyph[i].getAttribute("d"); | |
if (d !== undef) { | |
path = buildPath(d); | |
p.glyphTable[url][name] = { | |
name: name, | |
unicode: unicode, | |
horiz_adv_x: horiz_adv_x, | |
draw: path.draw | |
} | |
} | |
} | |
}; | |
var loadXML = function() { | |
var xmlDoc; | |
try { | |
xmlDoc = document.implementation.createDocument("", "", null) | |
} catch(e_fx_op) { | |
Processing.debug(e_fx_op.message); | |
return | |
} | |
try { | |
xmlDoc.async = false; | |
xmlDoc.load(url); | |
parseSVGFont(xmlDoc.getElementsByTagName("svg")[0]) | |
} catch(e_sf_ch) { | |
Processing.debug(e_sf_ch); | |
try { | |
var xmlhttp = new window.XMLHttpRequest; | |
xmlhttp.open("GET", url, false); | |
xmlhttp.send(null); | |
parseSVGFont(xmlhttp.responseXML.documentElement) | |
} catch(e) { | |
Processing.debug(e_sf_ch) | |
} | |
} | |
}; | |
p.glyphTable[url] = {}; | |
loadXML(url); | |
return p.glyphTable[url] | |
}; | |
p.param = function(name) { | |
var attributeName = "data-processing-" + name; | |
if (curElement.hasAttribute(attributeName)) return curElement.getAttribute(attributeName); | |
for (var i = 0, len = curElement.childNodes.length; i < len; ++i) { | |
var item = curElement.childNodes.item(i); | |
if (item.nodeType !== 1 || item.tagName.toLowerCase() !== "param") continue; | |
if (item.getAttribute("name") === name) return item.getAttribute("value") | |
} | |
if (curSketch.params.hasOwnProperty(name)) return curSketch.params[name]; | |
return null | |
}; | |
function wireDimensionalFunctions(mode) { | |
if (mode === "3D") drawing = new Drawing3D; | |
else if (mode === "2D") drawing = new Drawing2D; | |
else drawing = new DrawingPre; | |
for (var i in DrawingPre.prototype) if (DrawingPre.prototype.hasOwnProperty(i) && i.indexOf("$") < 0) p[i] = drawing[i]; | |
drawing.$init() | |
} | |
function createDrawingPreFunction(name) { | |
return function() { | |
wireDimensionalFunctions("2D"); | |
return drawing[name].apply(this, arguments) | |
} | |
} | |
DrawingPre.prototype.translate = createDrawingPreFunction("translate"); | |
DrawingPre.prototype.transform = createDrawingPreFunction("transform"); | |
DrawingPre.prototype.scale = createDrawingPreFunction("scale"); | |
DrawingPre.prototype.pushMatrix = createDrawingPreFunction("pushMatrix"); | |
DrawingPre.prototype.popMatrix = createDrawingPreFunction("popMatrix"); | |
DrawingPre.prototype.resetMatrix = createDrawingPreFunction("resetMatrix"); | |
DrawingPre.prototype.applyMatrix = createDrawingPreFunction("applyMatrix"); | |
DrawingPre.prototype.rotate = createDrawingPreFunction("rotate"); | |
DrawingPre.prototype.rotateZ = createDrawingPreFunction("rotateZ"); | |
DrawingPre.prototype.shearX = createDrawingPreFunction("shearX"); | |
DrawingPre.prototype.shearY = createDrawingPreFunction("shearY"); | |
DrawingPre.prototype.redraw = createDrawingPreFunction("redraw"); | |
DrawingPre.prototype.toImageData = createDrawingPreFunction("toImageData"); | |
DrawingPre.prototype.ambientLight = createDrawingPreFunction("ambientLight"); | |
DrawingPre.prototype.directionalLight = createDrawingPreFunction("directionalLight"); | |
DrawingPre.prototype.lightFalloff = createDrawingPreFunction("lightFalloff"); | |
DrawingPre.prototype.lightSpecular = createDrawingPreFunction("lightSpecular"); | |
DrawingPre.prototype.pointLight = createDrawingPreFunction("pointLight"); | |
DrawingPre.prototype.noLights = createDrawingPreFunction("noLights"); | |
DrawingPre.prototype.spotLight = createDrawingPreFunction("spotLight"); | |
DrawingPre.prototype.beginCamera = createDrawingPreFunction("beginCamera"); | |
DrawingPre.prototype.endCamera = createDrawingPreFunction("endCamera"); | |
DrawingPre.prototype.frustum = createDrawingPreFunction("frustum"); | |
DrawingPre.prototype.box = createDrawingPreFunction("box"); | |
DrawingPre.prototype.sphere = createDrawingPreFunction("sphere"); | |
DrawingPre.prototype.ambient = createDrawingPreFunction("ambient"); | |
DrawingPre.prototype.emissive = createDrawingPreFunction("emissive"); | |
DrawingPre.prototype.shininess = createDrawingPreFunction("shininess"); | |
DrawingPre.prototype.specular = createDrawingPreFunction("specular"); | |
DrawingPre.prototype.fill = createDrawingPreFunction("fill"); | |
DrawingPre.prototype.stroke = createDrawingPreFunction("stroke"); | |
DrawingPre.prototype.strokeWeight = createDrawingPreFunction("strokeWeight"); | |
DrawingPre.prototype.smooth = createDrawingPreFunction("smooth"); | |
DrawingPre.prototype.noSmooth = createDrawingPreFunction("noSmooth"); | |
DrawingPre.prototype.point = createDrawingPreFunction("point"); | |
DrawingPre.prototype.vertex = createDrawingPreFunction("vertex"); | |
DrawingPre.prototype.endShape = createDrawingPreFunction("endShape"); | |
DrawingPre.prototype.bezierVertex = createDrawingPreFunction("bezierVertex"); | |
DrawingPre.prototype.curveVertex = createDrawingPreFunction("curveVertex"); | |
DrawingPre.prototype.curve = createDrawingPreFunction("curve"); | |
DrawingPre.prototype.line = createDrawingPreFunction("line"); | |
DrawingPre.prototype.bezier = createDrawingPreFunction("bezier"); | |
DrawingPre.prototype.rect = createDrawingPreFunction("rect"); | |
DrawingPre.prototype.ellipse = createDrawingPreFunction("ellipse"); | |
DrawingPre.prototype.background = createDrawingPreFunction("background"); | |
DrawingPre.prototype.image = createDrawingPreFunction("image"); | |
DrawingPre.prototype.textWidth = createDrawingPreFunction("textWidth"); | |
DrawingPre.prototype.text$line = createDrawingPreFunction("text$line"); | |
DrawingPre.prototype.$ensureContext = createDrawingPreFunction("$ensureContext"); | |
DrawingPre.prototype.$newPMatrix = createDrawingPreFunction("$newPMatrix"); | |
DrawingPre.prototype.size = function(aWidth, aHeight, aMode) { | |
wireDimensionalFunctions(aMode === 2 ? "3D" : "2D"); | |
p.size(aWidth, aHeight, aMode) | |
}; | |
DrawingPre.prototype.$init = nop; | |
Drawing2D.prototype.$init = function() { | |
p.size(p.width, p.height); | |
curContext.lineCap = "round"; | |
p.noSmooth(); | |
p.disableContextMenu() | |
}; | |
Drawing3D.prototype.$init = function() { | |
p.use3DContext = true; | |
p.disableContextMenu() | |
}; | |
DrawingShared.prototype.$ensureContext = function() { | |
return curContext | |
}; | |
function calculateOffset(curElement, event) { | |
var element = curElement, | |
offsetX = 0, | |
offsetY = 0; | |
p.pmouseX = p.mouseX; | |
p.pmouseY = p.mouseY; | |
if (element.offsetParent) { | |
do { | |
offsetX += element.offsetLeft; | |
offsetY += element.offsetTop | |
} while ( !! (element = element.offsetParent)) | |
} | |
element = curElement; | |
do { | |
offsetX -= element.scrollLeft || 0; | |
offsetY -= element.scrollTop || 0 | |
} while ( !! (element = element.parentNode)); | |
offsetX += stylePaddingLeft; | |
offsetY += stylePaddingTop; | |
offsetX += styleBorderLeft; | |
offsetY += styleBorderTop; | |
offsetX += window.pageXOffset; | |
offsetY += window.pageYOffset; | |
return { | |
"X": offsetX, | |
"Y": offsetY | |
} | |
} | |
function updateMousePosition(curElement, event) { | |
var offset = calculateOffset(curElement, event); | |
p.mouseX = event.pageX - offset.X; | |
p.mouseY = event.pageY - offset.Y | |
} | |
function addTouchEventOffset(t) { | |
var offset = calculateOffset(t.changedTouches[0].target, t.changedTouches[0]), | |
i; | |
for (i = 0; i < t.touches.length; i++) { | |
var touch = t.touches[i]; | |
touch.offsetX = touch.pageX - offset.X; | |
touch.offsetY = touch.pageY - offset.Y | |
} | |
for (i = 0; i < t.targetTouches.length; i++) { | |
var targetTouch = t.targetTouches[i]; | |
targetTouch.offsetX = targetTouch.pageX - offset.X; | |
targetTouch.offsetY = targetTouch.pageY - offset.Y | |
} | |
for (i = 0; i < t.changedTouches.length; i++) { | |
var changedTouch = t.changedTouches[i]; | |
changedTouch.offsetX = changedTouch.pageX - offset.X; | |
changedTouch.offsetY = changedTouch.pageY - offset.Y | |
} | |
return t | |
} | |
attachEventHandler(curElement, "touchstart", function(t) { | |
curElement.setAttribute("style", "-webkit-user-select: none"); | |
curElement.setAttribute("onclick", "void(0)"); | |
curElement.setAttribute("style", "-webkit-tap-highlight-color:rgba(0,0,0,0)"); | |
for (var i = 0, ehl = eventHandlers.length; i < ehl; i++) { | |
var type = eventHandlers[i].type; | |
if (type === "mouseout" || type === "mousemove" || type === "mousedown" || type === "mouseup" || type === "DOMMouseScroll" || type === "mousewheel" || type === "touchstart") detachEventHandler(eventHandlers[i]) | |
} | |
if (p.touchStart !== undef || p.touchMove !== undef || p.touchEnd !== undef || p.touchCancel !== undef) { | |
attachEventHandler(curElement, "touchstart", function(t) { | |
if (p.touchStart !== undef) { | |
t = addTouchEventOffset(t); | |
p.touchStart(t) | |
} | |
}); | |
attachEventHandler(curElement, "touchmove", function(t) { | |
if (p.touchMove !== undef) { | |
t.preventDefault(); | |
t = addTouchEventOffset(t); | |
p.touchMove(t) | |
} | |
}); | |
attachEventHandler(curElement, "touchend", function(t) { | |
if (p.touchEnd !== undef) { | |
t = addTouchEventOffset(t); | |
p.touchEnd(t) | |
} | |
}); | |
attachEventHandler(curElement, "touchcancel", function(t) { | |
if (p.touchCancel !== undef) { | |
t = addTouchEventOffset(t); | |
p.touchCancel(t) | |
} | |
}) | |
} else { | |
attachEventHandler(curElement, "touchstart", function(e) { | |
updateMousePosition(curElement, e.touches[0]); | |
p.__mousePressed = true; | |
p.mouseDragging = false; | |
p.mouseButton = 37; | |
if (typeof p.mousePressed === "function") p.mousePressed() | |
}); | |
attachEventHandler(curElement, "touchmove", function(e) { | |
e.preventDefault(); | |
updateMousePosition(curElement, e.touches[0]); | |
if (typeof p.mouseMoved === "function" && !p.__mousePressed) p.mouseMoved(); | |
if (typeof p.mouseDragged === "function" && p.__mousePressed) { | |
p.mouseDragged(); | |
p.mouseDragging = true | |
} | |
}); | |
attachEventHandler(curElement, "touchend", function(e) { | |
p.__mousePressed = false; | |
if (typeof p.mouseClicked === "function" && !p.mouseDragging) p.mouseClicked(); | |
if (typeof p.mouseReleased === "function") p.mouseReleased() | |
}) | |
} | |
curElement.dispatchEvent(t) | |
}); | |
(function() { | |
var enabled = true, | |
contextMenu = function(e) { | |
e.preventDefault(); | |
e.stopPropagation() | |
}; | |
p.disableContextMenu = function() { | |
if (!enabled) return; | |
attachEventHandler(curElement, "contextmenu", contextMenu); | |
enabled = false | |
}; | |
p.enableContextMenu = function() { | |
if (enabled) return; | |
detachEventHandler({ | |
elem: curElement, | |
type: "contextmenu", | |
fn: contextMenu | |
}); | |
enabled = true | |
} | |
})(); | |
attachEventHandler(curElement, "mousemove", function(e) { | |
updateMousePosition(curElement, e); | |
if (typeof p.mouseMoved === "function" && !p.__mousePressed) p.mouseMoved(); | |
if (typeof p.mouseDragged === "function" && p.__mousePressed) { | |
p.mouseDragged(); | |
p.mouseDragging = true | |
} | |
}); | |
attachEventHandler(curElement, "mouseout", function(e) { | |
if (typeof p.mouseOut === "function") p.mouseOut() | |
}); | |
attachEventHandler(curElement, "mouseover", function(e) { | |
updateMousePosition(curElement, e); | |
if (typeof p.mouseOver === "function") p.mouseOver() | |
}); | |
curElement.onmousedown = function() { | |
curElement.focus(); | |
return false | |
}; | |
attachEventHandler(curElement, "mousedown", function(e) { | |
p.__mousePressed = true; | |
p.mouseDragging = false; | |
switch (e.which) { | |
case 1: | |
p.mouseButton = 37; | |
break; | |
case 2: | |
p.mouseButton = 3; | |
break; | |
case 3: | |
p.mouseButton = 39; | |
break | |
} | |
if (typeof p.mousePressed === "function") p.mousePressed() | |
}); | |
attachEventHandler(curElement, "mouseup", function(e) { | |
p.__mousePressed = false; | |
if (typeof p.mouseClicked === "function" && !p.mouseDragging) p.mouseClicked(); | |
if (typeof p.mouseReleased === "function") p.mouseReleased() | |
}); | |
var mouseWheelHandler = function(e) { | |
var delta = 0; | |
if (e.wheelDelta) { | |
delta = e.wheelDelta / 120; | |
if (window.opera) delta = -delta | |
} else if (e.detail) delta = -e.detail / 3; | |
p.mouseScroll = delta; | |
if (delta && typeof p.mouseScrolled === "function") p.mouseScrolled() | |
}; | |
attachEventHandler(document, "DOMMouseScroll", mouseWheelHandler); | |
attachEventHandler(document, "mousewheel", mouseWheelHandler); | |
if (!curElement.getAttribute("tabindex")) curElement.setAttribute("tabindex", 0); | |
function getKeyCode(e) { | |
var code = e.which || e.keyCode; | |
switch (code) { | |
case 13: | |
return 10; | |
case 91: | |
case 93: | |
case 224: | |
return 157; | |
case 57392: | |
return 17; | |
case 46: | |
return 127; | |
case 45: | |
return 155 | |
} | |
return code | |
} | |
function getKeyChar(e) { | |
var c = e.which || e.keyCode; | |
var anyShiftPressed = e.shiftKey || e.ctrlKey || e.altKey || e.metaKey; | |
switch (c) { | |
case 13: | |
c = anyShiftPressed ? 13 : 10; | |
break; | |
case 8: | |
c = anyShiftPressed ? 127 : 8; | |
break | |
} | |
return new Char(c) | |
} | |
function suppressKeyEvent(e) { | |
if (typeof e.preventDefault === "function") e.preventDefault(); | |
else if (typeof e.stopPropagation === "function") e.stopPropagation(); | |
return false | |
} | |
function updateKeyPressed() { | |
var ch; | |
for (ch in pressedKeysMap) if (pressedKeysMap.hasOwnProperty(ch)) { | |
p.__keyPressed = true; | |
return | |
} | |
p.__keyPressed = false | |
} | |
function resetKeyPressed() { | |
p.__keyPressed = false; | |
pressedKeysMap = []; | |
lastPressedKeyCode = null | |
} | |
function simulateKeyTyped(code, c) { | |
pressedKeysMap[code] = c; | |
lastPressedKeyCode = null; | |
p.key = c; | |
p.keyCode = code; | |
p.keyPressed(); | |
p.keyCode = 0; | |
p.keyTyped(); | |
updateKeyPressed() | |
} | |
function handleKeydown(e) { | |
var code = getKeyCode(e); | |
if (code === 127) { | |
simulateKeyTyped(code, new Char(127)); | |
return | |
} | |
if (codedKeys.indexOf(code) < 0) { | |
lastPressedKeyCode = code; | |
return | |
} | |
var c = new Char(65535); | |
p.key = c; | |
p.keyCode = code; | |
pressedKeysMap[code] = c; | |
p.keyPressed(); | |
lastPressedKeyCode = null; | |
updateKeyPressed(); | |
return suppressKeyEvent(e) | |
} | |
function handleKeypress(e) { | |
if (lastPressedKeyCode === null) return; | |
var code = lastPressedKeyCode, | |
c = getKeyChar(e); | |
simulateKeyTyped(code, c); | |
return suppressKeyEvent(e) | |
} | |
function handleKeyup(e) { | |
var code = getKeyCode(e), | |
c = pressedKeysMap[code]; | |
if (c === undef) return; | |
p.key = c; | |
p.keyCode = code; | |
p.keyReleased(); | |
delete pressedKeysMap[code]; | |
updateKeyPressed() | |
} | |
if (!pgraphicsMode) { | |
if (aCode instanceof Processing.Sketch) curSketch = aCode; | |
else if (typeof aCode === "function") curSketch = new Processing.Sketch(aCode); | |
else if (!aCode) curSketch = new Processing.Sketch(function() {}); | |
else curSketch = Processing.compile(aCode); | |
p.externals.sketch = curSketch; | |
wireDimensionalFunctions(); | |
curElement.onfocus = function() { | |
p.focused = true | |
}; | |
curElement.onblur = function() { | |
p.focused = false; | |
if (!curSketch.options.globalKeyEvents) resetKeyPressed() | |
}; | |
if (curSketch.options.pauseOnBlur) { | |
attachEventHandler(window, "focus", function() { | |
if (doLoop) p.loop() | |
}); | |
attachEventHandler(window, "blur", function() { | |
if (doLoop && loopStarted) { | |
p.noLoop(); | |
doLoop = true | |
} | |
resetKeyPressed() | |
}) | |
} | |
var keyTrigger = curSketch.options.globalKeyEvents ? window : curElement; | |
attachEventHandler(keyTrigger, "keydown", handleKeydown); | |
attachEventHandler(keyTrigger, "keypress", handleKeypress); | |
attachEventHandler(keyTrigger, "keyup", handleKeyup); | |
for (var i in Processing.lib) if (Processing.lib.hasOwnProperty(i)) if (Processing.lib[i].hasOwnProperty("attach")) Processing.lib[i].attach(p); | |
else if (Processing.lib[i] instanceof Function) Processing.lib[i].call(this); | |
var retryInterval = 100; | |
var executeSketch = function(processing) { | |
if (! (curSketch.imageCache.pending || PFont.preloading.pending(retryInterval))) { | |
if (window.opera) { | |
var link, element, operaCache = curSketch.imageCache.operaCache; | |
for (link in operaCache) if (operaCache.hasOwnProperty(link)) { | |
element = operaCache[link]; | |
if (element !== null) document.body.removeChild(element); | |
delete operaCache[link] | |
} | |
} | |
curSketch.attach(processing, defaultScope); | |
curSketch.onLoad(processing); | |
if (processing.setup) { | |
processing.setup(); | |
processing.resetMatrix(); | |
curSketch.onSetup() | |
} | |
resetContext(); | |
if (processing.draw) if (!doLoop) processing.redraw(); | |
else processing.loop() | |
} else window.setTimeout(function() { | |
executeSketch(processing) | |
}, | |
retryInterval) | |
}; | |
addInstance(this); | |
executeSketch(p) | |
} else { | |
curSketch = new Processing.Sketch; | |
wireDimensionalFunctions(); | |
p.size = function(w, h, render) { | |
if (render && render === 2) wireDimensionalFunctions("3D"); | |
else wireDimensionalFunctions("2D"); | |
p.size(w, h, render) | |
} | |
} | |
}; | |
Processing.debug = debug; | |
Processing.prototype = defaultScope; | |
function getGlobalMembers() { | |
var names = ["abs", "acos", "alpha", "ambient", "ambientLight", "append", | |
"applyMatrix", "arc", "arrayCopy", "asin", "atan", "atan2", "background", "beginCamera", "beginDraw", "beginShape", "bezier", "bezierDetail", "bezierPoint", "bezierTangent", "bezierVertex", "binary", "blend", "blendColor", "blit_resize", "blue", "box", "breakShape", "brightness", "camera", "ceil", "Character", "color", "colorMode", "concat", "constrain", "copy", "cos", "createFont", "createGraphics", "createImage", "cursor", "curve", "curveDetail", "curvePoint", "curveTangent", "curveTightness", "curveVertex", "day", "degrees", "directionalLight", | |
"disableContextMenu", "dist", "draw", "ellipse", "ellipseMode", "emissive", "enableContextMenu", "endCamera", "endDraw", "endShape", "exit", "exp", "expand", "externals", "fill", "filter", "floor", "focused", "frameCount", "frameRate", "frustum", "get", "glyphLook", "glyphTable", "green", "height", "hex", "hint", "hour", "hue", "image", "imageMode", "intersect", "join", "key", "keyCode", "keyPressed", "keyReleased", "keyTyped", "lerp", "lerpColor", "lightFalloff", "lights", "lightSpecular", "line", "link", "loadBytes", "loadFont", "loadGlyphs", | |
"loadImage", "loadPixels", "loadShape", "loadXML", "loadStrings", "log", "loop", "mag", "map", "match", "matchAll", "max", "millis", "min", "minute", "mix", "modelX", "modelY", "modelZ", "modes", "month", "mouseButton", "mouseClicked", "mouseDragged", "mouseMoved", "mouseOut", "mouseOver", "mousePressed", "mouseReleased", "mouseScroll", "mouseScrolled", "mouseX", "mouseY", "name", "nf", "nfc", "nfp", "nfs", "noCursor", "noFill", "noise", "noiseDetail", "noiseSeed", "noLights", "noLoop", "norm", "normal", "noSmooth", "noStroke", "noTint", "ortho", | |
"param", "parseBoolean", "parseByte", "parseChar", "parseFloat", "parseInt", "peg", "perspective", "PImage", "pixels", "PMatrix2D", "PMatrix3D", "PMatrixStack", "pmouseX", "pmouseY", "point", "pointLight", "popMatrix", "popStyle", "pow", "print", "printCamera", "println", "printMatrix", "printProjection", "PShape", "PShapeSVG", "pushMatrix", "pushStyle", "quad", "radians", "random", "Random", "randomSeed", "rect", "rectMode", "red", "redraw", "requestImage", "resetMatrix", "reverse", "rotate", "rotateX", "rotateY", "rotateZ", "round", "saturation", | |
"save", "saveFrame", "saveStrings", "scale", "screenX", "screenY", "screenZ", "second", "set", "setup", "shape", "shapeMode", "shared", "shearX", "shearY", "shininess", "shorten", "sin", "size", "smooth", "sort", "specular", "sphere", "sphereDetail", "splice", "split", "splitTokens", "spotLight", "sq", "sqrt", "status", "str", "stroke", "strokeCap", "strokeJoin", "strokeWeight", "subset", "tan", "text", "textAlign", "textAscent", "textDescent", "textFont", "textLeading", "textMode", "textSize", "texture", "textureMode", "textWidth", "tint", "toImageData", | |
"touchCancel", "touchEnd", "touchMove", "touchStart", "translate", "transform", "triangle", "trim", "unbinary", "unhex", "updatePixels", "use3DContext", "vertex", "width", "XMLElement", "XML", "year", "__contains", "__equals", "__equalsIgnoreCase", "__frameRate", "__hashCode", "__int_cast", "__instanceof", "__keyPressed", "__mousePressed", "__printStackTrace", "__replace", "__replaceAll", "__replaceFirst", "__toCharArray", "__split", "__codePointAt", "__startsWith", "__endsWith", "__matches"]; | |
var members = {}; | |
var i, l; | |
for (i = 0, l = names.length; i < l; ++i) members[names[i]] = null; | |
for (var lib in Processing.lib) if (Processing.lib.hasOwnProperty(lib)) if (Processing.lib[lib].exports) { | |
var exportedNames = Processing.lib[lib].exports; | |
for (i = 0, l = exportedNames.length; i < l; ++i) members[exportedNames[i]] = null | |
} | |
return members | |
} | |
function parseProcessing(code) { | |
var globalMembers = getGlobalMembers(); | |
function splitToAtoms(code) { | |
var atoms = []; | |
var items = code.split(/([\{\[\(\)\]\}])/); | |
var result = items[0]; | |
var stack = []; | |
for (var i = 1; i < items.length; i += 2) { | |
var item = items[i]; | |
if (item === "[" || item === "{" || item === "(") { | |
stack.push(result); | |
result = item | |
} else if (item === "]" || item === "}" || item === ")") { | |
var kind = item === "}" ? "A" : item === ")" ? "B" : "C"; | |
var index = atoms.length; | |
atoms.push(result + item); | |
result = stack.pop() + '"' + kind + (index + 1) + '"' | |
} | |
result += items[i + 1] | |
} | |
atoms.unshift(result); | |
return atoms | |
} | |
function injectStrings(code, strings) { | |
return code.replace(/'(\d+)'/g, function(all, index) { | |
var val = strings[index]; | |
if (val.charAt(0) === "/") return val; | |
return /^'((?:[^'\\\n])|(?:\\.[0-9A-Fa-f]*))'$/.test(val) ? "(new $p.Character(" + val + "))" : val | |
}) | |
} | |
function trimSpaces(string) { | |
var m1 = /^\s*/.exec(string), | |
result; | |
if (m1[0].length === string.length) result = { | |
left: m1[0], | |
middle: "", | |
right: "" | |
}; | |
else { | |
var m2 = /\s*$/.exec(string); | |
result = { | |
left: m1[0], | |
middle: string.substring(m1[0].length, m2.index), | |
right: m2[0] | |
} | |
} | |
result.untrim = function(t) { | |
return this.left + t + this.right | |
}; | |
return result | |
} | |
function trim(string) { | |
return string.replace(/^\s+/, "").replace(/\s+$/, "") | |
} | |
function appendToLookupTable(table, array) { | |
for (var i = 0, l = array.length; i < l; ++i) table[array[i]] = null; | |
return table | |
} | |
function isLookupTableEmpty(table) { | |
for (var i in table) if (table.hasOwnProperty(i)) return false; | |
return true | |
} | |
function getAtomIndex(templ) { | |
return templ.substring(2, templ.length - 1) | |
} | |
var codeWoExtraCr = code.replace(/\r\n?|\n\r/g, "\n"); | |
var strings = []; | |
var codeWoStrings = codeWoExtraCr.replace(/("(?:[^"\\\n]|\\.)*")|('(?:[^'\\\n]|\\.)*')|(([\[\(=|&!\^:?]\s*)(\/(?![*\/])(?:[^\/\\\n]|\\.)*\/[gim]*)\b)|(\/\/[^\n]*\n)|(\/\*(?:(?!\*\/)(?:.|\n))*\*\/)/g, function(all, quoted, aposed, regexCtx, prefix, regex, singleComment, comment) { | |
var index; | |
if (quoted || aposed) { | |
index = strings.length; | |
strings.push(all); | |
return "'" + index + "'" | |
} | |
if (regexCtx) { | |
index = strings.length; | |
strings.push(regex); | |
return prefix + "'" + index + "'" | |
} | |
return comment !== "" ? " " : "\n" | |
}); | |
codeWoStrings = codeWoStrings.replace(/__x([0-9A-F]{4})/g, function(all, hexCode) { | |
return "__x005F_x" + hexCode | |
}); | |
codeWoStrings = codeWoStrings.replace(/\$/g, "__x0024"); | |
var genericsWereRemoved; | |
var codeWoGenerics = codeWoStrings; | |
var replaceFunc = function(all, before, types, after) { | |
if ( !! before || !!after) return all; | |
genericsWereRemoved = true; | |
return "" | |
}; | |
do { | |
genericsWereRemoved = false; | |
codeWoGenerics = codeWoGenerics.replace(/([<]?)<\s*((?:\?|[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*)(?:\[\])*(?:\s+(?:extends|super)\s+[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*)?(?:\s*,\s*(?:\?|[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*)(?:\[\])*(?:\s+(?:extends|super)\s+[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*)?)*)\s*>([=]?)/g, replaceFunc) | |
} while (genericsWereRemoved); | |
var atoms = splitToAtoms(codeWoGenerics); | |
var replaceContext; | |
var declaredClasses = {}, | |
currentClassId, classIdSeed = 0; | |
function addAtom(text, type) { | |
var lastIndex = atoms.length; | |
atoms.push(text); | |
return '"' + type + lastIndex + '"' | |
} | |
function generateClassId() { | |
return "class" + ++classIdSeed | |
} | |
function appendClass(class_, classId, scopeId) { | |
class_.classId = classId; | |
class_.scopeId = scopeId; | |
declaredClasses[classId] = class_ | |
} | |
var transformClassBody, transformInterfaceBody, transformStatementsBlock, transformStatements, transformMain, transformExpression; | |
var classesRegex = /\b((?:(?:public|private|final|protected|static|abstract)\s+)*)(class|interface)\s+([A-Za-z_$][\w$]*\b)(\s+extends\s+[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*(?:\s*,\s*[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*\b)*)?(\s+implements\s+[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*(?:\s*,\s*[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*\b)*)?\s*("A\d+")/g; | |
var methodsRegex = /\b((?:(?:public|private|final|protected|static|abstract|synchronized)\s+)*)((?!(?:else|new|return|throw|function|public|private|protected)\b)[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*(?:\s*"C\d+")*)\s*([A-Za-z_$][\w$]*\b)\s*("B\d+")(\s*throws\s+[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*(?:\s*,\s*[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*)*)?\s*("A\d+"|;)/g; | |
var fieldTest = /^((?:(?:public|private|final|protected|static)\s+)*)((?!(?:else|new|return|throw)\b)[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*(?:\s*"C\d+")*)\s*([A-Za-z_$][\w$]*\b)\s*(?:"C\d+"\s*)*([=,]|$)/; | |
var cstrsRegex = /\b((?:(?:public|private|final|protected|static|abstract)\s+)*)((?!(?:new|return|throw)\b)[A-Za-z_$][\w$]*\b)\s*("B\d+")(\s*throws\s+[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*(?:\s*,\s*[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*)*)?\s*("A\d+")/g; | |
var attrAndTypeRegex = /^((?:(?:public|private|final|protected|static)\s+)*)((?!(?:new|return|throw)\b)[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*(?:\s*"C\d+")*)\s*/; | |
var functionsRegex = /\bfunction(?:\s+([A-Za-z_$][\w$]*))?\s*("B\d+")\s*("A\d+")/g; | |
function extractClassesAndMethods(code) { | |
var s = code; | |
s = s.replace(classesRegex, function(all) { | |
return addAtom(all, "E") | |
}); | |
s = s.replace(methodsRegex, function(all) { | |
return addAtom(all, "D") | |
}); | |
s = s.replace(functionsRegex, function(all) { | |
return addAtom(all, "H") | |
}); | |
return s | |
} | |
function extractConstructors(code, className) { | |
var result = code.replace(cstrsRegex, function(all, attr, name, params, throws_, body) { | |
if (name !== className) return all; | |
return addAtom(all, "G") | |
}); | |
return result | |
} | |
function AstParam(name) { | |
this.name = name | |
} | |
AstParam.prototype.toString = function() { | |
return this.name | |
}; | |
function AstParams(params, methodArgsParam) { | |
this.params = params; | |
this.methodArgsParam = methodArgsParam | |
} | |
AstParams.prototype.getNames = function() { | |
var names = []; | |
for (var i = 0, l = this.params.length; i < l; ++i) names.push(this.params[i].name); | |
return names | |
}; | |
AstParams.prototype.prependMethodArgs = function(body) { | |
if (!this.methodArgsParam) return body; | |
return "{\nvar " + this.methodArgsParam.name + " = Array.prototype.slice.call(arguments, " + this.params.length + ");\n" + body.substring(1) | |
}; | |
AstParams.prototype.toString = function() { | |
if (this.params.length === 0) return "()"; | |
var result = "("; | |
for (var i = 0, l = this.params.length; i < l; ++i) result += this.params[i] + ", "; | |
return result.substring(0, result.length - 2) + ")" | |
}; | |
function transformParams(params) { | |
var paramsWoPars = trim(params.substring(1, params.length - 1)); | |
var result = [], | |
methodArgsParam = null; | |
if (paramsWoPars !== "") { | |
var paramList = paramsWoPars.split(","); | |
for (var i = 0; i < paramList.length; ++i) { | |
var param = /\b([A-Za-z_$][\w$]*\b)(\s*"[ABC][\d]*")*\s*$/.exec(paramList[i]); | |
if (i === paramList.length - 1 && paramList[i].indexOf("...") >= 0) { | |
methodArgsParam = new AstParam(param[1]); | |
break | |
} | |
result.push(new AstParam(param[1])) | |
} | |
} | |
return new AstParams(result, methodArgsParam) | |
} | |
function preExpressionTransform(expr) { | |
var s = expr; | |
s = s.replace(/\bnew\s+([A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*)(?:\s*"C\d+")+\s*("A\d+")/g, function(all, type, init) { | |
return init | |
}); | |
s = s.replace(/\bnew\s+([A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*)(?:\s*"B\d+")\s*("A\d+")/g, function(all, type, init) { | |
return addAtom(all, "F") | |
}); | |
s = s.replace(functionsRegex, function(all) { | |
return addAtom(all, "H") | |
}); | |
s = s.replace(/\bnew\s+([A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*)\s*("C\d+"(?:\s*"C\d+")*)/g, function(all, type, index) { | |
var args = index.replace(/"C(\d+)"/g, function(all, j) { | |
return atoms[j] | |
}).replace(/\[\s*\]/g, "[null]").replace(/\s*\]\s*\[\s*/g, ", "); | |
var arrayInitializer = "{" + args.substring(1, args.length - 1) + "}"; | |
var createArrayArgs = "('" + type + "', " + addAtom(arrayInitializer, "A") + ")"; | |
return "$p.createJavaArray" + addAtom(createArrayArgs, "B") | |
}); | |
s = s.replace(/(\.\s*length)\s*"B\d+"/g, "$1"); | |
s = s.replace(/#([0-9A-Fa-f]{6})\b/g, function(all, digits) { | |
return "0xFF" + digits | |
}); | |
s = s.replace(/"B(\d+)"(\s*(?:[\w$']|"B))/g, function(all, index, next) { | |
var atom = atoms[index]; | |
if (!/^\(\s*[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*\s*(?:"C\d+"\s*)*\)$/.test(atom)) return all; | |
if (/^\(\s*int\s*\)$/.test(atom)) return "(int)" + next; | |
var indexParts = atom.split(/"C(\d+)"/g); | |
if (indexParts.length > 1) if (!/^\[\s*\]$/.test(atoms[indexParts[1]])) return all; | |
return "" + next | |
}); | |
s = s.replace(/\(int\)([^,\]\)\}\?\:\*\+\-\/\^\|\%\&\~<\>\=]+)/g, function(all, arg) { | |
var trimmed = trimSpaces(arg); | |
return trimmed.untrim("__int_cast(" + trimmed.middle + ")") | |
}); | |
s = s.replace(/\bsuper(\s*"B\d+")/g, "$$superCstr$1").replace(/\bsuper(\s*\.)/g, "$$super$1"); | |
s = s.replace(/\b0+((\d*)(?:\.[\d*])?(?:[eE][\-\+]?\d+)?[fF]?)\b/, function(all, numberWo0, intPart) { | |
if (numberWo0 === intPart) return all; | |
return intPart === "" ? "0" + numberWo0 : numberWo0 | |
}); | |
s = s.replace(/\b(\.?\d+\.?)[fF]\b/g, "$1"); | |
s = s.replace(/([^\s])%([^=\s])/g, "$1 % $2"); | |
s = s.replace(/\b(frameRate|keyPressed|mousePressed)\b(?!\s*"B)/g, "__$1"); | |
s = s.replace(/\b(boolean|byte|char|float|int)\s*"B/g, function(all, name) { | |
return "parse" + name.substring(0, 1).toUpperCase() + name.substring(1) + '"B' | |
}); | |
s = s.replace(/\bpixels\b\s*(("C(\d+)")|\.length)?(\s*=(?!=)([^,\]\)\}]+))?/g, function(all, indexOrLength, index, atomIndex, equalsPart, rightSide) { | |
if (index) { | |
var atom = atoms[atomIndex]; | |
if (equalsPart) return "pixels.setPixel" + addAtom("(" + atom.substring(1, atom.length - 1) + "," + rightSide + ")", "B"); | |
return "pixels.getPixel" + addAtom("(" + atom.substring(1, atom.length - 1) + ")", "B") | |
} | |
if (indexOrLength) return "pixels.getLength" + addAtom("()", "B"); | |
if (equalsPart) return "pixels.set" + addAtom("(" + rightSide + ")", "B"); | |
return "pixels.toArray" + addAtom("()", "B") | |
}); | |
var repeatJavaReplacement; | |
function replacePrototypeMethods(all, subject, method, atomIndex) { | |
var atom = atoms[atomIndex]; | |
repeatJavaReplacement = true; | |
var trimmed = trimSpaces(atom.substring(1, atom.length - 1)); | |
return "__" + method + (trimmed.middle === "" ? addAtom("(" + subject.replace(/\.\s*$/, "") + ")", "B") : addAtom("(" + subject.replace(/\.\s*$/, "") + "," + trimmed.middle + ")", "B")) | |
} | |
do { | |
repeatJavaReplacement = false; | |
s = s.replace(/((?:'\d+'|\b[A-Za-z_$][\w$]*\s*(?:"[BC]\d+")*)\s*\.\s*(?:[A-Za-z_$][\w$]*\s*(?:"[BC]\d+"\s*)*\.\s*)*)(replace|replaceAll|replaceFirst|contains|equals|equalsIgnoreCase|hashCode|toCharArray|printStackTrace|split|startsWith|endsWith|codePointAt|matches)\s*"B(\d+)"/g, replacePrototypeMethods) | |
} while (repeatJavaReplacement); | |
function replaceInstanceof(all, subject, type) { | |
repeatJavaReplacement = true; | |
return "__instanceof" + addAtom("(" + subject + ", " + type + ")", "B") | |
} | |
do { | |
repeatJavaReplacement = false; | |
s = s.replace(/((?:'\d+'|\b[A-Za-z_$][\w$]*\s*(?:"[BC]\d+")*)\s*(?:\.\s*[A-Za-z_$][\w$]*\s*(?:"[BC]\d+"\s*)*)*)instanceof\s+([A-Za-z_$][\w$]*\s*(?:\.\s*[A-Za-z_$][\w$]*)*)/g, replaceInstanceof) | |
} while (repeatJavaReplacement); | |
s = s.replace(/\bthis(\s*"B\d+")/g, "$$constr$1"); | |
return s | |
} | |
function AstInlineClass(baseInterfaceName, body) { | |
this.baseInterfaceName = baseInterfaceName; | |
this.body = body; | |
body.owner = this | |
} | |
AstInlineClass.prototype.toString = function() { | |
return "new (" + this.body + ")" | |
}; | |
function transformInlineClass(class_) { | |
var m = (new RegExp(/\bnew\s*([A-Za-z_$][\w$]*\s*(?:\.\s*[A-Za-z_$][\w$]*)*)\s*"B\d+"\s*"A(\d+)"/)).exec(class_); | |
var oldClassId = currentClassId, | |
newClassId = generateClassId(); | |
currentClassId = newClassId; | |
var uniqueClassName = m[1] + "$" + newClassId; | |
var inlineClass = new AstInlineClass(uniqueClassName, transformClassBody(atoms[m[2]], uniqueClassName, "", "implements " + m[1])); | |
appendClass(inlineClass, newClassId, oldClassId); | |
currentClassId = oldClassId; | |
return inlineClass | |
} | |
function AstFunction(name, params, body) { | |
this.name = name; | |
this.params = params; | |
this.body = body | |
} | |
AstFunction.prototype.toString = function() { | |
var oldContext = replaceContext; | |
var names = appendToLookupTable({ | |
"this": null | |
}, | |
this.params.getNames()); | |
replaceContext = function(subject) { | |
return names.hasOwnProperty(subject.name) ? subject.name : oldContext(subject) | |
}; | |
var result = "function"; | |
if (this.name) result += " " + this.name; | |
var body = this.params.prependMethodArgs(this.body.toString()); | |
result += this.params + " " + body; | |
replaceContext = oldContext; | |
return result | |
}; | |
function transformFunction(class_) { | |
var m = (new RegExp(/\b([A-Za-z_$][\w$]*)\s*"B(\d+)"\s*"A(\d+)"/)).exec(class_); | |
return new AstFunction(m[1] !== "function" ? m[1] : null, transformParams(atoms[m[2]]), transformStatementsBlock(atoms[m[3]])) | |
} | |
function AstInlineObject(members) { | |
this.members = members | |
} | |
AstInlineObject.prototype.toString = function() { | |
var oldContext = replaceContext; | |
replaceContext = function(subject) { | |
return subject.name === "this" ? "this" : oldContext(subject) | |
}; | |
var result = ""; | |
for (var i = 0, l = this.members.length; i < l; ++i) { | |
if (this.members[i].label) result += this.members[i].label + ": "; | |
result += this.members[i].value.toString() + ", " | |
} | |
replaceContext = oldContext; | |
return result.substring(0, result.length - 2) | |
}; | |
function transformInlineObject(obj) { | |
var members = obj.split(","); | |
for (var i = 0; i < members.length; ++i) { | |
var label = members[i].indexOf(":"); | |
if (label < 0) members[i] = { | |
value: transformExpression(members[i]) | |
}; | |
else members[i] = { | |
label: trim(members[i].substring(0, label)), | |
value: transformExpression(trim(members[i].substring(label + 1))) | |
} | |
} | |
return new AstInlineObject(members) | |
} | |
function expandExpression(expr) { | |
if (expr.charAt(0) === "(" || expr.charAt(0) === "[") return expr.charAt(0) + expandExpression(expr.substring(1, expr.length - 1)) + expr.charAt(expr.length - 1); | |
if (expr.charAt(0) === "{") { | |
if (/^\{\s*(?:[A-Za-z_$][\w$]*|'\d+')\s*:/.test(expr)) return "{" + addAtom(expr.substring(1, expr.length - 1), "I") + "}"; | |
return "[" + expandExpression(expr.substring(1, expr.length - 1)) + "]" | |
} | |
var trimmed = trimSpaces(expr); | |
var result = preExpressionTransform(trimmed.middle); | |
result = result.replace(/"[ABC](\d+)"/g, function(all, index) { | |
return expandExpression(atoms[index]) | |
}); | |
return trimmed.untrim(result) | |
} | |
function replaceContextInVars(expr) { | |
return expr.replace(/(\.\s*)?((?:\b[A-Za-z_]|\$)[\w$]*)(\s*\.\s*([A-Za-z_$][\w$]*)(\s*\()?)?/g, function(all, memberAccessSign, identifier, suffix, subMember, callSign) { | |
if (memberAccessSign) return all; | |
var subject = { | |
name: identifier, | |
member: subMember, | |
callSign: !!callSign | |
}; | |
return replaceContext(subject) + (suffix === undef ? "" : suffix) | |
}) | |
} | |
function AstExpression(expr, transforms) { | |
this.expr = expr; | |
this.transforms = transforms | |
} | |
AstExpression.prototype.toString = function() { | |
var transforms = this.transforms; | |
var expr = replaceContextInVars(this.expr); | |
return expr.replace(/"!(\d+)"/g, function(all, index) { | |
return transforms[index].toString() | |
}) | |
}; | |
transformExpression = function(expr) { | |
var transforms = []; | |
var s = expandExpression(expr); | |
s = s.replace(/"H(\d+)"/g, function(all, index) { | |
transforms.push(transformFunction(atoms[index])); | |
return '"!' + (transforms.length - 1) + '"' | |
}); | |
s = s.replace(/"F(\d+)"/g, function(all, index) { | |
transforms.push(transformInlineClass(atoms[index])); | |
return '"!' + (transforms.length - 1) + '"' | |
}); | |
s = s.replace(/"I(\d+)"/g, function(all, index) { | |
transforms.push(transformInlineObject(atoms[index])); | |
return '"!' + (transforms.length - 1) + '"' | |
}); | |
return new AstExpression(s, transforms) | |
}; | |
function AstVarDefinition(name, value, isDefault) { | |
this.name = name; | |
this.value = value; | |
this.isDefault = isDefault | |
} | |
AstVarDefinition.prototype.toString = function() { | |
return this.name + " = " + this.value | |
}; | |
function transformVarDefinition(def, defaultTypeValue) { | |
var eqIndex = def.indexOf("="); | |
var name, value, isDefault; | |
if (eqIndex < 0) { | |
name = def; | |
value = defaultTypeValue; | |
isDefault = true | |
} else { | |
name = def.substring(0, eqIndex); | |
value = transformExpression(def.substring(eqIndex + 1)); | |
isDefault = false | |
} | |
return new AstVarDefinition(trim(name.replace(/(\s*"C\d+")+/g, "")), value, isDefault) | |
} | |
function getDefaultValueForType(type) { | |
if (type === "int" || type === "float") return "0"; | |
if (type === "boolean") return "false"; | |
if (type === "color") return "0x00000000"; | |
return "null" | |
} | |
function AstVar(definitions, varType) { | |
this.definitions = definitions; | |
this.varType = varType | |
} | |
AstVar.prototype.getNames = function() { | |
var names = []; | |
for (var i = 0, l = this.definitions.length; i < l; ++i) names.push(this.definitions[i].name); | |
return names | |
}; | |
AstVar.prototype.toString = function() { | |
return "var " + this.definitions.join(",") | |
}; | |
function AstStatement(expression) { | |
this.expression = expression | |
} | |
AstStatement.prototype.toString = function() { | |
return this.expression.toString() | |
}; | |
function transformStatement(statement) { | |
if (fieldTest.test(statement)) { | |
var attrAndType = attrAndTypeRegex.exec(statement); | |
var definitions = statement.substring(attrAndType[0].length).split(","); | |
var defaultTypeValue = getDefaultValueForType(attrAndType[2]); | |
for (var i = 0; i < definitions.length; ++i) definitions[i] = transformVarDefinition(definitions[i], defaultTypeValue); | |
return new AstVar(definitions, attrAndType[2]) | |
} | |
return new AstStatement(transformExpression(statement)) | |
} | |
function AstForExpression(initStatement, condition, step) { | |
this.initStatement = initStatement; | |
this.condition = condition; | |
this.step = step | |
} | |
AstForExpression.prototype.toString = function() { | |
return "(" + this.initStatement + "; " + this.condition + "; " + this.step + ")" | |
}; | |
function AstForInExpression(initStatement, container) { | |
this.initStatement = initStatement; | |
this.container = container | |
} | |
AstForInExpression.prototype.toString = function() { | |
var init = this.initStatement.toString(); | |
if (init.indexOf("=") >= 0) init = init.substring(0, init.indexOf("=")); | |
return "(" + init + " in " + this.container + ")" | |
}; | |
function AstForEachExpression(initStatement, container) { | |
this.initStatement = initStatement; | |
this.container = container | |
} | |
AstForEachExpression.iteratorId = 0; | |
AstForEachExpression.prototype.toString = function() { | |
var init = this.initStatement.toString(); | |
var iterator = "$it" + AstForEachExpression.iteratorId++; | |
var variableName = init.replace(/^\s*var\s*/, "").split("=")[0]; | |
var initIteratorAndVariable = "var " + iterator + " = new $p.ObjectIterator(" + this.container + "), " + variableName + " = void(0)"; | |
var nextIterationCondition = iterator + ".hasNext() && ((" + variableName + " = " + iterator + ".next()) || true)"; | |
return "(" + initIteratorAndVariable + "; " + nextIterationCondition + ";)" | |
}; | |
function transformForExpression(expr) { | |
var content; | |
if (/\bin\b/.test(expr)) { | |
content = expr.substring(1, expr.length - 1).split(/\bin\b/g); | |
return new AstForInExpression(transformStatement(trim(content[0])), transformExpression(content[1])) | |
} | |
if (expr.indexOf(":") >= 0 && expr.indexOf(";") < 0) { | |
content = expr.substring(1, expr.length - 1).split(":"); | |
return new AstForEachExpression(transformStatement(trim(content[0])), transformExpression(content[1])) | |
} | |
content = expr.substring(1, expr.length - 1).split(";"); | |
return new AstForExpression(transformStatement(trim(content[0])), transformExpression(content[1]), transformExpression(content[2])) | |
} | |
function sortByWeight(array) { | |
array.sort(function(a, b) { | |
return b.weight - a.weight | |
}) | |
} | |
function AstInnerInterface(name, body, isStatic) { | |
this.name = name; | |
this.body = body; | |
this.isStatic = isStatic; | |
body.owner = this | |
} | |
AstInnerInterface.prototype.toString = function() { | |
return "" + this.body | |
}; | |
function AstInnerClass(name, body, isStatic) { | |
this.name = name; | |
this.body = body; | |
this.isStatic = isStatic; | |
body.owner = this | |
} | |
AstInnerClass.prototype.toString = function() { | |
return "" + this.body | |
}; | |
function transformInnerClass(class_) { | |
var m = classesRegex.exec(class_); | |
classesRegex.lastIndex = 0; | |
var isStatic = m[1].indexOf("static") >= 0; | |
var body = atoms[getAtomIndex(m[6])], | |
innerClass; | |
var oldClassId = currentClassId, | |
newClassId = generateClassId(); | |
currentClassId = newClassId; | |
if (m[2] === "interface") innerClass = new AstInnerInterface(m[3], transformInterfaceBody(body, m[3], m[4]), isStatic); | |
else innerClass = new AstInnerClass(m[3], transformClassBody(body, m[3], m[4], m[5]), isStatic); | |
appendClass(innerClass, newClassId, oldClassId); | |
currentClassId = oldClassId; | |
return innerClass | |
} | |
function AstClassMethod(name, params, body, isStatic) { | |
this.name = name; | |
this.params = params; | |
this.body = body; | |
this.isStatic = isStatic | |
} | |
AstClassMethod.prototype.toString = function() { | |
var paramNames = appendToLookupTable({}, | |
this.params.getNames()); | |
var oldContext = replaceContext; | |
replaceContext = function(subject) { | |
return paramNames.hasOwnProperty(subject.name) ? subject.name : oldContext(subject) | |
}; | |
var body = this.params.prependMethodArgs(this.body.toString()); | |
var result = "function " + this.methodId + this.params + " " + body + "\n"; | |
replaceContext = oldContext; | |
return result | |
}; | |
function transformClassMethod(method) { | |
var m = methodsRegex.exec(method); | |
methodsRegex.lastIndex = 0; | |
var isStatic = m[1].indexOf("static") >= 0; | |
var body = m[6] !== ";" ? atoms[getAtomIndex(m[6])] : "{}"; | |
return new AstClassMethod(m[3], transformParams(atoms[getAtomIndex(m[4])]), transformStatementsBlock(body), isStatic) | |
} | |
function AstClassField(definitions, fieldType, isStatic) { | |
this.definitions = definitions; | |
this.fieldType = fieldType; | |
this.isStatic = isStatic | |
} | |
AstClassField.prototype.getNames = function() { | |
var names = []; | |
for (var i = 0, l = this.definitions.length; i < l; ++i) names.push(this.definitions[i].name); | |
return names | |
}; | |
AstClassField.prototype.toString = function() { | |
var thisPrefix = replaceContext({ | |
name: "[this]" | |
}); | |
if (this.isStatic) { | |
var className = this.owner.name; | |
var staticDeclarations = []; | |
for (var i = 0, l = this.definitions.length; i < l; ++i) { | |
var definition = this.definitions[i]; | |
var name = definition.name, | |
staticName = className + "." + name; | |
var declaration = "if(" + staticName + " === void(0)) {\n" + " " + staticName + " = " + definition.value + "; }\n" + "$p.defineProperty(" + thisPrefix + ", " + "'" + name + "', { get: function(){return " + staticName + ";}, " + "set: function(val){" + staticName + " = val;} });\n"; | |
staticDeclarations.push(declaration) | |
} | |
return staticDeclarations.join("") | |
} | |
return thisPrefix + "." + this.definitions.join("; " + thisPrefix + ".") | |
}; | |
function transformClassField(statement) { | |
var attrAndType = attrAndTypeRegex.exec(statement); | |
var isStatic = attrAndType[1].indexOf("static") >= 0; | |
var definitions = statement.substring(attrAndType[0].length).split(/,\s*/g); | |
var defaultTypeValue = getDefaultValueForType(attrAndType[2]); | |
for (var i = 0; i < definitions.length; ++i) definitions[i] = transformVarDefinition(definitions[i], defaultTypeValue); | |
return new AstClassField(definitions, attrAndType[2], isStatic) | |
} | |
function AstConstructor(params, body) { | |
this.params = params; | |
this.body = body | |
} | |
AstConstructor.prototype.toString = function() { | |
var paramNames = appendToLookupTable({}, | |
this.params.getNames()); | |
var oldContext = replaceContext; | |
replaceContext = function(subject) { | |
return paramNames.hasOwnProperty(subject.name) ? subject.name : oldContext(subject) | |
}; | |
var prefix = "function $constr_" + this.params.params.length + this.params.toString(); | |
var body = this.params.prependMethodArgs(this.body.toString()); | |
if (!/\$(superCstr|constr)\b/.test(body)) body = "{\n$superCstr();\n" + body.substring(1); | |
replaceContext = oldContext; | |
return prefix + body + "\n" | |
}; | |
function transformConstructor(cstr) { | |
var m = (new RegExp(/"B(\d+)"\s*"A(\d+)"/)).exec(cstr); | |
var params = transformParams(atoms[m[1]]); | |
return new AstConstructor(params, transformStatementsBlock(atoms[m[2]])) | |
} | |
function AstInterfaceBody(name, interfacesNames, methodsNames, fields, innerClasses, misc) { | |
var i, l; | |
this.name = name; | |
this.interfacesNames = interfacesNames; | |
this.methodsNames = methodsNames; | |
this.fields = fields; | |
this.innerClasses = innerClasses; | |
this.misc = misc; | |
for (i = 0, l = fields.length; i < l; ++i) fields[i].owner = this | |
} | |
AstInterfaceBody.prototype.getMembers = function(classFields, classMethods, classInners) { | |
if (this.owner.base) this.owner.base.body.getMembers(classFields, classMethods, classInners); | |
var i, j, l, m; | |
for (i = 0, l = this.fields.length; i < l; ++i) { | |
var fieldNames = this.fields[i].getNames(); | |
for (j = 0, m = fieldNames.length; j < m; ++j) classFields[fieldNames[j]] = this.fields[i] | |
} | |
for (i = 0, l = this.methodsNames.length; i < l; ++i) { | |
var methodName = this.methodsNames[i]; | |
classMethods[methodName] = true | |
} | |
for (i = 0, l = this.innerClasses.length; i < l; ++i) { | |
var innerClass = this.innerClasses[i]; | |
classInners[innerClass.name] = innerClass | |
} | |
}; | |
AstInterfaceBody.prototype.toString = function() { | |
function getScopeLevel(p) { | |
var i = 0; | |
while (p) { | |
++i; | |
p = p.scope | |
} | |
return i | |
} | |
var scopeLevel = getScopeLevel(this.owner); | |
var className = this.name; | |
var staticDefinitions = ""; | |
var metadata = ""; | |
var thisClassFields = {}, | |
thisClassMethods = {}, | |
thisClassInners = {}; | |
this.getMembers(thisClassFields, thisClassMethods, thisClassInners); | |
var i, l, j, m; | |
if (this.owner.interfaces) { | |
var resolvedInterfaces = [], | |
resolvedInterface; | |
for (i = 0, l = this.interfacesNames.length; i < l; ++i) { | |
if (!this.owner.interfaces[i]) continue; | |
resolvedInterface = replaceContext({ | |
name: this.interfacesNames[i] | |
}); | |
resolvedInterfaces.push(resolvedInterface); | |
staticDefinitions += "$p.extendInterfaceMembers(" + className + ", " + resolvedInterface + ");\n" | |
} | |
metadata += className + ".$interfaces = [" + resolvedInterfaces.join(", ") + "];\n" | |
} | |
metadata += className + ".$isInterface = true;\n"; | |
metadata += className + ".$methods = ['" + this.methodsNames.join("', '") + "'];\n"; | |
sortByWeight(this.innerClasses); | |
for (i = 0, l = this.innerClasses.length; i < l; ++i) { | |
var innerClass = this.innerClasses[i]; | |
if (innerClass.isStatic) staticDefinitions += className + "." + innerClass.name + " = " + innerClass + ";\n" | |
} | |
for (i = 0, l = this.fields.length; i < l; ++i) { | |
var field = this.fields[i]; | |
if (field.isStatic) staticDefinitions += className + "." + field.definitions.join(";\n" + className + ".") + ";\n" | |
} | |
return "(function() {\n" + "function " + className + "() { throw 'Unable to create the interface'; }\n" + staticDefinitions + metadata + "return " + className + ";\n" + "})()" | |
}; | |
transformInterfaceBody = function(body, name, baseInterfaces) { | |
var declarations = body.substring(1, body.length - 1); | |
declarations = extractClassesAndMethods(declarations); | |
declarations = extractConstructors(declarations, name); | |
var methodsNames = [], | |
classes = []; | |
declarations = declarations.replace(/"([DE])(\d+)"/g, function(all, type, index) { | |
if (type === "D") methodsNames.push(index); | |
else if (type === "E") classes.push(index); | |
return "" | |
}); | |
var fields = declarations.split(/;(?:\s*;)*/g); | |
var baseInterfaceNames; | |
var i, l; | |
if (baseInterfaces !== undef) baseInterfaceNames = baseInterfaces.replace(/^\s*extends\s+(.+?)\s*$/g, "$1").split(/\s*,\s*/g); | |
for (i = 0, l = methodsNames.length; i < l; ++i) { | |
var method = transformClassMethod(atoms[methodsNames[i]]); | |
methodsNames[i] = method.name | |
} | |
for (i = 0, l = fields.length - 1; i < l; ++i) { | |
var field = trimSpaces(fields[i]); | |
fields[i] = transformClassField(field.middle) | |
} | |
var tail = fields.pop(); | |
for (i = 0, l = classes.length; i < l; ++i) classes[i] = transformInnerClass(atoms[classes[i]]); | |
return new AstInterfaceBody(name, baseInterfaceNames, methodsNames, fields, classes, { | |
tail: tail | |
}) | |
}; | |
function AstClassBody(name, baseClassName, interfacesNames, functions, methods, fields, cstrs, innerClasses, misc) { | |
var i, l; | |
this.name = name; | |
this.baseClassName = baseClassName; | |
this.interfacesNames = interfacesNames; | |
this.functions = functions; | |
this.methods = methods; | |
this.fields = fields; | |
this.cstrs = cstrs; | |
this.innerClasses = innerClasses; | |
this.misc = misc; | |
for (i = 0, l = fields.length; i < l; ++i) fields[i].owner = this | |
} | |
AstClassBody.prototype.getMembers = function(classFields, classMethods, classInners) { | |
if (this.owner.base) this.owner.base.body.getMembers(classFields, classMethods, classInners); | |
var i, j, l, m; | |
for (i = 0, l = this.fields.length; i < l; ++i) { | |
var fieldNames = this.fields[i].getNames(); | |
for (j = 0, m = fieldNames.length; j < m; ++j) classFields[fieldNames[j]] = this.fields[i] | |
} | |
for (i = 0, l = this.methods.length; i < l; ++i) { | |
var method = this.methods[i]; | |
classMethods[method.name] = method | |
} | |
for (i = 0, l = this.innerClasses.length; i < l; ++i) { | |
var innerClass = this.innerClasses[i]; | |
classInners[innerClass.name] = innerClass | |
} | |
}; | |
AstClassBody.prototype.toString = function() { | |
function getScopeLevel(p) { | |
var i = 0; | |
while (p) { | |
++i; | |
p = p.scope | |
} | |
return i | |
} | |
var scopeLevel = getScopeLevel(this.owner); | |
var selfId = "$this_" + scopeLevel; | |
var className = this.name; | |
var result = "var " + selfId + " = this;\n"; | |
var staticDefinitions = ""; | |
var metadata = ""; | |
var thisClassFields = {}, | |
thisClassMethods = {}, | |
thisClassInners = {}; | |
this.getMembers(thisClassFields, thisClassMethods, thisClassInners); | |
var oldContext = replaceContext; | |
replaceContext = function(subject) { | |
var name = subject.name; | |
if (name === "this") return subject.callSign || !subject.member ? selfId + ".$self" : selfId; | |
if (thisClassFields.hasOwnProperty(name)) return thisClassFields[name].isStatic ? className + "." + name : selfId + "." + name; | |
if (thisClassInners.hasOwnProperty(name)) return selfId + "." + name; | |
if (thisClassMethods.hasOwnProperty(name)) return thisClassMethods[name].isStatic ? className + "." + name : selfId + ".$self." + name; | |
return oldContext(subject) | |
}; | |
var resolvedBaseClassName; | |
if (this.baseClassName) { | |
resolvedBaseClassName = oldContext({ | |
name: this.baseClassName | |
}); | |
result += "var $super = { $upcast: " + selfId + " };\n"; | |
result += "function $superCstr(){" + resolvedBaseClassName + ".apply($super,arguments);if(!('$self' in $super)) $p.extendClassChain($super)}\n"; | |
metadata += className + ".$base = " + resolvedBaseClassName + ";\n" | |
} else result += "function $superCstr(){$p.extendClassChain(" + selfId + ")}\n"; | |
if (this.owner.base) staticDefinitions += "$p.extendStaticMembers(" + className + ", " + resolvedBaseClassName + ");\n"; | |
var i, l, j, m; | |
if (this.owner.interfaces) { | |
var resolvedInterfaces = [], | |
resolvedInterface; | |
for (i = 0, l = this.interfacesNames.length; i < l; ++i) { | |
if (!this.owner.interfaces[i]) continue; | |
resolvedInterface = oldContext({ | |
name: this.interfacesNames[i] | |
}); | |
resolvedInterfaces.push(resolvedInterface); | |
staticDefinitions += "$p.extendInterfaceMembers(" + className + ", " + resolvedInterface + ");\n" | |
} | |
metadata += className + ".$interfaces = [" + resolvedInterfaces.join(", ") + "];\n" | |
} | |
if (this.functions.length > 0) result += this.functions.join("\n") + "\n"; | |
sortByWeight(this.innerClasses); | |
for (i = 0, l = this.innerClasses.length; i < l; ++i) { | |
var innerClass = this.innerClasses[i]; | |
if (innerClass.isStatic) { | |
staticDefinitions += className + "." + innerClass.name + " = " + innerClass + ";\n"; | |
result += selfId + "." + innerClass.name + " = " + className + "." + innerClass.name + ";\n" | |
} else result += selfId + "." + innerClass.name + " = " + innerClass + ";\n" | |
} | |
for (i = 0, l = this.fields.length; i < l; ++i) { | |
var field = this.fields[i]; | |
if (field.isStatic) { | |
staticDefinitions += className + "." + field.definitions.join(";\n" + className + ".") + ";\n"; | |
for (j = 0, m = field.definitions.length; j < m; ++j) { | |
var fieldName = field.definitions[j].name, | |
staticName = className + "." + fieldName; | |
result += "$p.defineProperty(" + selfId + ", '" + fieldName + "', {" + "get: function(){return " + staticName + "}, " + "set: function(val){" + staticName + " = val}});\n" | |
} | |
} else result += selfId + "." + field.definitions.join(";\n" + selfId + ".") + ";\n" | |
} | |
var methodOverloads = {}; | |
for (i = 0, l = this.methods.length; i < l; ++i) { | |
var method = this.methods[i]; | |
var overload = methodOverloads[method.name]; | |
var methodId = method.name + "$" + method.params.params.length; | |
var hasMethodArgs = !!method.params.methodArgsParam; | |
if (overload) { | |
++overload; | |
methodId += "_" + overload | |
} else overload = 1; | |
method.methodId = methodId; | |
methodOverloads[method.name] = overload; | |
if (method.isStatic) { | |
staticDefinitions += method; | |
staticDefinitions += "$p.addMethod(" + className + ", '" + method.name + "', " + methodId + ", " + hasMethodArgs + ");\n"; | |
result += "$p.addMethod(" + selfId + ", '" + method.name + "', " + methodId + ", " + hasMethodArgs + ");\n" | |
} else { | |
result += method; | |
result += "$p.addMethod(" + selfId + ", '" + method.name + "', " + methodId + ", " + hasMethodArgs + ");\n" | |
} | |
} | |
result += trim(this.misc.tail); | |
if (this.cstrs.length > 0) result += this.cstrs.join("\n") + "\n"; | |
result += "function $constr() {\n"; | |
var cstrsIfs = []; | |
for (i = 0, l = this.cstrs.length; i < l; ++i) { | |
var paramsLength = this.cstrs[i].params.params.length; | |
var methodArgsPresent = !!this.cstrs[i].params.methodArgsParam; | |
cstrsIfs.push("if(arguments.length " + (methodArgsPresent ? ">=" : "===") + " " + paramsLength + ") { " + "$constr_" + paramsLength + ".apply(" + selfId + ", arguments); }") | |
} | |
if (cstrsIfs.length > 0) result += cstrsIfs.join(" else ") + " else "; | |
result += "$superCstr();\n}\n"; | |
result += "$constr.apply(null, arguments);\n"; | |
replaceContext = oldContext; | |
return "(function() {\n" + "function " + className + "() {\n" + result + "}\n" + staticDefinitions + metadata + "return " + className + ";\n" + "})()" | |
}; | |
transformClassBody = function(body, name, baseName, interfaces) { | |
var declarations = body.substring(1, body.length - 1); | |
declarations = extractClassesAndMethods(declarations); | |
declarations = extractConstructors(declarations, name); | |
var methods = [], | |
classes = [], | |
cstrs = [], | |
functions = []; | |
declarations = declarations.replace(/"([DEGH])(\d+)"/g, function(all, type, index) { | |
if (type === "D") methods.push(index); | |
else if (type === "E") classes.push(index); | |
else if (type === "H") functions.push(index); | |
else cstrs.push(index); | |
return "" | |
}); | |
var fields = declarations.replace(/^(?:\s*;)+/, "").split(/;(?:\s*;)*/g); | |
var baseClassName, interfacesNames; | |
var i; | |
if (baseName !== undef) baseClassName = baseName.replace(/^\s*extends\s+([A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*)\s*$/g, "$1"); | |
if (interfaces !== undef) interfacesNames = interfaces.replace(/^\s*implements\s+(.+?)\s*$/g, "$1").split(/\s*,\s*/g); | |
for (i = 0; i < functions.length; ++i) functions[i] = transformFunction(atoms[functions[i]]); | |
for (i = 0; i < methods.length; ++i) methods[i] = transformClassMethod(atoms[methods[i]]); | |
for (i = 0; i < fields.length - 1; ++i) { | |
var field = trimSpaces(fields[i]); | |
fields[i] = transformClassField(field.middle) | |
} | |
var tail = fields.pop(); | |
for (i = 0; i < cstrs.length; ++i) cstrs[i] = transformConstructor(atoms[cstrs[i]]); | |
for (i = 0; i < classes.length; ++i) classes[i] = transformInnerClass(atoms[classes[i]]); | |
return new AstClassBody(name, baseClassName, interfacesNames, functions, methods, fields, cstrs, classes, { | |
tail: tail | |
}) | |
}; | |
function AstInterface(name, body) { | |
this.name = name; | |
this.body = body; | |
body.owner = this | |
} | |
AstInterface.prototype.toString = function() { | |
return "var " + this.name + " = " + this.body + ";\n" + "$p." + this.name + " = " + this.name + ";\n" | |
}; | |
function AstClass(name, body) { | |
this.name = name; | |
this.body = body; | |
body.owner = this | |
} | |
AstClass.prototype.toString = function() { | |
return "var " + this.name + " = " + this.body + ";\n" + "$p." + this.name + " = " + this.name + ";\n" | |
}; | |
function transformGlobalClass(class_) { | |
var m = classesRegex.exec(class_); | |
classesRegex.lastIndex = 0; | |
var body = atoms[getAtomIndex(m[6])]; | |
var oldClassId = currentClassId, | |
newClassId = generateClassId(); | |
currentClassId = newClassId; | |
var globalClass; | |
if (m[2] === "interface") globalClass = new AstInterface(m[3], transformInterfaceBody(body, m[3], m[4])); | |
else globalClass = new AstClass(m[3], transformClassBody(body, m[3], m[4], m[5])); | |
appendClass(globalClass, newClassId, oldClassId); | |
currentClassId = oldClassId; | |
return globalClass | |
} | |
function AstMethod(name, params, body) { | |
this.name = name; | |
this.params = params; | |
this.body = body | |
} | |
AstMethod.prototype.toString = function() { | |
var paramNames = appendToLookupTable({}, | |
this.params.getNames()); | |
var oldContext = replaceContext; | |
replaceContext = function(subject) { | |
return paramNames.hasOwnProperty(subject.name) ? subject.name : oldContext(subject) | |
}; | |
var body = this.params.prependMethodArgs(this.body.toString()); | |
var result = "function " + this.name + this.params + " " + body + "\n" + "$p." + this.name + " = " + this.name + ";"; | |
replaceContext = oldContext; | |
return result | |
}; | |
function transformGlobalMethod(method) { | |
var m = methodsRegex.exec(method); | |
var result = methodsRegex.lastIndex = 0; | |
return new AstMethod(m[3], transformParams(atoms[getAtomIndex(m[4])]), transformStatementsBlock(atoms[getAtomIndex(m[6])])) | |
} | |
function preStatementsTransform(statements) { | |
var s = statements; | |
s = s.replace(/\b(catch\s*"B\d+"\s*"A\d+")(\s*catch\s*"B\d+"\s*"A\d+")+/g, "$1"); | |
return s | |
} | |
function AstForStatement(argument, misc) { | |
this.argument = argument; | |
this.misc = misc | |
} | |
AstForStatement.prototype.toString = function() { | |
return this.misc.prefix + this.argument.toString() | |
}; | |
function AstCatchStatement(argument, misc) { | |
this.argument = argument; | |
this.misc = misc | |
} | |
AstCatchStatement.prototype.toString = function() { | |
return this.misc.prefix + this.argument.toString() | |
}; | |
function AstPrefixStatement(name, argument, misc) { | |
this.name = name; | |
this.argument = argument; | |
this.misc = misc | |
} | |
AstPrefixStatement.prototype.toString = function() { | |
var result = this.misc.prefix; | |
if (this.argument !== undef) result += this.argument.toString(); | |
return result | |
}; | |
function AstSwitchCase(expr) { | |
this.expr = expr | |
} | |
AstSwitchCase.prototype.toString = function() { | |
return "case " + this.expr + ":" | |
}; | |
function AstLabel(label) { | |
this.label = label | |
} | |
AstLabel.prototype.toString = function() { | |
return this.label | |
}; | |
transformStatements = function(statements, transformMethod, transformClass) { | |
var nextStatement = new RegExp(/\b(catch|for|if|switch|while|with)\s*"B(\d+)"|\b(do|else|finally|return|throw|try|break|continue)\b|("[ADEH](\d+)")|\b(case)\s+([^:]+):|\b([A-Za-z_$][\w$]*\s*:)|(;)/g); | |
var res = []; | |
statements = preStatementsTransform(statements); | |
var lastIndex = 0, | |
m, space; | |
while ((m = nextStatement.exec(statements)) !== null) { | |
if (m[1] !== undef) { | |
var i = statements.lastIndexOf('"B', nextStatement.lastIndex); | |
var statementsPrefix = statements.substring(lastIndex, i); | |
if (m[1] === "for") res.push(new AstForStatement(transformForExpression(atoms[m[2]]), { | |
prefix: statementsPrefix | |
})); | |
else if (m[1] === "catch") res.push(new AstCatchStatement(transformParams(atoms[m[2]]), { | |
prefix: statementsPrefix | |
})); | |
else res.push(new AstPrefixStatement(m[1], transformExpression(atoms[m[2]]), { | |
prefix: statementsPrefix | |
})) | |
} else if (m[3] !== undef) res.push(new AstPrefixStatement(m[3], undef, { | |
prefix: statements.substring(lastIndex, nextStatement.lastIndex) | |
})); | |
else if (m[4] !== undef) { | |
space = statements.substring(lastIndex, nextStatement.lastIndex - m[4].length); | |
if (trim(space).length !== 0) continue; | |
res.push(space); | |
var kind = m[4].charAt(1), | |
atomIndex = m[5]; | |
if (kind === "D") res.push(transformMethod(atoms[atomIndex])); | |
else if (kind === "E") res.push(transformClass(atoms[atomIndex])); | |
else if (kind === "H") res.push(transformFunction(atoms[atomIndex])); | |
else res.push(transformStatementsBlock(atoms[atomIndex])) | |
} else if (m[6] !== undef) res.push(new AstSwitchCase(transformExpression(trim(m[7])))); | |
else if (m[8] !== undef) { | |
space = statements.substring(lastIndex, nextStatement.lastIndex - m[8].length); | |
if (trim(space).length !== 0) continue; | |
res.push(new AstLabel(statements.substring(lastIndex, nextStatement.lastIndex))) | |
} else { | |
var statement = trimSpaces(statements.substring(lastIndex, nextStatement.lastIndex - 1)); | |
res.push(statement.left); | |
res.push(transformStatement(statement.middle)); | |
res.push(statement.right + ";") | |
} | |
lastIndex = nextStatement.lastIndex | |
} | |
var statementsTail = trimSpaces(statements.substring(lastIndex)); | |
res.push(statementsTail.left); | |
if (statementsTail.middle !== "") { | |
res.push(transformStatement(statementsTail.middle)); | |
res.push(";" + statementsTail.right) | |
} | |
return res | |
}; | |
function getLocalNames(statements) { | |
var localNames = []; | |
for (var i = 0, l = statements.length; i < l; ++i) { | |
var statement = statements[i]; | |
if (statement instanceof AstVar) localNames = localNames.concat(statement.getNames()); | |
else if (statement instanceof AstForStatement && statement.argument.initStatement instanceof AstVar) localNames = localNames.concat(statement.argument.initStatement.getNames()); | |
else if (statement instanceof AstInnerInterface || statement instanceof AstInnerClass || statement instanceof AstInterface || statement instanceof AstClass || statement instanceof AstMethod || statement instanceof AstFunction) localNames.push(statement.name) | |
} | |
return appendToLookupTable({}, | |
localNames) | |
} | |
function AstStatementsBlock(statements) { | |
this.statements = statements | |
} | |
AstStatementsBlock.prototype.toString = function() { | |
var localNames = getLocalNames(this.statements); | |
var oldContext = replaceContext; | |
if (!isLookupTableEmpty(localNames)) replaceContext = function(subject) { | |
return localNames.hasOwnProperty(subject.name) ? subject.name : oldContext(subject) | |
}; | |
var result = "{\n" + this.statements.join("") + "\n}"; | |
replaceContext = oldContext; | |
return result | |
}; | |
transformStatementsBlock = function(block) { | |
var content = trimSpaces(block.substring(1, block.length - 1)); | |
return new AstStatementsBlock(transformStatements(content.middle)) | |
}; | |
function AstRoot(statements) { | |
this.statements = statements | |
} | |
AstRoot.prototype.toString = function() { | |
var classes = [], | |
otherStatements = [], | |
statement; | |
for (var i = 0, len = this.statements.length; i < len; ++i) { | |
statement = this.statements[i]; | |
if (statement instanceof AstClass || statement instanceof AstInterface) classes.push(statement); | |
else otherStatements.push(statement) | |
} | |
sortByWeight(classes); | |
var localNames = getLocalNames(this.statements); | |
replaceContext = function(subject) { | |
var name = subject.name; | |
if (localNames.hasOwnProperty(name)) return name; | |
if (globalMembers.hasOwnProperty(name) || PConstants.hasOwnProperty(name) || defaultScope.hasOwnProperty(name)) return "$p." + name; | |
return name | |
}; | |
var result = "// this code was autogenerated from PJS\n" + "(function($p) {\n" + classes.join("") + "\n" + otherStatements.join("") + "\n})"; | |
replaceContext = null; | |
return result | |
}; | |
transformMain = function() { | |
var statements = extractClassesAndMethods(atoms[0]); | |
statements = statements.replace(/\bimport\s+[^;]+;/g, ""); | |
return new AstRoot(transformStatements(statements, transformGlobalMethod, transformGlobalClass)) | |
}; | |
function generateMetadata(ast) { | |
var globalScope = {}; | |
var id, class_; | |
for (id in declaredClasses) if (declaredClasses.hasOwnProperty(id)) { | |
class_ = declaredClasses[id]; | |
var scopeId = class_.scopeId, | |
name = class_.name; | |
if (scopeId) { | |
var scope = declaredClasses[scopeId]; | |
class_.scope = scope; | |
if (scope.inScope === undef) scope.inScope = {}; | |
scope.inScope[name] = class_ | |
} else globalScope[name] = class_ | |
} | |
function findInScopes(class_, name) { | |
var parts = name.split("."); | |
var currentScope = class_.scope, | |
found; | |
while (currentScope) { | |
if (currentScope.hasOwnProperty(parts[0])) { | |
found = currentScope[parts[0]]; | |
break | |
} | |
currentScope = currentScope.scope | |
} | |
if (found === undef) found = globalScope[parts[0]]; | |
for (var i = 1, l = parts.length; i < l && found; ++i) found = found.inScope[parts[i]]; | |
return found | |
} | |
for (id in declaredClasses) if (declaredClasses.hasOwnProperty(id)) { | |
class_ = declaredClasses[id]; | |
var baseClassName = class_.body.baseClassName; | |
if (baseClassName) { | |
var parent = findInScopes(class_, baseClassName); | |
if (parent) { | |
class_.base = parent; | |
if (!parent.derived) parent.derived = []; | |
parent.derived.push(class_) | |
} | |
} | |
var interfacesNames = class_.body.interfacesNames, | |
interfaces = [], | |
i, l; | |
if (interfacesNames && interfacesNames.length > 0) { | |
for (i = 0, l = interfacesNames.length; i < l; ++i) { | |
var interface_ = findInScopes(class_, interfacesNames[i]); | |
interfaces.push(interface_); | |
if (!interface_) continue; | |
if (!interface_.derived) interface_.derived = []; | |
interface_.derived.push(class_) | |
} | |
if (interfaces.length > 0) class_.interfaces = interfaces | |
} | |
} | |
} | |
function setWeight(ast) { | |
var queue = [], | |
tocheck = {}; | |
var id, scopeId, class_; | |
for (id in declaredClasses) if (declaredClasses.hasOwnProperty(id)) { | |
class_ = declaredClasses[id]; | |
if (!class_.inScope && !class_.derived) { | |
queue.push(id); | |
class_.weight = 0 | |
} else { | |
var dependsOn = []; | |
if (class_.inScope) for (scopeId in class_.inScope) if (class_.inScope.hasOwnProperty(scopeId)) dependsOn.push(class_.inScope[scopeId]); | |
if (class_.derived) dependsOn = dependsOn.concat(class_.derived); | |
tocheck[id] = dependsOn | |
} | |
} | |
function removeDependentAndCheck(targetId, from) { | |
var dependsOn = tocheck[targetId]; | |
if (!dependsOn) return false; | |
var i = dependsOn.indexOf(from); | |
if (i < 0) return false; | |
dependsOn.splice(i, 1); | |
if (dependsOn.length > 0) return false; | |
delete tocheck[targetId]; | |
return true | |
} | |
while (queue.length > 0) { | |
id = queue.shift(); | |
class_ = declaredClasses[id]; | |
if (class_.scopeId && removeDependentAndCheck(class_.scopeId, class_)) { | |
queue.push(class_.scopeId); | |
declaredClasses[class_.scopeId].weight = class_.weight + 1 | |
} | |
if (class_.base && removeDependentAndCheck(class_.base.classId, class_)) { | |
queue.push(class_.base.classId); | |
class_.base.weight = class_.weight + 1 | |
} | |
if (class_.interfaces) { | |
var i, l; | |
for (i = 0, l = class_.interfaces.length; i < l; ++i) { | |
if (!class_.interfaces[i] || !removeDependentAndCheck(class_.interfaces[i].classId, class_)) continue; | |
queue.push(class_.interfaces[i].classId); | |
class_.interfaces[i].weight = class_.weight + 1 | |
} | |
} | |
} | |
} | |
var transformed = transformMain(); | |
generateMetadata(transformed); | |
setWeight(transformed); | |
var redendered = transformed.toString(); | |
redendered = redendered.replace(/\s*\n(?:[\t ]*\n)+/g, "\n\n"); | |
redendered = redendered.replace(/__x([0-9A-F]{4})/g, function(all, hexCode) { | |
return String.fromCharCode(parseInt(hexCode, 16)) | |
}); | |
return injectStrings(redendered, strings) | |
} | |
function preprocessCode(aCode, sketch) { | |
var dm = (new RegExp(/\/\*\s*@pjs\s+((?:[^\*]|\*+[^\*\/])*)\*\//g)).exec(aCode); | |
if (dm && dm.length === 2) { | |
var jsonItems = [], | |
directives = dm.splice(1, 2)[0].replace(/\{([\s\S]*?)\}/g, function() { | |
return function(all, item) { | |
jsonItems.push(item); | |
return "{" + (jsonItems.length - 1) + "}" | |
} | |
}()).replace("\n", "").replace("\r", "").split(";"); | |
var clean = function(s) { | |
return s.replace(/^\s*["']?/, "").replace(/["']?\s*$/, "") | |
}; | |
for (var i = 0, dl = directives.length; i < dl; i++) { | |
var pair = directives[i].split("="); | |
if (pair && pair.length === 2) { | |
var key = clean(pair[0]), | |
value = clean(pair[1]), | |
list = []; | |
if (key === "preload") { | |
list = value.split(","); | |
for (var j = 0, jl = list.length; j < jl; j++) { | |
var imageName = clean(list[j]); | |
sketch.imageCache.add(imageName) | |
} | |
} else if (key === "font") { | |
list = value.split(","); | |
for (var x = 0, xl = list.length; x < xl; x++) { | |
var fontName = clean(list[x]), | |
index = /^\{(\d*?)\}$/.exec(fontName); | |
PFont.preloading.add(index ? JSON.parse("{" + jsonItems[index[1]] + "}") : fontName) | |
} | |
} else if (key === "pauseOnBlur") sketch.options.pauseOnBlur = value === "true"; | |
else if (key === "globalKeyEvents") sketch.options.globalKeyEvents = value === "true"; | |
else if (key.substring(0, 6) === "param-") sketch.params[key.substring(6)] = value; | |
else sketch.options[key] = value | |
} | |
} | |
} | |
return aCode | |
} | |
Processing.compile = function(pdeCode) { | |
var sketch = new Processing.Sketch; | |
var code = preprocessCode(pdeCode, sketch); | |
var compiledPde = parseProcessing(code); | |
sketch.sourceCode = compiledPde; | |
return sketch | |
}; | |
var tinylogLite = function() { | |
var tinylogLite = {}, | |
undef = "undefined", | |
func = "function", | |
False = !1, | |
True = !0, | |
logLimit = 512, | |
log = "log"; | |
if (typeof tinylog !== undef && typeof tinylog[log] === func) tinylogLite[log] = tinylog[log]; | |
else if (typeof document !== undef && !document.fake)(function() { | |
var doc = document, | |
$div = "div", | |
$style = "style", | |
$title = "title", | |
containerStyles = { | |
zIndex: 1E4, | |
position: "fixed", | |
bottom: "0px", | |
width: "100%", | |
height: "15%", | |
fontFamily: "sans-serif", | |
color: "#ccc", | |
backgroundColor: "black" | |
}, | |
outputStyles = { | |
position: "relative", | |
fontFamily: "monospace", | |
overflow: "auto", | |
height: "100%", | |
paddingTop: "5px" | |
}, | |
resizerStyles = { | |
height: "5px", | |
marginTop: "-5px", | |
cursor: "n-resize", | |
backgroundColor: "darkgrey" | |
}, | |
closeButtonStyles = { | |
position: "absolute", | |
top: "5px", | |
right: "20px", | |
color: "#111", | |
MozBorderRadius: "4px", | |
webkitBorderRadius: "4px", | |
borderRadius: "4px", | |
cursor: "pointer", | |
fontWeight: "normal", | |
textAlign: "center", | |
padding: "3px 5px", | |
backgroundColor: "#333", | |
fontSize: "12px" | |
}, | |
entryStyles = { | |
minHeight: "16px" | |
}, | |
entryTextStyles = { | |
fontSize: "12px", | |
margin: "0 8px 0 8px", | |
maxWidth: "100%", | |
whiteSpace: "pre-wrap", | |
overflow: "auto" | |
}, | |
view = doc.defaultView, | |
docElem = doc.documentElement, | |
docElemStyle = docElem[$style], | |
setStyles = function() { | |
var i = arguments.length, | |
elemStyle, styles, style; | |
while (i--) { | |
styles = arguments[i--]; | |
elemStyle = arguments[i][$style]; | |
for (style in styles) if (styles.hasOwnProperty(style)) elemStyle[style] = styles[style] | |
} | |
}, | |
observer = function(obj, event, handler) { | |
if (obj.addEventListener) obj.addEventListener(event, handler, False); | |
else if (obj.attachEvent) obj.attachEvent("on" + event, handler); | |
return [obj, event, handler] | |
}, | |
unobserve = function(obj, event, handler) { | |
if (obj.removeEventListener) obj.removeEventListener(event, handler, False); | |
else if (obj.detachEvent) obj.detachEvent("on" + event, handler) | |
}, | |
clearChildren = function(node) { | |
var children = node.childNodes, | |
child = children.length; | |
while (child--) node.removeChild(children.item(0)) | |
}, | |
append = function(to, elem) { | |
return to.appendChild(elem) | |
}, | |
createElement = function(localName) { | |
return doc.createElement(localName) | |
}, | |
createTextNode = function(text) { | |
return doc.createTextNode(text) | |
}, | |
createLog = tinylogLite[log] = function(message) { | |
var uninit, originalPadding = docElemStyle.paddingBottom, | |
container = createElement($div), | |
containerStyle = container[$style], | |
resizer = append(container, createElement($div)), | |
output = append(container, createElement($div)), | |
closeButton = append(container, createElement($div)), | |
resizingLog = False, | |
previousHeight = False, | |
previousScrollTop = False, | |
messages = 0, | |
updateSafetyMargin = function() { | |
docElemStyle.paddingBottom = container.clientHeight + "px" | |
}, | |
setContainerHeight = function(height) { | |
var viewHeight = view.innerHeight, | |
resizerHeight = resizer.clientHeight; | |
if (height < 0) height = 0; | |
else if (height + resizerHeight > viewHeight) height = viewHeight - resizerHeight; | |
containerStyle.height = height / viewHeight * 100 + "%"; | |
updateSafetyMargin() | |
}, | |
observers = [observer(doc, "mousemove", function(evt) { | |
if (resizingLog) { | |
setContainerHeight(view.innerHeight - evt.clientY); | |
output.scrollTop = previousScrollTop | |
} | |
}), observer(doc, "mouseup", function() { | |
if (resizingLog) resizingLog = previousScrollTop = False | |
}), observer(resizer, "dblclick", function(evt) { | |
evt.preventDefault(); | |
if (previousHeight) { | |
setContainerHeight(previousHeight); | |
previousHeight = False | |
} else { | |
previousHeight = container.clientHeight; | |
containerStyle.height = "0px" | |
} | |
}), observer(resizer, "mousedown", function(evt) { | |
evt.preventDefault(); | |
resizingLog = True; | |
previousScrollTop = output.scrollTop | |
}), observer(resizer, "contextmenu", function() { | |
resizingLog = False | |
}), observer(closeButton, "click", function() { | |
uninit() | |
})]; | |
uninit = function() { | |
var i = observers.length; | |
while (i--) unobserve.apply(tinylogLite, observers[i]); | |
docElem.removeChild(container); | |
docElemStyle.paddingBottom = originalPadding; | |
clearChildren(output); | |
clearChildren(container); | |
tinylogLite[log] = createLog | |
}; | |
setStyles(container, containerStyles, output, outputStyles, resizer, resizerStyles, closeButton, closeButtonStyles); | |
closeButton[$title] = "Close Log"; | |
append(closeButton, createTextNode("\u2716")); | |
resizer[$title] = "Double-click to toggle log minimization"; | |
docElem.insertBefore(container, docElem.firstChild); | |
tinylogLite[log] = function(message) { | |
if (messages === logLimit) output.removeChild(output.firstChild); | |
else messages++; | |
var entry = append(output, createElement($div)), | |
entryText = append(entry, createElement($div)); | |
entry[$title] = (new Date).toLocaleTimeString(); | |
setStyles(entry, entryStyles, entryText, entryTextStyles); | |
append(entryText, createTextNode(message)); | |
output.scrollTop = output.scrollHeight | |
}; | |
tinylogLite[log](message); | |
updateSafetyMargin() | |
} | |
})(); | |
else if (typeof print === func) tinylogLite[log] = print; | |
return tinylogLite | |
}(); | |
Processing.logger = tinylogLite; | |
Processing.version = "1.4.1"; | |
Processing.lib = {}; | |
Processing.registerLibrary = function(name, desc) { | |
Processing.lib[name] = desc; | |
if (desc.hasOwnProperty("init")) desc.init(defaultScope) | |
}; | |
Processing.instances = processingInstances; | |
Processing.getInstanceById = function(name) { | |
return processingInstances[processingInstanceIds[name]] | |
}; | |
Processing.Sketch = function(attachFunction) { | |
this.attachFunction = attachFunction; | |
this.options = { | |
pauseOnBlur: false, | |
globalKeyEvents: false | |
}; | |
this.onLoad = nop; | |
this.onSetup = nop; | |
this.onPause = nop; | |
this.onLoop = nop; | |
this.onFrameStart = nop; | |
this.onFrameEnd = nop; | |
this.onExit = nop; | |
this.params = {}; | |
this.imageCache = { | |
pending: 0, | |
images: {}, | |
operaCache: {}, | |
add: function(href, img) { | |
if (this.images[href]) return; | |
if (!isDOMPresent) this.images[href] = null; | |
if (!img) { | |
img = new Image; | |
img.onload = function(owner) { | |
return function() { | |
owner.pending-- | |
} | |
}(this); | |
this.pending++; | |
img.src = href | |
} | |
this.images[href] = img; | |
if (window.opera) { | |
var div = document.createElement("div"); | |
div.appendChild(img); | |
div.style.position = "absolute"; | |
div.style.opacity = 0; | |
div.style.width = "1px"; | |
div.style.height = "1px"; | |
if (!this.operaCache[href]) { | |
document.body.appendChild(div); | |
this.operaCache[href] = div | |
} | |
} | |
} | |
}; | |
this.sourceCode = undefined; | |
this.attach = function(processing) { | |
if (typeof this.attachFunction === "function") this.attachFunction(processing); | |
else if (this.sourceCode) { | |
var func = (new Function("return (" + this.sourceCode + ");"))(); | |
func(processing); | |
this.attachFunction = func | |
} else throw "Unable to attach sketch to the processing instance"; | |
}; | |
this.toString = function() { | |
var i; | |
var code = "((function(Sketch) {\n"; | |
code += "var sketch = new Sketch(\n" + this.sourceCode + ");\n"; | |
for (i in this.options) if (this.options.hasOwnProperty(i)) { | |
var value = this.options[i]; | |
code += "sketch.options." + i + " = " + (typeof value === "string" ? '"' + value + '"' : "" + value) + ";\n" | |
} | |
for (i in this.imageCache) if (this.options.hasOwnProperty(i)) code += 'sketch.imageCache.add("' + i + '");\n'; | |
code += "return sketch;\n})(Processing.Sketch))"; | |
return code | |
} | |
}; | |
var loadSketchFromSources = function(canvas, sources) { | |
var code = [], | |
errors = [], | |
sourcesCount = sources.length, | |
loaded = 0; | |
function ajaxAsync(url, callback) { | |
var xhr = new XMLHttpRequest; | |
xhr.onreadystatechange = function() { | |
if (xhr.readyState === 4) { | |
var error; | |
if (xhr.status !== 200 && xhr.status !== 0) error = "Invalid XHR status " + xhr.status; | |
else if (xhr.responseText === "") if ("withCredentials" in new XMLHttpRequest && (new XMLHttpRequest).withCredentials === false && window.location.protocol === "file:") error = "XMLHttpRequest failure, possibly due to a same-origin policy violation. You can try loading this page in another browser, or load it from http://localhost using a local webserver. See the Processing.js README for a more detailed explanation of this problem and solutions."; | |
else error = "File is empty."; | |
callback(xhr.responseText, error) | |
} | |
}; | |
xhr.open("GET", url, true); | |
if (xhr.overrideMimeType) xhr.overrideMimeType("application/json"); | |
xhr.setRequestHeader("If-Modified-Since", "Fri, 01 Jan 1960 00:00:00 GMT"); | |
xhr.send(null) | |
} | |
function loadBlock(index, filename) { | |
function callback(block, error) { | |
code[index] = block; | |
++loaded; | |
if (error) errors.push(filename + " ==> " + error); | |
if (loaded === sourcesCount) if (errors.length === 0) try { | |
return new Processing(canvas, code.join("\n")) | |
} catch(e) { | |
throw "Processing.js: Unable to execute pjs sketch: " + e; | |
} else throw "Processing.js: Unable to load pjs sketch files: " + errors.join("\n"); | |
} | |
if (filename.charAt(0) === "#") { | |
var scriptElement = document.getElementById(filename.substring(1)); | |
if (scriptElement) callback(scriptElement.text || scriptElement.textContent); | |
else callback("", "Unable to load pjs sketch: element with id '" + filename.substring(1) + "' was not found"); | |
return | |
} | |
ajaxAsync(filename, callback) | |
} | |
for (var i = 0; i < sourcesCount; ++i) loadBlock(i, sources[i]) | |
}; | |
var init = function() { | |
document.removeEventListener("DOMContentLoaded", init, false); | |
processingInstances = []; | |
var canvas = document.getElementsByTagName("canvas"), | |
filenames; | |
for (var i = 0, l = canvas.length; i < l; i++) { | |
var processingSources = canvas[i].getAttribute("data-processing-sources"); | |
if (processingSources === null) { | |
processingSources = canvas[i].getAttribute("data-src"); | |
if (processingSources === null) processingSources = canvas[i].getAttribute("datasrc") | |
} | |
if (processingSources) { | |
filenames = processingSources.split(/\s+/g); | |
for (var j = 0; j < filenames.length;) if (filenames[j]) j++; | |
else filenames.splice(j, 1); | |
loadSketchFromSources(canvas[i], filenames) | |
} | |
} | |
var s, last, source, instance, nodelist = document.getElementsByTagName("script"), | |
scripts = []; | |
for (s = nodelist.length - 1; s >= 0; s--) scripts.push(nodelist[s]); | |
for (s = 0, last = scripts.length; s < last; s++) { | |
var script = scripts[s]; | |
if (!script.getAttribute) continue; | |
var type = script.getAttribute("type"); | |
if (type && (type.toLowerCase() === "text/processing" || type.toLowerCase() === "application/processing")) { | |
var target = script.getAttribute("data-processing-target"); | |
canvas = undef; | |
if (target) canvas = document.getElementById(target); | |
else { | |
var nextSibling = script.nextSibling; | |
while (nextSibling && nextSibling.nodeType !== 1) nextSibling = nextSibling.nextSibling; | |
if (nextSibling && nextSibling.nodeName.toLowerCase() === "canvas") canvas = nextSibling | |
} | |
if (canvas) { | |
if (script.getAttribute("src")) { | |
filenames = script.getAttribute("src").split(/\s+/); | |
loadSketchFromSources(canvas, filenames); | |
continue | |
} | |
source = script.textContent || script.text; | |
instance = new Processing(canvas, source) | |
} | |
} | |
} | |
}; | |
Processing.reload = function() { | |
if (processingInstances.length > 0) for (var i = processingInstances.length - 1; i >= 0; i--) if (processingInstances[i]) processingInstances[i].exit(); | |
init() | |
}; | |
Processing.loadSketchFromSources = loadSketchFromSources; | |
Processing.disableInit = function() { | |
if (isDOMPresent) document.removeEventListener("DOMContentLoaded", init, false) | |
}; | |
if (isDOMPresent) { | |
window["Processing"] = Processing; | |
document.addEventListener("DOMContentLoaded", init, false) | |
} else this.Processing = Processing | |
})(window, window.document, Math); | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Global variables | |
float radius = 50.0; | |
int X, Y; | |
int eX, eY; | |
int nX, nY; | |
int delay = 55; | |
int evilDelay = 20; | |
int BACKGROUND_COLOR = 100; | |
int STROKE_COLOR = 255; | |
int TEXT_COLOR = 0; | |
int CIRCLE_BORDER = 5; | |
// Setup the Processing Canvas | |
void setup(){ | |
size(300, 300); | |
strokeWeight(CIRCLE_BORDER); | |
frameRate(15); | |
textSize(32); | |
nX = X; | |
nY = Y; | |
X = width / 2; | |
Y = height / 2; | |
eX = 0; | |
eY = 0; | |
} | |
// Main draw loop | |
void draw(){ | |
radius = radius + sin(frameCount / 4); | |
// Fill canvas grey | |
background(BACKGROUND_COLOR); | |
// Set stroke-color white | |
stroke(STROKE_COLOR); | |
drawGoodCircle(true); | |
drawEvilCircle(); | |
checkCollisions(); | |
} | |
// Set circle's next destination | |
void mouseMoved(){ | |
nX = mouseX; | |
nY = mouseY; | |
} | |
// Draw circle at new location | |
void drawGoodCircle(safe){ | |
// Set fill-color to blue | |
if (safe){ | |
fill(0, 121, 184); | |
} | |
// Set fill-color to red | |
else { | |
fill(255, 50, 50); | |
} | |
for (int i = 0; i < delay; i = i+2) { | |
// Track circle to new destination | |
X += (nX-X)/delay; | |
Y += (nY-Y)/delay; | |
// Draw circle | |
ellipse(X, Y, radius, radius); | |
} | |
} | |
void drawEvilCircle(){ | |
// Set fill-color to pink | |
fill(200, 121, 184); | |
// Track circle to new destination | |
if (!(abs(X - eX) < radius + CIRCLE_BORDER && abs(Y - eY) < radius + CIRCLE_BORDER)){ | |
eX += (nX-eX)/evilDelay; | |
eY += (nY-eY)/evilDelay; | |
} | |
// Draw circle | |
ellipse(eX, eY, radius, radius); | |
} | |
void checkCollisions(){ | |
if (abs(X - eX) < radius + CIRCLE_BORDER && abs(Y - eY) < radius + CIRCLE_BORDER){ | |
drawGoodCircle(false); | |
fill(TEXT_COLOR); | |
String textstring = "No touch!"; | |
float twidth = textWidth(textstring); | |
text(textstring, (width-twidth)/2, height/2); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment