| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| /* |
| * Processing.js - John Resig (http://ejohn.org/) |
| * MIT Licensed |
| * http://ejohn.org/blog/processingjs/ |
| * |
| * This is a port of the Processing Visualization Language. |
| * More information: http://processing.org/ |
| */ |
| |
| (function(){ |
| |
| this.Processing = function Processing( aElement, aCode ) { |
| if ( typeof aElement == "string" ) |
| aElement = document.getElementById( aElement ); |
| |
| var p = buildProcessing( aElement ); |
| |
| if ( aCode ) |
| p.init( aCode ); |
| |
| return p; |
| }; |
| |
| function log() { |
| try { |
| console.log.apply( console, arguments ); |
| } catch(e) { |
| try { |
| opera.postError.apply( opera, arguments ); |
| } catch(e){} |
| } |
| } |
| |
| var parse = Processing.parse = function parse( aCode, p ) { |
| // Angels weep at this parsing code :-( |
| |
| // Remove end-of-line comments |
| aCode = aCode.replace(/\/\/ .*\n/g, "\n"); |
| |
| // Weird parsing errors with % |
| aCode = aCode.replace(/([^\s])%([^\s])/g, "$1 % $2"); |
| |
| // Simple convert a function-like thing to function |
| aCode = aCode.replace(/(?:static )?(\w+ )(\w+)\s*(\([^\)]*\)\s*{)/g, function(all, type, name, args) { |
| if ( name == "if" || name == "for" || name == "while" ) { |
| return all; |
| } else { |
| return "Processing." + name + " = function " + name + args; |
| } |
| }); |
| |
| // Force .length() to be .length |
| aCode = aCode.replace(/\.length\(\)/g, ".length"); |
| |
| // foo( int foo, float bar ) |
| aCode = aCode.replace(/([\(,]\s*)(\w+)((?:\[\])+| )\s*(\w+\s*[\),])/g, "$1$4"); |
| aCode = aCode.replace(/([\(,]\s*)(\w+)((?:\[\])+| )\s*(\w+\s*[\),])/g, "$1$4"); |
| |
| // float[] foo = new float[5]; |
| aCode = aCode.replace(/new (\w+)((?:\[([^\]]*)\])+)/g, function(all, name, args) { |
| return "new ArrayList(" + args.slice(1,-1).split("][").join(", ") + ")"; |
| }); |
| |
| aCode = aCode.replace(/(?:static )?\w+\[\]\s*(\w+)\[?\]?\s*=\s*{.*?};/g, function(all) { |
| return all.replace(/{/g, "[").replace(/}/g, "]"); |
| }); |
| |
| // int|float foo; |
| var intFloat = /(\n\s*(?:int|float)(?:\[\])?(?:\s*|[^\(]*?,\s*))([a-z]\w*)(;|,)/i; |
| while ( intFloat.test(aCode) ) { |
| aCode = aCode.replace(new RegExp(intFloat), function(all, type, name, sep) { |
| return type + " " + name + " = 0" + sep; |
| }); |
| } |
| |
| // float foo = 5; |
| aCode = aCode.replace(/(?:static )?(\w+)((?:\[\])+| ) *(\w+)\[?\]?(\s*[=,;])/g, function(all, type, arr, name, sep) { |
| if ( type == "return" ) |
| return all; |
| else |
| return "var " + name + sep; |
| }); |
| |
| // Fix Array[] foo = {...} to [...] |
| aCode = aCode.replace(/=\s*{((.|\s)*?)};/g, function(all,data) { |
| return "= [" + data.replace(/{/g, "[").replace(/}/g, "]") + "]"; |
| }); |
| |
| // static { ... } blocks |
| aCode = aCode.replace(/static\s*{((.|\n)*?)}/g, function(all, init) { |
| // Convert the static definitons to variable assignments |
| //return init.replace(/\((.*?)\)/g, " = $1"); |
| return init; |
| }); |
| |
| // super() is a reserved word |
| aCode = aCode.replace(/super\(/g, "superMethod("); |
| |
| var classes = ["int", "float", "boolean", "string"]; |
| |
| function ClassReplace(all, name, extend, vars, last) { |
| classes.push( name ); |
| |
| var static = ""; |
| |
| vars = vars.replace(/final\s+var\s+(\w+\s*=\s*.*?;)/g, function(all,set) { |
| static += " " + name + "." + set; |
| return ""; |
| }); |
| |
| // Move arguments up from constructor and wrap contents with |
| // a with(this), and unwrap constructor |
| return "function " + name + "() {with(this){\n " + |
| (extend ? "var __self=this;function superMethod(){extendClass(__self,arguments," + extend + ");}\n" : "") + |
| // Replace var foo = 0; with this.foo = 0; |
| // and force var foo; to become this.foo = null; |
| vars |
| .replace(/,\s?/g, ";\n this.") |
| .replace(/\b(var |final |public )+\s*/g, "this.") |
| .replace(/this.(\w+);/g, "this.$1 = null;") + |
| (extend ? "extendClass(this, " + extend + ");\n" : "") + |
| "<CLASS " + name + " " + static + ">" + (typeof last == "string" ? last : name + "("); |
| } |
| |
| var matchClasses = /(?:public |abstract |static )*class (\w+)\s*(?:extends\s*(\w+)\s*)?{\s*((?:.|\n)*?)\b\1\s*\(/g; |
| var matchNoCon = /(?:public |abstract |static )*class (\w+)\s*(?:extends\s*(\w+)\s*)?{\s*((?:.|\n)*?)(Processing)/g; |
| |
| aCode = aCode.replace(matchClasses, ClassReplace); |
| aCode = aCode.replace(matchNoCon, ClassReplace); |
| |
| var matchClass = /<CLASS (\w+) (.*?)>/, m; |
| |
| while ( (m = aCode.match( matchClass )) ) { |
| var left = RegExp.leftContext, |
| allRest = RegExp.rightContext, |
| rest = nextBrace(allRest), |
| className = m[1], |
| staticVars = m[2] || ""; |
| |
| allRest = allRest.slice( rest.length + 1 ); |
| |
| rest = rest.replace(new RegExp("\\b" + className + "\\(([^\\)]*?)\\)\\s*{", "g"), function(all, args) { |
| args = args.split(/,\s*?/); |
| |
| if ( args[0].match(/^\s*$/) ) |
| args.shift(); |
| |
| var fn = "if ( arguments.length == " + args.length + " ) {\n"; |
| |
| for ( var i = 0; i < args.length; i++ ) { |
| fn += " var " + args[i] + " = arguments[" + i + "];\n"; |
| } |
| |
| return fn; |
| }); |
| |
| // Fix class method names |
| // this.collide = function() { ... } |
| // and add closing } for with(this) ... |
| rest = rest.replace(/(?:public )?Processing.\w+ = function (\w+)\((.*?)\)/g, function(all, name, args) { |
| return "ADDMETHOD(this, '" + name + "', function(" + args + ")"; |
| }); |
| |
| var matchMethod = /ADDMETHOD([\s\S]*?{)/, mc; |
| var methods = ""; |
| |
| while ( (mc = rest.match( matchMethod )) ) { |
| var prev = RegExp.leftContext, |
| allNext = RegExp.rightContext, |
| next = nextBrace(allNext); |
| |
| methods += "addMethod" + mc[1] + next + "});" |
| |
| rest = prev + allNext.slice( next.length + 1 ); |
| } |
| |
| rest = methods + rest; |
| |
| aCode = left + rest + "\n}}" + staticVars + allRest; |
| } |
| |
| // Do some tidying up, where necessary |
| aCode = aCode.replace(/Processing.\w+ = function addMethod/g, "addMethod"); |
| |
| function nextBrace( right ) { |
| var rest = right; |
| var position = 0; |
| var leftCount = 1, rightCount = 0; |
| |
| while ( leftCount != rightCount ) { |
| var nextLeft = rest.indexOf("{"); |
| var nextRight = rest.indexOf("}"); |
| |
| if ( nextLeft < nextRight && nextLeft != -1 ) { |
| leftCount++; |
| rest = rest.slice( nextLeft + 1 ); |
| position += nextLeft + 1; |
| } else { |
| rightCount++; |
| rest = rest.slice( nextRight + 1 ); |
| position += nextRight + 1; |
| } |
| } |
| |
| return right.slice(0, position - 1); |
| } |
| |
| // Handle (int) Casting |
| aCode = aCode.replace(/\(int\)/g, "0|"); |
| |
| // Remove Casting |
| aCode = aCode.replace(new RegExp("\\((" + classes.join("|") + ")(\\[\\])?\\)", "g"), ""); |
| |
| // Convert 3.0f to just 3.0 |
| aCode = aCode.replace(/(\d+)f/g, "$1"); |
| |
| // Force numbers to exist |
| //aCode = aCode.replace(/([^.])(\w+)\s*\+=/g, "$1$2 = ($2||0) +"); |
| |
| // Force characters-as-bytes to work |
| aCode = aCode.replace(/('[a-zA-Z0-9]')/g, "$1.charCodeAt(0)"); |
| |
| // Convert #aaaaaa into color |
| aCode = aCode.replace(/#([a-f0-9]{6})/ig, function(m, hex){ |
| var num = toNumbers(hex); |
| return "color(" + num[0] + "," + num[1] + "," + num[2] + ")"; |
| }); |
| |
| function toNumbers( str ){ |
| var ret = []; |
| str.replace(/(..)/g, function(str){ |
| ret.push( parseInt( str, 16 ) ); |
| }); |
| return ret; |
| } |
| |
| log(aCode); |
| |
| return aCode; |
| }; |
| |
| function buildProcessing( curElement ){ |
| |
| var p = {}; |
| |
| // init |
| p.PI = Math.PI; |
| p.TWO_PI = 2 * p.PI; |
| p.HALF_PI = p.PI / 2; |
| p.P3D = 3; |
| p.CORNER = 0; |
| p.RADIUS = 1; |
| p.CENTER_RADIUS = 1; |
| p.CENTER = 2; |
| p.POLYGON = 2; |
| p.QUADS = 5; |
| p.TRIANGLES = 6; |
| p.POINTS = 7; |
| p.LINES = 8; |
| p.TRIANGLE_STRIP = 9; |
| p.TRIANGLE_FAN = 4; |
| p.QUAD_STRIP = 3; |
| p.CORNERS = 10; |
| p.CLOSE = true; |
| p.RGB = 1; |
| p.HSB = 2; |
| |
| // mouseButton constants: values adjusted to come directly from e.which |
| p.LEFT = 1; |
| p.CENTER = 2; |
| p.RIGHT = 3; |
| |
| // "Private" variables used to maintain state |
| var curContext = curElement.getContext("2d"); |
| var doFill = true; |
| var doStroke = true; |
| var loopStarted = false; |
| var hasBackground = false; |
| var doLoop = true; |
| var looping = 0; |
| var curRectMode = p.CORNER; |
| var curEllipseMode = p.CENTER; |
| var inSetup = false; |
| var inDraw = false; |
| var curBackground = "rgba(204,204,204,1)"; |
| var curFrameRate = 1000; |
| var curShape = p.POLYGON; |
| var curShapeCount = 0; |
| var curvePoints = []; |
| var curTightness = 0; |
| var opacityRange = 255; |
| var redRange = 255; |
| var greenRange = 255; |
| var blueRange = 255; |
| var pathOpen = false; |
| var mousePressed = false; |
| var keyPressed = false; |
| var firstX, firstY, secondX, secondY, prevX, prevY; |
| var curColorMode = p.RGB; |
| var curTint = -1; |
| var curTextSize = 12; |
| var curTextFont = "Arial"; |
| var getLoaded = false; |
| var start = (new Date).getTime(); |
| |
| // Global vars for tracking mouse position |
| p.pmouseX = 0; |
| p.pmouseY = 0; |
| p.mouseX = 0; |
| p.mouseY = 0; |
| p.mouseButton = 0; |
| |
| // Will be replaced by the user, most likely |
| p.mouseDragged = undefined; |
| p.mouseMoved = undefined; |
| p.mousePressed = undefined; |
| p.mouseReleased = undefined; |
| p.keyPressed = undefined; |
| p.keyReleased = undefined; |
| p.draw = undefined; |
| p.setup = undefined; |
| |
| // The height/width of the canvas |
| p.width = curElement.width - 0; |
| p.height = curElement.height - 0; |
| |
| // The current animation frame |
| p.frameCount = 0; |
| |
| // In case I ever need to do HSV conversion: |
| // http://srufaculty.sru.edu/david.dailey/javascript/js/5rml.js |
| p.color = function color( aValue1, aValue2, aValue3, aValue4 ) { |
| var aColor = ""; |
| |
| if ( arguments.length == 3 ) { |
| aColor = p.color( aValue1, aValue2, aValue3, opacityRange ); |
| } else if ( arguments.length == 4 ) { |
| var a = aValue4 / opacityRange; |
| a = isNaN(a) ? 1 : a; |
| |
| if ( curColorMode == p.HSB ) { |
| var rgb = HSBtoRGB(aValue1, aValue2, aValue3); |
| var r = rgb[0], g = rgb[1], b = rgb[2]; |
| } else { |
| var r = getColor(aValue1, redRange); |
| var g = getColor(aValue2, greenRange); |
| var b = getColor(aValue3, blueRange); |
| } |
| |
| aColor = "rgba(" + r + "," + g + "," + b + "," + a + ")"; |
| } else if ( typeof aValue1 == "string" ) { |
| aColor = aValue1; |
| |
| if ( arguments.length == 2 ) { |
| var c = aColor.split(","); |
| c[3] = (aValue2 / opacityRange) + ")"; |
| aColor = c.join(","); |
| } |
| } else if ( arguments.length == 2 ) { |
| aColor = p.color( aValue1, aValue1, aValue1, aValue2 ); |
| } else if ( typeof aValue1 == "number" ) { |
| aColor = p.color( aValue1, aValue1, aValue1, opacityRange ); |
| } else { |
| aColor = p.color( redRange, greenRange, blueRange, opacityRange ); |
| } |
| |
| // HSB conversion function from Mootools, MIT Licensed |
| function HSBtoRGB(h, s, b) { |
| h = (h / redRange) * 360; |
| s = (s / greenRange) * 100; |
| b = (b / blueRange) * 100; |
| var hue = h % 360; |
| var f = hue % 60; |
| var br = Math.round(b / 100 * 255); |
| var p = Math.round((b * (100 - s)) / 10000 * 255); |
| var q = Math.round((b * (6000 - s * f)) / 600000 * 255); |
| var t = Math.round((b * (6000 - s * (60 - f))) / 600000 * 255); |
| switch (Math.floor(hue / 60)){ |
| case 0: return [br, t, p]; |
| case 1: return [q, br, p]; |
| case 2: return [p, br, t]; |
| case 3: return [p, q, br]; |
| case 4: return [t, p, br]; |
| case 5: return [br, p, q]; |
| } |
| } |
| |
| function getColor( aValue, range ) { |
| return Math.round(255 * (aValue / range)); |
| } |
| |
| return aColor; |
| } |
| |
| p.nf = function( num, pad ) { |
| var str = "" + num; |
| while ( pad - str.length ) |
| str = "0" + str; |
| return str; |
| }; |
| |
| p.AniSprite = function( prefix, frames ) { |
| this.images = []; |
| this.pos = 0; |
| |
| for ( var i = 0; i < frames; i++ ) { |
| this.images.push( prefix + p.nf( i, ("" + frames).length ) + ".gif" ); |
| } |
| |
| this.display = function( x, y ) { |
| p.image( this.images[ this.pos ], x, y ); |
| |
| if ( ++this.pos >= frames ) |
| this.pos = 0; |
| }; |
| |
| this.getWidth = function() { |
| return getImage(this.images[0]).width; |
| }; |
| |
| this.getHeight = function() { |
| return getImage(this.images[0]).height; |
| }; |
| }; |
| |
| function buildImageObject( obj ) { |
| var pixels = obj.data; |
| var data = p.createImage( obj.width, obj.height ); |
| |
| if ( data.__defineGetter__ && data.__lookupGetter__ && !data.__lookupGetter__("pixels") ) { |
| var pixelsDone; |
| data.__defineGetter__("pixels", function() { |
| if ( pixelsDone ) |
| return pixelsDone; |
| |
| pixelsDone = []; |
| |
| for ( var i = 0; i < pixels.length; i += 4 ) { |
| pixelsDone.push( p.color(pixels[i], pixels[i+1], pixels[i+2], pixels[i+3]) ); |
| } |
| |
| return pixelsDone; |
| }); |
| } else { |
| data.pixels = []; |
| |
| for ( var i = 0; i < pixels.length; i += 4 ) { |
| data.pixels.push( p.color(pixels[i], pixels[i+1], pixels[i+2], pixels[i+3]) ); |
| } |
| } |
| |
| return data; |
| } |
| |
| p.createImage = function createImage( w, h, mode ) { |
| var data = {}; |
| data.width = w; |
| data.height = h; |
| data.data = []; |
| |
| if ( curContext.createImageData ) { |
| data = curContext.createImageData( w, h ); |
| } |
| |
| data.pixels = new Array( w * h ); |
| data.get = function(x,y) { |
| return this.pixels[w*y+x]; |
| }; |
| data._mask = null; |
| data.mask = function(img) { |
| this._mask = img; |
| }; |
| data.loadPixels = function(){}; |
| data.updatePixels = function(){}; |
| |
| return data; |
| }; |
| |
| p.createGraphics = function createGraphics( w, h ) { |
| var canvas = document.createElement("canvas"); |
| var ret = buildProcessing( canvas ); |
| ret.size( w, h ); |
| ret.canvas = canvas; |
| return ret; |
| }; |
| |
| p.beginDraw = function beginDraw(){}; |
| |
| p.endDraw = function endDraw(){}; |
| |
| p.tint = function tint( rgb, a ) { |
| curTint = a; |
| }; |
| |
| function getImage( img ) { |
| if ( typeof img == "string" ) { |
| return document.getElementById(img); |
| } |
| |
| if ( img.img || img.canvas ) { |
| return img.img || img.canvas; |
| } |
| |
| for ( var i = 0, l = img.pixels.length; i < l; i++ ) { |
| var pos = i * 4; |
| var c = (img.pixels[i] || "rgba(0,0,0,1)").slice(5,-1).split(","); |
| img.data[pos] = parseInt(c[0]); |
| img.data[pos+1] = parseInt(c[1]); |
| img.data[pos+2] = parseInt(c[2]); |
| img.data[pos+3] = parseFloat(c[3]) * 100; |
| } |
| |
| var canvas = document.createElement("canvas") |
| canvas.width = img.width; |
| canvas.height = img.height; |
| var context = canvas.getContext("2d"); |
| context.putImageData( img, 0, 0 ); |
| |
| img.canvas = canvas; |
| |
| return canvas; |
| } |
| |
| p.image = function image( img, x, y, w, h ) { |
| x = x || 0; |
| y = y || 0; |
| |
| var obj = getImage(img); |
| |
| if ( curTint >= 0 ) { |
| var oldAlpha = curContext.globalAlpha; |
| curContext.globalAlpha = curTint / opacityRange; |
| } |
| |
| if ( arguments.length == 3 ) { |
| curContext.drawImage( obj, x, y ); |
| } else { |
| curContext.drawImage( obj, x, y, w, h ); |
| } |
| |
| if ( curTint >= 0 ) { |
| curContext.globalAlpha = oldAlpha; |
| } |
| |
| if ( img._mask ) { |
| var oldComposite = curContext.globalCompositeOperation; |
| curContext.globalCompositeOperation = "darker"; |
| p.image( img._mask, x, y ); |
| curContext.globalCompositeOperation = oldComposite; |
| } |
| }; |
| |
| p.exit = function exit() { |
| clearInterval(looping); |
| }; |
| |
| p.save = function save( file ){}; |
| |
| p.loadImage = function loadImage( file ) { |
| var img = document.getElementById(file); |
| if ( !img ) |
| return; |
| |
| var h = img.height, w = img.width; |
| |
| var canvas = document.createElement("canvas"); |
| canvas.width = w; |
| canvas.height = h; |
| var context = canvas.getContext("2d"); |
| |
| context.drawImage( img, 0, 0 ); |
| var data = buildImageObject( context.getImageData( 0, 0, w, h ) ); |
| data.img = img; |
| return data; |
| }; |
| |
| p.loadFont = function loadFont( name ) { |
| return { |
| name: name, |
| width: function( str ) { |
| if ( curContext.mozMeasureText ) { |
| curContext.save(); |
| curContext.mozTextStyle = curTextSize + "px " + curTextFont.name; |
| var width = curContext.mozMeasureText( typeof str == "number" ? |
| String.fromCharCode( str ) : |
| str) / curTextSize; |
| curContext.restore(); |
| return width; |
| } else { |
| return 0; |
| } |
| } |
| }; |
| }; |
| |
| p.textFont = function textFont( name, size ) { |
| curTextFont = name; |
| p.textSize( size ); |
| }; |
| |
| p.textSize = function textSize( size ) { |
| if ( size ) { |
| curTextSize = size; |
| } |
| }; |
| |
| p.textAlign = function textAlign(){}; |
| |
| p.text = function text( str, x, y ) { |
| if ( str && curContext.mozDrawText ) { |
| curContext.save(); |
| curContext.mozTextStyle = curTextSize + "px " + curTextFont.name; |
| curContext.translate(x, y); |
| curContext.mozDrawText( typeof str == "number" ? |
| String.fromCharCode( str ) : |
| str ); |
| curContext.restore(); |
| } |
| }; |
| |
| p.char = function char( key ) { |
| return key; |
| }; |
| |
| p.println = function println(){}; |
| |
| p.map = function map( value, istart, istop, ostart, ostop ) { |
| return ostart + (ostop - ostart) * ((value - istart) / (istop - istart)); |
| }; |
| |
| String.prototype.replaceAll = function(re, replace) { |
| return this.replace(new RegExp(re, "g"), replace); |
| }; |
| |
| p.Point = function Point( x, y ) { |
| this.x = x; |
| this.y = y; |
| this.copy = function() { |
| return new Point( x, y ); |
| } |
| }; |
| |
| p.Random = function() { |
| var haveNextNextGaussian = false; |
| var nextNextGaussian; |
| |
| this.nextGaussian = function() { |
| if (haveNextNextGaussian) { |
| haveNextNextGaussian = false; |
| |
| return nextNextGaussian; |
| } else { |
| var v1, v2, s; |
| do { |
| v1 = 2 * p.random(1) - 1; // between -1.0 and 1.0 |
| v2 = 2 * p.random(1) - 1; // between -1.0 and 1.0 |
| s = v1 * v1 + v2 * v2; |
| } while (s >= 1 || s == 0); |
| var multiplier = Math.sqrt(-2 * Math.log(s)/s); |
| nextNextGaussian = v2 * multiplier; |
| haveNextNextGaussian = true; |
| |
| return v1 * multiplier; |
| } |
| }; |
| }; |
| |
| p.ArrayList = function ArrayList( size, size2, size3 ) { |
| var array = new Array( 0 | size ); |
| |
| if ( size2 ) { |
| for ( var i = 0; i < size; i++ ) { |
| array[i] = []; |
| |
| for ( var j = 0; j < size2; j++ ) { |
| var a = array[i][j] = size3 ? new Array( size3 ) : 0; |
| for ( var k = 0; k < size3; k++ ) { |
| a[k] = 0; |
| } |
| } |
| } |
| } else { |
| for ( var i = 0; i < size; i++ ) { |
| array[i] = 0; |
| } |
| } |
| |
| array.size = function() { |
| return this.length; |
| }; |
| array.get = function( i ) { |
| return this[ i ]; |
| }; |
| array.remove = function( i ) { |
| return this.splice( i, 1 ); |
| }; |
| array.add = function( item ) { |
| return this.push( item ); |
| }; |
| array.clone = function() { |
| var a = new ArrayList( size ); |
| for ( var i = 0; i < size; i++ ) { |
| a[ i ] = this[ i ]; |
| } |
| return a; |
| }; |
| array.isEmpty = function() { |
| return !this.length; |
| }; |
| array.clear = function() { |
| this.length = 0; |
| }; |
| |
| return array; |
| }; |
| |
| p.colorMode = function colorMode( mode, range1, range2, range3, range4 ) { |
| curColorMode = mode; |
| |
| if ( arguments.length == 1) { |
| redRange = mode == p.HSB ? 360 : 255; |
| } |
| |
| if ( arguments.length >= 4 ) { |
| redRange = range1; |
| greenRange = range2; |
| blueRange = range3; |
| } |
| |
| if ( arguments.length == 5 ) { |
| opacityRange = range4; |
| } |
| |
| if ( arguments.length == 2 ) { |
| p.colorMode( mode, range1, range1, range1, range1 ); |
| } |
| }; |
| |
| p.beginShape = function beginShape( type ) { |
| curShape = type; |
| curShapeCount = 0; |
| curvePoints = []; |
| }; |
| |
| p.endShape = function endShape( close ) { |
| if ( curShapeCount != 0 ) { |
| if ( close || doFill ) |
| curContext.lineTo( firstX, firstY ); |
| |
| if ( doFill ) |
| curContext.fill(); |
| |
| if ( doStroke ) |
| curContext.stroke(); |
| |
| curContext.closePath(); |
| curShapeCount = 0; |
| pathOpen = false; |
| } |
| |
| if ( pathOpen ) { |
| if ( doFill ) |
| curContext.fill(); |
| |
| if ( doStroke ) |
| curContext.stroke(); |
| |
| curContext.closePath(); |
| curShapeCount = 0; |
| pathOpen = false; |
| } |
| }; |
| |
| p.vertex = function vertex( x, y, x2, y2, x3, y3 ) { |
| if ( curShapeCount == 0 && curShape != p.POINTS ) { |
| pathOpen = true; |
| curContext.beginPath(); |
| curContext.moveTo( x, y ); |
| firstX = x; |
| firstY = y; |
| } else { |
| if ( curShape == p.POINTS ) { |
| p.point( x, y ); |
| } else if ( arguments.length == 2 ) { |
| if ( curShape != p.QUAD_STRIP || curShapeCount != 2 ) |
| curContext.lineTo( x, y ); |
| |
| if ( curShape == p.TRIANGLE_STRIP ) { |
| if ( curShapeCount == 2 ) { |
| // finish shape |
| p.endShape(p.CLOSE); |
| pathOpen = true; |
| curContext.beginPath(); |
| |
| // redraw last line to start next shape |
| curContext.moveTo( prevX, prevY ); |
| curContext.lineTo( x, y ); |
| curShapeCount = 1; |
| } |
| firstX = prevX; |
| firstY = prevY; |
| } |
| |
| if ( curShape == p.TRIANGLE_FAN && curShapeCount == 2 ) { |
| // finish shape |
| p.endShape(p.CLOSE); |
| pathOpen = true; |
| curContext.beginPath(); |
| |
| // redraw last line to start next shape |
| curContext.moveTo( firstX, firstY ); |
| curContext.lineTo( x, y ); |
| curShapeCount = 1; |
| } |
| |
| if ( curShape == p.QUAD_STRIP && curShapeCount == 3 ) { |
| // finish shape |
| curContext.lineTo( prevX, prevY ); |
| p.endShape(p.CLOSE); |
| pathOpen = true; |
| curContext.beginPath(); |
| |
| // redraw lines to start next shape |
| curContext.moveTo( prevX, prevY ); |
| curContext.lineTo( x, y ); |
| curShapeCount = 1; |
| } |
| |
| if ( curShape == p.QUAD_STRIP) { |
| firstX = secondX; |
| firstY = secondY; |
| secondX = prevX; |
| secondY = prevY; |
| } |
| } else if ( arguments.length == 4 ) { |
| if ( curShapeCount > 1 ) { |
| curContext.moveTo( prevX, prevY ); |
| curContext.quadraticCurveTo( firstX, firstY, x, y ); |
| curShapeCount = 1; |
| } |
| } else if ( arguments.length == 6 ) { |
| curContext.bezierCurveTo( x, y, x2, y2, x3, y3 ); |
| curShapeCount = -1; |
| } |
| } |
| |
| prevX = x; |
| prevY = y; |
| curShapeCount++; |
| |
| if ( curShape == p.LINES && curShapeCount == 2 || |
| (curShape == p.TRIANGLES) && curShapeCount == 3 || |
| (curShape == p.QUADS) && curShapeCount == 4 ) { |
| p.endShape(p.CLOSE); |
| } |
| }; |
| |
| p.curveVertex = function( x, y, x2, y2 ) { |
| if ( curvePoints.length < 3 ) { |
| curvePoints.push([x,y]); |
| } else { |
| var b = [], s = 1 - curTightness; |
| |
| /* |
| * Matrix to convert from Catmull-Rom to cubic Bezier |
| * where t = curTightness |
| * |0 1 0 0 | |
| * |(t-1)/6 1 (1-t)/6 0 | |
| * |0 (1-t)/6 1 (t-1)/6 | |
| * |0 0 0 0 | |
| */ |
| |
| curvePoints.push([x,y]); |
| |
| b[0] = [curvePoints[1][0],curvePoints[1][1]]; |
| b[1] = [curvePoints[1][0]+(s*curvePoints[2][0]-s*curvePoints[0][0])/6,curvePoints[1][1]+(s*curvePoints[2][1]-s*curvePoints[0][1])/6]; |
| b[2] = [curvePoints[2][0]+(s*curvePoints[1][0]-s*curvePoints[3][0])/6,curvePoints[2][1]+(s*curvePoints[1][1]-s*curvePoints[3][1])/6]; |
| b[3] = [curvePoints[2][0],curvePoints[2][1]]; |
| |
| if ( !pathOpen ) { |
| p.vertex( b[0][0], b[0][1] ); |
| } else { |
| curShapeCount = 1; |
| } |
| |
| p.vertex( b[1][0], b[1][1], b[2][0], b[2][1], b[3][0], b[3][1] ); |
| curvePoints.shift(); |
| } |
| }; |
| |
| p.curveTightness = function( tightness ) { |
| curTightness = tightness; |
| }; |
| |
| p.bezierVertex = p.vertex; |
| |
| p.rectMode = function rectMode( aRectMode ) { |
| curRectMode = aRectMode; |
| }; |
| |
| p.imageMode = function(){}; |
| |
| p.ellipseMode = function ellipseMode( aEllipseMode ) { |
| curEllipseMode = aEllipseMode; |
| }; |
| |
| p.dist = function dist( x1, y1, x2, y2 ) { |
| return Math.sqrt( Math.pow( x2 - x1, 2 ) + Math.pow( y2 - y1, 2 ) ); |
| }; |
| |
| p.year = function year() { |
| return (new Date).getYear() + 1900; |
| }; |
| |
| p.month = function month() { |
| return (new Date).getMonth(); |
| }; |
| |
| p.day = function day() { |
| return (new Date).getDay(); |
| }; |
| |
| p.hour = function hour() { |
| return (new Date).getHours(); |
| }; |
| |
| p.minute = function minute() { |
| return (new Date).getMinutes(); |
| }; |
| |
| p.second = function second() { |
| return (new Date).getSeconds(); |
| }; |
| |
| p.millis = function millis() { |
| return (new Date).getTime() - start; |
| }; |
| |
| p.ortho = function ortho(){}; |
| |
| p.translate = function translate( x, y ) { |
| curContext.translate( x, y ); |
| }; |
| |
| p.scale = function scale( x, y ) { |
| curContext.scale( x, y || x ); |
| }; |
| |
| p.rotate = function rotate( aAngle ) { |
| curContext.rotate( aAngle ); |
| }; |
| |
| p.pushMatrix = function pushMatrix() { |
| curContext.save(); |
| }; |
| |
| p.popMatrix = function popMatrix() { |
| curContext.restore(); |
| }; |
| |
| p.redraw = function redraw() { |
| if ( hasBackground ) { |
| p.background(); |
| } |
| |
| p.frameCount++; |
| |
| inDraw = true; |
| p.pushMatrix(); |
| p.draw(); |
| p.popMatrix(); |
| inDraw = false; |
| }; |
| |
| p.loop = function loop() { |
| if ( loopStarted ) |
| return; |
| |
| looping = setInterval(function() { |
| try { |
| p.redraw(); |
| } |
| catch(e) { |
| clearInterval( looping ); |
| throw e; |
| } |
| }, 1000 / curFrameRate ); |
| |
| loopStarted = true; |
| }; |
| |
| p.frameRate = function frameRate( aRate ) { |
| curFrameRate = aRate; |
| }; |
| |
| p.background = function background( img ) { |
| if ( arguments.length ) { |
| if ( img && img.img ) { |
| curBackground = img; |
| } else { |
| curBackground = p.color.apply( this, arguments ); |
| } |
| } |
| |
| |
| if ( curBackground.img ) { |
| p.image( curBackground, 0, 0 ); |
| } else { |
| var oldFill = curContext.fillStyle; |
| curContext.fillStyle = curBackground + ""; |
| curContext.fillRect( 0, 0, p.width, p.height ); |
| curContext.fillStyle = oldFill; |
| } |
| }; |
| |
| p.sq = function sq( aNumber ) { |
| return aNumber * aNumber; |
| }; |
| |
| p.sqrt = function sqrt( aNumber ) { |
| return Math.sqrt( aNumber ); |
| }; |
| |
| p.int = function int( aNumber ) { |
| return Math.floor( aNumber ); |
| }; |
| |
| p.min = function min( aNumber, aNumber2 ) { |
| return Math.min( aNumber, aNumber2 ); |
| }; |
| |
| p.max = function max( aNumber, aNumber2 ) { |
| return Math.max( aNumber, aNumber2 ); |
| }; |
| |
| p.ceil = function ceil( aNumber ) { |
| return Math.ceil( aNumber ); |
| }; |
| |
| p.round = function round( aNumber ) { |
| return Math.round( aNumber ); |
| }; |
| |
| p.floor = function floor( aNumber ) { |
| return Math.floor( aNumber ); |
| }; |
| |
| p.float = function float( aNumber ) { |
| return typeof aNumber == "string" ? |
| p.float( aNumber.charCodeAt(0) ) : |
| parseFloat( aNumber ); |
| }; |
| |
| p.byte = function byte( aNumber ) { |
| return aNumber || 0; |
| }; |
| |
| p.random = function random( aMin, aMax ) { |
| return arguments.length == 2 ? |
| aMin + (Math.random() * (aMax - aMin)) : |
| Math.random() * aMin; |
| }; |
| |
| // From: http://freespace.virgin.net/hugo.elias/models/m_perlin.htm |
| p.noise = function( x, y, z ) { |
| return arguments.length >= 2 ? |
| PerlinNoise_2D( x, y ) : |
| PerlinNoise_2D( x, x ); |
| }; |
| |
| function Noise(x, y) { |
| var n = x + y * 57; |
| n = (n<<13) ^ n; |
| return Math.abs(1.0 - (((n * ((n * n * 15731) + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0)); |
| }; |
| |
| function SmoothedNoise(x, y) { |
| var corners = ( Noise(x-1, y-1)+Noise(x+1, y-1)+Noise(x-1, y+1)+Noise(x+1, y+1) ) / 16; |
| var sides = ( Noise(x-1, y) +Noise(x+1, y) +Noise(x, y-1) +Noise(x, y+1) ) / 8; |
| var center = Noise(x, y) / 4; |
| return corners + sides + center; |
| }; |
| |
| function InterpolatedNoise(x, y) { |
| var integer_X = Math.floor(x); |
| var fractional_X = x - integer_X; |
| |
| var integer_Y = Math.floor(y); |
| var fractional_Y = y - integer_Y; |
| |
| var v1 = SmoothedNoise(integer_X, integer_Y); |
| var v2 = SmoothedNoise(integer_X + 1, integer_Y); |
| var v3 = SmoothedNoise(integer_X, integer_Y + 1); |
| var v4 = SmoothedNoise(integer_X + 1, integer_Y + 1); |
| |
| var i1 = Interpolate(v1 , v2 , fractional_X); |
| var i2 = Interpolate(v3 , v4 , fractional_X); |
| |
| return Interpolate(i1 , i2 , fractional_Y); |
| } |
| |
| function PerlinNoise_2D(x, y) { |
| var total = 0; |
| var p = 0.25; |
| var n = 3; |
| |
| for ( var i = 0; i <= n; i++ ) { |
| var frequency = Math.pow(2, i); |
| var amplitude = Math.pow(p, i); |
| |
| total = total + InterpolatedNoise(x * frequency, y * frequency) * amplitude; |
| } |
| |
| return total; |
| } |
| |
| function Interpolate(a, b, x) { |
| var ft = x * p.PI; |
| var f = (1 - p.cos(ft)) * .5; |
| return a*(1-f) + b*f; |
| } |
| |
| p.red = function( aColor ) { |
| return parseInt(aColor.slice(5)); |
| }; |
| |
| p.green = function( aColor ) { |
| return parseInt(aColor.split(",")[1]); |
| }; |
| |
| p.blue = function( aColor ) { |
| return parseInt(aColor.split(",")[2]); |
| }; |
| |
| p.alpha = function( aColor ) { |
| return parseInt(aColor.split(",")[3]); |
| }; |
| |
| p.abs = function abs( aNumber ) { |
| return Math.abs( aNumber ); |
| }; |
| |
| p.cos = function cos( aNumber ) { |
| return Math.cos( aNumber ); |
| }; |
| |
| p.sin = function sin( aNumber ) { |
| return Math.sin( aNumber ); |
| }; |
| |
| p.pow = function pow( aNumber, aExponent ) { |
| return Math.pow( aNumber, aExponent ); |
| }; |
| |
| p.constrain = function constrain( aNumber, aMin, aMax ) { |
| return Math.min( Math.max( aNumber, aMin ), aMax ); |
| }; |
| |
| p.sqrt = function sqrt( aNumber ) { |
| return Math.sqrt( aNumber ); |
| }; |
| |
| p.atan2 = function atan2( aNumber, aNumber2 ) { |
| return Math.atan2( aNumber, aNumber2 ); |
| }; |
| |
| p.radians = function radians( aAngle ) { |
| return ( aAngle / 180 ) * p.PI; |
| }; |
| |
| p.size = function size( aWidth, aHeight ) { |
| var fillStyle = curContext.fillStyle; |
| var strokeStyle = curContext.strokeStyle; |
| |
| curElement.width = p.width = aWidth; |
| curElement.height = p.height = aHeight; |
| |
| curContext.fillStyle = fillStyle; |
| curContext.strokeStyle = strokeStyle; |
| }; |
| |
| p.noStroke = function noStroke() { |
| doStroke = false; |
| }; |
| |
| p.noFill = function noFill() { |
| doFill = false; |
| }; |
| |
| p.smooth = function smooth(){}; |
| |
| p.noLoop = function noLoop() { |
| doLoop = false; |
| }; |
| |
| p.fill = function fill() { |
| doFill = true; |
| curContext.fillStyle = p.color.apply( this, arguments ); |
| }; |
| |
| p.stroke = function stroke() { |
| doStroke = true; |
| curContext.strokeStyle = p.color.apply( this, arguments ); |
| }; |
| |
| p.strokeWeight = function strokeWeight( w ) { |
| curContext.lineWidth = w; |
| }; |
| |
| p.point = function point( x, y ) { |
| var oldFill = curContext.fillStyle; |
| curContext.fillStyle = curContext.strokeStyle; |
| curContext.fillRect( Math.round( x ), Math.round( y ), 1, 1 ); |
| curContext.fillStyle = oldFill; |
| }; |
| |
| p.get = function get( x, y ) { |
| if ( arguments.length == 0 ) { |
| var c = p.createGraphics( p.width, p.height ); |
| c.image( curContext, 0, 0 ); |
| return c; |
| } |
| |
| if ( !getLoaded ) { |
| getLoaded = buildImageObject( curContext.getImageData(0, 0, p.width, p.height) ); |
| } |
| |
| return getLoaded.get( x, y ); |
| }; |
| |
| p.set = function set( x, y, obj ) { |
| if ( obj && obj.img ) { |
| p.image( obj, x, y ); |
| } else { |
| var oldFill = curContext.fillStyle; |
| var color = obj; |
| curContext.fillStyle = color; |
| curContext.fillRect( Math.round( x ), Math.round( y ), 1, 1 ); |
| curContext.fillStyle = oldFill; |
| } |
| }; |
| |
| p.arc = function arc( x, y, width, height, start, stop ) { |
| if ( width <= 0 ) |
| return; |
| |
| if ( curEllipseMode == p.CORNER ) { |
| x += width / 2; |
| y += height / 2; |
| } |
| |
| curContext.beginPath(); |
| |
| curContext.moveTo( x, y ); |
| curContext.arc( x, y, curEllipseMode == p.CENTER_RADIUS ? width : width/2, start, stop, false ); |
| |
| if ( doFill ) |
| curContext.fill(); |
| |
| if ( doStroke ) |
| curContext.stroke(); |
| |
| curContext.closePath(); |
| }; |
| |
| p.line = function line( x1, y1, x2, y2 ) { |
| curContext.lineCap = "round"; |
| curContext.beginPath(); |
| |
| curContext.moveTo( x1 || 0, y1 || 0 ); |
| curContext.lineTo( x2 || 0, y2 || 0 ); |
| |
| curContext.stroke(); |
| |
| curContext.closePath(); |
| }; |
| |
| p.bezier = function bezier( x1, y1, x2, y2, x3, y3, x4, y4 ) { |
| curContext.lineCap = "butt"; |
| curContext.beginPath(); |
| |
| curContext.moveTo( x1, y1 ); |
| curContext.bezierCurveTo( x2, y2, x3, y3, x4, y4 ); |
| |
| curContext.stroke(); |
| |
| curContext.closePath(); |
| }; |
| |
| p.triangle = function triangle( x1, y1, x2, y2, x3, y3 ) { |
| p.beginShape(); |
| p.vertex( x1, y1 ); |
| p.vertex( x2, y2 ); |
| p.vertex( x3, y3 ); |
| p.endShape(); |
| }; |
| |
| p.quad = function quad( x1, y1, x2, y2, x3, y3, x4, y4 ) { |
| p.beginShape(); |
| p.vertex( x1, y1 ); |
| p.vertex( x2, y2 ); |
| p.vertex( x3, y3 ); |
| p.vertex( x4, y4 ); |
| p.endShape(); |
| }; |
| |
| p.rect = function rect( x, y, width, height ) { |
| if ( width == 0 && height == 0 ) |
| return; |
| |
| curContext.beginPath(); |
| |
| var offsetStart = 0; |
| var offsetEnd = 0; |
| |
| if ( curRectMode == p.CORNERS ) { |
| width -= x; |
| height -= y; |
| } |
| |
| if ( curRectMode == p.RADIUS ) { |
| width *= 2; |
| height *= 2; |
| } |
| |
| if ( curRectMode == p.CENTER || curRectMode == p.RADIUS ) { |
| x -= width / 2; |
| y -= height / 2; |
| } |
| |
| curContext.rect( |
| Math.round( x ) - offsetStart, |
| Math.round( y ) - offsetStart, |
| Math.round( width ) + offsetEnd, |
| Math.round( height ) + offsetEnd |
| ); |
| |
| if ( doFill ) |
| curContext.fill(); |
| |
| if ( doStroke ) |
| curContext.stroke(); |
| |
| curContext.closePath(); |
| }; |
| |
| p.ellipse = function ellipse( x, y, width, height ) { |
| x = x || 0; |
| y = y || 0; |
| |
| if ( width <= 0 && height <= 0 ) |
| return; |
| |
| curContext.beginPath(); |
| |
| if ( curEllipseMode == p.RADIUS ) { |
| width *= 2; |
| height *= 2; |
| } |
| |
| var offsetStart = 0; |
| |
| // Shortcut for drawing a circle |
| if ( width == height ) |
| curContext.arc( x - offsetStart, y - offsetStart, width / 2, 0, Math.PI * 2, false ); |
| |
| if ( doFill ) |
| curContext.fill(); |
| |
| if ( doStroke ) |
| curContext.stroke(); |
| |
| curContext.closePath(); |
| }; |
| |
| p.link = function( href, target ) { |
| window.location = href; |
| }; |
| |
| p.loadPixels = function() { |
| p.pixels = buildImageObject( curContext.getImageData(0, 0, p.width, p.height) ).pixels; |
| }; |
| |
| p.updatePixels = function() { |
| var colors = /(\d+),(\d+),(\d+),(\d+)/; |
| var pixels = {}; |
| pixels.width = p.width; |
| pixels.height = p.height; |
| pixels.data = []; |
| |
| if ( curContext.createImageData ) { |
| pixels = curContext.createImageData( p.width, p.height ); |
| } |
| |
| var data = pixels.data; |
| var pos = 0; |
| |
| for ( var i = 0, l = p.pixels.length; i < l; i++ ) { |
| var c = (p.pixels[i] || "rgba(0,0,0,1)").match(colors); |
| data[pos] = parseInt(c[1]); |
| data[pos+1] = parseInt(c[2]); |
| data[pos+2] = parseInt(c[3]); |
| data[pos+3] = parseFloat(c[4]) * 100; |
| pos += 4; |
| } |
| |
| curContext.putImageData(pixels, 0, 0); |
| }; |
| |
| p.extendClass = function extendClass( obj, args, fn ) { |
| if ( arguments.length == 3 ) { |
| fn.apply( obj, args ); |
| } else { |
| args.call( obj ); |
| } |
| }; |
| |
| p.addMethod = function addMethod( object, name, fn ) { |
| if ( object[ name ] ) { |
| var args = fn.length; |
| |
| var oldfn = object[ name ]; |
| object[ name ] = function() { |
| if ( arguments.length == args ) |
| return fn.apply( this, arguments ); |
| else |
| return oldfn.apply( this, arguments ); |
| }; |
| } else { |
| object[ name ] = fn; |
| } |
| }; |
| |
| p.init = function init(code){ |
| p.stroke( 0 ); |
| p.fill( 255 ); |
| |
| // Canvas has trouble rendering single pixel stuff on whole-pixel |
| // counts, so we slightly offset it (this is super lame). |
| curContext.translate( 0.5, 0.5 ); |
| |
| if ( code ) { |
| (function(Processing){with (p){ |
| eval(parse(code, p)); |
| }})(p); |
| } |
| |
| if ( p.setup ) { |
| inSetup = true; |
| p.setup(); |
| } |
| |
| inSetup = false; |
| |
| if ( p.draw ) { |
| if ( !doLoop ) { |
| p.redraw(); |
| } else { |
| p.loop(); |
| } |
| } |
| |
| attach( curElement, "mousemove", function(e) { |
| var scrollX = window.scrollX != null ? window.scrollX : window.pageXOffset; |
| var scrollY = window.scrollY != null ? window.scrollY : window.pageYOffset; |
| p.pmouseX = p.mouseX; |
| p.pmouseY = p.mouseY; |
| p.mouseX = e.clientX - curElement.offsetLeft + scrollX; |
| p.mouseY = e.clientY - curElement.offsetTop + scrollY; |
| |
| if ( p.mouseMoved ) { |
| p.mouseMoved(); |
| } |
| |
| if ( mousePressed && p.mouseDragged ) { |
| p.mouseDragged(); |
| } |
| }); |
| |
| attach( curElement, "mousedown", function(e) { |
| mousePressed = true; |
| p.mouseButton = e.which; |
| |
| if ( typeof p.mousePressed == "function" ) { |
| p.mousePressed(); |
| } else { |
| p.mousePressed = true; |
| } |
| }); |
| |
| attach( curElement, "contextmenu", function(e) { |
| e.preventDefault(); |
| e.stopPropagation(); |
| }); |
| |
| attach( curElement, "mouseup", function(e) { |
| mousePressed = false; |
| |
| if ( typeof p.mousePressed != "function" ) { |
| p.mousePressed = false; |
| } |
| |
| if ( p.mouseReleased ) { |
| p.mouseReleased(); |
| } |
| }); |
| |
| attach( document, "keydown", function(e) { |
| keyPressed = true; |
| |
| p.key = e.keyCode + 32; |
| |
| if ( e.shiftKey ) { |
| p.key = String.fromCharCode(p.key).toUpperCase().charCodeAt(0); |
| } |
| |
| if ( typeof p.keyPressed == "function" ) { |
| p.keyPressed(); |
| } else { |
| p.keyPressed = true; |
| } |
| }); |
| |
| attach( document, "keyup", function(e) { |
| keyPressed = false; |
| |
| if ( typeof p.keyPressed != "function" ) { |
| p.keyPressed = false; |
| } |
| |
| if ( p.keyReleased ) { |
| p.keyReleased(); |
| } |
| }); |
| |
| function attach(elem, type, fn) { |
| if ( elem.addEventListener ) |
| elem.addEventListener( type, fn, false ); |
| else |
| elem.attachEvent( "on" + type, fn ); |
| } |
| }; |
| |
| return p; |
| } |
| |
| })(); |
| |
| |