dspjs
Version:
DSP.js is a comprehensive digital signal processing library for javascript
1,506 lines (1,379 loc) • 431 kB
JavaScript
/*
P R O C E S S I N G . J S - @VERSION@
a port of the Processing visualization language
License : MIT
Developer : John Resig: http://ejohn.org
Web Site : http://processingjs.org
Java Version : http://processing.org
Github Repo. : http://github.com/jeresig/processing-js
Bug Tracking : http://processing-js.lighthouseapp.com
Mozilla POW! : http://wiki.Mozilla.org/Education/Projects/ProcessingForTheWeb
Maintained by : Seneca: http://zenit.senecac.on.ca/wiki/index.php/Processing.js
Hyper-Metrix: http://hyper-metrix.com/#Processing
BuildingSky: http://weare.buildingsky.net/pages/processing-js
*/
(function() {
var undef; // intentionally left undefined
var ajax = function ajax(url) {
var xhr = new XMLHttpRequest();
xhr.open("GET", url, false);
xhr.setRequestHeader("If-Modified-Since", "Fri, 1 Jan 1960 00:00:00 GMT");
xhr.send(null);
// failed request?
if (xhr.status !== 200 && xhr.status !== 0) { throw ("XMLHttpRequest failed, status code " + xhr.status); }
return xhr.responseText;
};
var PVector = function(x, y, z) {
this.x = x || 0;
this.y = y || 0;
this.z = z || 0;
},
createPVectorMethod = function(method) {
return function(v1, v2) {
var v = v1.get();
v[method](v2);
return v;
};
},
createSimplePVectorMethod = function(method) {
return function(v1, v2) {
return v1[method](v2);
};
},
simplePVMethods = "dist dot cross".split(" "),
method = simplePVMethods.length;
PVector.angleBetween = function(v1, v2) {
return Math.acos(v1.dot(v2) / (v1.mag() * v2.mag()));
};
// Common vector operations for PVector
PVector.prototype = {
set: function(v, y, z) {
if (arguments.length === 1) {
this.set(v.x || v[0], v.y || v[1], v.z || v[2]);
} else {
this.x = v;
this.y = y;
this.z = z;
}
},
get: function() {
return new PVector(this.x, this.y, this.z);
},
mag: function() {
return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
},
add: function(v, y, z) {
if (arguments.length === 3) {
this.x += v;
this.y += y;
this.z += z;
} else if (arguments.length === 1) {
this.x += v.x;
this.y += v.y;
this.z += v.z;
}
},
sub: function(v, y, z) {
if (arguments.length === 3) {
this.x -= v;
this.y -= y;
this.z -= z;
} else if (arguments.length === 1) {
this.x -= v.x;
this.y -= v.y;
this.z -= v.z;
}
},
mult: function(v) {
if (typeof v === 'number') {
this.x *= v;
this.y *= v;
this.z *= v;
} else if (typeof v === 'object') {
this.x *= v.x;
this.y *= v.y;
this.z *= v.z;
}
},
div: function(v) {
if (typeof v === 'number') {
this.x /= v;
this.y /= v;
this.z /= v;
} else if (typeof v === 'object') {
this.x /= v.x;
this.y /= v.y;
this.z /= v.z;
}
},
dist: function(v) {
var dx = this.x - v.x,
dy = this.y - v.y,
dz = this.z - v.z;
return Math.sqrt(dx * dx + dy * dy + dz * dz);
},
dot: function(v, y, z) {
if (arguments.length === 3) {
return (this.x * v + this.y * y + this.z * z);
} else if (arguments.length === 1) {
return (this.x * v.x + this.y * v.y + this.z * v.z);
}
},
cross: function(v) {
return new PVector(this.y * v.z - v.y * this.z,
this.z * v.x - v.z * this.x,
this.x * v.y - v.x * this.y);
},
normalize: function() {
var m = this.mag();
if (m > 0) {
this.div(m);
}
},
limit: function(high) {
if (this.mag() > high) {
this.normalize();
this.mult(high);
}
},
heading2D: function() {
return (-Math.atan2(-this.y, this.x));
},
toString: function() {
return "[" + this.x + ", " + this.y + ", " + this.z + "]";
},
array: function() {
return [this.x, this.y, this.z];
}
};
while (method--) {
PVector[simplePVMethods[method]] = createSimplePVectorMethod(simplePVMethods[method]);
}
for (method in PVector.prototype) {
if (PVector.prototype.hasOwnProperty(method) && !PVector.hasOwnProperty(method)) {
PVector[method] = createPVectorMethod(method);
}
}
var Processing = this.Processing = function Processing(curElement, aCode) {
var p = this;
// Include Package Classes -- do this differently in the future.
p.PVector = PVector;
//p.PShapeSVG = PShapeSVG;
//etc
p.name = 'Processing.js Instance'; // Set Processing defaults / environment variables
p.use3DContext = false; // default '2d' canvas context
// PJS specific (non-p5) methods and properties to externalize
p.externals = {
canvas: curElement,
context: undef,
sketch: undef,
onblur: function() {},
onfocus: function() {}
};
// Glyph path storage for textFonts
p.glyphTable = {};
// Global vars for tracking mouse position
p.pmouseX = 0;
p.pmouseY = 0;
p.mouseX = 0;
p.mouseY = 0;
p.mouseButton = 0;
p.mouseScroll = 0;
// Undefined event handlers to be replaced by user when needed
p.mouseClicked = undef;
p.mouseDragged = undef;
p.mouseMoved = undef;
p.mousePressed = undef;
p.mouseReleased = undef;
p.mouseScrolled = undef;
p.key = undef;
p.keyCode = undef;
p.keyPressed = undef;
p.keyReleased = undef;
p.keyTyped = undef;
p.draw = undef;
p.setup = undef;
// Remapped vars
p.__mousePressed = false;
p.__keyPressed = false;
p.__frameRate = 0;
// The current animation frame
p.frameCount = 0;
// The height/width of the canvas
p.width = curElement.width - 0;
p.height = curElement.height - 0;
// Color modes
p.RGB = 1;
p.ARGB = 2;
p.HSB = 3;
p.ALPHA = 4;
p.CMYK = 5;
// Renderers
p.P2D = 1;
p.JAVA2D = 1;
p.WEBGL = 2;
p.P3D = 2;
p.OPENGL = 2;
p.EPSILON = 0.0001;
p.MAX_FLOAT = 3.4028235e+38;
p.MIN_FLOAT = -3.4028235e+38;
p.MAX_INT = 2147483647;
p.MIN_INT = -2147483648;
p.PI = Math.PI;
p.TWO_PI = 2 * p.PI;
p.HALF_PI = p.PI / 2;
p.THIRD_PI = p.PI / 3;
p.QUARTER_PI = p.PI / 4;
p.DEG_TO_RAD = p.PI / 180;
p.RAD_TO_DEG = 180 / p.PI;
p.WHITESPACE = " \t\n\r\f\u00A0";
// Filter/convert types
p.BLUR = 11;
p.GRAY = 12;
p.INVERT = 13;
p.OPAQUE = 14;
p.POSTERIZE = 15;
p.THRESHOLD = 16;
p.ERODE = 17;
p.DILATE = 18;
// Blend modes
p.REPLACE = 0;
p.BLEND = 1 << 0;
p.ADD = 1 << 1;
p.SUBTRACT = 1 << 2;
p.LIGHTEST = 1 << 3;
p.DARKEST = 1 << 4;
p.DIFFERENCE = 1 << 5;
p.EXCLUSION = 1 << 6;
p.MULTIPLY = 1 << 7;
p.SCREEN = 1 << 8;
p.OVERLAY = 1 << 9;
p.HARD_LIGHT = 1 << 10;
p.SOFT_LIGHT = 1 << 11;
p.DODGE = 1 << 12;
p.BURN = 1 << 13;
// Color component bit masks
p.ALPHA_MASK = 0xff000000;
p.RED_MASK = 0x00ff0000;
p.GREEN_MASK = 0x0000ff00;
p.BLUE_MASK = 0x000000ff;
// Projection matrices
p.CUSTOM = 0;
p.ORTHOGRAPHIC = 2;
p.PERSPECTIVE = 3;
// Shapes
p.POINT = 2;
p.POINTS = 2;
p.LINE = 4;
p.LINES = 4;
p.TRIANGLE = 8;
p.TRIANGLES = 9;
p.TRIANGLE_STRIP = 10;
p.TRIANGLE_FAN = 11;
p.QUAD = 16;
p.QUADS = 16;
p.QUAD_STRIP = 17;
p.POLYGON = 20;
p.PATH = 21;
p.RECT = 30;
p.ELLIPSE = 31;
p.ARC = 32;
p.SPHERE = 40;
p.BOX = 41;
p.GROUP = 0;
p.PRIMITIVE = 1;
p.PATH = 2;
p.GEOMETRY = 3;
p.breakShape = false;
// Shape Vertex
p.VERTEX = 0;
p.BEZIER_VERTEX = 1;
p.CURVE_VERTEX = 2;
p.BREAK = 3;
p.CLOSESHAPE = 4;
// Shape closing modes
p.OPEN = 1;
p.CLOSE = 2;
// Shape drawing modes
p.CORNER = 0; // Draw mode convention to use (x, y) to (width, height)
p.CORNERS = 1; // Draw mode convention to use (x1, y1) to (x2, y2) coordinates
p.RADIUS = 2; // Draw mode from the center, and using the radius
p.CENTER_RADIUS = 2; // Deprecated! Use RADIUS instead
p.CENTER = 3; // Draw from the center, using second pair of values as the diameter
p.DIAMETER = 3; // Synonym for the CENTER constant. Draw from the center
p.CENTER_DIAMETER = 3; // Deprecated! Use DIAMETER instead
// Text vertical alignment modes
p.BASELINE = 0; // Default vertical alignment for text placement
p.TOP = 101; // Align text to the top
p.BOTTOM = 102; // Align text from the bottom, using the baseline
// UV Texture coordinate modes
p.NORMAL = 1;
p.NORMALIZED = 1;
p.IMAGE = 2;
// Text placement modes
p.MODEL = 4;
p.SHAPE = 5;
// Stroke modes
p.SQUARE = 'butt';
p.ROUND = 'round';
p.PROJECT = 'square';
p.MITER = 'miter';
p.BEVEL = 'bevel';
// Lighting modes
p.AMBIENT = 0;
p.DIRECTIONAL = 1;
//POINT = 2; Shared with Shape constant
p.SPOT = 3;
// Key constants
// Both key and keyCode will be equal to these values
p.BACKSPACE = 8;
p.TAB = 9;
p.ENTER = 10;
p.RETURN = 13;
p.ESC = 27;
p.DELETE = 127;
p.CODED = 0xffff;
// p.key will be CODED and p.keyCode will be this value
p.SHIFT = 16;
p.CONTROL = 17;
p.ALT = 18;
p.UP = 38;
p.RIGHT = 39;
p.DOWN = 40;
p.LEFT = 37;
var codedKeys = [p.SHIFT, p.CONTROL, p.ALT, p.UP, p.RIGHT, p.DOWN, p.LEFT];
// Cursor types
p.ARROW = 'default';
p.CROSS = 'crosshair';
p.HAND = 'pointer';
p.MOVE = 'move';
p.TEXT = 'text';
p.WAIT = 'wait';
p.NOCURSOR = "url('data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=='), auto";
// Hints
p.DISABLE_OPENGL_2X_SMOOTH = 1;
p.ENABLE_OPENGL_2X_SMOOTH = -1;
p.ENABLE_OPENGL_4X_SMOOTH = 2;
p.ENABLE_NATIVE_FONTS = 3;
p.DISABLE_DEPTH_TEST = 4;
p.ENABLE_DEPTH_TEST = -4;
p.ENABLE_DEPTH_SORT = 5;
p.DISABLE_DEPTH_SORT = -5;
p.DISABLE_OPENGL_ERROR_REPORT = 6;
p.ENABLE_OPENGL_ERROR_REPORT = -6;
p.ENABLE_ACCURATE_TEXTURES = 7;
p.DISABLE_ACCURATE_TEXTURES = -7;
p.HINT_COUNT = 10;
// PJS defined constants
p.SINCOS_LENGTH = parseInt(360 / 0.5, 10);
p.PRECISIONB = 15; // fixed point precision is limited to 15 bits!!
p.PRECISIONF = 1 << p.PRECISIONB;
p.PREC_MAXVAL = p.PRECISIONF - 1;
p.PREC_ALPHA_SHIFT = 24 - p.PRECISIONB;
p.PREC_RED_SHIFT = 16 - p.PRECISIONB;
p.NORMAL_MODE_AUTO = 0;
p.NORMAL_MODE_SHAPE = 1;
p.NORMAL_MODE_VERTEX = 2;
p.MAX_LIGHTS = 8;
p.focused = true;
// "Private" variables used to maintain state
var curContext,
curSketch,
online = true,
doFill = true,
fillStyle = [1.0, 1.0, 1.0, 1.0],
currentFillColor = 0xFFFFFFFF,
isFillDirty = true,
doStroke = true,
strokeStyle = [0.8, 0.8, 0.8, 1.0],
currentStrokeColor = 0xFFFDFDFD,
isStrokeDirty = true,
lineWidth = 1,
loopStarted = false,
doLoop = true,
looping = 0,
curRectMode = p.CORNER,
curEllipseMode = p.CENTER,
normalX = 0,
normalY = 0,
normalZ = 0,
normalMode = p.NORMAL_MODE_AUTO,
inDraw = false,
curFrameRate = 60,
curCursor = p.ARROW,
oldCursor = curElement.style.cursor,
curMsPerFrame = 1,
curShape = p.POLYGON,
curShapeCount = 0,
curvePoints = [],
curTightness = 0,
curveDet = 20,
curveInited = false,
bezDetail = 20,
colorModeA = 255,
colorModeX = 255,
colorModeY = 255,
colorModeZ = 255,
pathOpen = false,
mouseDragging = false,
curColorMode = p.RGB,
curTint = function() {},
curTextSize = 12,
curTextFont = "Arial",
getLoaded = false,
start = new Date().getTime(),
timeSinceLastFPS = start,
framesSinceLastFPS = 0,
textcanvas,
curveBasisMatrix,
curveToBezierMatrix,
curveDrawMatrix,
bezierDrawMatrix,
bezierBasisInverse,
bezierBasisMatrix,
// Shaders
programObject3D,
programObject2D,
programObjectUnlitShape,
boxBuffer,
boxNormBuffer,
boxOutlineBuffer,
rectBuffer,
rectNormBuffer,
sphereBuffer,
lineBuffer,
fillBuffer,
fillColorBuffer,
strokeColorBuffer,
pointBuffer,
shapeTexVBO,
curTexture = {width:0,height:0},
curTextureMode = p.IMAGE,
usingTexture = false,
textBuffer,
textureBuffer,
indexBuffer,
// Text alignment
horizontalTextAlignment = p.LEFT,
verticalTextAlignment = p.BASELINE,
baselineOffset = 0.2, // percent
// Pixels cache
originalContext,
proxyContext = null,
isContextReplaced = false,
setPixelsCached,
maxPixelsCached = 1000;
// Work-around for Minefield. using ctx.VERTEX_PROGRAM_POINT_SIZE
// in Minefield does nothing and does not report any errors.
var VERTEX_PROGRAM_POINT_SIZE = 0x8642;
var POINT_SMOOTH = 0x0B10;
// Get padding and border style widths for mouse offsets
var stylePaddingLeft, stylePaddingTop, styleBorderLeft, styleBorderTop;
if (document.defaultView && document.defaultView.getComputedStyle) {
stylePaddingLeft = parseInt(document.defaultView.getComputedStyle(curElement, null)['paddingLeft'], 10) || 0;
stylePaddingTop = parseInt(document.defaultView.getComputedStyle(curElement, null)['paddingTop'], 10) || 0;
styleBorderLeft = parseInt(document.defaultView.getComputedStyle(curElement, null)['borderLeftWidth'], 10) || 0;
styleBorderTop = parseInt(document.defaultView.getComputedStyle(curElement, null)['borderTopWidth'], 10) || 0;
}
// User can only have MAX_LIGHTS lights
var lightCount = 0;
//sphere stuff
var sphereDetailV = 0,
sphereDetailU = 0,
sphereX = [],
sphereY = [],
sphereZ = [],
sinLUT = new Array(p.SINCOS_LENGTH),
cosLUT = new Array(p.SINCOS_LENGTH),
sphereVerts,
sphereNorms;
// Camera defaults and settings
var cam,
cameraInv,
forwardTransform,
reverseTransform,
modelView,
modelViewInv,
userMatrixStack,
inverseCopy,
projection,
manipulatingCamera = false,
frustumMode = false,
cameraFOV = 60 * (Math.PI / 180),
cameraX = curElement.width / 2,
cameraY = curElement.height / 2,
cameraZ = cameraY / Math.tan(cameraFOV / 2),
cameraNear = cameraZ / 10,
cameraFar = cameraZ * 10,
cameraAspect = curElement.width / curElement.height;
var vertArray = [],
curveVertArray = [],
curveVertCount = 0,
isCurve = false,
isBezier = false,
firstVert = true;
//PShape stuff
var curShapeMode = p.CORNER;
var colors = {};
colors.aliceblue = "#f0f8ff";
colors.antiquewhite = "#faebd7";
colors.aqua = "#00ffff";
colors.aquamarine = "#7fffd4";
colors.azure = "#f0ffff";
colors.beige = "#f5f5dc";
colors.bisque = "#ffe4c4";
colors.black = "#000000";
colors.blanchedalmond = "#ffebcd";
colors.blue = "#0000ff";
colors.blueviolet = "#8a2be2";
colors.brown = "#a52a2a";
colors.burlywood = "#deb887";
colors.cadetblue = "#5f9ea0";
colors.chartreuse = "#7fff00";
colors.chocolate = "#d2691e";
colors.coral = "#ff7f50";
colors.cornflowerblue = "#6495ed";
colors.cornsilk = "#fff8dc";
colors.crimson = "#dc143c";
colors.cyan = "#00ffff";
colors.darkblue = "#00008b";
colors.darkcyan = "#008b8b";
colors.darkgoldenrod = "#b8860b";
colors.darkgray = "#a9a9a9";
colors.darkgreen = "#006400";
colors.darkkhaki = "#bdb76b";
colors.darkmagenta = "#8b008b";
colors.darkolivegreen = "#556b2f";
colors.darkorange = "#ff8c00";
colors.darkorchid = "#9932cc";
colors.darkred = "#8b0000";
colors.darksalmon = "#e9967a";
colors.darkseagreen = "#8fbc8f";
colors.darkslateblue = "#483d8b";
colors.darkslategray = "#2f4f4f";
colors.darkturquoise = "#00ced1";
colors.darkviolet = "#9400d3";
colors.deeppink = "#ff1493";
colors.deepskyblue = "#00bfff";
colors.dimgray = "#696969";
colors.dodgerblue = "#1e90ff";
colors.firebrick = "#b22222";
colors.floralwhite = "#fffaf0";
colors.forestgreen = "#228b22";
colors.fuchsia = "#ff00ff";
colors.gainsboro = "#dcdcdc";
colors.ghostwhite = "#f8f8ff";
colors.gold = "#ffd700";
colors.goldenrod = "#daa520";
colors.gray = "#808080";
colors.green = "#008000";
colors.greenyellow = "#adff2f";
colors.honeydew = "#f0fff0";
colors.hotpink = "#ff69b4";
colors.indianred = "#cd5c5c";
colors.indigo = "#4b0082";
colors.ivory = "#fffff0";
colors.khaki = "#f0e68c";
colors.lavender = "#e6e6fa";
colors.lavenderblush = "#fff0f5";
colors.lawngreen = "#7cfc00";
colors.lemonchiffon = "#fffacd";
colors.lightblue = "#add8e6";
colors.lightcoral = "#f08080";
colors.lightcyan = "#e0ffff";
colors.lightgoldenrodyellow = "#fafad2";
colors.lightgrey = "#d3d3d3";
colors.lightgreen = "#90ee90";
colors.lightpink = "#ffb6c1";
colors.lightsalmon = "#ffa07a";
colors.lightseagreen = "#20b2aa";
colors.lightskyblue = "#87cefa";
colors.lightslategray = "#778899";
colors.lightsteelblue = "#b0c4de";
colors.lightyellow = "#ffffe0";
colors.lime = "#00ff00";
colors.limegreen = "#32cd32";
colors.linen = "#faf0e6";
colors.magenta = "#ff00ff";
colors.maroon = "#800000";
colors.mediumaquamarine = "#66cdaa";
colors.mediumblue = "#0000cd";
colors.mediumorchid = "#ba55d3";
colors.mediumpurple = "#9370d8";
colors.mediumseagreen = "#3cb371";
colors.mediumslateblue = "#7b68ee";
colors.mediumspringgreen = "#00fa9a";
colors.mediumturquoise = "#48d1cc";
colors.mediumvioletred = "#c71585";
colors.midnightblue = "#191970";
colors.mintcream = "#f5fffa";
colors.mistyrose = "#ffe4e1";
colors.moccasin = "#ffe4b5";
colors.navajowhite = "#ffdead";
colors.navy = "#000080";
colors.oldlace = "#fdf5e6";
colors.olive = "#808000";
colors.olivedrab = "#6b8e23";
colors.orange = "#ffa500";
colors.orangered = "#ff4500";
colors.orchid = "#da70d6";
colors.palegoldenrod = "#eee8aa";
colors.palegreen = "#98fb98";
colors.paleturquoise = "#afeeee";
colors.palevioletred = "#d87093";
colors.papayawhip = "#ffefd5";
colors.peachpuff = "#ffdab9";
colors.peru = "#cd853f";
colors.pink = "#ffc0cb";
colors.plum = "#dda0dd";
colors.powderblue = "#b0e0e6";
colors.purple = "#800080";
colors.red = "#ff0000";
colors.rosybrown = "#bc8f8f";
colors.royalblue = "#4169e1";
colors.saddlebrown = "#8b4513";
colors.salmon = "#fa8072";
colors.sandybrown = "#f4a460";
colors.seagreen = "#2e8b57";
colors.seashell = "#fff5ee";
colors.sienna = "#a0522d";
colors.silver = "#c0c0c0";
colors.skyblue = "#87ceeb";
colors.slateblue = "#6a5acd";
colors.slategray = "#708090";
colors.snow = "#fffafa";
colors.springgreen = "#00ff7f";
colors.steelblue = "#4682b4";
colors.tan = "#d2b48c";
colors.teal = "#008080";
colors.thistle = "#d8bfd8";
colors.tomato = "#ff6347";
colors.turquoise = "#40e0d0";
colors.violet = "#ee82ee";
colors.wheat = "#f5deb3";
colors.white = "#ffffff";
colors.whitesmoke = "#f5f5f5";
colors.yellow = "#ffff00";
colors.yellowgreen = "#9acd32";
// Stores states for pushStyle() and popStyle().
var styleArray = new Array(0);
// Vertices are specified in a counter-clockwise order
// triangles are in this order: back, front, right, bottom, left, top
var boxVerts = [0.5, 0.5, -0.5, 0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5,
-0.5, 0.5, -0.5, 0.5, 0.5, -0.5, 0.5, 0.5, 0.5, -0.5, 0.5, 0.5,
-0.5, -0.5, 0.5, -0.5, -0.5, 0.5, 0.5, -0.5, 0.5, 0.5, 0.5, 0.5,
0.5, 0.5, -0.5, 0.5, 0.5, 0.5, 0.5, -0.5, 0.5, 0.5, -0.5, 0.5,
0.5, -0.5, -0.5, 0.5, 0.5, -0.5, 0.5, -0.5, -0.5, 0.5, -0.5, 0.5,
-0.5, -0.5, 0.5, -0.5, -0.5, 0.5, -0.5, -0.5, -0.5, 0.5, -0.5, -0.5,
-0.5, -0.5, -0.5, -0.5, -0.5, 0.5, -0.5, 0.5, 0.5, -0.5, 0.5, 0.5,
-0.5, 0.5, -0.5, -0.5, -0.5, -0.5, 0.5, 0.5, 0.5, 0.5, 0.5, -0.5,
-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 = [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 boxOutlineVerts = [0.5, 0.5, 0.5, 0.5, -0.5, 0.5, 0.5, 0.5, -0.5, 0.5, -0.5, -0.5,
-0.5, 0.5, -0.5, -0.5, -0.5, -0.5, -0.5, 0.5, 0.5, -0.5, -0.5, 0.5,
0.5, 0.5, 0.5, 0.5, 0.5, -0.5, 0.5, 0.5, -0.5, -0.5, 0.5, -0.5,
-0.5, 0.5, -0.5, -0.5, 0.5, 0.5, -0.5, 0.5, 0.5, 0.5, 0.5, 0.5,
0.5, -0.5, 0.5, 0.5, -0.5, -0.5, 0.5, -0.5, -0.5, -0.5, -0.5, -0.5,
-0.5, -0.5, -0.5, -0.5, -0.5, 0.5, -0.5, -0.5, 0.5, 0.5, -0.5, 0.5];
// These verts are used for the fill and stroke using TRIANGLE_FAN and LINE_LOOP
var rectVerts = [0,0,0, 0,1,0, 1,1,0, 1,0,0];
var rectNorms = [0,0,-1, 0,0,-1, 0,0,-1, 0,0,-1];
// Vertex shader for points and lines
var vShaderSrcUnlitShape =
"attribute vec3 aVertex;" +
"attribute vec4 aColor;" +
"uniform mat4 uView;" +
"uniform mat4 uProjection;" +
"void main(void) {" +
" gl_FrontColor = aColor;" +
" gl_Position = uProjection * uView * vec4(aVertex, 1.0);" +
"}";
var fShaderSrcUnlitShape =
"void main(void){" +
" gl_FragColor = gl_Color;" +
"}";
// Vertex shader for points and lines
var vertexShaderSource2D =
"attribute vec3 Vertex;" +
"attribute vec2 aTextureCoord;" +
"uniform vec4 color;" +
"uniform mat4 model;" +
"uniform mat4 view;" +
"uniform mat4 projection;" +
"uniform float pointSize;" +
"varying vec2 vTextureCoord;"+
"void main(void) {" +
" gl_PointSize = pointSize;" +
" gl_FrontColor = color;" +
" gl_Position = projection * view * model * vec4(Vertex, 1.0);" +
" vTextureCoord = aTextureCoord;" +
"}";
var fragmentShaderSource2D =
"varying vec2 vTextureCoord;"+
"uniform vec4 color;"+
"uniform sampler2D uSampler;"+
"uniform int picktype;"+
"void main(void){" +
" if(picktype==0){"+
" gl_FragColor = color;" +
" }else if(picktype==1){"+
" float alpha = texture2D(uSampler,vTextureCoord).a;"+
" gl_FragColor = vec4(color.rgb*alpha,alpha);\n"+
" }"+
"}";
// Vertex shader for boxes and spheres
var vertexShaderSource3D =
"attribute vec3 Vertex;" +
"attribute vec3 Normal;" +
"attribute vec4 aColor;" +
"attribute vec2 aTexture;" +
"varying vec2 vTexture;" +
"uniform vec4 color;" +
"uniform bool usingMat;" +
"uniform vec3 specular;" +
"uniform vec3 mat_emissive;" +
"uniform vec3 mat_ambient;" +
"uniform vec3 mat_specular;" +
"uniform float shininess;" +
"uniform mat4 model;" +
"uniform mat4 view;" +
"uniform mat4 projection;" +
"uniform mat4 normalTransform;" +
"uniform int lightCount;" +
"uniform vec3 falloff;" +
"struct Light {" +
" bool dummy;" +
" int type;" +
" vec3 color;" +
" vec3 position;" +
" vec3 direction;" +
" float angle;" +
" vec3 halfVector;" +
" float concentration;" +
"};" +
"uniform Light lights[8];" +
"void AmbientLight( inout vec3 totalAmbient, in vec3 ecPos, in Light light ) {" +
// Get the vector from the light to the vertex
// Get the distance from the current vector to the light position
" float d = length( light.position - ecPos );" +
" float attenuation = 1.0 / ( falloff[0] + ( falloff[1] * d ) + ( falloff[2] * d * d ));" + " totalAmbient += light.color * attenuation;" +
"}" +
"void DirectionalLight( inout vec3 col, in vec3 ecPos, inout vec3 spec, in vec3 vertNormal, in Light light ) {" +
" float powerfactor = 0.0;" +
" float nDotVP = max(0.0, dot( vertNormal, light.position ));" +
" float nDotVH = max(0.0, dot( vertNormal, normalize( light.position-ecPos )));" +
" if( nDotVP != 0.0 ){" +
" powerfactor = pow( nDotVH, shininess );" +
" }" +
" col += light.color * nDotVP;" +
" spec += specular * powerfactor;" +
"}" +
"void PointLight( inout vec3 col, inout vec3 spec, in vec3 vertNormal, in vec3 ecPos, in vec3 eye, in Light light ) {" +
" float powerfactor;" +
// Get the vector from the light to the vertex
" vec3 VP = light.position - ecPos;" +
// Get the distance from the current vector to the light position
" float d = length( VP ); " +
// Normalize the light ray so it can be used in the dot product operation.
" VP = normalize( VP );" +
" float attenuation = 1.0 / ( falloff[0] + ( falloff[1] * d ) + ( falloff[2] * d * d ));" +
" float nDotVP = max( 0.0, dot( vertNormal, VP ));" +
" vec3 halfVector = normalize( VP + eye );" +
" float nDotHV = max( 0.0, dot( vertNormal, halfVector ));" +
" if( nDotVP == 0.0) {" +
" powerfactor = 0.0;" +
" }" +
" else{" +
" powerfactor = pow( nDotHV, shininess );" +
" }" +
" spec += specular * powerfactor * attenuation;" +
" col += light.color * nDotVP * attenuation;" +
"}" +
/*
*/
"void SpotLight( inout vec3 col, inout vec3 spec, in vec3 vertNormal, in vec3 ecPos, in vec3 eye, in Light light ) {" +
" float spotAttenuation;" +
" float powerfactor;" +
// calculate the vector from the current vertex to the light.
" vec3 VP = light.position - ecPos; " +
" vec3 ldir = normalize( light.direction );" +
// get the distance from the spotlight and the vertex
" float d = length( VP );" +
" VP = normalize( VP );" +
" float attenuation = 1.0 / ( falloff[0] + ( falloff[1] * d ) + ( falloff[2] * d * d ) );" +
// dot product of the vector from vertex to light and light direction.
" float spotDot = dot( VP, ldir );" +
// if the vertex falls inside the cone
" if( spotDot < cos( light.angle ) ) {" +
" spotAttenuation = pow( spotDot, light.concentration );" +
" }" +
" else{" +
" spotAttenuation = 1.0;" +
" }" +
" attenuation *= spotAttenuation;" +
" float nDotVP = max( 0.0, dot( vertNormal, VP ));" +
" vec3 halfVector = normalize( VP + eye );" +
" float nDotHV = max( 0.0, dot( vertNormal, halfVector ));" +
" if( nDotVP == 0.0 ) {" +
" powerfactor = 0.0;" +
" }" +
" else {" +
" powerfactor = pow( nDotHV, shininess );" +
" }" +
" spec += specular * powerfactor * attenuation;" +
" col += light.color * nDotVP * attenuation;" +
"}" +
"void main(void) {" +
" vec3 finalAmbient = vec3( 0.0, 0.0, 0.0 );" +
" vec3 finalDiffuse = vec3( 0.0, 0.0, 0.0 );" +
" vec3 finalSpecular = vec3( 0.0, 0.0, 0.0 );" +
" vec4 col = color;" +
" if(color[0] == -1.0){" +
" col = aColor;" +
" }" +
" vec3 norm = vec3( normalTransform * vec4( Normal, 0.0 ) );" +
" vec4 ecPos4 = view * model * vec4(Vertex,1.0);" +
" vec3 ecPos = (vec3(ecPos4))/ecPos4.w;" +
" vec3 eye = vec3( 0.0, 0.0, 1.0 );" +
// If there were no lights this draw call, just use the
// assigned fill color of the shape and the specular value
" if( lightCount == 0 ) {" +
" gl_FrontColor = col + vec4(mat_specular,1.0);" +
" }" +
" else {" +
" for( int i = 0; i < lightCount; i++ ) {" +
" if( lights[i].type == 0 ) {" +
" AmbientLight( finalAmbient, ecPos, lights[i] );" +
" }" +
" else if( lights[i].type == 1 ) {" +
" DirectionalLight( finalDiffuse,ecPos, finalSpecular, norm, lights[i] );" +
" }" +
" else if( lights[i].type == 2 ) {" +
" PointLight( finalDiffuse, finalSpecular, norm, ecPos, eye, lights[i] );" +
" }" +
" else if( lights[i].type == 3 ) {" +
" SpotLight( finalDiffuse, finalSpecular, norm, ecPos, eye, lights[i] );" +
" }" +
" }" +
" if( usingMat == false ) {" +
" gl_FrontColor = vec4( " +
" vec3(col) * finalAmbient +" +
" vec3(col) * finalDiffuse +" +
" vec3(col) * finalSpecular," +
" col[3] );" +
" }" +
" else{" +
" gl_FrontColor = vec4( " +
" mat_emissive + " +
" (vec3(col) * mat_ambient * finalAmbient) + " +
" (vec3(col) * finalDiffuse) + " +
" (mat_specular * finalSpecular), " +
" col[3] );" +
" }" +
" }" +
" vTexture.xy = aTexture.xy;" +
" gl_Position = projection * view * model * vec4( Vertex, 1.0 );" +
"}";
var fragmentShaderSource3D =
"uniform sampler2D sampler;" +
"uniform bool usingTexture;" +
"varying vec2 vTexture;" +
// In Processing, when a texture is used, the fill color is ignored
"void main(void){" +
" if(usingTexture){" +
" gl_FragColor = vec4(texture2D(sampler, vTexture.xy));" +
" }"+
" else{" +
" gl_FragColor = vec4(gl_Color);" +
" }" +
"}";
////////////////////////////////////////////////////////////////////////////
// 3D Functions
////////////////////////////////////////////////////////////////////////////
/*
Sets the uniform variable 'varName' to the value specified by 'value'.
Before calling this function, make sure the correct program object
has been installed as part of the current rendering state.
On some systems, if the variable exists in the shader but isn't used,
the compiler will optimize it out and this function will fail.
*/
function uniformf(programObj, varName, varValue) {
var varLocation = curContext.getUniformLocation(programObj, varName);
// the variable won't be found if it was optimized out.
if (varLocation !== -1) {
if (varValue.length === 4) {
curContext.uniform4fv(varLocation, varValue);
} else if (varValue.length === 3) {
curContext.uniform3fv(varLocation, varValue);
} else if (varValue.length === 2) {
curContext.uniform2fv(varLocation, varValue);
} else {
curContext.uniform1f(varLocation, varValue);
}
}
}
function uniformi(programObj, varName, varValue) {
var varLocation = curContext.getUniformLocation(programObj, varName);
// the variable won't be found if it was optimized out.
if (varLocation !== -1) {
if (varValue.length === 4) {
curContext.uniform4iv(varLocation, varValue);
} else if (varValue.length === 3) {
curContext.uniform3iv(varLocation, varValue);
} else if (varValue.length === 2) {
curContext.uniform2iv(varLocation, varValue);
} else {
curContext.uniform1i(varLocation, varValue);
}
}
}
function vertexAttribPointer(programObj, varName, size, VBO) {
var varLocation = curContext.getAttribLocation(programObj, varName);
if (varLocation !== -1) {
curContext.bindBuffer(curContext.ARRAY_BUFFER, VBO);
curContext.vertexAttribPointer(varLocation, size, curContext.FLOAT, false, 0, 0);
curContext.enableVertexAttribArray(varLocation);
}
}
function disableVertexAttribPointer(programObj, varName){
var varLocation = curContext.getAttribLocation(programObj, varName);
if (varLocation !== -1) {
curContext.disableVertexAttribArray(varLocation);
}
}
function uniformMatrix(programObj, varName, transpose, matrix) {
var varLocation = curContext.getUniformLocation(programObj, varName);
// the variable won't be found if it was optimized out.
if (varLocation !== -1) {
if (matrix.length === 16) {
curContext.uniformMatrix4fv(varLocation, transpose, matrix);
} else if (matrix.length === 9) {
curContext.uniformMatrix3fv(varLocation, transpose, matrix);
} else {
curContext.uniformMatrix2fv(varLocation, transpose, matrix);
}
}
}
// Wrapper to easily deal with array names changes. TODO: Don't think we need this wrapper anymore consensus has been reached.
var newWebGLArray = function(data) {
return new WebGLFloatArray(data);
};
var imageModeCorner = function imageModeCorner(x, y, w, h, whAreSizes) {
return {
x: x,
y: y,
w: w,
h: h
};
};
var imageModeConvert = imageModeCorner;
var imageModeCorners = function imageModeCorners(x, y, w, h, whAreSizes) {
return {
x: x,
y: y,
w: whAreSizes ? w : w - x,
h: whAreSizes ? h : h - y
};
};
var imageModeCenter = function imageModeCenter(x, y, w, h, whAreSizes) {
return {
x: x - w / 2,
y: y - h / 2,
w: w,
h: h
};
};
var createProgramObject = function(curContext, vetexShaderSource, fragmentShaderSource) {
var vertexShaderObject = curContext.createShader(curContext.VERTEX_SHADER);
curContext.shaderSource(vertexShaderObject, vetexShaderSource);
curContext.compileShader(vertexShaderObject);
if (!curContext.getShaderParameter(vertexShaderObject, curContext.COMPILE_STATUS)) {
throw curContext.getShaderInfoLog(vertexShaderObject);
}
var fragmentShaderObject = curContext.createShader(curContext.FRAGMENT_SHADER);
curContext.shaderSource(fragmentShaderObject, fragmentShaderSource);
curContext.compileShader(fragmentShaderObject);
if (!curContext.getShaderParameter(fragmentShaderObject, curContext.COMPILE_STATUS)) {
throw curContext.getShaderInfoLog(fragmentShaderObject);
}
var programObject = curContext.createProgram();
curContext.attachShader(programObject, vertexShaderObject);
curContext.attachShader(programObject, fragmentShaderObject);
curContext.linkProgram(programObject);
if (!curContext.getProgramParameter(programObject, curContext.LINK_STATUS)) {
throw "Error linking shaders.";
}
return programObject;
};
////////////////////////////////////////////////////////////////////////////
// Char handling
////////////////////////////////////////////////////////////////////////////
var charMap = {};
var Char = p.Character = function Char(chr) {
if (typeof chr === 'string' && chr.length === 1) {
this.code = chr.charCodeAt(0);
} else {
this.code = NaN;
}
return (charMap[this.code] === undef) ? charMap[this.code] = this : charMap[this.code];
};
Char.prototype.toString = function() {
return String.fromCharCode(this.code);
};
Char.prototype.valueOf = function() {
return this.code;
};
////////////////////////////////////////////////////////////////////////////
// PShape
////////////////////////////////////////////////////////////////////////////
var PShape = p.PShape = function(family) {
this.family = family || p.GROUP;
this.visible = true;
this.style = true;
this.children = [];
this.nameTable = [];
this.params = [];
this.name = "";
this.image = null; //type PImage
this.matrix = null;
this.kind = null;
this.close = null;
this.width = null;
this.height = null;
this.parent = null;
/* methods */
this.isVisible = function(){
return this.visible;
};
this.setVisible = function (visible){
this.visible = visible;
};
this.disableStyle = function(){
this.style = false;
for(var i = 0; i < this.children.length; i++)
{
this.children[i].disableStyle();
}
};
this.enableStyle = function(){
this.style = true;
for(var i = 0; i < this.children.length; i++)
{
this.children[i].enableStyle();
}
};
this.getFamily = function(){
return this.family;
};
this.getWidth = function(){
return this.width;
};
this.getHeight = function(){
return this.height;
};
this.setName = function(name){
this.name = name;
};
this.getName = function(){
return this.name;
};
this.draw = function(){
if (this.visible) {
this.pre();
this.drawImpl();
this.post();
}
};
this.drawImpl = function(){
if (this.family === p.GROUP) {
this.drawGroup();
} else if (this.family === p.PRIMITIVE) {
this.drawPrimitive();
} else if (this.family === p.GEOMETRY) {
this.drawGeometry();
} else if (this.family === p.PATH) {
this.drawPath();
}
};
this.drawPath = function(){
if (this.vertices.length === 0) { return; }
p.beginShape();
var i;
if (this.vertexCodes.length === 0) { // each point is a simple vertex
if (this.vertices[0].length === 2) { // drawing 2D vertices
for (i = 0; i < this.vertices.length; i++) {
p.vertex(this.vertices[i][0], this.vertices[i][1]);
}
} else { // drawing 3D vertices
for (i = 0; i < this.vertices.length; i++) {
p.vertex(this.vertices[i][0], this.vertices[i][1], this.vertices[i][2]);
}
}
} else { // coded set of vertices
var index = 0;
var j;
if (this.vertices[0].length === 2) { // drawing a 2D path
for (j = 0; j < this.vertexCodes.length; j++) {
switch (this.vertexCodes[j]) {
case p.VERTEX:
p.vertex(this.vertices[index][0], this.vertices[index][1]);
if ( this.vertices[index]["moveTo"] === true) {
vertArray[vertArray.length-1]["moveTo"] = true;
} else if ( this.vertices[index]["moveTo"] === false) {
vertArray[vertArray.length-1]["moveTo"] = false;
}
p.breakShape = false;
index++;
break;
case p.BEZIER_VERTEX:
p.bezierVertex(this.vertices[index+0][0], this.vertices[index+0][1],
this.vertices[index+1][0], this.vertices[index+1][1],
this.vertices[index+2][0], this.vertices[index+2][1]);
index += 3;
break;
case p.CURVE_VERTEX:
p.curveVertex(this.vertices[index][0], this.vertices[index][1]);
index++;
break;
case p.BREAK:
p.breakShape = true;
break;
}
}
} else { // drawing a 3D path
for (j = 0; j < this.vertexCodes.length; j++) {
switch (this.vertexCodes[j]) {
case p.VERTEX:
p.vertex(this.vertices[index][0], this.vertices[index][1], this.vertices[index][2]);
if (this.vertices[index]["moveTo"] === true) {
vertArray[vertArray.length-1]["moveTo"] = true;
} else if (this.vertices[index]["moveTo"] === false) {
vertArray[vertArray.length-1]["moveTo"] = false;
}
p.breakShape = false;
break;
case p.BEZIER_VERTEX:
p.bezierVertex(this.vertices[index+0][0], this.vertices[index+0][1], this.vertices[index+0][2],
this.vertices[index+1][0], this.vertices[index+1][1], this.vertices[index+1][2],
this.vertices[index+2][0], this.vertices[index+2][1], this.vertices[index+2][2]);
index += 3;
break;
case p.CURVE_VERTEX:
p.curveVertex(this.vertices[index][0], this.vertices[index][1], this.vertices[index][2]);
index++;
break;
case p.BREAK:
p.breakShape = true;
break;
}
}
}
}
p.endShape(this.close ? p.CLOSE : p.OPEN);
};
this.drawGeometry = function() {
p.beginShape(this.kind);
var i;
if (this.style) {
for (i = 0; i < this.vertices.length; i++) {
p.vertex(this.vertices[i]);
}
} else {
for (i = 0; i < this.vertices.length; i++) {
var vert = this.vertices[i];
if (vert[2] === 0) {
p.vertex(vert[0], vert[1]);
} else {
p.vertex(vert[0], vert[1], vert[2]);
}
}
}
p.endShape();
};
this.drawGroup = function() {
for (var i = 0; i < this.children.length; i++) {
this.children[i].draw();
}
};
this.drawPrimitive = function() {
switch (this.kind) {
case p.POINT:
p.point(this.params[0], this.params[1]);
break;
case p.LINE:
if (this.params.length === 4) { // 2D
p.line(this.params[0], this.params[1],
this.params[2], this.params[3]);
} else { // 3D
p.line(this.params[0], this.params[1], this.params[2],
this.params[3], this.params[4], this.params[5]);
}
break;
case p.TRIANGLE:
p.triangle(this.params[0], this.params[1],
this.params[2], this.params[3],
this.params[4], this.params[5]);
break;
case p.QUAD:
p.quad(this.params[0], this.params[1],
this.params[2], this.params[3],
this.params[4], this.params[5],
this.params[6], this.params[7]);
break;
case p.RECT:
if (this.image !== null) {
p.imageMode(p.CORNER);
p.image(this.image, this.params[0], this.params[1], this.params[2], this.params[3]);
} else {
p.rectMode(p.CORNER);
p.rect(this.params[0], this.params[1], this.params[2], this.params[3]);
}
break;
case p.ELLIPSE:
p.ellipseMode(p.CORNER);
p.ellipse(this.params[0], this.params[1], this.params[2], this.params[3]);
break;
case p.ARC:
p.ellipseMode(p.CORNER);
p.arc(this.params[0], this.params[1], this.params[2], this.params[3], this.params[4], this.params[5]);
break;
case p.BOX:
if (this.params.length === 1) {
p.box(this.params[0]);
} else {
p.box(this.params[0], this.params[1], this.params[2]);
}
break;
case p.SPHERE:
p.sphere(this.params[0]);
break;
}
};
this.pre = function() {
if (this.matrix) {
p.pushMatrix();
//this.applyMatrix( this.matrix ); //applyMatrix missing
}
if (this.style) {
p.pushStyle();
this.styles();
}
};
this.post = function() {
if (this.matrix) {
p.popMatrix();
}
if (this.style) {
p.popStyle();
}
};
this.styles = function() {
if (this.stroke) {
p.stroke(this.strokeColor);
p.strokeWeight(this.strokeWeight);
p.strokeCap(this.strokeCap);
p.strokeJoin(this.strokeJoin);
} else {
p.noStroke();
}
if (this.fill) {
p.fill(this.fillColor);
} else {
p.noFill();
}
};
// return the PShape at the specific index from the children array or
// return the Phape from a parent shape specified by its name
this.getChild = function(child) {
if (typeof child === 'number') {
return this.children[child];
} else {
var found,
i;
if(child === "" || this.name === child){
return this;
} else {
if(this.nameTable.length > 0)
{
for(i = 0; i < this.nameTable.length || found; i++)
{
if(this.nameTable[i].getName === child) {
found = this.nameTable[i];
}
}
if (found) { return found; }
}
for(i = 0; i < this.children.lenth; i++)
{
found = this.children[i].getChild(child);
if(found) { return found; }
}
}
return null;
}
};
this.getChildCount = function () {
return this.children.length;
};
this.addChild = function( child ) {
this.children.push(child);
child.parent = this;
if (child.getName() !== null) {
this.addName(child.getName(), child);
}
};
this.addName = function(name, shape) {
if (this.parent !== null) {
this.parent.addName( name, shape );
} else {
this.nameTable.push( [name, shape] );
}
};
// findChild not in yet
this.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);
}
};
this.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();
}
};
this.rotateX = function(angle) {
this.rotate(angle, 1, 0, 0);
};
this.rotateY = function(angle) {
this.rotate(angle, 0, 1, 0);
};
this.rotateZ = function(angle) {
this.rotate(angle, 0, 0, 1);
};
this.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]);
}
};
this.scale = function() {
if(arguments.length === 2) {
t