annotate www/oi/js/processing.js @ 101:52e44ee1c791 tip master

enabled all scores in autostart script
author Rob Canning <rc@kiben.net>
date Tue, 21 Apr 2015 16:20:57 +0100
parents d802954248a7
children
rev   line source
rob@100 1 ;(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
rob@100 2 // build script for generating processing.js
rob@100 3
rob@100 4 var Browser = {
rob@100 5 isDomPresent: true,
rob@100 6 navigator: navigator,
rob@100 7 window: window,
rob@100 8 document: document,
rob@100 9 ajax: function(url) {
rob@100 10 var xhr = new XMLHttpRequest();
rob@100 11 xhr.open("GET", url, false);
rob@100 12 if (xhr.overrideMimeType) {
rob@100 13 xhr.overrideMimeType("text/plain");
rob@100 14 }
rob@100 15 xhr.setRequestHeader("If-Modified-Since", "Fri, 01 Jan 1960 00:00:00 GMT");
rob@100 16 xhr.send(null);
rob@100 17 // failed request?
rob@100 18 if (xhr.status !== 200 && xhr.status !== 0) { throw ("XMLHttpRequest failed, status code " + xhr.status); }
rob@100 19 return xhr.responseText;
rob@100 20 }
rob@100 21 };
rob@100 22
rob@100 23 window.Processing = require('./src/')(Browser);
rob@100 24
rob@100 25 },{"./src/":27}],2:[function(require,module,exports){
rob@100 26 module.exports={
rob@100 27 "name": "Processing.js",
rob@100 28 "version": "1.4.8",
rob@100 29 "dependencies": {
rob@100 30 "argv": "~0.0.2",
rob@100 31 "browserify": "~2.18.1",
rob@100 32 "express": "~3.3.3",
rob@100 33 "node-minify": "~0.7.3",
rob@100 34 "nunjucks": "~0.1.9",
rob@100 35 "open": "0.0.3"
rob@100 36 },
rob@100 37 "devDependencies": {
rob@100 38 "grunt": "~0.4.1",
rob@100 39 "grunt-cli": "~0.1.8",
rob@100 40 "grunt-contrib-jshint": "~0.4.3"
rob@100 41 }
rob@100 42 }
rob@100 43
rob@100 44 },{}],3:[function(require,module,exports){
rob@100 45 /**
rob@100 46 * A ObjectIterator is an iterator wrapper for objects. If passed object contains
rob@100 47 * the iterator method, the object instance will be replaced by the result returned by
rob@100 48 * this method call. If passed object is an array, the ObjectIterator instance iterates
rob@100 49 * through its items.
rob@100 50 *
rob@100 51 * @param {Object} obj The object to be iterated.
rob@100 52 */
rob@100 53 module.exports = function ObjectIterator(obj) {
rob@100 54 if (obj instanceof Array) {
rob@100 55 // iterate through array items
rob@100 56 var index = -1;
rob@100 57 this.hasNext = function() {
rob@100 58 return ++index < obj.length;
rob@100 59 };
rob@100 60 this.next = function() {
rob@100 61 return obj[index];
rob@100 62 };
rob@100 63 } else if (obj.iterator instanceof Function) {
rob@100 64 return obj.iterator();
rob@100 65 } else {
rob@100 66 throw "Unable to iterate: " + obj;
rob@100 67 }
rob@100 68 };
rob@100 69
rob@100 70 },{}],4:[function(require,module,exports){
rob@100 71 /**
rob@100 72 * Processing.js environment constants
rob@100 73 */
rob@100 74 module.exports = {
rob@100 75 X: 0,
rob@100 76 Y: 1,
rob@100 77 Z: 2,
rob@100 78
rob@100 79 R: 3,
rob@100 80 G: 4,
rob@100 81 B: 5,
rob@100 82 A: 6,
rob@100 83
rob@100 84 U: 7,
rob@100 85 V: 8,
rob@100 86
rob@100 87 NX: 9,
rob@100 88 NY: 10,
rob@100 89 NZ: 11,
rob@100 90
rob@100 91 EDGE: 12,
rob@100 92
rob@100 93 // Stroke
rob@100 94 SR: 13,
rob@100 95 SG: 14,
rob@100 96 SB: 15,
rob@100 97 SA: 16,
rob@100 98
rob@100 99 SW: 17,
rob@100 100
rob@100 101 // Transformations (2D and 3D)
rob@100 102 TX: 18,
rob@100 103 TY: 19,
rob@100 104 TZ: 20,
rob@100 105
rob@100 106 VX: 21,
rob@100 107 VY: 22,
rob@100 108 VZ: 23,
rob@100 109 VW: 24,
rob@100 110
rob@100 111 // Material properties
rob@100 112 AR: 25,
rob@100 113 AG: 26,
rob@100 114 AB: 27,
rob@100 115
rob@100 116 DR: 3,
rob@100 117 DG: 4,
rob@100 118 DB: 5,
rob@100 119 DA: 6,
rob@100 120
rob@100 121 SPR: 28,
rob@100 122 SPG: 29,
rob@100 123 SPB: 30,
rob@100 124
rob@100 125 SHINE: 31,
rob@100 126
rob@100 127 ER: 32,
rob@100 128 EG: 33,
rob@100 129 EB: 34,
rob@100 130
rob@100 131 BEEN_LIT: 35,
rob@100 132
rob@100 133 VERTEX_FIELD_COUNT: 36,
rob@100 134
rob@100 135 // Renderers
rob@100 136 P2D: 1,
rob@100 137 JAVA2D: 1,
rob@100 138 WEBGL: 2,
rob@100 139 P3D: 2,
rob@100 140 OPENGL: 2,
rob@100 141 PDF: 0,
rob@100 142 DXF: 0,
rob@100 143
rob@100 144 // Platform IDs
rob@100 145 OTHER: 0,
rob@100 146 WINDOWS: 1,
rob@100 147 MAXOSX: 2,
rob@100 148 LINUX: 3,
rob@100 149
rob@100 150 EPSILON: 0.0001,
rob@100 151
rob@100 152 MAX_FLOAT: 3.4028235e+38,
rob@100 153 MIN_FLOAT: -3.4028235e+38,
rob@100 154 MAX_INT: 2147483647,
rob@100 155 MIN_INT: -2147483648,
rob@100 156
rob@100 157 PI: Math.PI,
rob@100 158 TWO_PI: 2 * Math.PI,
rob@100 159 TAU: 2 * Math.PI,
rob@100 160 HALF_PI: Math.PI / 2,
rob@100 161 THIRD_PI: Math.PI / 3,
rob@100 162 QUARTER_PI: Math.PI / 4,
rob@100 163
rob@100 164 DEG_TO_RAD: Math.PI / 180,
rob@100 165 RAD_TO_DEG: 180 / Math.PI,
rob@100 166
rob@100 167 WHITESPACE: " \t\n\r\f\u00A0",
rob@100 168
rob@100 169 // Color modes
rob@100 170 RGB: 1,
rob@100 171 ARGB: 2,
rob@100 172 HSB: 3,
rob@100 173 ALPHA: 4,
rob@100 174 CMYK: 5,
rob@100 175
rob@100 176 // Image file types
rob@100 177 TIFF: 0,
rob@100 178 TARGA: 1,
rob@100 179 JPEG: 2,
rob@100 180 GIF: 3,
rob@100 181
rob@100 182 // Filter/convert types
rob@100 183 BLUR: 11,
rob@100 184 GRAY: 12,
rob@100 185 INVERT: 13,
rob@100 186 OPAQUE: 14,
rob@100 187 POSTERIZE: 15,
rob@100 188 THRESHOLD: 16,
rob@100 189 ERODE: 17,
rob@100 190 DILATE: 18,
rob@100 191
rob@100 192 // Blend modes
rob@100 193 REPLACE: 0,
rob@100 194 BLEND: 1 << 0,
rob@100 195 ADD: 1 << 1,
rob@100 196 SUBTRACT: 1 << 2,
rob@100 197 LIGHTEST: 1 << 3,
rob@100 198 DARKEST: 1 << 4,
rob@100 199 DIFFERENCE: 1 << 5,
rob@100 200 EXCLUSION: 1 << 6,
rob@100 201 MULTIPLY: 1 << 7,
rob@100 202 SCREEN: 1 << 8,
rob@100 203 OVERLAY: 1 << 9,
rob@100 204 HARD_LIGHT: 1 << 10,
rob@100 205 SOFT_LIGHT: 1 << 11,
rob@100 206 DODGE: 1 << 12,
rob@100 207 BURN: 1 << 13,
rob@100 208
rob@100 209 // Color component bit masks
rob@100 210 ALPHA_MASK: 0xff000000,
rob@100 211 RED_MASK: 0x00ff0000,
rob@100 212 GREEN_MASK: 0x0000ff00,
rob@100 213 BLUE_MASK: 0x000000ff,
rob@100 214
rob@100 215 // Projection matrices
rob@100 216 CUSTOM: 0,
rob@100 217 ORTHOGRAPHIC: 2,
rob@100 218 PERSPECTIVE: 3,
rob@100 219
rob@100 220 // Shapes
rob@100 221 POINT: 2,
rob@100 222 POINTS: 2,
rob@100 223 LINE: 4,
rob@100 224 LINES: 4,
rob@100 225 TRIANGLE: 8,
rob@100 226 TRIANGLES: 9,
rob@100 227 TRIANGLE_STRIP: 10,
rob@100 228 TRIANGLE_FAN: 11,
rob@100 229 QUAD: 16,
rob@100 230 QUADS: 16,
rob@100 231 QUAD_STRIP: 17,
rob@100 232 POLYGON: 20,
rob@100 233 PATH: 21,
rob@100 234 RECT: 30,
rob@100 235 ELLIPSE: 31,
rob@100 236 ARC: 32,
rob@100 237 SPHERE: 40,
rob@100 238 BOX: 41,
rob@100 239
rob@100 240 GROUP: 0,
rob@100 241 PRIMITIVE: 1,
rob@100 242 //PATH: 21, // shared with Shape PATH
rob@100 243 GEOMETRY: 3,
rob@100 244
rob@100 245 // Shape Vertex
rob@100 246 VERTEX: 0,
rob@100 247 BEZIER_VERTEX: 1,
rob@100 248 CURVE_VERTEX: 2,
rob@100 249 BREAK: 3,
rob@100 250 CLOSESHAPE: 4,
rob@100 251
rob@100 252 // Shape closing modes
rob@100 253 OPEN: 1,
rob@100 254 CLOSE: 2,
rob@100 255
rob@100 256 // Shape drawing modes
rob@100 257 CORNER: 0, // Draw mode convention to use (x, y) to (width, height)
rob@100 258 CORNERS: 1, // Draw mode convention to use (x1, y1) to (x2, y2) coordinates
rob@100 259 RADIUS: 2, // Draw mode from the center, and using the radius
rob@100 260 CENTER_RADIUS: 2, // Deprecated! Use RADIUS instead
rob@100 261 CENTER: 3, // Draw from the center, using second pair of values as the diameter
rob@100 262 DIAMETER: 3, // Synonym for the CENTER constant. Draw from the center
rob@100 263 CENTER_DIAMETER: 3, // Deprecated! Use DIAMETER instead
rob@100 264
rob@100 265 // Text vertical alignment modes
rob@100 266 BASELINE: 0, // Default vertical alignment for text placement
rob@100 267 TOP: 101, // Align text to the top
rob@100 268 BOTTOM: 102, // Align text from the bottom, using the baseline
rob@100 269
rob@100 270 // UV Texture coordinate modes
rob@100 271 NORMAL: 1,
rob@100 272 NORMALIZED: 1,
rob@100 273 IMAGE: 2,
rob@100 274
rob@100 275 // Text placement modes
rob@100 276 MODEL: 4,
rob@100 277 SHAPE: 5,
rob@100 278
rob@100 279 // Stroke modes
rob@100 280 SQUARE: 'butt',
rob@100 281 ROUND: 'round',
rob@100 282 PROJECT: 'square',
rob@100 283 MITER: 'miter',
rob@100 284 BEVEL: 'bevel',
rob@100 285
rob@100 286 // Lighting modes
rob@100 287 AMBIENT: 0,
rob@100 288 DIRECTIONAL: 1,
rob@100 289 //POINT: 2, Shared with Shape constant
rob@100 290 SPOT: 3,
rob@100 291
rob@100 292 // Key constants
rob@100 293
rob@100 294 // Both key and keyCode will be equal to these values
rob@100 295 BACKSPACE: 8,
rob@100 296 TAB: 9,
rob@100 297 ENTER: 10,
rob@100 298 RETURN: 13,
rob@100 299 ESC: 27,
rob@100 300 DELETE: 127,
rob@100 301 CODED: 0xffff,
rob@100 302
rob@100 303 // p.key will be CODED and p.keyCode will be this value
rob@100 304 SHIFT: 16,
rob@100 305 CONTROL: 17,
rob@100 306 ALT: 18,
rob@100 307 CAPSLK: 20,
rob@100 308 PGUP: 33,
rob@100 309 PGDN: 34,
rob@100 310 END: 35,
rob@100 311 HOME: 36,
rob@100 312 LEFT: 37,
rob@100 313 UP: 38,
rob@100 314 RIGHT: 39,
rob@100 315 DOWN: 40,
rob@100 316 F1: 112,
rob@100 317 F2: 113,
rob@100 318 F3: 114,
rob@100 319 F4: 115,
rob@100 320 F5: 116,
rob@100 321 F6: 117,
rob@100 322 F7: 118,
rob@100 323 F8: 119,
rob@100 324 F9: 120,
rob@100 325 F10: 121,
rob@100 326 F11: 122,
rob@100 327 F12: 123,
rob@100 328 NUMLK: 144,
rob@100 329 META: 157,
rob@100 330 INSERT: 155,
rob@100 331
rob@100 332 // Cursor types
rob@100 333 ARROW: 'default',
rob@100 334 CROSS: 'crosshair',
rob@100 335 HAND: 'pointer',
rob@100 336 MOVE: 'move',
rob@100 337 TEXT: 'text',
rob@100 338 WAIT: 'wait',
rob@100 339 NOCURSOR: "url('data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=='), auto",
rob@100 340
rob@100 341 // Hints
rob@100 342 DISABLE_OPENGL_2X_SMOOTH: 1,
rob@100 343 ENABLE_OPENGL_2X_SMOOTH: -1,
rob@100 344 ENABLE_OPENGL_4X_SMOOTH: 2,
rob@100 345 ENABLE_NATIVE_FONTS: 3,
rob@100 346 DISABLE_DEPTH_TEST: 4,
rob@100 347 ENABLE_DEPTH_TEST: -4,
rob@100 348 ENABLE_DEPTH_SORT: 5,
rob@100 349 DISABLE_DEPTH_SORT: -5,
rob@100 350 DISABLE_OPENGL_ERROR_REPORT: 6,
rob@100 351 ENABLE_OPENGL_ERROR_REPORT: -6,
rob@100 352 ENABLE_ACCURATE_TEXTURES: 7,
rob@100 353 DISABLE_ACCURATE_TEXTURES: -7,
rob@100 354 HINT_COUNT: 10,
rob@100 355
rob@100 356 // PJS defined constants
rob@100 357 SINCOS_LENGTH: 720, // every half degree
rob@100 358 PRECISIONB: 15, // fixed point precision is limited to 15 bits!!
rob@100 359 PRECISIONF: 1 << 15,
rob@100 360 PREC_MAXVAL: (1 << 15) - 1,
rob@100 361 PREC_ALPHA_SHIFT: 24 - 15,
rob@100 362 PREC_RED_SHIFT: 16 - 15,
rob@100 363 NORMAL_MODE_AUTO: 0,
rob@100 364 NORMAL_MODE_SHAPE: 1,
rob@100 365 NORMAL_MODE_VERTEX: 2,
rob@100 366 MAX_LIGHTS: 8
rob@100 367 };
rob@100 368
rob@100 369 },{}],5:[function(require,module,exports){
rob@100 370 /**
rob@100 371 * Processing.js default scope
rob@100 372 */
rob@100 373 module.exports = function(options) {
rob@100 374
rob@100 375 // Building defaultScope. Changing of the prototype protects
rob@100 376 // internal Processing code from the changes in defaultScope
rob@100 377 function DefaultScope() {}
rob@100 378 DefaultScope.prototype = options.PConstants;
rob@100 379
rob@100 380 var defaultScope = new DefaultScope();
rob@100 381
rob@100 382 // copy over all known Object types and helper objects
rob@100 383 Object.keys(options).forEach(function(prop) {
rob@100 384 defaultScope[prop] = options[prop];
rob@100 385 });
rob@100 386
rob@100 387 ////////////////////////////////////////////////////////////////////////////
rob@100 388 // Class inheritance helper methods
rob@100 389 ////////////////////////////////////////////////////////////////////////////
rob@100 390
rob@100 391 defaultScope.defineProperty = function(obj, name, desc) {
rob@100 392 if("defineProperty" in Object) {
rob@100 393 Object.defineProperty(obj, name, desc);
rob@100 394 } else {
rob@100 395 if (desc.hasOwnProperty("get")) {
rob@100 396 obj.__defineGetter__(name, desc.get);
rob@100 397 }
rob@100 398 if (desc.hasOwnProperty("set")) {
rob@100 399 obj.__defineSetter__(name, desc.set);
rob@100 400 }
rob@100 401 }
rob@100 402 };
rob@100 403
rob@100 404 /**
rob@100 405 * class overloading, part 1
rob@100 406 */
rob@100 407 function overloadBaseClassFunction(object, name, basefn) {
rob@100 408 if (!object.hasOwnProperty(name) || typeof object[name] !== 'function') {
rob@100 409 // object method is not a function or just inherited from Object.prototype
rob@100 410 object[name] = basefn;
rob@100 411 return;
rob@100 412 }
rob@100 413 var fn = object[name];
rob@100 414 if ("$overloads" in fn) {
rob@100 415 // the object method already overloaded (see defaultScope.addMethod)
rob@100 416 // let's just change a fallback method
rob@100 417 fn.$defaultOverload = basefn;
rob@100 418 return;
rob@100 419 }
rob@100 420 if (!("$overloads" in basefn) && fn.length === basefn.length) {
rob@100 421 // special case when we just overriding the method
rob@100 422 return;
rob@100 423 }
rob@100 424 var overloads, defaultOverload;
rob@100 425 if ("$overloads" in basefn) {
rob@100 426 // let's inherit base class overloads to speed up things
rob@100 427 overloads = basefn.$overloads.slice(0);
rob@100 428 overloads[fn.length] = fn;
rob@100 429 defaultOverload = basefn.$defaultOverload;
rob@100 430 } else {
rob@100 431 overloads = [];
rob@100 432 overloads[basefn.length] = basefn;
rob@100 433 overloads[fn.length] = fn;
rob@100 434 defaultOverload = fn;
rob@100 435 }
rob@100 436 var hubfn = function() {
rob@100 437 var fn = hubfn.$overloads[arguments.length] ||
rob@100 438 ("$methodArgsIndex" in hubfn && arguments.length > hubfn.$methodArgsIndex ?
rob@100 439 hubfn.$overloads[hubfn.$methodArgsIndex] : null) ||
rob@100 440 hubfn.$defaultOverload;
rob@100 441 return fn.apply(this, arguments);
rob@100 442 };
rob@100 443 hubfn.$overloads = overloads;
rob@100 444 if ("$methodArgsIndex" in basefn) {
rob@100 445 hubfn.$methodArgsIndex = basefn.$methodArgsIndex;
rob@100 446 }
rob@100 447 hubfn.$defaultOverload = defaultOverload;
rob@100 448 hubfn.name = name;
rob@100 449 object[name] = hubfn;
rob@100 450 }
rob@100 451
rob@100 452 /**
rob@100 453 * class overloading, part 2
rob@100 454 */
rob@100 455
rob@100 456 function extendClass(subClass, baseClass) {
rob@100 457 function extendGetterSetter(propertyName) {
rob@100 458 defaultScope.defineProperty(subClass, propertyName, {
rob@100 459 get: function() {
rob@100 460 return baseClass[propertyName];
rob@100 461 },
rob@100 462 set: function(v) {
rob@100 463 baseClass[propertyName]=v;
rob@100 464 },
rob@100 465 enumerable: true
rob@100 466 });
rob@100 467 }
rob@100 468
rob@100 469 var properties = [];
rob@100 470 for (var propertyName in baseClass) {
rob@100 471 if (typeof baseClass[propertyName] === 'function') {
rob@100 472 overloadBaseClassFunction(subClass, propertyName, baseClass[propertyName]);
rob@100 473 } else if(propertyName.charAt(0) !== "$" && !(propertyName in subClass)) {
rob@100 474 // Delaying the properties extension due to the IE9 bug (see #918).
rob@100 475 properties.push(propertyName);
rob@100 476 }
rob@100 477 }
rob@100 478 while (properties.length > 0) {
rob@100 479 extendGetterSetter(properties.shift());
rob@100 480 }
rob@100 481
rob@100 482 subClass.$super = baseClass;
rob@100 483 }
rob@100 484
rob@100 485 /**
rob@100 486 * class overloading, part 3
rob@100 487 */
rob@100 488 defaultScope.extendClassChain = function(base) {
rob@100 489 var path = [base];
rob@100 490 for (var self = base.$upcast; self; self = self.$upcast) {
rob@100 491 extendClass(self, base);
rob@100 492 path.push(self);
rob@100 493 base = self;
rob@100 494 }
rob@100 495 while (path.length > 0) {
rob@100 496 path.pop().$self=base;
rob@100 497 }
rob@100 498 };
rob@100 499
rob@100 500 // static
rob@100 501 defaultScope.extendStaticMembers = function(derived, base) {
rob@100 502 extendClass(derived, base);
rob@100 503 };
rob@100 504
rob@100 505 // interface
rob@100 506 defaultScope.extendInterfaceMembers = function(derived, base) {
rob@100 507 extendClass(derived, base);
rob@100 508 };
rob@100 509
rob@100 510 /**
rob@100 511 * Java methods and JavaScript functions differ enough that
rob@100 512 * we need a special function to make sure it all links up
rob@100 513 * as classical hierarchical class chains.
rob@100 514 */
rob@100 515 defaultScope.addMethod = function(object, name, fn, hasMethodArgs) {
rob@100 516 var existingfn = object[name];
rob@100 517 if (existingfn || hasMethodArgs) {
rob@100 518 var args = fn.length;
rob@100 519 // builds the overload methods table
rob@100 520 if ("$overloads" in existingfn) {
rob@100 521 existingfn.$overloads[args] = fn;
rob@100 522 } else {
rob@100 523 var hubfn = function() {
rob@100 524 var fn = hubfn.$overloads[arguments.length] ||
rob@100 525 ("$methodArgsIndex" in hubfn && arguments.length > hubfn.$methodArgsIndex ?
rob@100 526 hubfn.$overloads[hubfn.$methodArgsIndex] : null) ||
rob@100 527 hubfn.$defaultOverload;
rob@100 528 return fn.apply(this, arguments);
rob@100 529 };
rob@100 530 var overloads = [];
rob@100 531 if (existingfn) {
rob@100 532 overloads[existingfn.length] = existingfn;
rob@100 533 }
rob@100 534 overloads[args] = fn;
rob@100 535 hubfn.$overloads = overloads;
rob@100 536 hubfn.$defaultOverload = existingfn || fn;
rob@100 537 if (hasMethodArgs) {
rob@100 538 hubfn.$methodArgsIndex = args;
rob@100 539 }
rob@100 540 hubfn.name = name;
rob@100 541 object[name] = hubfn;
rob@100 542 }
rob@100 543 } else {
rob@100 544 object[name] = fn;
rob@100 545 }
rob@100 546 };
rob@100 547
rob@100 548 // internal helper function
rob@100 549 function isNumericalJavaType(type) {
rob@100 550 if (typeof type !== "string") {
rob@100 551 return false;
rob@100 552 }
rob@100 553 return ["byte", "int", "char", "color", "float", "long", "double"].indexOf(type) !== -1;
rob@100 554 }
rob@100 555
rob@100 556 /**
rob@100 557 * Java's arrays are pre-filled when declared with
rob@100 558 * an initial size, but no content. JS arrays are not.
rob@100 559 */
rob@100 560 defaultScope.createJavaArray = function(type, bounds) {
rob@100 561 var result = null,
rob@100 562 defaultValue = null;
rob@100 563 if (typeof type === "string") {
rob@100 564 if (type === "boolean") {
rob@100 565 defaultValue = false;
rob@100 566 } else if (isNumericalJavaType(type)) {
rob@100 567 defaultValue = 0;
rob@100 568 }
rob@100 569 }
rob@100 570 if (typeof bounds[0] === 'number') {
rob@100 571 var itemsCount = 0 | bounds[0];
rob@100 572 if (bounds.length <= 1) {
rob@100 573 result = [];
rob@100 574 result.length = itemsCount;
rob@100 575 for (var i = 0; i < itemsCount; ++i) {
rob@100 576 result[i] = defaultValue;
rob@100 577 }
rob@100 578 } else {
rob@100 579 result = [];
rob@100 580 var newBounds = bounds.slice(1);
rob@100 581 for (var j = 0; j < itemsCount; ++j) {
rob@100 582 result.push(defaultScope.createJavaArray(type, newBounds));
rob@100 583 }
rob@100 584 }
rob@100 585 }
rob@100 586 return result;
rob@100 587 };
rob@100 588
rob@100 589 // screenWidth and screenHeight are shared by all instances.
rob@100 590 // and return the width/height of the browser's viewport.
rob@100 591 defaultScope.defineProperty(defaultScope, 'screenWidth',
rob@100 592 { get: function() { return window.innerWidth; } });
rob@100 593
rob@100 594 defaultScope.defineProperty(defaultScope, 'screenHeight',
rob@100 595 { get: function() { return window.innerHeight; } });
rob@100 596
rob@100 597 return defaultScope;
rob@100 598 };
rob@100 599
rob@100 600 },{}],6:[function(require,module,exports){
rob@100 601 /**
rob@100 602 * Finalise the Processing.js object.
rob@100 603 */
rob@100 604 module.exports = function finalizeProcessing(Processing, options) {
rob@100 605
rob@100 606 // unpack options
rob@100 607 var window = options.window,
rob@100 608 document = options.document,
rob@100 609 XMLHttpRequest = window.XMLHttpRequest,
rob@100 610 noop = options.noop,
rob@100 611 isDOMPresent = options.isDOMPresent,
rob@100 612 version = options.version,
rob@100 613 undef;
rob@100 614
rob@100 615 // versioning
rob@100 616 Processing.version = (version ? version : "@DEV-VERSION@");
rob@100 617
rob@100 618 // Share lib space
rob@100 619 Processing.lib = {};
rob@100 620
rob@100 621 /**
rob@100 622 * External libraries can be added to the global Processing
rob@100 623 * objects with the `registerLibrary` function.
rob@100 624 */
rob@100 625 Processing.registerLibrary = function(name, library) {
rob@100 626 Processing.lib[name] = library;
rob@100 627 if(library.hasOwnProperty("init")) {
rob@100 628 library.init(defaultScope);
rob@100 629 }
rob@100 630 };
rob@100 631
rob@100 632 /**
rob@100 633 * This is the object that acts as our version of PApplet.
rob@100 634 * This can be called as Processing.Sketch() or as
rob@100 635 * Processing.Sketch(function) in which case the function
rob@100 636 * must be an already-compiled-to-JS sketch function.
rob@100 637 */
rob@100 638 Processing.Sketch = function(attachFunction) {
rob@100 639 this.attachFunction = attachFunction;
rob@100 640 this.options = {
rob@100 641 pauseOnBlur: false,
rob@100 642 globalKeyEvents: false
rob@100 643 };
rob@100 644
rob@100 645 /* Optional Sketch event hooks:
rob@100 646 * onLoad - parsing/preloading is done, before sketch starts
rob@100 647 * onSetup - setup() has been called, before first draw()
rob@100 648 * onPause - noLoop() has been called, pausing draw loop
rob@100 649 * onLoop - loop() has been called, resuming draw loop
rob@100 650 * onFrameStart - draw() loop about to begin
rob@100 651 * onFrameEnd - draw() loop finished
rob@100 652 * onExit - exit() done being called
rob@100 653 */
rob@100 654 this.onLoad = noop;
rob@100 655 this.onSetup = noop;
rob@100 656 this.onPause = noop;
rob@100 657 this.onLoop = noop;
rob@100 658 this.onFrameStart = noop;
rob@100 659 this.onFrameEnd = noop;
rob@100 660 this.onExit = noop;
rob@100 661
rob@100 662 this.params = {};
rob@100 663 this.imageCache = {
rob@100 664 pending: 0,
rob@100 665 images: {},
rob@100 666 // Opera requires special administration for preloading
rob@100 667 operaCache: {},
rob@100 668 // Specify an optional img arg if the image is already loaded in the DOM,
rob@100 669 // otherwise href will get loaded.
rob@100 670 add: function(href, img) {
rob@100 671 // Prevent muliple loads for an image, in case it gets
rob@100 672 // preloaded more than once, or is added via JS and then preloaded.
rob@100 673 if (this.images[href]) {
rob@100 674 return;
rob@100 675 }
rob@100 676
rob@100 677 if (!isDOMPresent) {
rob@100 678 this.images[href] = null;
rob@100 679 }
rob@100 680
rob@100 681 // No image in the DOM, kick-off a background load
rob@100 682 if (!img) {
rob@100 683 img = new Image();
rob@100 684 img.onload = (function(owner) {
rob@100 685 return function() {
rob@100 686 owner.pending--;
rob@100 687 };
rob@100 688 }(this));
rob@100 689 this.pending++;
rob@100 690 img.src = href;
rob@100 691 }
rob@100 692
rob@100 693 this.images[href] = img;
rob@100 694
rob@100 695 // Opera will not load images until they are inserted into the DOM.
rob@100 696 if (window.opera) {
rob@100 697 var div = document.createElement("div");
rob@100 698 div.appendChild(img);
rob@100 699 // we can't use "display: none", since that makes it invisible, and thus not load
rob@100 700 div.style.position = "absolute";
rob@100 701 div.style.opacity = 0;
rob@100 702 div.style.width = "1px";
rob@100 703 div.style.height= "1px";
rob@100 704 if (!this.operaCache[href]) {
rob@100 705 document.body.appendChild(div);
rob@100 706 this.operaCache[href] = div;
rob@100 707 }
rob@100 708 }
rob@100 709 }
rob@100 710 };
rob@100 711
rob@100 712 this.sourceCode = undefined;
rob@100 713 this.attach = function(processing) {
rob@100 714 // either attachFunction or sourceCode must be present on attach
rob@100 715 if(typeof this.attachFunction === "function") {
rob@100 716 this.attachFunction(processing);
rob@100 717 } else if(this.sourceCode) {
rob@100 718 var func = ((new Function("return (" + this.sourceCode + ");"))());
rob@100 719 func(processing);
rob@100 720 this.attachFunction = func;
rob@100 721 } else {
rob@100 722 throw "Unable to attach sketch to the processing instance";
rob@100 723 }
rob@100 724 };
rob@100 725
rob@100 726 this.toString = function() {
rob@100 727 var i;
rob@100 728 var code = "((function(Sketch) {\n";
rob@100 729 code += "var sketch = new Sketch(\n" + this.sourceCode + ");\n";
rob@100 730 for(i in this.options) {
rob@100 731 if(this.options.hasOwnProperty(i)) {
rob@100 732 var value = this.options[i];
rob@100 733 code += "sketch.options." + i + " = " +
rob@100 734 (typeof value === 'string' ? '\"' + value + '\"' : "" + value) + ";\n";
rob@100 735 }
rob@100 736 }
rob@100 737 for(i in this.imageCache) {
rob@100 738 if(this.options.hasOwnProperty(i)) {
rob@100 739 code += "sketch.imageCache.add(\"" + i + "\");\n";
rob@100 740 }
rob@100 741 }
rob@100 742 // TODO serialize fonts
rob@100 743 code += "return sketch;\n})(Processing.Sketch))";
rob@100 744 return code;
rob@100 745 };
rob@100 746 };
rob@100 747
rob@100 748 /**
rob@100 749 * aggregate all source code into a single file, then rewrite that
rob@100 750 * source and bind to canvas via new Processing(canvas, sourcestring).
rob@100 751 * @param {CANVAS} canvas The html canvas element to bind to
rob@100 752 * @param {String[]} source The array of files that must be loaded
rob@100 753 */
rob@100 754 var loadSketchFromSources = Processing.loadSketchFromSources = function(canvas, sources) {
rob@100 755 var code = [], errors = [], sourcesCount = sources.length, loaded = 0;
rob@100 756
rob@100 757 function ajaxAsync(url, callback) {
rob@100 758 var xhr = new XMLHttpRequest();
rob@100 759 xhr.onreadystatechange = function() {
rob@100 760 if (xhr.readyState === 4) {
rob@100 761 var error;
rob@100 762 if (xhr.status !== 200 && xhr.status !== 0) {
rob@100 763 error = "Invalid XHR status " + xhr.status;
rob@100 764 } else if (xhr.responseText === "") {
rob@100 765 // Give a hint when loading fails due to same-origin issues on file:/// urls
rob@100 766 if ( ("withCredentials" in new XMLHttpRequest()) &&
rob@100 767 (new XMLHttpRequest()).withCredentials === false &&
rob@100 768 window.location.protocol === "file:" ) {
rob@100 769 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.";
rob@100 770 } else {
rob@100 771 error = "File is empty.";
rob@100 772 }
rob@100 773 }
rob@100 774
rob@100 775 callback(xhr.responseText, error);
rob@100 776 }
rob@100 777 };
rob@100 778 xhr.open("GET", url, true);
rob@100 779 if (xhr.overrideMimeType) {
rob@100 780 xhr.overrideMimeType("application/json");
rob@100 781 }
rob@100 782 xhr.setRequestHeader("If-Modified-Since", "Fri, 01 Jan 1960 00:00:00 GMT"); // no cache
rob@100 783 xhr.send(null);
rob@100 784 }
rob@100 785
rob@100 786 function loadBlock(index, filename) {
rob@100 787 function callback(block, error) {
rob@100 788 code[index] = block;
rob@100 789 ++loaded;
rob@100 790 if (error) {
rob@100 791 errors.push(filename + " ==> " + error);
rob@100 792 }
rob@100 793 if (loaded === sourcesCount) {
rob@100 794 if (errors.length === 0) {
rob@100 795 try {
rob@100 796 return new Processing(canvas, code.join("\n"));
rob@100 797 } catch(e) {
rob@100 798 console.log("Processing.js: Unable to execute pjs sketch.");
rob@100 799 throw e;
rob@100 800 }
rob@100 801 } else {
rob@100 802 throw "Processing.js: Unable to load pjs sketch files: " + errors.join("\n");
rob@100 803 }
rob@100 804 }
rob@100 805 }
rob@100 806 if (filename.charAt(0) === '#') {
rob@100 807 // trying to get script from the element
rob@100 808 var scriptElement = document.getElementById(filename.substring(1));
rob@100 809 if (scriptElement) {
rob@100 810 callback(scriptElement.text || scriptElement.textContent);
rob@100 811 } else {
rob@100 812 callback("", "Unable to load pjs sketch: element with id \'" + filename.substring(1) + "\' was not found");
rob@100 813 }
rob@100 814 return;
rob@100 815 }
rob@100 816
rob@100 817 ajaxAsync(filename, callback);
rob@100 818 }
rob@100 819
rob@100 820 for (var i = 0; i < sourcesCount; ++i) {
rob@100 821 loadBlock(i, sources[i]);
rob@100 822 }
rob@100 823 };
rob@100 824
rob@100 825 /**
rob@100 826 * Automatic initialization function.
rob@100 827 */
rob@100 828 var init = function() {
rob@100 829 document.removeEventListener('DOMContentLoaded', init, false);
rob@100 830
rob@100 831 // before running through init, clear the instances list, to prevent
rob@100 832 // sketch duplication when page content is dynamically swapped without
rob@100 833 // swapping out processing.js
rob@100 834 processingInstances = [];
rob@100 835 Processing.instances = processingInstances;
rob@100 836
rob@100 837 var canvas = document.getElementsByTagName('canvas'),
rob@100 838 filenames;
rob@100 839
rob@100 840 for (var i = 0, l = canvas.length; i < l; i++) {
rob@100 841 // datasrc and data-src are deprecated.
rob@100 842 var processingSources = canvas[i].getAttribute('data-processing-sources');
rob@100 843 if (processingSources === null) {
rob@100 844 // Temporary fallback for datasrc and data-src
rob@100 845 processingSources = canvas[i].getAttribute('data-src');
rob@100 846 if (processingSources === null) {
rob@100 847 processingSources = canvas[i].getAttribute('datasrc');
rob@100 848 }
rob@100 849 }
rob@100 850 if (processingSources) {
rob@100 851 filenames = processingSources.split(/\s+/g);
rob@100 852 for (var j = 0; j < filenames.length;) {
rob@100 853 if (filenames[j]) {
rob@100 854 j++;
rob@100 855 } else {
rob@100 856 filenames.splice(j, 1);
rob@100 857 }
rob@100 858 }
rob@100 859 loadSketchFromSources(canvas[i], filenames);
rob@100 860 }
rob@100 861 }
rob@100 862
rob@100 863 // also process all <script>-indicated sketches, if there are any
rob@100 864 var s, last, source, instance,
rob@100 865 nodelist = document.getElementsByTagName('script'),
rob@100 866 scripts=[];
rob@100 867
rob@100 868 // snapshot the DOM, as the nodelist is only a DOM view, and is
rob@100 869 // updated instantly when a script element is added or removed.
rob@100 870 for (s = nodelist.length - 1; s >= 0; s--) {
rob@100 871 scripts.push(nodelist[s]);
rob@100 872 }
rob@100 873
rob@100 874 // iterate over all script elements to see if they contain Processing code
rob@100 875 for (s = 0, last = scripts.length; s < last; s++) {
rob@100 876 var script = scripts[s];
rob@100 877 if (!script.getAttribute) {
rob@100 878 continue;
rob@100 879 }
rob@100 880
rob@100 881 var type = script.getAttribute("type");
rob@100 882 if (type && (type.toLowerCase() === "text/processing" || type.toLowerCase() === "application/processing")) {
rob@100 883 var target = script.getAttribute("data-processing-target");
rob@100 884 canvas = undef;
rob@100 885 if (target) {
rob@100 886 canvas = document.getElementById(target);
rob@100 887 } else {
rob@100 888 var nextSibling = script.nextSibling;
rob@100 889 while (nextSibling && nextSibling.nodeType !== 1) {
rob@100 890 nextSibling = nextSibling.nextSibling;
rob@100 891 }
rob@100 892 if (nextSibling && nextSibling.nodeName.toLowerCase() === "canvas") {
rob@100 893 canvas = nextSibling;
rob@100 894 }
rob@100 895 }
rob@100 896
rob@100 897 if (canvas) {
rob@100 898 if (script.getAttribute("src")) {
rob@100 899 filenames = script.getAttribute("src").split(/\s+/);
rob@100 900 loadSketchFromSources(canvas, filenames);
rob@100 901 continue;
rob@100 902 }
rob@100 903 source = script.textContent || script.text;
rob@100 904 instance = new Processing(canvas, source);
rob@100 905 }
rob@100 906 }
rob@100 907 }
rob@100 908 };
rob@100 909
rob@100 910 /**
rob@100 911 * automatic loading of all sketches on the page
rob@100 912 */
rob@100 913 document.addEventListener('DOMContentLoaded', init, false);
rob@100 914
rob@100 915 /**
rob@100 916 * Make Processing run through init after already having
rob@100 917 * been set up for a page. This function exists mostly for pages
rob@100 918 * that swap content in/out without reloading a page.
rob@100 919 */
rob@100 920 Processing.reload = function() {
rob@100 921 if (processingInstances.length > 0) {
rob@100 922 // unload sketches
rob@100 923 for (var i = processingInstances.length - 1; i >= 0; i--) {
rob@100 924 if (processingInstances[i]) {
rob@100 925 processingInstances[i].exit();
rob@100 926 }
rob@100 927 }
rob@100 928 }
rob@100 929 // rerun init() to scan the DOM for sketches
rob@100 930 init();
rob@100 931 };
rob@100 932
rob@100 933 /**
rob@100 934 * Disable the automatic loading of all sketches on the page.
rob@100 935 * This will work as long as it's issued before DOMContentLoaded.
rob@100 936 */
rob@100 937 Processing.disableInit = function() {
rob@100 938 document.removeEventListener('DOMContentLoaded', init, false);
rob@100 939 };
rob@100 940
rob@100 941 // done.
rob@100 942 return Processing;
rob@100 943 };
rob@100 944 },{}],7:[function(require,module,exports){
rob@100 945 /**
rob@100 946 * Returns Java equals() result for two objects. If the first object
rob@100 947 * has the "equals" function, it preforms the call of this function.
rob@100 948 * Otherwise the method uses the JavaScript === operator.
rob@100 949 *
rob@100 950 * @param {Object} obj The first object.
rob@100 951 * @param {Object} other The second object.
rob@100 952 *
rob@100 953 * @returns {boolean} true if the objects are equal.
rob@100 954 */
rob@100 955 module.exports = function virtEquals(obj, other) {
rob@100 956 if (obj === null || other === null) {
rob@100 957 return (obj === null) && (other === null);
rob@100 958 }
rob@100 959 if (typeof (obj) === "string") {
rob@100 960 return obj === other;
rob@100 961 }
rob@100 962 if (typeof(obj) !== "object") {
rob@100 963 return obj === other;
rob@100 964 }
rob@100 965 if (obj.equals instanceof Function) {
rob@100 966 return obj.equals(other);
rob@100 967 }
rob@100 968 return obj === other;
rob@100 969 };
rob@100 970
rob@100 971 },{}],8:[function(require,module,exports){
rob@100 972 /**
rob@100 973 * Returns Java hashCode() result for the object. If the object has the "hashCode" function,
rob@100 974 * it preforms the call of this function. Otherwise it uses/creates the "$id" property,
rob@100 975 * which is used as the hashCode.
rob@100 976 *
rob@100 977 * @param {Object} obj The object.
rob@100 978 * @returns {int} The object's hash code.
rob@100 979 */
rob@100 980 module.exports = function virtHashCode(obj, undef) {
rob@100 981 if (typeof(obj) === "string") {
rob@100 982 var hash = 0;
rob@100 983 for (var i = 0; i < obj.length; ++i) {
rob@100 984 hash = (hash * 31 + obj.charCodeAt(i)) & 0xFFFFFFFF;
rob@100 985 }
rob@100 986 return hash;
rob@100 987 }
rob@100 988 if (typeof(obj) !== "object") {
rob@100 989 return obj & 0xFFFFFFFF;
rob@100 990 }
rob@100 991 if (obj.hashCode instanceof Function) {
rob@100 992 return obj.hashCode();
rob@100 993 }
rob@100 994 if (obj.$id === undef) {
rob@100 995 obj.$id = ((Math.floor(Math.random() * 0x10000) - 0x8000) << 16) | Math.floor(Math.random() * 0x10000);
rob@100 996 }
rob@100 997 return obj.$id;
rob@100 998 };
rob@100 999
rob@100 1000 },{}],9:[function(require,module,exports){
rob@100 1001 /**
rob@100 1002 * An ArrayList stores a variable number of objects.
rob@100 1003 *
rob@100 1004 * @param {int} initialCapacity optional defines the initial capacity of the list, it's empty by default
rob@100 1005 *
rob@100 1006 * @returns {ArrayList} new ArrayList object
rob@100 1007 */
rob@100 1008 module.exports = function(options) {
rob@100 1009 var virtHashCode = options.virtHashCode,
rob@100 1010 virtEquals = options.virtEquals;
rob@100 1011
rob@100 1012 function Iterator(array) {
rob@100 1013 var index = -1;
rob@100 1014 this.hasNext = function() {
rob@100 1015 return (index + 1) < array.length;
rob@100 1016 };
rob@100 1017
rob@100 1018 this.next = function() {
rob@100 1019 return array[++index];
rob@100 1020 };
rob@100 1021
rob@100 1022 this.remove = function() {
rob@100 1023 array.splice(index--, 1);
rob@100 1024 };
rob@100 1025 }
rob@100 1026
rob@100 1027 function ArrayList(a) {
rob@100 1028 var array = [];
rob@100 1029
rob@100 1030 if (a && a.toArray) {
rob@100 1031 array = a.toArray();
rob@100 1032 }
rob@100 1033
rob@100 1034 /**
rob@100 1035 * @member ArrayList
rob@100 1036 * ArrayList.get() Returns the element at the specified position in this list.
rob@100 1037 *
rob@100 1038 * @param {int} i index of element to return
rob@100 1039 *
rob@100 1040 * @returns {Object} the element at the specified position in this list.
rob@100 1041 */
rob@100 1042 this.get = function(i) {
rob@100 1043 return array[i];
rob@100 1044 };
rob@100 1045 /**
rob@100 1046 * @member ArrayList
rob@100 1047 * ArrayList.contains() Returns true if this list contains the specified element.
rob@100 1048 *
rob@100 1049 * @param {Object} item element whose presence in this List is to be tested.
rob@100 1050 *
rob@100 1051 * @returns {boolean} true if the specified element is present; false otherwise.
rob@100 1052 */
rob@100 1053 this.contains = function(item) {
rob@100 1054 return this.indexOf(item)>-1;
rob@100 1055 };
rob@100 1056 /**
rob@100 1057 * @member ArrayList
rob@100 1058 * ArrayList.indexOf() Returns the position this element takes in the list, or -1 if the element is not found.
rob@100 1059 *
rob@100 1060 * @param {Object} item element whose position in this List is to be tested.
rob@100 1061 *
rob@100 1062 * @returns {int} the list position that the first match for this element holds in the list, or -1 if it is not in the list.
rob@100 1063 */
rob@100 1064 this.indexOf = function(item) {
rob@100 1065 for (var i = 0, len = array.length; i < len; ++i) {
rob@100 1066 if (virtEquals(item, array[i])) {
rob@100 1067 return i;
rob@100 1068 }
rob@100 1069 }
rob@100 1070 return -1;
rob@100 1071 };
rob@100 1072 /**
rob@100 1073 * @member ArrayList
rob@100 1074 * ArrayList.lastIndexOf() Returns the index of the last occurrence of the specified element in this list,
rob@100 1075 * or -1 if this list does not contain the element. More formally, returns the highest index i such that
rob@100 1076 * (o==null ? get(i)==null : o.equals(get(i))), or -1 if there is no such index.
rob@100 1077 *
rob@100 1078 * @param {Object} item element to search for.
rob@100 1079 *
rob@100 1080 * @returns {int} the index of the last occurrence of the specified element in this list, or -1 if this list does not contain the element.
rob@100 1081 */
rob@100 1082 this.lastIndexOf = function(item) {
rob@100 1083 for (var i = array.length-1; i >= 0; --i) {
rob@100 1084 if (virtEquals(item, array[i])) {
rob@100 1085 return i;
rob@100 1086 }
rob@100 1087 }
rob@100 1088 return -1;
rob@100 1089 };
rob@100 1090 /**
rob@100 1091 * @member ArrayList
rob@100 1092 * ArrayList.add() Adds the specified element to this list.
rob@100 1093 *
rob@100 1094 * @param {int} index optional index at which the specified element is to be inserted
rob@100 1095 * @param {Object} object element to be added to the list
rob@100 1096 */
rob@100 1097 this.add = function() {
rob@100 1098 if (arguments.length === 1) {
rob@100 1099 array.push(arguments[0]); // for add(Object)
rob@100 1100 } else if (arguments.length === 2) {
rob@100 1101 var arg0 = arguments[0];
rob@100 1102 if (typeof arg0 === 'number') {
rob@100 1103 if (arg0 >= 0 && arg0 <= array.length) {
rob@100 1104 array.splice(arg0, 0, arguments[1]); // for add(i, Object)
rob@100 1105 } else {
rob@100 1106 throw(arg0 + " is not a valid index");
rob@100 1107 }
rob@100 1108 } else {
rob@100 1109 throw(typeof arg0 + " is not a number");
rob@100 1110 }
rob@100 1111 } else {
rob@100 1112 throw("Please use the proper number of parameters.");
rob@100 1113 }
rob@100 1114 };
rob@100 1115 /**
rob@100 1116 * @member ArrayList
rob@100 1117 * ArrayList.addAll(collection) appends all of the elements in the specified
rob@100 1118 * Collection to the end of this list, in the order that they are returned by
rob@100 1119 * the specified Collection's Iterator.
rob@100 1120 *
rob@100 1121 * When called as addAll(index, collection) the elements are inserted into
rob@100 1122 * this list at the position indicated by index.
rob@100 1123 *
rob@100 1124 * @param {index} Optional; specifies the position the colletion should be inserted at
rob@100 1125 * @param {collection} Any iterable object (ArrayList, HashMap.keySet(), etc.)
rob@100 1126 * @throws out of bounds error for negative index, or index greater than list size.
rob@100 1127 */
rob@100 1128 this.addAll = function(arg1, arg2) {
rob@100 1129 // addAll(int, Collection)
rob@100 1130 var it;
rob@100 1131 if (typeof arg1 === "number") {
rob@100 1132 if (arg1 < 0 || arg1 > array.length) {
rob@100 1133 throw("Index out of bounds for addAll: " + arg1 + " greater or equal than " + array.length);
rob@100 1134 }
rob@100 1135 it = new ObjectIterator(arg2);
rob@100 1136 while (it.hasNext()) {
rob@100 1137 array.splice(arg1++, 0, it.next());
rob@100 1138 }
rob@100 1139 }
rob@100 1140 // addAll(Collection)
rob@100 1141 else {
rob@100 1142 it = new ObjectIterator(arg1);
rob@100 1143 while (it.hasNext()) {
rob@100 1144 array.push(it.next());
rob@100 1145 }
rob@100 1146 }
rob@100 1147 };
rob@100 1148 /**
rob@100 1149 * @member ArrayList
rob@100 1150 * ArrayList.set() Replaces the element at the specified position in this list with the specified element.
rob@100 1151 *
rob@100 1152 * @param {int} index index of element to replace
rob@100 1153 * @param {Object} object element to be stored at the specified position
rob@100 1154 */
rob@100 1155 this.set = function() {
rob@100 1156 if (arguments.length === 2) {
rob@100 1157 var arg0 = arguments[0];
rob@100 1158 if (typeof arg0 === 'number') {
rob@100 1159 if (arg0 >= 0 && arg0 < array.length) {
rob@100 1160 array.splice(arg0, 1, arguments[1]);
rob@100 1161 } else {
rob@100 1162 throw(arg0 + " is not a valid index.");
rob@100 1163 }
rob@100 1164 } else {
rob@100 1165 throw(typeof arg0 + " is not a number");
rob@100 1166 }
rob@100 1167 } else {
rob@100 1168 throw("Please use the proper number of parameters.");
rob@100 1169 }
rob@100 1170 };
rob@100 1171
rob@100 1172 /**
rob@100 1173 * @member ArrayList
rob@100 1174 * ArrayList.size() Returns the number of elements in this list.
rob@100 1175 *
rob@100 1176 * @returns {int} the number of elements in this list
rob@100 1177 */
rob@100 1178 this.size = function() {
rob@100 1179 return array.length;
rob@100 1180 };
rob@100 1181
rob@100 1182 /**
rob@100 1183 * @member ArrayList
rob@100 1184 * ArrayList.clear() Removes all of the elements from this list. The list will be empty after this call returns.
rob@100 1185 */
rob@100 1186 this.clear = function() {
rob@100 1187 array.length = 0;
rob@100 1188 };
rob@100 1189
rob@100 1190 /**
rob@100 1191 * @member ArrayList
rob@100 1192 * ArrayList.remove() Removes an element either based on index, if the argument is a number, or
rob@100 1193 * by equality check, if the argument is an object.
rob@100 1194 *
rob@100 1195 * @param {int|Object} item either the index of the element to be removed, or the element itself.
rob@100 1196 *
rob@100 1197 * @returns {Object|boolean} If removal is by index, the element that was removed, or null if nothing was removed. If removal is by object, true if removal occurred, otherwise false.
rob@100 1198 */
rob@100 1199 this.remove = function(item) {
rob@100 1200 if (typeof item === 'number') {
rob@100 1201 return array.splice(item, 1)[0];
rob@100 1202 }
rob@100 1203 item = this.indexOf(item);
rob@100 1204 if (item > -1) {
rob@100 1205 array.splice(item, 1);
rob@100 1206 return true;
rob@100 1207 }
rob@100 1208 return false;
rob@100 1209 };
rob@100 1210
rob@100 1211 /**
rob@100 1212 * @member ArrayList
rob@100 1213 * ArrayList.removeAll Removes from this List all of the elements from
rob@100 1214 * the current ArrayList which are present in the passed in paramater ArrayList 'c'.
rob@100 1215 * Shifts any succeeding elements to the left (reduces their index).
rob@100 1216 *
rob@100 1217 * @param {ArrayList} the ArrayList to compare to the current ArrayList
rob@100 1218 *
rob@100 1219 * @returns {boolean} true if the ArrayList had an element removed; false otherwise
rob@100 1220 */
rob@100 1221 this.removeAll = function(c) {
rob@100 1222 var i, x, item,
rob@100 1223 newList = new ArrayList();
rob@100 1224 newList.addAll(this);
rob@100 1225 this.clear();
rob@100 1226 // For every item that exists in the original ArrayList and not in the c ArrayList
rob@100 1227 // copy it into the empty 'this' ArrayList to create the new 'this' Array.
rob@100 1228 for (i = 0, x = 0; i < newList.size(); i++) {
rob@100 1229 item = newList.get(i);
rob@100 1230 if (!c.contains(item)) {
rob@100 1231 this.add(x++, item);
rob@100 1232 }
rob@100 1233 }
rob@100 1234 if (this.size() < newList.size()) {
rob@100 1235 return true;
rob@100 1236 }
rob@100 1237 return false;
rob@100 1238 };
rob@100 1239
rob@100 1240 /**
rob@100 1241 * @member ArrayList
rob@100 1242 * ArrayList.isEmpty() Tests if this list has no elements.
rob@100 1243 *
rob@100 1244 * @returns {boolean} true if this list has no elements; false otherwise
rob@100 1245 */
rob@100 1246 this.isEmpty = function() {
rob@100 1247 return !array.length;
rob@100 1248 };
rob@100 1249
rob@100 1250 /**
rob@100 1251 * @member ArrayList
rob@100 1252 * ArrayList.clone() Returns a shallow copy of this ArrayList instance. (The elements themselves are not copied.)
rob@100 1253 *
rob@100 1254 * @returns {ArrayList} a clone of this ArrayList instance
rob@100 1255 */
rob@100 1256 this.clone = function() {
rob@100 1257 return new ArrayList(this);
rob@100 1258 };
rob@100 1259
rob@100 1260 /**
rob@100 1261 * @member ArrayList
rob@100 1262 * ArrayList.toArray() Returns an array containing all of the elements in this list in the correct order.
rob@100 1263 *
rob@100 1264 * @returns {Object[]} Returns an array containing all of the elements in this list in the correct order
rob@100 1265 */
rob@100 1266 this.toArray = function() {
rob@100 1267 return array.slice(0);
rob@100 1268 };
rob@100 1269
rob@100 1270 this.iterator = function() {
rob@100 1271 return new Iterator(array);
rob@100 1272 };
rob@100 1273 }
rob@100 1274
rob@100 1275 return ArrayList;
rob@100 1276 };
rob@100 1277
rob@100 1278 },{}],10:[function(require,module,exports){
rob@100 1279 module.exports = (function(charMap, undef) {
rob@100 1280
rob@100 1281 var Char = function(chr) {
rob@100 1282 if (typeof chr === 'string' && chr.length === 1) {
rob@100 1283 this.code = chr.charCodeAt(0);
rob@100 1284 } else if (typeof chr === 'number') {
rob@100 1285 this.code = chr;
rob@100 1286 } else if (chr instanceof Char) {
rob@100 1287 this.code = chr;
rob@100 1288 } else {
rob@100 1289 this.code = NaN;
rob@100 1290 }
rob@100 1291 return (charMap[this.code] === undef) ? charMap[this.code] = this : charMap[this.code];
rob@100 1292 };
rob@100 1293
rob@100 1294 Char.prototype.toString = function() {
rob@100 1295 return String.fromCharCode(this.code);
rob@100 1296 };
rob@100 1297
rob@100 1298 Char.prototype.valueOf = function() {
rob@100 1299 return this.code;
rob@100 1300 };
rob@100 1301
rob@100 1302 return Char;
rob@100 1303 }({}));
rob@100 1304
rob@100 1305 },{}],11:[function(require,module,exports){
rob@100 1306 /**
rob@100 1307 * A HashMap stores a collection of objects, each referenced by a key. This is similar to an Array, only
rob@100 1308 * instead of accessing elements with a numeric index, a String is used. (If you are familiar with
rob@100 1309 * associative arrays from other languages, this is the same idea.)
rob@100 1310 *
rob@100 1311 * @param {int} initialCapacity defines the initial capacity of the map, it's 16 by default
rob@100 1312 * @param {float} loadFactor the load factor for the map, the default is 0.75
rob@100 1313 * @param {Map} m gives the new HashMap the same mappings as this Map
rob@100 1314 */
rob@100 1315 module.exports = function(options) {
rob@100 1316 var virtHashCode = options.virtHashCode,
rob@100 1317 virtEquals = options.virtEquals;
rob@100 1318
rob@100 1319 /**
rob@100 1320 * @member HashMap
rob@100 1321 * A HashMap stores a collection of objects, each referenced by a key. This is similar to an Array, only
rob@100 1322 * instead of accessing elements with a numeric index, a String is used. (If you are familiar with
rob@100 1323 * associative arrays from other languages, this is the same idea.)
rob@100 1324 *
rob@100 1325 * @param {int} initialCapacity defines the initial capacity of the map, it's 16 by default
rob@100 1326 * @param {float} loadFactor the load factor for the map, the default is 0.75
rob@100 1327 * @param {Map} m gives the new HashMap the same mappings as this Map
rob@100 1328 */
rob@100 1329 function HashMap() {
rob@100 1330 if (arguments.length === 1 && arguments[0] instanceof HashMap) {
rob@100 1331 return arguments[0].clone();
rob@100 1332 }
rob@100 1333
rob@100 1334 var initialCapacity = arguments.length > 0 ? arguments[0] : 16;
rob@100 1335 var loadFactor = arguments.length > 1 ? arguments[1] : 0.75;
rob@100 1336 var buckets = [];
rob@100 1337 buckets.length = initialCapacity;
rob@100 1338 var count = 0;
rob@100 1339 var hashMap = this;
rob@100 1340
rob@100 1341 function getBucketIndex(key) {
rob@100 1342 var index = virtHashCode(key) % buckets.length;
rob@100 1343 return index < 0 ? buckets.length + index : index;
rob@100 1344 }
rob@100 1345 function ensureLoad() {
rob@100 1346 if (count <= loadFactor * buckets.length) {
rob@100 1347 return;
rob@100 1348 }
rob@100 1349 var allEntries = [];
rob@100 1350 for (var i = 0; i < buckets.length; ++i) {
rob@100 1351 if (buckets[i] !== undefined) {
rob@100 1352 allEntries = allEntries.concat(buckets[i]);
rob@100 1353 }
rob@100 1354 }
rob@100 1355 var newBucketsLength = buckets.length * 2;
rob@100 1356 buckets = [];
rob@100 1357 buckets.length = newBucketsLength;
rob@100 1358 for (var j = 0; j < allEntries.length; ++j) {
rob@100 1359 var index = getBucketIndex(allEntries[j].key);
rob@100 1360 var bucket = buckets[index];
rob@100 1361 if (bucket === undefined) {
rob@100 1362 buckets[index] = bucket = [];
rob@100 1363 }
rob@100 1364 bucket.push(allEntries[j]);
rob@100 1365 }
rob@100 1366 }
rob@100 1367
rob@100 1368 function Iterator(conversion, removeItem) {
rob@100 1369 var bucketIndex = 0;
rob@100 1370 var itemIndex = -1;
rob@100 1371 var endOfBuckets = false;
rob@100 1372 var currentItem;
rob@100 1373
rob@100 1374 function findNext() {
rob@100 1375 while (!endOfBuckets) {
rob@100 1376 ++itemIndex;
rob@100 1377 if (bucketIndex >= buckets.length) {
rob@100 1378 endOfBuckets = true;
rob@100 1379 } else if (buckets[bucketIndex] === undefined || itemIndex >= buckets[bucketIndex].length) {
rob@100 1380 itemIndex = -1;
rob@100 1381 ++bucketIndex;
rob@100 1382 } else {
rob@100 1383 return;
rob@100 1384 }
rob@100 1385 }
rob@100 1386 }
rob@100 1387
rob@100 1388 /*
rob@100 1389 * @member Iterator
rob@100 1390 * Checks if the Iterator has more items
rob@100 1391 */
rob@100 1392 this.hasNext = function() {
rob@100 1393 return !endOfBuckets;
rob@100 1394 };
rob@100 1395
rob@100 1396 /*
rob@100 1397 * @member Iterator
rob@100 1398 * Return the next Item
rob@100 1399 */
rob@100 1400 this.next = function() {
rob@100 1401 currentItem = conversion(buckets[bucketIndex][itemIndex]);
rob@100 1402 findNext();
rob@100 1403 return currentItem;
rob@100 1404 };
rob@100 1405
rob@100 1406 /*
rob@100 1407 * @member Iterator
rob@100 1408 * Remove the current item
rob@100 1409 */
rob@100 1410 this.remove = function() {
rob@100 1411 if (currentItem !== undefined) {
rob@100 1412 removeItem(currentItem);
rob@100 1413 --itemIndex;
rob@100 1414 findNext();
rob@100 1415 }
rob@100 1416 };
rob@100 1417
rob@100 1418 findNext();
rob@100 1419 }
rob@100 1420
rob@100 1421 function Set(conversion, isIn, removeItem) {
rob@100 1422 this.clear = function() {
rob@100 1423 hashMap.clear();
rob@100 1424 };
rob@100 1425
rob@100 1426 this.contains = function(o) {
rob@100 1427 return isIn(o);
rob@100 1428 };
rob@100 1429
rob@100 1430 this.containsAll = function(o) {
rob@100 1431 var it = o.iterator();
rob@100 1432 while (it.hasNext()) {
rob@100 1433 if (!this.contains(it.next())) {
rob@100 1434 return false;
rob@100 1435 }
rob@100 1436 }
rob@100 1437 return true;
rob@100 1438 };
rob@100 1439
rob@100 1440 this.isEmpty = function() {
rob@100 1441 return hashMap.isEmpty();
rob@100 1442 };
rob@100 1443
rob@100 1444 this.iterator = function() {
rob@100 1445 return new Iterator(conversion, removeItem);
rob@100 1446 };
rob@100 1447
rob@100 1448 this.remove = function(o) {
rob@100 1449 if (this.contains(o)) {
rob@100 1450 removeItem(o);
rob@100 1451 return true;
rob@100 1452 }
rob@100 1453 return false;
rob@100 1454 };
rob@100 1455
rob@100 1456 this.removeAll = function(c) {
rob@100 1457 var it = c.iterator();
rob@100 1458 var changed = false;
rob@100 1459 while (it.hasNext()) {
rob@100 1460 var item = it.next();
rob@100 1461 if (this.contains(item)) {
rob@100 1462 removeItem(item);
rob@100 1463 changed = true;
rob@100 1464 }
rob@100 1465 }
rob@100 1466 return true;
rob@100 1467 };
rob@100 1468
rob@100 1469 this.retainAll = function(c) {
rob@100 1470 var it = this.iterator();
rob@100 1471 var toRemove = [];
rob@100 1472 while (it.hasNext()) {
rob@100 1473 var entry = it.next();
rob@100 1474 if (!c.contains(entry)) {
rob@100 1475 toRemove.push(entry);
rob@100 1476 }
rob@100 1477 }
rob@100 1478 for (var i = 0; i < toRemove.length; ++i) {
rob@100 1479 removeItem(toRemove[i]);
rob@100 1480 }
rob@100 1481 return toRemove.length > 0;
rob@100 1482 };
rob@100 1483
rob@100 1484 this.size = function() {
rob@100 1485 return hashMap.size();
rob@100 1486 };
rob@100 1487
rob@100 1488 this.toArray = function() {
rob@100 1489 var result = [];
rob@100 1490 var it = this.iterator();
rob@100 1491 while (it.hasNext()) {
rob@100 1492 result.push(it.next());
rob@100 1493 }
rob@100 1494 return result;
rob@100 1495 };
rob@100 1496 }
rob@100 1497
rob@100 1498 function Entry(pair) {
rob@100 1499 this._isIn = function(map) {
rob@100 1500 return map === hashMap && (pair.removed === undefined);
rob@100 1501 };
rob@100 1502
rob@100 1503 this.equals = function(o) {
rob@100 1504 return virtEquals(pair.key, o.getKey());
rob@100 1505 };
rob@100 1506
rob@100 1507 this.getKey = function() {
rob@100 1508 return pair.key;
rob@100 1509 };
rob@100 1510
rob@100 1511 this.getValue = function() {
rob@100 1512 return pair.value;
rob@100 1513 };
rob@100 1514
rob@100 1515 this.hashCode = function(o) {
rob@100 1516 return virtHashCode(pair.key);
rob@100 1517 };
rob@100 1518
rob@100 1519 this.setValue = function(value) {
rob@100 1520 var old = pair.value;
rob@100 1521 pair.value = value;
rob@100 1522 return old;
rob@100 1523 };
rob@100 1524 }
rob@100 1525
rob@100 1526 this.clear = function() {
rob@100 1527 count = 0;
rob@100 1528 buckets = [];
rob@100 1529 buckets.length = initialCapacity;
rob@100 1530 };
rob@100 1531
rob@100 1532 this.clone = function() {
rob@100 1533 var map = new HashMap();
rob@100 1534 map.putAll(this);
rob@100 1535 return map;
rob@100 1536 };
rob@100 1537
rob@100 1538 this.containsKey = function(key) {
rob@100 1539 var index = getBucketIndex(key);
rob@100 1540 var bucket = buckets[index];
rob@100 1541 if (bucket === undefined) {
rob@100 1542 return false;
rob@100 1543 }
rob@100 1544 for (var i = 0; i < bucket.length; ++i) {
rob@100 1545 if (virtEquals(bucket[i].key, key)) {
rob@100 1546 return true;
rob@100 1547 }
rob@100 1548 }
rob@100 1549 return false;
rob@100 1550 };
rob@100 1551
rob@100 1552 this.containsValue = function(value) {
rob@100 1553 for (var i = 0; i < buckets.length; ++i) {
rob@100 1554 var bucket = buckets[i];
rob@100 1555 if (bucket === undefined) {
rob@100 1556 continue;
rob@100 1557 }
rob@100 1558 for (var j = 0; j < bucket.length; ++j) {
rob@100 1559 if (virtEquals(bucket[j].value, value)) {
rob@100 1560 return true;
rob@100 1561 }
rob@100 1562 }
rob@100 1563 }
rob@100 1564 return false;
rob@100 1565 };
rob@100 1566
rob@100 1567 this.entrySet = function() {
rob@100 1568 return new Set(
rob@100 1569
rob@100 1570 function(pair) {
rob@100 1571 return new Entry(pair);
rob@100 1572 },
rob@100 1573
rob@100 1574 function(pair) {
rob@100 1575 return (pair instanceof Entry) && pair._isIn(hashMap);
rob@100 1576 },
rob@100 1577
rob@100 1578 function(pair) {
rob@100 1579 return hashMap.remove(pair.getKey());
rob@100 1580 });
rob@100 1581 };
rob@100 1582
rob@100 1583 this.get = function(key) {
rob@100 1584 var index = getBucketIndex(key);
rob@100 1585 var bucket = buckets[index];
rob@100 1586 if (bucket === undefined) {
rob@100 1587 return null;
rob@100 1588 }
rob@100 1589 for (var i = 0; i < bucket.length; ++i) {
rob@100 1590 if (virtEquals(bucket[i].key, key)) {
rob@100 1591 return bucket[i].value;
rob@100 1592 }
rob@100 1593 }
rob@100 1594 return null;
rob@100 1595 };
rob@100 1596
rob@100 1597 this.isEmpty = function() {
rob@100 1598 return count === 0;
rob@100 1599 };
rob@100 1600
rob@100 1601 this.keySet = function() {
rob@100 1602 return new Set(
rob@100 1603 // get key from pair
rob@100 1604 function(pair) {
rob@100 1605 return pair.key;
rob@100 1606 },
rob@100 1607 // is-in test
rob@100 1608 function(key) {
rob@100 1609 return hashMap.containsKey(key);
rob@100 1610 },
rob@100 1611 // remove from hashmap by key
rob@100 1612 function(key) {
rob@100 1613 return hashMap.remove(key);
rob@100 1614 }
rob@100 1615 );
rob@100 1616 };
rob@100 1617
rob@100 1618 this.values = function() {
rob@100 1619 return new Set(
rob@100 1620 // get value from pair
rob@100 1621 function(pair) {
rob@100 1622 return pair.value;
rob@100 1623 },
rob@100 1624 // is-in test
rob@100 1625 function(value) {
rob@100 1626 return hashMap.containsValue(value);
rob@100 1627 },
rob@100 1628 // remove from hashmap by value
rob@100 1629 function(value) {
rob@100 1630 return hashMap.removeByValue(value);
rob@100 1631 }
rob@100 1632 );
rob@100 1633 };
rob@100 1634
rob@100 1635 this.put = function(key, value) {
rob@100 1636 var index = getBucketIndex(key);
rob@100 1637 var bucket = buckets[index];
rob@100 1638 if (bucket === undefined) {
rob@100 1639 ++count;
rob@100 1640 buckets[index] = [{
rob@100 1641 key: key,
rob@100 1642 value: value
rob@100 1643 }];
rob@100 1644 ensureLoad();
rob@100 1645 return null;
rob@100 1646 }
rob@100 1647 for (var i = 0; i < bucket.length; ++i) {
rob@100 1648 if (virtEquals(bucket[i].key, key)) {
rob@100 1649 var previous = bucket[i].value;
rob@100 1650 bucket[i].value = value;
rob@100 1651 return previous;
rob@100 1652 }
rob@100 1653 }
rob@100 1654 ++count;
rob@100 1655 bucket.push({
rob@100 1656 key: key,
rob@100 1657 value: value
rob@100 1658 });
rob@100 1659 ensureLoad();
rob@100 1660 return null;
rob@100 1661 };
rob@100 1662
rob@100 1663 this.putAll = function(m) {
rob@100 1664 var it = m.entrySet().iterator();
rob@100 1665 while (it.hasNext()) {
rob@100 1666 var entry = it.next();
rob@100 1667 this.put(entry.getKey(), entry.getValue());
rob@100 1668 }
rob@100 1669 };
rob@100 1670
rob@100 1671 this.remove = function(key) {
rob@100 1672 var index = getBucketIndex(key);
rob@100 1673 var bucket = buckets[index];
rob@100 1674 if (bucket === undefined) {
rob@100 1675 return null;
rob@100 1676 }
rob@100 1677 for (var i = 0; i < bucket.length; ++i) {
rob@100 1678 if (virtEquals(bucket[i].key, key)) {
rob@100 1679 --count;
rob@100 1680 var previous = bucket[i].value;
rob@100 1681 bucket[i].removed = true;
rob@100 1682 if (bucket.length > 1) {
rob@100 1683 bucket.splice(i, 1);
rob@100 1684 } else {
rob@100 1685 buckets[index] = undefined;
rob@100 1686 }
rob@100 1687 return previous;
rob@100 1688 }
rob@100 1689 }
rob@100 1690 return null;
rob@100 1691 };
rob@100 1692
rob@100 1693 this.removeByValue = function(value) {
rob@100 1694 var bucket, i, ilen, pair;
rob@100 1695 for (bucket in buckets) {
rob@100 1696 if (buckets.hasOwnProperty(bucket)) {
rob@100 1697 for (i = 0, ilen = buckets[bucket].length; i < ilen; i++) {
rob@100 1698 pair = buckets[bucket][i];
rob@100 1699 // removal on values is based on identity, not equality
rob@100 1700 if (pair.value === value) {
rob@100 1701 buckets[bucket].splice(i, 1);
rob@100 1702 return true;
rob@100 1703 }
rob@100 1704 }
rob@100 1705 }
rob@100 1706 }
rob@100 1707 return false;
rob@100 1708 };
rob@100 1709
rob@100 1710 this.size = function() {
rob@100 1711 return count;
rob@100 1712 };
rob@100 1713 }
rob@100 1714
rob@100 1715 return HashMap;
rob@100 1716 };
rob@100 1717
rob@100 1718 },{}],12:[function(require,module,exports){
rob@100 1719 // module export
rob@100 1720 module.exports = function(options,undef) {
rob@100 1721 var window = options.Browser.window,
rob@100 1722 document = options.Browser.document,
rob@100 1723 noop = options.noop;
rob@100 1724
rob@100 1725 /**
rob@100 1726 * [internal function] computeFontMetrics() calculates various metrics for text
rob@100 1727 * placement. Currently this function computes the ascent, descent and leading
rob@100 1728 * (from "lead", used for vertical space) values for the currently active font.
rob@100 1729 */
rob@100 1730 function computeFontMetrics(pfont) {
rob@100 1731 var emQuad = 250,
rob@100 1732 correctionFactor = pfont.size / emQuad,
rob@100 1733 canvas = document.createElement("canvas");
rob@100 1734 canvas.width = 2*emQuad;
rob@100 1735 canvas.height = 2*emQuad;
rob@100 1736 canvas.style.opacity = 0;
rob@100 1737 var cfmFont = pfont.getCSSDefinition(emQuad+"px", "normal"),
rob@100 1738 ctx = canvas.getContext("2d");
rob@100 1739 ctx.font = cfmFont;
rob@100 1740
rob@100 1741 // Size the canvas using a string with common max-ascent and max-descent letters.
rob@100 1742 // Changing the canvas dimensions resets the context, so we must reset the font.
rob@100 1743 var protrusions = "dbflkhyjqpg";
rob@100 1744 canvas.width = ctx.measureText(protrusions).width;
rob@100 1745 ctx.font = cfmFont;
rob@100 1746
rob@100 1747 // for text lead values, we meaure a multiline text container.
rob@100 1748 var leadDiv = document.createElement("div");
rob@100 1749 leadDiv.style.position = "absolute";
rob@100 1750 leadDiv.style.opacity = 0;
rob@100 1751 leadDiv.style.fontFamily = '"' + pfont.name + '"';
rob@100 1752 leadDiv.style.fontSize = emQuad + "px";
rob@100 1753 leadDiv.innerHTML = protrusions + "<br/>" + protrusions;
rob@100 1754 document.body.appendChild(leadDiv);
rob@100 1755
rob@100 1756 var w = canvas.width,
rob@100 1757 h = canvas.height,
rob@100 1758 baseline = h/2;
rob@100 1759
rob@100 1760 // Set all canvas pixeldata values to 255, with all the content
rob@100 1761 // data being 0. This lets us scan for data[i] != 255.
rob@100 1762 ctx.fillStyle = "white";
rob@100 1763 ctx.fillRect(0, 0, w, h);
rob@100 1764 ctx.fillStyle = "black";
rob@100 1765 ctx.fillText(protrusions, 0, baseline);
rob@100 1766 var pixelData = ctx.getImageData(0, 0, w, h).data;
rob@100 1767
rob@100 1768 // canvas pixel data is w*4 by h*4, because R, G, B and A are separate,
rob@100 1769 // consecutive values in the array, rather than stored as 32 bit ints.
rob@100 1770 var i = 0,
rob@100 1771 w4 = w * 4,
rob@100 1772 len = pixelData.length;
rob@100 1773
rob@100 1774 // Finding the ascent uses a normal, forward scanline
rob@100 1775 while (++i < len && pixelData[i] === 255) {
rob@100 1776 noop();
rob@100 1777 }
rob@100 1778 var ascent = Math.round(i / w4);
rob@100 1779
rob@100 1780 // Finding the descent uses a reverse scanline
rob@100 1781 i = len - 1;
rob@100 1782 while (--i > 0 && pixelData[i] === 255) {
rob@100 1783 noop();
rob@100 1784 }
rob@100 1785 var descent = Math.round(i / w4);
rob@100 1786
rob@100 1787 // set font metrics
rob@100 1788 pfont.ascent = correctionFactor * (baseline - ascent);
rob@100 1789 pfont.descent = correctionFactor * (descent - baseline);
rob@100 1790
rob@100 1791 // Then we try to get the real value from the browser
rob@100 1792 if (document.defaultView.getComputedStyle) {
rob@100 1793 var leadDivHeight = document.defaultView.getComputedStyle(leadDiv,null).getPropertyValue("height");
rob@100 1794 leadDivHeight = correctionFactor * leadDivHeight.replace("px","");
rob@100 1795 if (leadDivHeight >= pfont.size * 2) {
rob@100 1796 pfont.leading = Math.round(leadDivHeight/2);
rob@100 1797 }
rob@100 1798 }
rob@100 1799 document.body.removeChild(leadDiv);
rob@100 1800
rob@100 1801 // if we're caching, cache the context used for this pfont
rob@100 1802 if (pfont.caching) {
rob@100 1803 return ctx;
rob@100 1804 }
rob@100 1805 }
rob@100 1806
rob@100 1807 /**
rob@100 1808 * Constructor for a system or from-file (non-SVG) font.
rob@100 1809 */
rob@100 1810 function PFont(name, size) {
rob@100 1811 // according to the P5 API, new PFont() is legal (albeit completely useless)
rob@100 1812 if (name === undef) {
rob@100 1813 name = "";
rob@100 1814 }
rob@100 1815 this.name = name;
rob@100 1816 if (size === undef) {
rob@100 1817 size = 0;
rob@100 1818 }
rob@100 1819 this.size = size;
rob@100 1820 this.glyph = false;
rob@100 1821 this.ascent = 0;
rob@100 1822 this.descent = 0;
rob@100 1823 // For leading, the "safe" value uses the standard TEX ratio
rob@100 1824 this.leading = 1.2 * size;
rob@100 1825
rob@100 1826 // Note that an italic, bold font must used "... Bold Italic"
rob@100 1827 // in P5. "... Italic Bold" is treated as normal/normal.
rob@100 1828 var illegalIndicator = name.indexOf(" Italic Bold");
rob@100 1829 if (illegalIndicator !== -1) {
rob@100 1830 name = name.substring(0, illegalIndicator);
rob@100 1831 }
rob@100 1832
rob@100 1833 // determine font style
rob@100 1834 this.style = "normal";
rob@100 1835 var italicsIndicator = name.indexOf(" Italic");
rob@100 1836 if (italicsIndicator !== -1) {
rob@100 1837 name = name.substring(0, italicsIndicator);
rob@100 1838 this.style = "italic";
rob@100 1839 }
rob@100 1840
rob@100 1841 // determine font weight
rob@100 1842 this.weight = "normal";
rob@100 1843 var boldIndicator = name.indexOf(" Bold");
rob@100 1844 if (boldIndicator !== -1) {
rob@100 1845 name = name.substring(0, boldIndicator);
rob@100 1846 this.weight = "bold";
rob@100 1847 }
rob@100 1848
rob@100 1849 // determine font-family name
rob@100 1850 this.family = "sans-serif";
rob@100 1851 if (name !== undef) {
rob@100 1852 switch(name) {
rob@100 1853 case "sans-serif":
rob@100 1854 case "serif":
rob@100 1855 case "monospace":
rob@100 1856 case "fantasy":
rob@100 1857 case "cursive":
rob@100 1858 this.family = name;
rob@100 1859 break;
rob@100 1860 default:
rob@100 1861 this.family = '"' + name + '", sans-serif';
rob@100 1862 break;
rob@100 1863 }
rob@100 1864 }
rob@100 1865 // Calculate the ascent/descent/leading value based on
rob@100 1866 // how the browser renders this font.
rob@100 1867 this.context2d = computeFontMetrics(this);
rob@100 1868 this.css = this.getCSSDefinition();
rob@100 1869 if (this.context2d) {
rob@100 1870 this.context2d.font = this.css;
rob@100 1871 }
rob@100 1872 }
rob@100 1873
rob@100 1874 /**
rob@100 1875 * regulates whether or not we're caching the canvas
rob@100 1876 * 2d context for quick text width computation.
rob@100 1877 */
rob@100 1878 PFont.prototype.caching = true;
rob@100 1879
rob@100 1880 /**
rob@100 1881 * This function generates the CSS "font" string for this PFont
rob@100 1882 */
rob@100 1883 PFont.prototype.getCSSDefinition = function(fontSize, lineHeight) {
rob@100 1884 if(fontSize===undef) {
rob@100 1885 fontSize = this.size + "px";
rob@100 1886 }
rob@100 1887 if(lineHeight===undef) {
rob@100 1888 lineHeight = this.leading + "px";
rob@100 1889 }
rob@100 1890 // CSS "font" definition: font-style font-variant font-weight font-size/line-height font-family
rob@100 1891 var components = [this.style, "normal", this.weight, fontSize + "/" + lineHeight, this.family];
rob@100 1892 return components.join(" ");
rob@100 1893 };
rob@100 1894
rob@100 1895 /**
rob@100 1896 * Rely on the cached context2d measureText function.
rob@100 1897 */
rob@100 1898 PFont.prototype.measureTextWidth = function(string) {
rob@100 1899 return this.context2d.measureText(string).width;
rob@100 1900 };
rob@100 1901
rob@100 1902 /**
rob@100 1903 * FALLBACK FUNCTION -- replaces Pfont.prototype.measureTextWidth
rob@100 1904 * when the font cache becomes too large. This contructs a new
rob@100 1905 * canvas 2d context object for calling measureText on.
rob@100 1906 */
rob@100 1907 PFont.prototype.measureTextWidthFallback = function(string) {
rob@100 1908 var canvas = document.createElement("canvas"),
rob@100 1909 ctx = canvas.getContext("2d");
rob@100 1910 ctx.font = this.css;
rob@100 1911 return ctx.measureText(string).width;
rob@100 1912 };
rob@100 1913
rob@100 1914 /**
rob@100 1915 * Global "loaded fonts" list, internal to PFont
rob@100 1916 */
rob@100 1917 PFont.PFontCache = { length: 0 };
rob@100 1918
rob@100 1919 /**
rob@100 1920 * This function acts as single access point for getting and caching
rob@100 1921 * fonts across all sketches handled by an instance of Processing.js
rob@100 1922 */
rob@100 1923 PFont.get = function(fontName, fontSize) {
rob@100 1924 // round fontSize to one decimal point
rob@100 1925 fontSize = ((fontSize*10)+0.5|0)/10;
rob@100 1926 var cache = PFont.PFontCache,
rob@100 1927 idx = fontName+"/"+fontSize;
rob@100 1928 if (!cache[idx]) {
rob@100 1929 cache[idx] = new PFont(fontName, fontSize);
rob@100 1930 cache.length++;
rob@100 1931
rob@100 1932 // FALLBACK FUNCTIONALITY 1:
rob@100 1933 // If the cache has become large, switch over from full caching
rob@100 1934 // to caching only the static metrics for each new font request.
rob@100 1935 if (cache.length === 50) {
rob@100 1936 PFont.prototype.measureTextWidth = PFont.prototype.measureTextWidthFallback;
rob@100 1937 PFont.prototype.caching = false;
rob@100 1938 // clear contexts stored for each cached font
rob@100 1939 var entry;
rob@100 1940 for (entry in cache) {
rob@100 1941 if (entry !== "length") {
rob@100 1942 cache[entry].context2d = null;
rob@100 1943 }
rob@100 1944 }
rob@100 1945 return new PFont(fontName, fontSize);
rob@100 1946 }
rob@100 1947
rob@100 1948 // FALLBACK FUNCTIONALITY 2:
rob@100 1949 // If the cache has become too large, switch off font caching entirely.
rob@100 1950 if (cache.length === 400) {
rob@100 1951 PFont.PFontCache = {};
rob@100 1952 PFont.get = PFont.getFallback;
rob@100 1953 return new PFont(fontName, fontSize);
rob@100 1954 }
rob@100 1955 }
rob@100 1956 return cache[idx];
rob@100 1957 };
rob@100 1958
rob@100 1959 /**
rob@100 1960 * FALLBACK FUNCTION -- replaces PFont.get when the font cache
rob@100 1961 * becomes too large. This function bypasses font caching entirely.
rob@100 1962 */
rob@100 1963 PFont.getFallback = function(fontName, fontSize) {
rob@100 1964 return new PFont(fontName, fontSize);
rob@100 1965 };
rob@100 1966
rob@100 1967 /**
rob@100 1968 * Lists all standard fonts. Due to browser limitations, this list is
rob@100 1969 * not the system font list, like in P5, but the CSS "genre" list.
rob@100 1970 */
rob@100 1971 PFont.list = function() {
rob@100 1972 return ["sans-serif", "serif", "monospace", "fantasy", "cursive"];
rob@100 1973 };
rob@100 1974
rob@100 1975 /**
rob@100 1976 * Loading external fonts through @font-face rules is handled by PFont,
rob@100 1977 * to ensure fonts loaded in this way are globally available.
rob@100 1978 */
rob@100 1979 PFont.preloading = {
rob@100 1980 // template element used to compare font sizes
rob@100 1981 template: {},
rob@100 1982 // indicates whether or not the reference tiny font has been loaded
rob@100 1983 initialized: false,
rob@100 1984 // load the reference tiny font via a css @font-face rule
rob@100 1985 initialize: function() {
rob@100 1986 var generateTinyFont = function() {
rob@100 1987 var encoded = "#E3KAI2wAgT1MvMg7Eo3VmNtYX7ABi3CxnbHlm" +
rob@100 1988 "7Abw3kaGVhZ7ACs3OGhoZWE7A53CRobXR47AY3" +
rob@100 1989 "AGbG9jYQ7G03Bm1heH7ABC3CBuYW1l7Ae3AgcG" +
rob@100 1990 "9zd7AI3AE#B3AQ2kgTY18PPPUACwAg3ALSRoo3" +
rob@100 1991 "#yld0xg32QAB77#E777773B#E3C#I#Q77773E#" +
rob@100 1992 "Q7777777772CMAIw7AB77732B#M#Q3wAB#g3B#" +
rob@100 1993 "E#E2BB//82BB////w#B7#gAEg3E77x2B32B#E#" +
rob@100 1994 "Q#MTcBAQ32gAe#M#QQJ#E32M#QQJ#I#g32Q77#";
rob@100 1995 var expand = function(input) {
rob@100 1996 return "AAAAAAAA".substr(~~input ? 7-input : 6);
rob@100 1997 };
rob@100 1998 return encoded.replace(/[#237]/g, expand);
rob@100 1999 };
rob@100 2000 var fontface = document.createElement("style");
rob@100 2001 fontface.setAttribute("type","text/css");
rob@100 2002 fontface.innerHTML = "@font-face {\n" +
rob@100 2003 ' font-family: "PjsEmptyFont";' + "\n" +
rob@100 2004 " src: url('data:application/x-font-ttf;base64,"+generateTinyFont()+"')\n" +
rob@100 2005 " format('truetype');\n" +
rob@100 2006 "}";
rob@100 2007 document.head.appendChild(fontface);
rob@100 2008
rob@100 2009 // set up the template element
rob@100 2010 var element = document.createElement("span");
rob@100 2011 element.style.cssText = 'position: absolute; top: 0; left: 0; opacity: 0; font-family: "PjsEmptyFont", fantasy;';
rob@100 2012 element.innerHTML = "AAAAAAAA";
rob@100 2013 document.body.appendChild(element);
rob@100 2014 this.template = element;
rob@100 2015
rob@100 2016 this.initialized = true;
rob@100 2017 },
rob@100 2018 // Shorthand function to get the computed width for an element.
rob@100 2019 getElementWidth: function(element) {
rob@100 2020 return document.defaultView.getComputedStyle(element,"").getPropertyValue("width");
rob@100 2021 },
rob@100 2022 // time taken so far in attempting to load a font
rob@100 2023 timeAttempted: 0,
rob@100 2024 // returns false if no fonts are pending load, or true otherwise.
rob@100 2025 pending: function(intervallength) {
rob@100 2026 if (!this.initialized) {
rob@100 2027 this.initialize();
rob@100 2028 }
rob@100 2029 var element,
rob@100 2030 computedWidthFont,
rob@100 2031 computedWidthRef = this.getElementWidth(this.template);
rob@100 2032 for (var i = 0; i < this.fontList.length; i++) {
rob@100 2033 // compares size of text in pixels. if equal, custom font is not yet loaded
rob@100 2034 element = this.fontList[i];
rob@100 2035 computedWidthFont = this.getElementWidth(element);
rob@100 2036 if (this.timeAttempted < 4000 && computedWidthFont === computedWidthRef) {
rob@100 2037 this.timeAttempted += intervallength;
rob@100 2038 return true;
rob@100 2039 } else {
rob@100 2040 document.body.removeChild(element);
rob@100 2041 this.fontList.splice(i--, 1);
rob@100 2042 this.timeAttempted = 0;
rob@100 2043 }
rob@100 2044 }
rob@100 2045 // if there are no more fonts to load, pending is false
rob@100 2046 if (this.fontList.length === 0) {
rob@100 2047 return false;
rob@100 2048 }
rob@100 2049 // We should have already returned before getting here.
rob@100 2050 // But, if we do get here, length!=0 so fonts are pending.
rob@100 2051 return true;
rob@100 2052 },
rob@100 2053 // fontList contains elements to compare font sizes against a template
rob@100 2054 fontList: [],
rob@100 2055 // addedList contains the fontnames of all the fonts loaded via @font-face
rob@100 2056 addedList: {},
rob@100 2057 // adds a font to the font cache
rob@100 2058 // creates an element using the font, to start loading the font,
rob@100 2059 // and compare against a default font to see if the custom font is loaded
rob@100 2060 add: function(fontSrc) {
rob@100 2061 if (!this.initialized) {
rob@100 2062 this.initialize();
rob@100 2063 }
rob@100 2064 // fontSrc can be a string or a javascript object
rob@100 2065 // acceptable fonts are .ttf, .otf, and data uri
rob@100 2066 var fontName = (typeof fontSrc === 'object' ? fontSrc.fontFace : fontSrc),
rob@100 2067 fontUrl = (typeof fontSrc === 'object' ? fontSrc.url : fontSrc);
rob@100 2068
rob@100 2069 // check whether we already created the @font-face rule for this font
rob@100 2070 if (this.addedList[fontName]) {
rob@100 2071 return;
rob@100 2072 }
rob@100 2073
rob@100 2074 // if we didn't, create the @font-face rule
rob@100 2075 var style = document.createElement("style");
rob@100 2076 style.setAttribute("type","text/css");
rob@100 2077 style.innerHTML = "@font-face{\n font-family: '" + fontName + "';\n src: url('" + fontUrl + "');\n}\n";
rob@100 2078 document.head.appendChild(style);
rob@100 2079 this.addedList[fontName] = true;
rob@100 2080
rob@100 2081 // also create the element to load and compare the new font
rob@100 2082 var element = document.createElement("span");
rob@100 2083 element.style.cssText = "position: absolute; top: 0; left: 0; opacity: 0;";
rob@100 2084 element.style.fontFamily = '"' + fontName + '", "PjsEmptyFont", fantasy';
rob@100 2085 element.innerHTML = "AAAAAAAA";
rob@100 2086 document.body.appendChild(element);
rob@100 2087 this.fontList.push(element);
rob@100 2088 }
rob@100 2089 };
rob@100 2090
rob@100 2091 return PFont;
rob@100 2092 };
rob@100 2093 },{}],13:[function(require,module,exports){
rob@100 2094 module.exports = function(options, undef) {
rob@100 2095
rob@100 2096 // FIXME: hack
rob@100 2097 var p = options.p;
rob@100 2098
rob@100 2099 /**
rob@100 2100 * PMatrix2D is a 3x2 affine matrix implementation. The constructor accepts another PMatrix2D or a list of six float elements.
rob@100 2101 * If no parameters are provided the matrix is set to the identity matrix.
rob@100 2102 *
rob@100 2103 * @param {PMatrix2D} matrix the initial matrix to set to
rob@100 2104 * @param {float} m00 the first element of the matrix
rob@100 2105 * @param {float} m01 the second element of the matrix
rob@100 2106 * @param {float} m02 the third element of the matrix
rob@100 2107 * @param {float} m10 the fourth element of the matrix
rob@100 2108 * @param {float} m11 the fifth element of the matrix
rob@100 2109 * @param {float} m12 the sixth element of the matrix
rob@100 2110 */
rob@100 2111 var PMatrix2D = function() {
rob@100 2112 if (arguments.length === 0) {
rob@100 2113 this.reset();
rob@100 2114 } else if (arguments.length === 1 && arguments[0] instanceof PMatrix2D) {
rob@100 2115 this.set(arguments[0].array());
rob@100 2116 } else if (arguments.length === 6) {
rob@100 2117 this.set(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5]);
rob@100 2118 }
rob@100 2119 };
rob@100 2120
rob@100 2121 /**
rob@100 2122 * PMatrix2D methods
rob@100 2123 */
rob@100 2124 PMatrix2D.prototype = {
rob@100 2125 /**
rob@100 2126 * @member PMatrix2D
rob@100 2127 * The set() function sets the matrix elements. The function accepts either another PMatrix2D, an array of elements, or a list of six floats.
rob@100 2128 *
rob@100 2129 * @param {PMatrix2D} matrix the matrix to set this matrix to
rob@100 2130 * @param {float[]} elements an array of elements to set this matrix to
rob@100 2131 * @param {float} m00 the first element of the matrix
rob@100 2132 * @param {float} m01 the third element of the matrix
rob@100 2133 * @param {float} m10 the fourth element of the matrix
rob@100 2134 * @param {float} m11 the fith element of the matrix
rob@100 2135 * @param {float} m12 the sixth element of the matrix
rob@100 2136 */
rob@100 2137 set: function() {
rob@100 2138 if (arguments.length === 6) {
rob@100 2139 var a = arguments;
rob@100 2140 this.set([a[0], a[1], a[2],
rob@100 2141 a[3], a[4], a[5]]);
rob@100 2142 } else if (arguments.length === 1 && arguments[0] instanceof PMatrix2D) {
rob@100 2143 this.elements = arguments[0].array();
rob@100 2144 } else if (arguments.length === 1 && arguments[0] instanceof Array) {
rob@100 2145 this.elements = arguments[0].slice();
rob@100 2146 }
rob@100 2147 },
rob@100 2148 /**
rob@100 2149 * @member PMatrix2D
rob@100 2150 * The get() function returns a copy of this PMatrix2D.
rob@100 2151 *
rob@100 2152 * @return {PMatrix2D} a copy of this PMatrix2D
rob@100 2153 */
rob@100 2154 get: function() {
rob@100 2155 var outgoing = new PMatrix2D();
rob@100 2156 outgoing.set(this.elements);
rob@100 2157 return outgoing;
rob@100 2158 },
rob@100 2159 /**
rob@100 2160 * @member PMatrix2D
rob@100 2161 * The reset() function sets this PMatrix2D to the identity matrix.
rob@100 2162 */
rob@100 2163 reset: function() {
rob@100 2164 this.set([1, 0, 0, 0, 1, 0]);
rob@100 2165 },
rob@100 2166 /**
rob@100 2167 * @member PMatrix2D
rob@100 2168 * The array() function returns a copy of the element values.
rob@100 2169 * @addon
rob@100 2170 *
rob@100 2171 * @return {float[]} returns a copy of the element values
rob@100 2172 */
rob@100 2173 array: function array() {
rob@100 2174 return this.elements.slice();
rob@100 2175 },
rob@100 2176 /**
rob@100 2177 * @member PMatrix2D
rob@100 2178 * The translate() function translates this matrix by moving the current coordinates to the location specified by tx and ty.
rob@100 2179 *
rob@100 2180 * @param {float} tx the x-axis coordinate to move to
rob@100 2181 * @param {float} ty the y-axis coordinate to move to
rob@100 2182 */
rob@100 2183 translate: function(tx, ty) {
rob@100 2184 this.elements[2] = tx * this.elements[0] + ty * this.elements[1] + this.elements[2];
rob@100 2185 this.elements[5] = tx * this.elements[3] + ty * this.elements[4] + this.elements[5];
rob@100 2186 },
rob@100 2187 /**
rob@100 2188 * @member PMatrix2D
rob@100 2189 * The invTranslate() function translates this matrix by moving the current coordinates to the negative location specified by tx and ty.
rob@100 2190 *
rob@100 2191 * @param {float} tx the x-axis coordinate to move to
rob@100 2192 * @param {float} ty the y-axis coordinate to move to
rob@100 2193 */
rob@100 2194 invTranslate: function(tx, ty) {
rob@100 2195 this.translate(-tx, -ty);
rob@100 2196 },
rob@100 2197 /**
rob@100 2198 * @member PMatrix2D
rob@100 2199 * The transpose() function is not used in processingjs.
rob@100 2200 */
rob@100 2201 transpose: function() {
rob@100 2202 // Does nothing in Processing.
rob@100 2203 },
rob@100 2204 /**
rob@100 2205 * @member PMatrix2D
rob@100 2206 * The mult() function multiplied this matrix.
rob@100 2207 * If two array elements are passed in the function will multiply a two element vector against this matrix.
rob@100 2208 * If target is null or not length four, a new float array will be returned.
rob@100 2209 * The values for vec and target can be the same (though that's less efficient).
rob@100 2210 * If two PVectors are passed in the function multiply the x and y coordinates of a PVector against this matrix.
rob@100 2211 *
rob@100 2212 * @param {PVector} source, target the PVectors used to multiply this matrix
rob@100 2213 * @param {float[]} source, target the arrays used to multiply this matrix
rob@100 2214 *
rob@100 2215 * @return {PVector|float[]} returns a PVector or an array representing the new matrix
rob@100 2216 */
rob@100 2217 mult: function(source, target) {
rob@100 2218 var x, y;
rob@100 2219 if (source instanceof PVector) {
rob@100 2220 x = source.x;
rob@100 2221 y = source.y;
rob@100 2222 if (!target) {
rob@100 2223 target = new PVector();
rob@100 2224 }
rob@100 2225 } else if (source instanceof Array) {
rob@100 2226 x = source[0];
rob@100 2227 y = source[1];
rob@100 2228 if (!target) {
rob@100 2229 target = [];
rob@100 2230 }
rob@100 2231 }
rob@100 2232 if (target instanceof Array) {
rob@100 2233 target[0] = this.elements[0] * x + this.elements[1] * y + this.elements[2];
rob@100 2234 target[1] = this.elements[3] * x + this.elements[4] * y + this.elements[5];
rob@100 2235 } else if (target instanceof PVector) {
rob@100 2236 target.x = this.elements[0] * x + this.elements[1] * y + this.elements[2];
rob@100 2237 target.y = this.elements[3] * x + this.elements[4] * y + this.elements[5];
rob@100 2238 target.z = 0;
rob@100 2239 }
rob@100 2240 return target;
rob@100 2241 },
rob@100 2242 /**
rob@100 2243 * @member PMatrix2D
rob@100 2244 * The multX() function calculates the x component of a vector from a transformation.
rob@100 2245 *
rob@100 2246 * @param {float} x the x component of the vector being transformed
rob@100 2247 * @param {float} y the y component of the vector being transformed
rob@100 2248 *
rob@100 2249 * @return {float} returnes the result of the calculation
rob@100 2250 */
rob@100 2251 multX: function(x, y) {
rob@100 2252 return (x * this.elements[0] + y * this.elements[1] + this.elements[2]);
rob@100 2253 },
rob@100 2254 /**
rob@100 2255 * @member PMatrix2D
rob@100 2256 * The multY() function calculates the y component of a vector from a transformation.
rob@100 2257 *
rob@100 2258 * @param {float} x the x component of the vector being transformed
rob@100 2259 * @param {float} y the y component of the vector being transformed
rob@100 2260 *
rob@100 2261 * @return {float} returnes the result of the calculation
rob@100 2262 */
rob@100 2263 multY: function(x, y) {
rob@100 2264 return (x * this.elements[3] + y * this.elements[4] + this.elements[5]);
rob@100 2265 },
rob@100 2266 /**
rob@100 2267 * @member PMatrix2D
rob@100 2268 * The skewX() function skews the matrix along the x-axis the amount specified by the angle parameter.
rob@100 2269 * Angles should be specified in radians (values from 0 to PI*2) or converted to radians with the <b>radians()</b> function.
rob@100 2270 *
rob@100 2271 * @param {float} angle angle of skew specified in radians
rob@100 2272 */
rob@100 2273 skewX: function(angle) {
rob@100 2274 this.apply(1, 0, 1, angle, 0, 0);
rob@100 2275 },
rob@100 2276 /**
rob@100 2277 * @member PMatrix2D
rob@100 2278 * The skewY() function skews the matrix along the y-axis the amount specified by the angle parameter.
rob@100 2279 * Angles should be specified in radians (values from 0 to PI*2) or converted to radians with the <b>radians()</b> function.
rob@100 2280 *
rob@100 2281 * @param {float} angle angle of skew specified in radians
rob@100 2282 */
rob@100 2283 skewY: function(angle) {
rob@100 2284 this.apply(1, 0, 1, 0, angle, 0);
rob@100 2285 },
rob@100 2286 /**
rob@100 2287 * @member PMatrix2D
rob@100 2288 * The shearX() function shears the matrix along the x-axis the amount specified by the angle parameter.
rob@100 2289 * Angles should be specified in radians (values from 0 to PI*2) or converted to radians with the <b>radians()</b> function.
rob@100 2290 *
rob@100 2291 * @param {float} angle angle of skew specified in radians
rob@100 2292 */
rob@100 2293 shearX: function(angle) {
rob@100 2294 this.apply(1, 0, 1, Math.tan(angle) , 0, 0);
rob@100 2295 },
rob@100 2296 /**
rob@100 2297 * @member PMatrix2D
rob@100 2298 * The shearY() function shears the matrix along the y-axis the amount specified by the angle parameter.
rob@100 2299 * Angles should be specified in radians (values from 0 to PI*2) or converted to radians with the <b>radians()</b> function.
rob@100 2300 *
rob@100 2301 * @param {float} angle angle of skew specified in radians
rob@100 2302 */
rob@100 2303 shearY: function(angle) {
rob@100 2304 this.apply(1, 0, 1, 0, Math.tan(angle), 0);
rob@100 2305 },
rob@100 2306 /**
rob@100 2307 * @member PMatrix2D
rob@100 2308 * The determinant() function calvculates the determinant of this matrix.
rob@100 2309 *
rob@100 2310 * @return {float} the determinant of the matrix
rob@100 2311 */
rob@100 2312 determinant: function() {
rob@100 2313 return (this.elements[0] * this.elements[4] - this.elements[1] * this.elements[3]);
rob@100 2314 },
rob@100 2315 /**
rob@100 2316 * @member PMatrix2D
rob@100 2317 * The invert() function inverts this matrix
rob@100 2318 *
rob@100 2319 * @return {boolean} true if successful
rob@100 2320 */
rob@100 2321 invert: function() {
rob@100 2322 var d = this.determinant();
rob@100 2323 if (Math.abs( d ) > PConstants.MIN_INT) {
rob@100 2324 var old00 = this.elements[0];
rob@100 2325 var old01 = this.elements[1];
rob@100 2326 var old02 = this.elements[2];
rob@100 2327 var old10 = this.elements[3];
rob@100 2328 var old11 = this.elements[4];
rob@100 2329 var old12 = this.elements[5];
rob@100 2330 this.elements[0] = old11 / d;
rob@100 2331 this.elements[3] = -old10 / d;
rob@100 2332 this.elements[1] = -old01 / d;
rob@100 2333 this.elements[4] = old00 / d;
rob@100 2334 this.elements[2] = (old01 * old12 - old11 * old02) / d;
rob@100 2335 this.elements[5] = (old10 * old02 - old00 * old12) / d;
rob@100 2336 return true;
rob@100 2337 }
rob@100 2338 return false;
rob@100 2339 },
rob@100 2340 /**
rob@100 2341 * @member PMatrix2D
rob@100 2342 * The scale() function increases or decreases the size of a shape by expanding and contracting vertices. When only one parameter is specified scale will occur in all dimensions.
rob@100 2343 * This is equivalent to a two parameter call.
rob@100 2344 *
rob@100 2345 * @param {float} sx the amount to scale on the x-axis
rob@100 2346 * @param {float} sy the amount to scale on the y-axis
rob@100 2347 */
rob@100 2348 scale: function(sx, sy) {
rob@100 2349 if (sx && !sy) {
rob@100 2350 sy = sx;
rob@100 2351 }
rob@100 2352 if (sx && sy) {
rob@100 2353 this.elements[0] *= sx;
rob@100 2354 this.elements[1] *= sy;
rob@100 2355 this.elements[3] *= sx;
rob@100 2356 this.elements[4] *= sy;
rob@100 2357 }
rob@100 2358 },
rob@100 2359 /**
rob@100 2360 * @member PMatrix2D
rob@100 2361 * The invScale() function decreases or increases the size of a shape by contracting and expanding vertices. When only one parameter is specified scale will occur in all dimensions.
rob@100 2362 * This is equivalent to a two parameter call.
rob@100 2363 *
rob@100 2364 * @param {float} sx the amount to scale on the x-axis
rob@100 2365 * @param {float} sy the amount to scale on the y-axis
rob@100 2366 */
rob@100 2367 invScale: function(sx, sy) {
rob@100 2368 if (sx && !sy) {
rob@100 2369 sy = sx;
rob@100 2370 }
rob@100 2371 this.scale(1 / sx, 1 / sy);
rob@100 2372 },
rob@100 2373 /**
rob@100 2374 * @member PMatrix2D
rob@100 2375 * The apply() function multiplies the current matrix by the one specified through the parameters. Note that either a PMatrix2D or a list of floats can be passed in.
rob@100 2376 *
rob@100 2377 * @param {PMatrix2D} matrix the matrix to apply this matrix to
rob@100 2378 * @param {float} m00 the first element of the matrix
rob@100 2379 * @param {float} m01 the third element of the matrix
rob@100 2380 * @param {float} m10 the fourth element of the matrix
rob@100 2381 * @param {float} m11 the fith element of the matrix
rob@100 2382 * @param {float} m12 the sixth element of the matrix
rob@100 2383 */
rob@100 2384 apply: function() {
rob@100 2385 var source;
rob@100 2386 if (arguments.length === 1 && arguments[0] instanceof PMatrix2D) {
rob@100 2387 source = arguments[0].array();
rob@100 2388 } else if (arguments.length === 6) {
rob@100 2389 source = Array.prototype.slice.call(arguments);
rob@100 2390 } else if (arguments.length === 1 && arguments[0] instanceof Array) {
rob@100 2391 source = arguments[0];
rob@100 2392 }
rob@100 2393
rob@100 2394 var result = [0, 0, this.elements[2],
rob@100 2395 0, 0, this.elements[5]];
rob@100 2396 var e = 0;
rob@100 2397 for (var row = 0; row < 2; row++) {
rob@100 2398 for (var col = 0; col < 3; col++, e++) {
rob@100 2399 result[e] += this.elements[row * 3 + 0] * source[col + 0] +
rob@100 2400 this.elements[row * 3 + 1] * source[col + 3];
rob@100 2401 }
rob@100 2402 }
rob@100 2403 this.elements = result.slice();
rob@100 2404 },
rob@100 2405 /**
rob@100 2406 * @member PMatrix2D
rob@100 2407 * The preApply() function applies another matrix to the left of this one. Note that either a PMatrix2D or elements of a matrix can be passed in.
rob@100 2408 *
rob@100 2409 * @param {PMatrix2D} matrix the matrix to apply this matrix to
rob@100 2410 * @param {float} m00 the first element of the matrix
rob@100 2411 * @param {float} m01 the third element of the matrix
rob@100 2412 * @param {float} m10 the fourth element of the matrix
rob@100 2413 * @param {float} m11 the fith element of the matrix
rob@100 2414 * @param {float} m12 the sixth element of the matrix
rob@100 2415 */
rob@100 2416 preApply: function() {
rob@100 2417 var source;
rob@100 2418 if (arguments.length === 1 && arguments[0] instanceof PMatrix2D) {
rob@100 2419 source = arguments[0].array();
rob@100 2420 } else if (arguments.length === 6) {
rob@100 2421 source = Array.prototype.slice.call(arguments);
rob@100 2422 } else if (arguments.length === 1 && arguments[0] instanceof Array) {
rob@100 2423 source = arguments[0];
rob@100 2424 }
rob@100 2425 var result = [0, 0, source[2],
rob@100 2426 0, 0, source[5]];
rob@100 2427 result[2] = source[2] + this.elements[2] * source[0] + this.elements[5] * source[1];
rob@100 2428 result[5] = source[5] + this.elements[2] * source[3] + this.elements[5] * source[4];
rob@100 2429 result[0] = this.elements[0] * source[0] + this.elements[3] * source[1];
rob@100 2430 result[3] = this.elements[0] * source[3] + this.elements[3] * source[4];
rob@100 2431 result[1] = this.elements[1] * source[0] + this.elements[4] * source[1];
rob@100 2432 result[4] = this.elements[1] * source[3] + this.elements[4] * source[4];
rob@100 2433 this.elements = result.slice();
rob@100 2434 },
rob@100 2435 /**
rob@100 2436 * @member PMatrix2D
rob@100 2437 * The rotate() function rotates the matrix.
rob@100 2438 *
rob@100 2439 * @param {float} angle the angle of rotation in radiants
rob@100 2440 */
rob@100 2441 rotate: function(angle) {
rob@100 2442 var c = Math.cos(angle);
rob@100 2443 var s = Math.sin(angle);
rob@100 2444 var temp1 = this.elements[0];
rob@100 2445 var temp2 = this.elements[1];
rob@100 2446 this.elements[0] = c * temp1 + s * temp2;
rob@100 2447 this.elements[1] = -s * temp1 + c * temp2;
rob@100 2448 temp1 = this.elements[3];
rob@100 2449 temp2 = this.elements[4];
rob@100 2450 this.elements[3] = c * temp1 + s * temp2;
rob@100 2451 this.elements[4] = -s * temp1 + c * temp2;
rob@100 2452 },
rob@100 2453 /**
rob@100 2454 * @member PMatrix2D
rob@100 2455 * The rotateZ() function rotates the matrix.
rob@100 2456 *
rob@100 2457 * @param {float} angle the angle of rotation in radiants
rob@100 2458 */
rob@100 2459 rotateZ: function(angle) {
rob@100 2460 this.rotate(angle);
rob@100 2461 },
rob@100 2462 /**
rob@100 2463 * @member PMatrix2D
rob@100 2464 * The invRotateZ() function rotates the matrix in opposite direction.
rob@100 2465 *
rob@100 2466 * @param {float} angle the angle of rotation in radiants
rob@100 2467 */
rob@100 2468 invRotateZ: function(angle) {
rob@100 2469 this.rotateZ(angle - Math.PI);
rob@100 2470 },
rob@100 2471 /**
rob@100 2472 * @member PMatrix2D
rob@100 2473 * The print() function prints out the elements of this matrix
rob@100 2474 */
rob@100 2475 print: function() {
rob@100 2476 var digits = printMatrixHelper(this.elements);
rob@100 2477 var output = "" + p.nfs(this.elements[0], digits, 4) + " " +
rob@100 2478 p.nfs(this.elements[1], digits, 4) + " " +
rob@100 2479 p.nfs(this.elements[2], digits, 4) + "\n" +
rob@100 2480 p.nfs(this.elements[3], digits, 4) + " " +
rob@100 2481 p.nfs(this.elements[4], digits, 4) + " " +
rob@100 2482 p.nfs(this.elements[5], digits, 4) + "\n\n";
rob@100 2483 p.println(output);
rob@100 2484 }
rob@100 2485 };
rob@100 2486
rob@100 2487 return PMatrix2D;
rob@100 2488 };
rob@100 2489
rob@100 2490 },{}],14:[function(require,module,exports){
rob@100 2491 module.exports = function(options, undef) {
rob@100 2492
rob@100 2493 // FIXME: hack
rob@100 2494 var p = options.p;
rob@100 2495
rob@100 2496 /**
rob@100 2497 * PMatrix3D is a 4x4 matrix implementation. The constructor accepts another PMatrix3D or a list of six or sixteen float elements.
rob@100 2498 * If no parameters are provided the matrix is set to the identity matrix.
rob@100 2499 */
rob@100 2500 var PMatrix3D = function() {
rob@100 2501 // When a matrix is created, it is set to an identity matrix
rob@100 2502 this.reset();
rob@100 2503 };
rob@100 2504
rob@100 2505 /**
rob@100 2506 * PMatrix3D methods
rob@100 2507 */
rob@100 2508 PMatrix3D.prototype = {
rob@100 2509 /**
rob@100 2510 * @member PMatrix2D
rob@100 2511 * The set() function sets the matrix elements. The function accepts either another PMatrix3D, an array of elements, or a list of six or sixteen floats.
rob@100 2512 *
rob@100 2513 * @param {PMatrix3D} matrix the initial matrix to set to
rob@100 2514 * @param {float[]} elements an array of elements to set this matrix to
rob@100 2515 * @param {float} m00 the first element of the matrix
rob@100 2516 * @param {float} m01 the second element of the matrix
rob@100 2517 * @param {float} m02 the third element of the matrix
rob@100 2518 * @param {float} m03 the fourth element of the matrix
rob@100 2519 * @param {float} m10 the fifth element of the matrix
rob@100 2520 * @param {float} m11 the sixth element of the matrix
rob@100 2521 * @param {float} m12 the seventh element of the matrix
rob@100 2522 * @param {float} m13 the eight element of the matrix
rob@100 2523 * @param {float} m20 the nineth element of the matrix
rob@100 2524 * @param {float} m21 the tenth element of the matrix
rob@100 2525 * @param {float} m22 the eleventh element of the matrix
rob@100 2526 * @param {float} m23 the twelveth element of the matrix
rob@100 2527 * @param {float} m30 the thirteenth element of the matrix
rob@100 2528 * @param {float} m31 the fourtheenth element of the matrix
rob@100 2529 * @param {float} m32 the fivetheenth element of the matrix
rob@100 2530 * @param {float} m33 the sixteenth element of the matrix
rob@100 2531 */
rob@100 2532 set: function() {
rob@100 2533 if (arguments.length === 16) {
rob@100 2534 this.elements = Array.prototype.slice.call(arguments);
rob@100 2535 } else if (arguments.length === 1 && arguments[0] instanceof PMatrix3D) {
rob@100 2536 this.elements = arguments[0].array();
rob@100 2537 } else if (arguments.length === 1 && arguments[0] instanceof Array) {
rob@100 2538 this.elements = arguments[0].slice();
rob@100 2539 }
rob@100 2540 },
rob@100 2541 /**
rob@100 2542 * @member PMatrix3D
rob@100 2543 * The get() function returns a copy of this PMatrix3D.
rob@100 2544 *
rob@100 2545 * @return {PMatrix3D} a copy of this PMatrix3D
rob@100 2546 */
rob@100 2547 get: function() {
rob@100 2548 var outgoing = new PMatrix3D();
rob@100 2549 outgoing.set(this.elements);
rob@100 2550 return outgoing;
rob@100 2551 },
rob@100 2552 /**
rob@100 2553 * @member PMatrix3D
rob@100 2554 * The reset() function sets this PMatrix3D to the identity matrix.
rob@100 2555 */
rob@100 2556 reset: function() {
rob@100 2557 this.elements = [1,0,0,0,
rob@100 2558 0,1,0,0,
rob@100 2559 0,0,1,0,
rob@100 2560 0,0,0,1];
rob@100 2561 },
rob@100 2562 /**
rob@100 2563 * @member PMatrix3D
rob@100 2564 * The array() function returns a copy of the element values.
rob@100 2565 * @addon
rob@100 2566 *
rob@100 2567 * @return {float[]} returns a copy of the element values
rob@100 2568 */
rob@100 2569 array: function array() {
rob@100 2570 return this.elements.slice();
rob@100 2571 },
rob@100 2572 /**
rob@100 2573 * @member PMatrix3D
rob@100 2574 * The translate() function translates this matrix by moving the current coordinates to the location specified by tx, ty, and tz.
rob@100 2575 *
rob@100 2576 * @param {float} tx the x-axis coordinate to move to
rob@100 2577 * @param {float} ty the y-axis coordinate to move to
rob@100 2578 * @param {float} tz the z-axis coordinate to move to
rob@100 2579 */
rob@100 2580 translate: function(tx, ty, tz) {
rob@100 2581 if (tz === undef) {
rob@100 2582 tz = 0;
rob@100 2583 }
rob@100 2584
rob@100 2585 this.elements[3] += tx * this.elements[0] + ty * this.elements[1] + tz * this.elements[2];
rob@100 2586 this.elements[7] += tx * this.elements[4] + ty * this.elements[5] + tz * this.elements[6];
rob@100 2587 this.elements[11] += tx * this.elements[8] + ty * this.elements[9] + tz * this.elements[10];
rob@100 2588 this.elements[15] += tx * this.elements[12] + ty * this.elements[13] + tz * this.elements[14];
rob@100 2589 },
rob@100 2590 /**
rob@100 2591 * @member PMatrix3D
rob@100 2592 * The transpose() function transpose this matrix.
rob@100 2593 */
rob@100 2594 transpose: function() {
rob@100 2595 var temp = this.elements[4];
rob@100 2596 this.elements[4] = this.elements[1];
rob@100 2597 this.elements[1] = temp;
rob@100 2598
rob@100 2599 temp = this.elements[8];
rob@100 2600 this.elements[8] = this.elements[2];
rob@100 2601 this.elements[2] = temp;
rob@100 2602
rob@100 2603 temp = this.elements[6];
rob@100 2604 this.elements[6] = this.elements[9];
rob@100 2605 this.elements[9] = temp;
rob@100 2606
rob@100 2607 temp = this.elements[3];
rob@100 2608 this.elements[3] = this.elements[12];
rob@100 2609 this.elements[12] = temp;
rob@100 2610
rob@100 2611 temp = this.elements[7];
rob@100 2612 this.elements[7] = this.elements[13];
rob@100 2613 this.elements[13] = temp;
rob@100 2614
rob@100 2615 temp = this.elements[11];
rob@100 2616 this.elements[11] = this.elements[14];
rob@100 2617 this.elements[14] = temp;
rob@100 2618 },
rob@100 2619 /**
rob@100 2620 * @member PMatrix3D
rob@100 2621 * The mult() function multiplied this matrix.
rob@100 2622 * If two array elements are passed in the function will multiply a two element vector against this matrix.
rob@100 2623 * If target is null or not length four, a new float array will be returned.
rob@100 2624 * The values for vec and target can be the same (though that's less efficient).
rob@100 2625 * If two PVectors are passed in the function multiply the x and y coordinates of a PVector against this matrix.
rob@100 2626 *
rob@100 2627 * @param {PVector} source, target the PVectors used to multiply this matrix
rob@100 2628 * @param {float[]} source, target the arrays used to multiply this matrix
rob@100 2629 *
rob@100 2630 * @return {PVector|float[]} returns a PVector or an array representing the new matrix
rob@100 2631 */
rob@100 2632 mult: function(source, target) {
rob@100 2633 var x, y, z, w;
rob@100 2634 if (source instanceof PVector) {
rob@100 2635 x = source.x;
rob@100 2636 y = source.y;
rob@100 2637 z = source.z;
rob@100 2638 w = 1;
rob@100 2639 if (!target) {
rob@100 2640 target = new PVector();
rob@100 2641 }
rob@100 2642 } else if (source instanceof Array) {
rob@100 2643 x = source[0];
rob@100 2644 y = source[1];
rob@100 2645 z = source[2];
rob@100 2646 w = source[3] || 1;
rob@100 2647
rob@100 2648 if ( !target || (target.length !== 3 && target.length !== 4) ) {
rob@100 2649 target = [0, 0, 0];
rob@100 2650 }
rob@100 2651 }
rob@100 2652
rob@100 2653 if (target instanceof Array) {
rob@100 2654 if (target.length === 3) {
rob@100 2655 target[0] = this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3];
rob@100 2656 target[1] = this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7];
rob@100 2657 target[2] = this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11];
rob@100 2658 } else if (target.length === 4) {
rob@100 2659 target[0] = this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3] * w;
rob@100 2660 target[1] = this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7] * w;
rob@100 2661 target[2] = this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11] * w;
rob@100 2662 target[3] = this.elements[12] * x + this.elements[13] * y + this.elements[14] * z + this.elements[15] * w;
rob@100 2663 }
rob@100 2664 }
rob@100 2665 if (target instanceof PVector) {
rob@100 2666 target.x = this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3];
rob@100 2667 target.y = this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7];
rob@100 2668 target.z = this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11];
rob@100 2669 }
rob@100 2670 return target;
rob@100 2671 },
rob@100 2672 /**
rob@100 2673 * @member PMatrix3D
rob@100 2674 * The preApply() function applies another matrix to the left of this one. Note that either a PMatrix3D or elements of a matrix can be passed in.
rob@100 2675 *
rob@100 2676 * @param {PMatrix3D} matrix the matrix to apply this matrix to
rob@100 2677 * @param {float} m00 the first element of the matrix
rob@100 2678 * @param {float} m01 the second element of the matrix
rob@100 2679 * @param {float} m02 the third element of the matrix
rob@100 2680 * @param {float} m03 the fourth element of the matrix
rob@100 2681 * @param {float} m10 the fifth element of the matrix
rob@100 2682 * @param {float} m11 the sixth element of the matrix
rob@100 2683 * @param {float} m12 the seventh element of the matrix
rob@100 2684 * @param {float} m13 the eight element of the matrix
rob@100 2685 * @param {float} m20 the nineth element of the matrix
rob@100 2686 * @param {float} m21 the tenth element of the matrix
rob@100 2687 * @param {float} m22 the eleventh element of the matrix
rob@100 2688 * @param {float} m23 the twelveth element of the matrix
rob@100 2689 * @param {float} m30 the thirteenth element of the matrix
rob@100 2690 * @param {float} m31 the fourtheenth element of the matrix
rob@100 2691 * @param {float} m32 the fivetheenth element of the matrix
rob@100 2692 * @param {float} m33 the sixteenth element of the matrix
rob@100 2693 */
rob@100 2694 preApply: function() {
rob@100 2695 var source;
rob@100 2696 if (arguments.length === 1 && arguments[0] instanceof PMatrix3D) {
rob@100 2697 source = arguments[0].array();
rob@100 2698 } else if (arguments.length === 16) {
rob@100 2699 source = Array.prototype.slice.call(arguments);
rob@100 2700 } else if (arguments.length === 1 && arguments[0] instanceof Array) {
rob@100 2701 source = arguments[0];
rob@100 2702 }
rob@100 2703
rob@100 2704 var result = [0, 0, 0, 0,
rob@100 2705 0, 0, 0, 0,
rob@100 2706 0, 0, 0, 0,
rob@100 2707 0, 0, 0, 0];
rob@100 2708 var e = 0;
rob@100 2709 for (var row = 0; row < 4; row++) {
rob@100 2710 for (var col = 0; col < 4; col++, e++) {
rob@100 2711 result[e] += this.elements[col + 0] * source[row * 4 + 0] + this.elements[col + 4] *
rob@100 2712 source[row * 4 + 1] + this.elements[col + 8] * source[row * 4 + 2] +
rob@100 2713 this.elements[col + 12] * source[row * 4 + 3];
rob@100 2714 }
rob@100 2715 }
rob@100 2716 this.elements = result.slice();
rob@100 2717 },
rob@100 2718 /**
rob@100 2719 * @member PMatrix3D
rob@100 2720 * The apply() function multiplies the current matrix by the one specified through the parameters. Note that either a PMatrix3D or a list of floats can be passed in.
rob@100 2721 *
rob@100 2722 * @param {PMatrix3D} matrix the matrix to apply this matrix to
rob@100 2723 * @param {float} m00 the first element of the matrix
rob@100 2724 * @param {float} m01 the second element of the matrix
rob@100 2725 * @param {float} m02 the third element of the matrix
rob@100 2726 * @param {float} m03 the fourth element of the matrix
rob@100 2727 * @param {float} m10 the fifth element of the matrix
rob@100 2728 * @param {float} m11 the sixth element of the matrix
rob@100 2729 * @param {float} m12 the seventh element of the matrix
rob@100 2730 * @param {float} m13 the eight element of the matrix
rob@100 2731 * @param {float} m20 the nineth element of the matrix
rob@100 2732 * @param {float} m21 the tenth element of the matrix
rob@100 2733 * @param {float} m22 the eleventh element of the matrix
rob@100 2734 * @param {float} m23 the twelveth element of the matrix
rob@100 2735 * @param {float} m30 the thirteenth element of the matrix
rob@100 2736 * @param {float} m31 the fourtheenth element of the matrix
rob@100 2737 * @param {float} m32 the fivetheenth element of the matrix
rob@100 2738 * @param {float} m33 the sixteenth element of the matrix
rob@100 2739 */
rob@100 2740 apply: function() {
rob@100 2741 var source;
rob@100 2742 if (arguments.length === 1 && arguments[0] instanceof PMatrix3D) {
rob@100 2743 source = arguments[0].array();
rob@100 2744 } else if (arguments.length === 16) {
rob@100 2745 source = Array.prototype.slice.call(arguments);
rob@100 2746 } else if (arguments.length === 1 && arguments[0] instanceof Array) {
rob@100 2747 source = arguments[0];
rob@100 2748 }
rob@100 2749
rob@100 2750 var result = [0, 0, 0, 0,
rob@100 2751 0, 0, 0, 0,
rob@100 2752 0, 0, 0, 0,
rob@100 2753 0, 0, 0, 0];
rob@100 2754 var e = 0;
rob@100 2755 for (var row = 0; row < 4; row++) {
rob@100 2756 for (var col = 0; col < 4; col++, e++) {
rob@100 2757 result[e] += this.elements[row * 4 + 0] * source[col + 0] + this.elements[row * 4 + 1] *
rob@100 2758 source[col + 4] + this.elements[row * 4 + 2] * source[col + 8] +
rob@100 2759 this.elements[row * 4 + 3] * source[col + 12];
rob@100 2760 }
rob@100 2761 }
rob@100 2762 this.elements = result.slice();
rob@100 2763 },
rob@100 2764 /**
rob@100 2765 * @member PMatrix3D
rob@100 2766 * The rotate() function rotates the matrix.
rob@100 2767 *
rob@100 2768 * @param {float} angle the angle of rotation in radiants
rob@100 2769 */
rob@100 2770 rotate: function(angle, v0, v1, v2) {
rob@100 2771 if (!v1) {
rob@100 2772 this.rotateZ(angle);
rob@100 2773 } else {
rob@100 2774 // TODO should make sure this vector is normalized
rob@100 2775 var c = Math.cos(angle);
rob@100 2776 var s = Math.sin(angle);
rob@100 2777 var t = 1.0 - c;
rob@100 2778
rob@100 2779 this.apply((t * v0 * v0) + c,
rob@100 2780 (t * v0 * v1) - (s * v2),
rob@100 2781 (t * v0 * v2) + (s * v1),
rob@100 2782 0,
rob@100 2783 (t * v0 * v1) + (s * v2),
rob@100 2784 (t * v1 * v1) + c,
rob@100 2785 (t * v1 * v2) - (s * v0),
rob@100 2786 0,
rob@100 2787 (t * v0 * v2) - (s * v1),
rob@100 2788 (t * v1 * v2) + (s * v0),
rob@100 2789 (t * v2 * v2) + c,
rob@100 2790 0,
rob@100 2791 0, 0, 0, 1);
rob@100 2792 }
rob@100 2793 },
rob@100 2794 /**
rob@100 2795 * @member PMatrix3D
rob@100 2796 * The invApply() function applies the inverted matrix to this matrix.
rob@100 2797 *
rob@100 2798 * @param {float} m00 the first element of the matrix
rob@100 2799 * @param {float} m01 the second element of the matrix
rob@100 2800 * @param {float} m02 the third element of the matrix
rob@100 2801 * @param {float} m03 the fourth element of the matrix
rob@100 2802 * @param {float} m10 the fifth element of the matrix
rob@100 2803 * @param {float} m11 the sixth element of the matrix
rob@100 2804 * @param {float} m12 the seventh element of the matrix
rob@100 2805 * @param {float} m13 the eight element of the matrix
rob@100 2806 * @param {float} m20 the nineth element of the matrix
rob@100 2807 * @param {float} m21 the tenth element of the matrix
rob@100 2808 * @param {float} m22 the eleventh element of the matrix
rob@100 2809 * @param {float} m23 the twelveth element of the matrix
rob@100 2810 * @param {float} m30 the thirteenth element of the matrix
rob@100 2811 * @param {float} m31 the fourtheenth element of the matrix
rob@100 2812 * @param {float} m32 the fivetheenth element of the matrix
rob@100 2813 * @param {float} m33 the sixteenth element of the matrix
rob@100 2814 *
rob@100 2815 * @return {boolean} returns true if the operation was successful.
rob@100 2816 */
rob@100 2817 invApply: function() {
rob@100 2818 if (inverseCopy === undef) {
rob@100 2819 inverseCopy = new PMatrix3D();
rob@100 2820 }
rob@100 2821 var a = arguments;
rob@100 2822 inverseCopy.set(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8],
rob@100 2823 a[9], a[10], a[11], a[12], a[13], a[14], a[15]);
rob@100 2824
rob@100 2825 if (!inverseCopy.invert()) {
rob@100 2826 return false;
rob@100 2827 }
rob@100 2828 this.preApply(inverseCopy);
rob@100 2829 return true;
rob@100 2830 },
rob@100 2831 /**
rob@100 2832 * @member PMatrix3D
rob@100 2833 * The rotateZ() function rotates the matrix.
rob@100 2834 *
rob@100 2835 * @param {float} angle the angle of rotation in radiants
rob@100 2836 */
rob@100 2837 rotateX: function(angle) {
rob@100 2838 var c = Math.cos(angle);
rob@100 2839 var s = Math.sin(angle);
rob@100 2840 this.apply([1, 0, 0, 0, 0, c, -s, 0, 0, s, c, 0, 0, 0, 0, 1]);
rob@100 2841 },
rob@100 2842 /**
rob@100 2843 * @member PMatrix3D
rob@100 2844 * The rotateY() function rotates the matrix.
rob@100 2845 *
rob@100 2846 * @param {float} angle the angle of rotation in radiants
rob@100 2847 */
rob@100 2848 rotateY: function(angle) {
rob@100 2849 var c = Math.cos(angle);
rob@100 2850 var s = Math.sin(angle);
rob@100 2851 this.apply([c, 0, s, 0, 0, 1, 0, 0, -s, 0, c, 0, 0, 0, 0, 1]);
rob@100 2852 },
rob@100 2853 /**
rob@100 2854 * @member PMatrix3D
rob@100 2855 * The rotateZ() function rotates the matrix.
rob@100 2856 *
rob@100 2857 * @param {float} angle the angle of rotation in radiants
rob@100 2858 */
rob@100 2859 rotateZ: function(angle) {
rob@100 2860 var c = Math.cos(angle);
rob@100 2861 var s = Math.sin(angle);
rob@100 2862 this.apply([c, -s, 0, 0, s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
rob@100 2863 },
rob@100 2864 /**
rob@100 2865 * @member PMatrix3D
rob@100 2866 * The scale() function increases or decreases the size of a matrix by expanding and contracting vertices. When only one parameter is specified scale will occur in all dimensions.
rob@100 2867 * This is equivalent to a three parameter call.
rob@100 2868 *
rob@100 2869 * @param {float} sx the amount to scale on the x-axis
rob@100 2870 * @param {float} sy the amount to scale on the y-axis
rob@100 2871 * @param {float} sz the amount to scale on the z-axis
rob@100 2872 */
rob@100 2873 scale: function(sx, sy, sz) {
rob@100 2874 if (sx && !sy && !sz) {
rob@100 2875 sy = sz = sx;
rob@100 2876 } else if (sx && sy && !sz) {
rob@100 2877 sz = 1;
rob@100 2878 }
rob@100 2879
rob@100 2880 if (sx && sy && sz) {
rob@100 2881 this.elements[0] *= sx;
rob@100 2882 this.elements[1] *= sy;
rob@100 2883 this.elements[2] *= sz;
rob@100 2884 this.elements[4] *= sx;
rob@100 2885 this.elements[5] *= sy;
rob@100 2886 this.elements[6] *= sz;
rob@100 2887 this.elements[8] *= sx;
rob@100 2888 this.elements[9] *= sy;
rob@100 2889 this.elements[10] *= sz;
rob@100 2890 this.elements[12] *= sx;
rob@100 2891 this.elements[13] *= sy;
rob@100 2892 this.elements[14] *= sz;
rob@100 2893 }
rob@100 2894 },
rob@100 2895 /**
rob@100 2896 * @member PMatrix3D
rob@100 2897 * The skewX() function skews the matrix along the x-axis the amount specified by the angle parameter.
rob@100 2898 * Angles should be specified in radians (values from 0 to PI*2) or converted to radians with the <b>radians()</b> function.
rob@100 2899 *
rob@100 2900 * @param {float} angle angle of skew specified in radians
rob@100 2901 */
rob@100 2902 skewX: function(angle) {
rob@100 2903 var t = Math.tan(angle);
rob@100 2904 this.apply(1, t, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
rob@100 2905 },
rob@100 2906 /**
rob@100 2907 * @member PMatrix3D
rob@100 2908 * The skewY() function skews the matrix along the y-axis the amount specified by the angle parameter.
rob@100 2909 * Angles should be specified in radians (values from 0 to PI*2) or converted to radians with the <b>radians()</b> function.
rob@100 2910 *
rob@100 2911 * @param {float} angle angle of skew specified in radians
rob@100 2912 */
rob@100 2913 skewY: function(angle) {
rob@100 2914 var t = Math.tan(angle);
rob@100 2915 this.apply(1, 0, 0, 0, t, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
rob@100 2916 },
rob@100 2917 /**
rob@100 2918 * @member PMatrix3D
rob@100 2919 * The shearX() function shears the matrix along the x-axis the amount specified by the angle parameter.
rob@100 2920 * Angles should be specified in radians (values from 0 to PI*2) or converted to radians with the <b>radians()</b> function.
rob@100 2921 *
rob@100 2922 * @param {float} angle angle of shear specified in radians
rob@100 2923 */
rob@100 2924 shearX: function(angle) {
rob@100 2925 var t = Math.tan(angle);
rob@100 2926 this.apply(1, t, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
rob@100 2927 },
rob@100 2928 /**
rob@100 2929 * @member PMatrix3D
rob@100 2930 * The shearY() function shears the matrix along the y-axis the amount specified by the angle parameter.
rob@100 2931 * Angles should be specified in radians (values from 0 to PI*2) or converted to radians with the <b>radians()</b> function.
rob@100 2932 *
rob@100 2933 * @param {float} angle angle of shear specified in radians
rob@100 2934 */
rob@100 2935 shearY: function(angle) {
rob@100 2936 var t = Math.tan(angle);
rob@100 2937 this.apply(1, 0, 0, 0, t, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
rob@100 2938 },
rob@100 2939 multX: function(x, y, z, w) {
rob@100 2940 if (!z) {
rob@100 2941 return this.elements[0] * x + this.elements[1] * y + this.elements[3];
rob@100 2942 }
rob@100 2943 if (!w) {
rob@100 2944 return this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3];
rob@100 2945 }
rob@100 2946 return this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3] * w;
rob@100 2947 },
rob@100 2948 multY: function(x, y, z, w) {
rob@100 2949 if (!z) {
rob@100 2950 return this.elements[4] * x + this.elements[5] * y + this.elements[7];
rob@100 2951 }
rob@100 2952 if (!w) {
rob@100 2953 return this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7];
rob@100 2954 }
rob@100 2955 return this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7] * w;
rob@100 2956 },
rob@100 2957 multZ: function(x, y, z, w) {
rob@100 2958 if (!w) {
rob@100 2959 return this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11];
rob@100 2960 }
rob@100 2961 return this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11] * w;
rob@100 2962 },
rob@100 2963 multW: function(x, y, z, w) {
rob@100 2964 if (!w) {
rob@100 2965 return this.elements[12] * x + this.elements[13] * y + this.elements[14] * z + this.elements[15];
rob@100 2966 }
rob@100 2967 return this.elements[12] * x + this.elements[13] * y + this.elements[14] * z + this.elements[15] * w;
rob@100 2968 },
rob@100 2969 /**
rob@100 2970 * @member PMatrix3D
rob@100 2971 * The invert() function inverts this matrix
rob@100 2972 *
rob@100 2973 * @return {boolean} true if successful
rob@100 2974 */
rob@100 2975 invert: function() {
rob@100 2976 var fA0 = this.elements[0] * this.elements[5] - this.elements[1] * this.elements[4];
rob@100 2977 var fA1 = this.elements[0] * this.elements[6] - this.elements[2] * this.elements[4];
rob@100 2978 var fA2 = this.elements[0] * this.elements[7] - this.elements[3] * this.elements[4];
rob@100 2979 var fA3 = this.elements[1] * this.elements[6] - this.elements[2] * this.elements[5];
rob@100 2980 var fA4 = this.elements[1] * this.elements[7] - this.elements[3] * this.elements[5];
rob@100 2981 var fA5 = this.elements[2] * this.elements[7] - this.elements[3] * this.elements[6];
rob@100 2982 var fB0 = this.elements[8] * this.elements[13] - this.elements[9] * this.elements[12];
rob@100 2983 var fB1 = this.elements[8] * this.elements[14] - this.elements[10] * this.elements[12];
rob@100 2984 var fB2 = this.elements[8] * this.elements[15] - this.elements[11] * this.elements[12];
rob@100 2985 var fB3 = this.elements[9] * this.elements[14] - this.elements[10] * this.elements[13];
rob@100 2986 var fB4 = this.elements[9] * this.elements[15] - this.elements[11] * this.elements[13];
rob@100 2987 var fB5 = this.elements[10] * this.elements[15] - this.elements[11] * this.elements[14];
rob@100 2988
rob@100 2989 // Determinant
rob@100 2990 var fDet = fA0 * fB5 - fA1 * fB4 + fA2 * fB3 + fA3 * fB2 - fA4 * fB1 + fA5 * fB0;
rob@100 2991
rob@100 2992 // Account for a very small value
rob@100 2993 // return false if not successful.
rob@100 2994 if (Math.abs(fDet) <= 1e-9) {
rob@100 2995 return false;
rob@100 2996 }
rob@100 2997
rob@100 2998 var kInv = [];
rob@100 2999 kInv[0] = +this.elements[5] * fB5 - this.elements[6] * fB4 + this.elements[7] * fB3;
rob@100 3000 kInv[4] = -this.elements[4] * fB5 + this.elements[6] * fB2 - this.elements[7] * fB1;
rob@100 3001 kInv[8] = +this.elements[4] * fB4 - this.elements[5] * fB2 + this.elements[7] * fB0;
rob@100 3002 kInv[12] = -this.elements[4] * fB3 + this.elements[5] * fB1 - this.elements[6] * fB0;
rob@100 3003 kInv[1] = -this.elements[1] * fB5 + this.elements[2] * fB4 - this.elements[3] * fB3;
rob@100 3004 kInv[5] = +this.elements[0] * fB5 - this.elements[2] * fB2 + this.elements[3] * fB1;
rob@100 3005 kInv[9] = -this.elements[0] * fB4 + this.elements[1] * fB2 - this.elements[3] * fB0;
rob@100 3006 kInv[13] = +this.elements[0] * fB3 - this.elements[1] * fB1 + this.elements[2] * fB0;
rob@100 3007 kInv[2] = +this.elements[13] * fA5 - this.elements[14] * fA4 + this.elements[15] * fA3;
rob@100 3008 kInv[6] = -this.elements[12] * fA5 + this.elements[14] * fA2 - this.elements[15] * fA1;
rob@100 3009 kInv[10] = +this.elements[12] * fA4 - this.elements[13] * fA2 + this.elements[15] * fA0;
rob@100 3010 kInv[14] = -this.elements[12] * fA3 + this.elements[13] * fA1 - this.elements[14] * fA0;
rob@100 3011 kInv[3] = -this.elements[9] * fA5 + this.elements[10] * fA4 - this.elements[11] * fA3;
rob@100 3012 kInv[7] = +this.elements[8] * fA5 - this.elements[10] * fA2 + this.elements[11] * fA1;
rob@100 3013 kInv[11] = -this.elements[8] * fA4 + this.elements[9] * fA2 - this.elements[11] * fA0;
rob@100 3014 kInv[15] = +this.elements[8] * fA3 - this.elements[9] * fA1 + this.elements[10] * fA0;
rob@100 3015
rob@100 3016 // Inverse using Determinant
rob@100 3017 var fInvDet = 1.0 / fDet;
rob@100 3018 kInv[0] *= fInvDet;
rob@100 3019 kInv[1] *= fInvDet;
rob@100 3020 kInv[2] *= fInvDet;
rob@100 3021 kInv[3] *= fInvDet;
rob@100 3022 kInv[4] *= fInvDet;
rob@100 3023 kInv[5] *= fInvDet;
rob@100 3024 kInv[6] *= fInvDet;
rob@100 3025 kInv[7] *= fInvDet;
rob@100 3026 kInv[8] *= fInvDet;
rob@100 3027 kInv[9] *= fInvDet;
rob@100 3028 kInv[10] *= fInvDet;
rob@100 3029 kInv[11] *= fInvDet;
rob@100 3030 kInv[12] *= fInvDet;
rob@100 3031 kInv[13] *= fInvDet;
rob@100 3032 kInv[14] *= fInvDet;
rob@100 3033 kInv[15] *= fInvDet;
rob@100 3034
rob@100 3035 this.elements = kInv.slice();
rob@100 3036 return true;
rob@100 3037 },
rob@100 3038 toString: function() {
rob@100 3039 var str = "";
rob@100 3040 for (var i = 0; i < 15; i++) {
rob@100 3041 str += this.elements[i] + ", ";
rob@100 3042 }
rob@100 3043 str += this.elements[15];
rob@100 3044 return str;
rob@100 3045 },
rob@100 3046 /**
rob@100 3047 * @member PMatrix3D
rob@100 3048 * The print() function prints out the elements of this matrix
rob@100 3049 */
rob@100 3050 print: function() {
rob@100 3051 var digits = printMatrixHelper(this.elements);
rob@100 3052
rob@100 3053 var output = "" + p.nfs(this.elements[0], digits, 4) + " " + p.nfs(this.elements[1], digits, 4) +
rob@100 3054 " " + p.nfs(this.elements[2], digits, 4) + " " + p.nfs(this.elements[3], digits, 4) +
rob@100 3055 "\n" + p.nfs(this.elements[4], digits, 4) + " " + p.nfs(this.elements[5], digits, 4) +
rob@100 3056 " " + p.nfs(this.elements[6], digits, 4) + " " + p.nfs(this.elements[7], digits, 4) +
rob@100 3057 "\n" + p.nfs(this.elements[8], digits, 4) + " " + p.nfs(this.elements[9], digits, 4) +
rob@100 3058 " " + p.nfs(this.elements[10], digits, 4) + " " + p.nfs(this.elements[11], digits, 4) +
rob@100 3059 "\n" + p.nfs(this.elements[12], digits, 4) + " " + p.nfs(this.elements[13], digits, 4) +
rob@100 3060 " " + p.nfs(this.elements[14], digits, 4) + " " + p.nfs(this.elements[15], digits, 4) + "\n\n";
rob@100 3061 p.println(output);
rob@100 3062 },
rob@100 3063 invTranslate: function(tx, ty, tz) {
rob@100 3064 this.preApply(1, 0, 0, -tx, 0, 1, 0, -ty, 0, 0, 1, -tz, 0, 0, 0, 1);
rob@100 3065 },
rob@100 3066 invRotateX: function(angle) {
rob@100 3067 var c = Math.cos(-angle);
rob@100 3068 var s = Math.sin(-angle);
rob@100 3069 this.preApply([1, 0, 0, 0, 0, c, -s, 0, 0, s, c, 0, 0, 0, 0, 1]);
rob@100 3070 },
rob@100 3071 invRotateY: function(angle) {
rob@100 3072 var c = Math.cos(-angle);
rob@100 3073 var s = Math.sin(-angle);
rob@100 3074 this.preApply([c, 0, s, 0, 0, 1, 0, 0, -s, 0, c, 0, 0, 0, 0, 1]);
rob@100 3075 },
rob@100 3076 invRotateZ: function(angle) {
rob@100 3077 var c = Math.cos(-angle);
rob@100 3078 var s = Math.sin(-angle);
rob@100 3079 this.preApply([c, -s, 0, 0, s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
rob@100 3080 },
rob@100 3081 invScale: function(x, y, z) {
rob@100 3082 this.preApply([1 / x, 0, 0, 0, 0, 1 / y, 0, 0, 0, 0, 1 / z, 0, 0, 0, 0, 1]);
rob@100 3083 }
rob@100 3084 };
rob@100 3085
rob@100 3086 return PMatrix3D;
rob@100 3087 };
rob@100 3088 },{}],15:[function(require,module,exports){
rob@100 3089 module.exports = function(options) {
rob@100 3090 var PConstants = options.PConstants,
rob@100 3091 PMatrix2D = options.PMatrix2D,
rob@100 3092 PMatrix3D = options.PMatrix3D;
rob@100 3093
rob@100 3094 /**
rob@100 3095 * Datatype for storing shapes. Processing can currently load and display SVG (Scalable Vector Graphics) shapes.
rob@100 3096 * Before a shape is used, it must be loaded with the <b>loadShape()</b> function. The <b>shape()</b> function is used to draw the shape to the display window.
rob@100 3097 * The <b>PShape</b> object contain a group of methods, linked below, that can operate on the shape data.
rob@100 3098 * <br><br>The <b>loadShape()</b> method supports SVG files created with Inkscape and Adobe Illustrator.
rob@100 3099 * It is not a full SVG implementation, but offers some straightforward support for handling vector data.
rob@100 3100 *
rob@100 3101 * @param {int} family the shape type, one of GROUP, PRIMITIVE, PATH, or GEOMETRY
rob@100 3102 *
rob@100 3103 * @see #shape()
rob@100 3104 * @see #loadShape()
rob@100 3105 * @see #shapeMode()
rob@100 3106 */
rob@100 3107 var PShape = function(family) {
rob@100 3108 this.family = family || PConstants.GROUP;
rob@100 3109 this.visible = true;
rob@100 3110 this.style = true;
rob@100 3111 this.children = [];
rob@100 3112 this.nameTable = [];
rob@100 3113 this.params = [];
rob@100 3114 this.name = "";
rob@100 3115 this.image = null; //type PImage
rob@100 3116 this.matrix = null;
rob@100 3117 this.kind = null;
rob@100 3118 this.close = null;
rob@100 3119 this.width = null;
rob@100 3120 this.height = null;
rob@100 3121 this.parent = null;
rob@100 3122 };
rob@100 3123 /**
rob@100 3124 * PShape methods
rob@100 3125 * missing: findChild(), apply(), contains(), findChild(), getPrimitive(), getParams(), getVertex() , getVertexCount(),
rob@100 3126 * getVertexCode() , getVertexCodes() , getVertexCodeCount(), getVertexX(), getVertexY(), getVertexZ()
rob@100 3127 */
rob@100 3128 PShape.prototype = {
rob@100 3129 /**
rob@100 3130 * @member PShape
rob@100 3131 * The isVisible() function returns a boolean value "true" if the image is set to be visible, "false" if not. This is modified with the <b>setVisible()</b> parameter.
rob@100 3132 * <br><br>The visibility of a shape is usually controlled by whatever program created the SVG file.
rob@100 3133 * For instance, this parameter is controlled by showing or hiding the shape in the layers palette in Adobe Illustrator.
rob@100 3134 *
rob@100 3135 * @return {boolean} returns "true" if the image is set to be visible, "false" if not
rob@100 3136 */
rob@100 3137 isVisible: function(){
rob@100 3138 return this.visible;
rob@100 3139 },
rob@100 3140 /**
rob@100 3141 * @member PShape
rob@100 3142 * The setVisible() function sets the shape to be visible or invisible. This is determined by the value of the <b>visible</b> parameter.
rob@100 3143 * <br><br>The visibility of a shape is usually controlled by whatever program created the SVG file.
rob@100 3144 * For instance, this parameter is controlled by showing or hiding the shape in the layers palette in Adobe Illustrator.
rob@100 3145 *
rob@100 3146 * @param {boolean} visible "false" makes the shape invisible and "true" makes it visible
rob@100 3147 */
rob@100 3148 setVisible: function (visible){
rob@100 3149 this.visible = visible;
rob@100 3150 },
rob@100 3151 /**
rob@100 3152 * @member PShape
rob@100 3153 * The disableStyle() function disables the shape's style data and uses Processing's current styles. Styles include attributes such as colors, stroke weight, and stroke joints.
rob@100 3154 * Overrides this shape's style information and uses PGraphics styles and colors. Identical to ignoreStyles(true). Also disables styles for all child shapes.
rob@100 3155 */
rob@100 3156 disableStyle: function(){
rob@100 3157 this.style = false;
rob@100 3158 for(var i = 0, j=this.children.length; i<j; i++) {
rob@100 3159 this.children[i].disableStyle();
rob@100 3160 }
rob@100 3161 },
rob@100 3162 /**
rob@100 3163 * @member PShape
rob@100 3164 * The enableStyle() function enables the shape's style data and ignores Processing's current styles. Styles include attributes such as colors, stroke weight, and stroke joints.
rob@100 3165 */
rob@100 3166 enableStyle: function(){
rob@100 3167 this.style = true;
rob@100 3168 for(var i = 0, j=this.children.length; i<j; i++) {
rob@100 3169 this.children[i].enableStyle();
rob@100 3170 }
rob@100 3171 },
rob@100 3172 /**
rob@100 3173 * @member PShape
rob@100 3174 * The getFamily function returns the shape type
rob@100 3175 *
rob@100 3176 * @return {int} the shape type, one of GROUP, PRIMITIVE, PATH, or GEOMETRY
rob@100 3177 */
rob@100 3178 getFamily: function(){
rob@100 3179 return this.family;
rob@100 3180 },
rob@100 3181 /**
rob@100 3182 * @member PShape
rob@100 3183 * The getWidth() function gets the width of the drawing area (not necessarily the shape boundary).
rob@100 3184 */
rob@100 3185 getWidth: function(){
rob@100 3186 return this.width;
rob@100 3187 },
rob@100 3188 /**
rob@100 3189 * @member PShape
rob@100 3190 * The getHeight() function gets the height of the drawing area (not necessarily the shape boundary).
rob@100 3191 */
rob@100 3192 getHeight: function(){
rob@100 3193 return this.height;
rob@100 3194 },
rob@100 3195 /**
rob@100 3196 * @member PShape
rob@100 3197 * The setName() function sets the name of the shape
rob@100 3198 *
rob@100 3199 * @param {String} name the name of the shape
rob@100 3200 */
rob@100 3201 setName: function(name){
rob@100 3202 this.name = name;
rob@100 3203 },
rob@100 3204 /**
rob@100 3205 * @member PShape
rob@100 3206 * The getName() function returns the name of the shape
rob@100 3207 *
rob@100 3208 * @return {String} the name of the shape
rob@100 3209 */
rob@100 3210 getName: function(){
rob@100 3211 return this.name;
rob@100 3212 },
rob@100 3213 /**
rob@100 3214 * @member PShape
rob@100 3215 * Called by the following (the shape() command adds the g)
rob@100 3216 * PShape s = loadShapes("blah.svg");
rob@100 3217 * shape(s);
rob@100 3218 */
rob@100 3219 draw: function(renderContext) {
rob@100 3220 if(!renderContext) {
rob@100 3221 throw "render context missing for draw() in PShape";
rob@100 3222 }
rob@100 3223 if (this.visible) {
rob@100 3224 this.pre(renderContext);
rob@100 3225 this.drawImpl(renderContext);
rob@100 3226 this.post(renderContext);
rob@100 3227 }
rob@100 3228 },
rob@100 3229 /**
rob@100 3230 * @member PShape
rob@100 3231 * the drawImpl() function draws the SVG document.
rob@100 3232 */
rob@100 3233 drawImpl: function(renderContext) {
rob@100 3234 if (this.family === PConstants.GROUP) {
rob@100 3235 this.drawGroup(renderContext);
rob@100 3236 } else if (this.family === PConstants.PRIMITIVE) {
rob@100 3237 this.drawPrimitive(renderContext);
rob@100 3238 } else if (this.family === PConstants.GEOMETRY) {
rob@100 3239 this.drawGeometry(renderContext);
rob@100 3240 } else if (this.family === PConstants.PATH) {
rob@100 3241 this.drawPath(renderContext);
rob@100 3242 }
rob@100 3243 },
rob@100 3244 /**
rob@100 3245 * @member PShape
rob@100 3246 * The drawPath() function draws the <path> part of the SVG document.
rob@100 3247 */
rob@100 3248 drawPath: function(renderContext) {
rob@100 3249 var i, j;
rob@100 3250 if (this.vertices.length === 0) { return; }
rob@100 3251 renderContext.beginShape();
rob@100 3252 if (this.vertexCodes.length === 0) { // each point is a simple vertex
rob@100 3253 if (this.vertices[0].length === 2) { // drawing 2D vertices
rob@100 3254 for (i = 0, j = this.vertices.length; i < j; i++) {
rob@100 3255 renderContext.vertex(this.vertices[i][0], this.vertices[i][1]);
rob@100 3256 }
rob@100 3257 } else { // drawing 3D vertices
rob@100 3258 for (i = 0, j = this.vertices.length; i < j; i++) {
rob@100 3259 renderContext.vertex(this.vertices[i][0],
rob@100 3260 this.vertices[i][1],
rob@100 3261 this.vertices[i][2]);
rob@100 3262 }
rob@100 3263 }
rob@100 3264 } else { // coded set of vertices
rob@100 3265 var index = 0;
rob@100 3266 if (this.vertices[0].length === 2) { // drawing a 2D path
rob@100 3267 for (i = 0, j = this.vertexCodes.length; i < j; i++) {
rob@100 3268 if (this.vertexCodes[i] === PConstants.VERTEX) {
rob@100 3269 renderContext.vertex(this.vertices[index][0], this.vertices[index][1], this.vertices[index].moveTo);
rob@100 3270 renderContext.breakShape = false;
rob@100 3271 index++;
rob@100 3272 } else if (this.vertexCodes[i] === PConstants.BEZIER_VERTEX) {
rob@100 3273 renderContext.bezierVertex(this.vertices[index+0][0],
rob@100 3274 this.vertices[index+0][1],
rob@100 3275 this.vertices[index+1][0],
rob@100 3276 this.vertices[index+1][1],
rob@100 3277 this.vertices[index+2][0],
rob@100 3278 this.vertices[index+2][1]);
rob@100 3279 index += 3;
rob@100 3280 } else if (this.vertexCodes[i] === PConstants.CURVE_VERTEX) {
rob@100 3281 renderContext.curveVertex(this.vertices[index][0],
rob@100 3282 this.vertices[index][1]);
rob@100 3283 index++;
rob@100 3284 } else if (this.vertexCodes[i] === PConstants.BREAK) {
rob@100 3285 renderContext.breakShape = true;
rob@100 3286 }
rob@100 3287 }
rob@100 3288 } else { // drawing a 3D path
rob@100 3289 for (i = 0, j = this.vertexCodes.length; i < j; i++) {
rob@100 3290 if (this.vertexCodes[i] === PConstants.VERTEX) {
rob@100 3291 renderContext.vertex(this.vertices[index][0],
rob@100 3292 this.vertices[index][1],
rob@100 3293 this.vertices[index][2]);
rob@100 3294 if (this.vertices[index].moveTo === true) {
rob@100 3295 vertArray[vertArray.length-1].moveTo = true;
rob@100 3296 } else if (this.vertices[index].moveTo === false) {
rob@100 3297 vertArray[vertArray.length-1].moveTo = false;
rob@100 3298 }
rob@100 3299 renderContext.breakShape = false;
rob@100 3300 } else if (this.vertexCodes[i] === PConstants.BEZIER_VERTEX) {
rob@100 3301 renderContext.bezierVertex(this.vertices[index+0][0],
rob@100 3302 this.vertices[index+0][1],
rob@100 3303 this.vertices[index+0][2],
rob@100 3304 this.vertices[index+1][0],
rob@100 3305 this.vertices[index+1][1],
rob@100 3306 this.vertices[index+1][2],
rob@100 3307 this.vertices[index+2][0],
rob@100 3308 this.vertices[index+2][1],
rob@100 3309 this.vertices[index+2][2]);
rob@100 3310 index += 3;
rob@100 3311 } else if (this.vertexCodes[i] === PConstants.CURVE_VERTEX) {
rob@100 3312 renderContext.curveVertex(this.vertices[index][0],
rob@100 3313 this.vertices[index][1],
rob@100 3314 this.vertices[index][2]);
rob@100 3315 index++;
rob@100 3316 } else if (this.vertexCodes[i] === PConstants.BREAK) {
rob@100 3317 renderContext.breakShape = true;
rob@100 3318 }
rob@100 3319 }
rob@100 3320 }
rob@100 3321 }
rob@100 3322 renderContext.endShape(this.close ? PConstants.CLOSE : PConstants.OPEN);
rob@100 3323 },
rob@100 3324 /**
rob@100 3325 * @member PShape
rob@100 3326 * The drawGeometry() function draws the geometry part of the SVG document.
rob@100 3327 */
rob@100 3328 drawGeometry: function(renderContext) {
rob@100 3329 var i, j;
rob@100 3330 renderContext.beginShape(this.kind);
rob@100 3331 if (this.style) {
rob@100 3332 for (i = 0, j = this.vertices.length; i < j; i++) {
rob@100 3333 renderContext.vertex(this.vertices[i]);
rob@100 3334 }
rob@100 3335 } else {
rob@100 3336 for (i = 0, j = this.vertices.length; i < j; i++) {
rob@100 3337 var vert = this.vertices[i];
rob@100 3338 if (vert[2] === 0) {
rob@100 3339 renderContext.vertex(vert[0], vert[1]);
rob@100 3340 } else {
rob@100 3341 renderContext.vertex(vert[0], vert[1], vert[2]);
rob@100 3342 }
rob@100 3343 }
rob@100 3344 }
rob@100 3345 renderContext.endShape();
rob@100 3346 },
rob@100 3347 /**
rob@100 3348 * @member PShape
rob@100 3349 * The drawGroup() function draws the <g> part of the SVG document.
rob@100 3350 */
rob@100 3351 drawGroup: function(renderContext) {
rob@100 3352 for (var i = 0, j = this.children.length; i < j; i++) {
rob@100 3353 this.children[i].draw(renderContext);
rob@100 3354 }
rob@100 3355 },
rob@100 3356 /**
rob@100 3357 * @member PShape
rob@100 3358 * The drawPrimitive() function draws SVG document shape elements. These can be point, line, triangle, quad, rect, ellipse, arc, box, or sphere.
rob@100 3359 */
rob@100 3360 drawPrimitive: function(renderContext) {
rob@100 3361 if (this.kind === PConstants.POINT) {
rob@100 3362 renderContext.point(this.params[0], this.params[1]);
rob@100 3363 } else if (this.kind === PConstants.LINE) {
rob@100 3364 if (this.params.length === 4) { // 2D
rob@100 3365 renderContext.line(this.params[0], this.params[1],
rob@100 3366 this.params[2], this.params[3]);
rob@100 3367 } else { // 3D
rob@100 3368 renderContext.line(this.params[0], this.params[1], this.params[2],
rob@100 3369 this.params[3], this.params[4], this.params[5]);
rob@100 3370 }
rob@100 3371 } else if (this.kind === PConstants.TRIANGLE) {
rob@100 3372 renderContext.triangle(this.params[0], this.params[1],
rob@100 3373 this.params[2], this.params[3],
rob@100 3374 this.params[4], this.params[5]);
rob@100 3375 } else if (this.kind === PConstants.QUAD) {
rob@100 3376 renderContext.quad(this.params[0], this.params[1],
rob@100 3377 this.params[2], this.params[3],
rob@100 3378 this.params[4], this.params[5],
rob@100 3379 this.params[6], this.params[7]);
rob@100 3380 } else if (this.kind === PConstants.RECT) {
rob@100 3381 if (this.image !== null) {
rob@100 3382 var imMode = imageModeConvert;
rob@100 3383 renderContext.imageMode(PConstants.CORNER);
rob@100 3384 renderContext.image(this.image,
rob@100 3385 this.params[0],
rob@100 3386 this.params[1],
rob@100 3387 this.params[2],
rob@100 3388 this.params[3]);
rob@100 3389 imageModeConvert = imMode;
rob@100 3390 } else {
rob@100 3391 var rcMode = renderContext.curRectMode;
rob@100 3392 renderContext.rectMode(PConstants.CORNER);
rob@100 3393 renderContext.rect(this.params[0],
rob@100 3394 this.params[1],
rob@100 3395 this.params[2],
rob@100 3396 this.params[3]);
rob@100 3397 renderContext.curRectMode = rcMode;
rob@100 3398 }
rob@100 3399 } else if (this.kind === PConstants.ELLIPSE) {
rob@100 3400 var elMode = renderContext.curEllipseMode;
rob@100 3401 renderContext.ellipseMode(PConstants.CORNER);
rob@100 3402 renderContext.ellipse(this.params[0],
rob@100 3403 this.params[1],
rob@100 3404 this.params[2],
rob@100 3405 this.params[3]);
rob@100 3406 renderContext.curEllipseMode = elMode;
rob@100 3407 } else if (this.kind === PConstants.ARC) {
rob@100 3408 var eMode = curEllipseMode;
rob@100 3409 renderContext.ellipseMode(PConstants.CORNER);
rob@100 3410 renderContext.arc(this.params[0],
rob@100 3411 this.params[1],
rob@100 3412 this.params[2],
rob@100 3413 this.params[3],
rob@100 3414 this.params[4],
rob@100 3415 this.params[5]);
rob@100 3416 curEllipseMode = eMode;
rob@100 3417 } else if (this.kind === PConstants.BOX) {
rob@100 3418 if (this.params.length === 1) {
rob@100 3419 renderContext.box(this.params[0]);
rob@100 3420 } else {
rob@100 3421 renderContext.box(this.params[0], this.params[1], this.params[2]);
rob@100 3422 }
rob@100 3423 } else if (this.kind === PConstants.SPHERE) {
rob@100 3424 renderContext.sphere(this.params[0]);
rob@100 3425 }
rob@100 3426 },
rob@100 3427 /**
rob@100 3428 * @member PShape
rob@100 3429 * The pre() function performs the preparations before the SVG is drawn. This includes doing transformations and storing previous styles.
rob@100 3430 */
rob@100 3431 pre: function(renderContext) {
rob@100 3432 if (this.matrix) {
rob@100 3433 renderContext.pushMatrix();
rob@100 3434 renderContext.transform(this.matrix);
rob@100 3435 }
rob@100 3436 if (this.style) {
rob@100 3437 renderContext.pushStyle();
rob@100 3438 this.styles(renderContext);
rob@100 3439 }
rob@100 3440 },
rob@100 3441 /**
rob@100 3442 * @member PShape
rob@100 3443 * The post() function performs the necessary actions after the SVG is drawn. This includes removing transformations and removing added styles.
rob@100 3444 */
rob@100 3445 post: function(renderContext) {
rob@100 3446 if (this.matrix) {
rob@100 3447 renderContext.popMatrix();
rob@100 3448 }
rob@100 3449 if (this.style) {
rob@100 3450 renderContext.popStyle();
rob@100 3451 }
rob@100 3452 },
rob@100 3453 /**
rob@100 3454 * @member PShape
rob@100 3455 * The styles() function changes the Processing's current styles
rob@100 3456 */
rob@100 3457 styles: function(renderContext) {
rob@100 3458 if (this.stroke) {
rob@100 3459 renderContext.stroke(this.strokeColor);
rob@100 3460 renderContext.strokeWeight(this.strokeWeight);
rob@100 3461 renderContext.strokeCap(this.strokeCap);
rob@100 3462 renderContext.strokeJoin(this.strokeJoin);
rob@100 3463 } else {
rob@100 3464 renderContext.noStroke();
rob@100 3465 }
rob@100 3466
rob@100 3467 if (this.fill) {
rob@100 3468 renderContext.fill(this.fillColor);
rob@100 3469
rob@100 3470 } else {
rob@100 3471 renderContext.noFill();
rob@100 3472 }
rob@100 3473 },
rob@100 3474 /**
rob@100 3475 * @member PShape
rob@100 3476 * The getChild() function extracts a child shape from a parent shape. Specify the name of the shape with the <b>target</b> parameter or the
rob@100 3477 * layer position of the shape to get with the <b>index</b> parameter.
rob@100 3478 * The shape is returned as a <b>PShape</b> object, or <b>null</b> is returned if there is an error.
rob@100 3479 *
rob@100 3480 * @param {String} target the name of the shape to get
rob@100 3481 * @param {int} index the layer position of the shape to get
rob@100 3482 *
rob@100 3483 * @return {PShape} returns a child element of a shape as a PShape object or null if there is an error
rob@100 3484 */
rob@100 3485 getChild: function(child) {
rob@100 3486 var i, j;
rob@100 3487 if (typeof child === 'number') {
rob@100 3488 return this.children[child];
rob@100 3489 }
rob@100 3490 var found;
rob@100 3491 if(child === "" || this.name === child){
rob@100 3492 return this;
rob@100 3493 }
rob@100 3494 if(this.nameTable.length > 0) {
rob@100 3495 for(i = 0, j = this.nameTable.length; i < j || found; i++) {
rob@100 3496 if(this.nameTable[i].getName === child) {
rob@100 3497 found = this.nameTable[i];
rob@100 3498 break;
rob@100 3499 }
rob@100 3500 }
rob@100 3501 if (found) { return found; }
rob@100 3502 }
rob@100 3503 for(i = 0, j = this.children.length; i < j; i++) {
rob@100 3504 found = this.children[i].getChild(child);
rob@100 3505 if(found) { return found; }
rob@100 3506 }
rob@100 3507 return null;
rob@100 3508 },
rob@100 3509 /**
rob@100 3510 * @member PShape
rob@100 3511 * The getChildCount() returns the number of children
rob@100 3512 *
rob@100 3513 * @return {int} returns a count of children
rob@100 3514 */
rob@100 3515 getChildCount: function () {
rob@100 3516 return this.children.length;
rob@100 3517 },
rob@100 3518 /**
rob@100 3519 * @member PShape
rob@100 3520 * The addChild() adds a child to the PShape.
rob@100 3521 *
rob@100 3522 * @param {PShape} child the child to add
rob@100 3523 */
rob@100 3524 addChild: function( child ) {
rob@100 3525 this.children.push(child);
rob@100 3526 child.parent = this;
rob@100 3527 if (child.getName() !== null) {
rob@100 3528 this.addName(child.getName(), child);
rob@100 3529 }
rob@100 3530 },
rob@100 3531 /**
rob@100 3532 * @member PShape
rob@100 3533 * The addName() functions adds a shape to the name lookup table.
rob@100 3534 *
rob@100 3535 * @param {String} name the name to be added
rob@100 3536 * @param {PShape} shape the shape
rob@100 3537 */
rob@100 3538 addName: function(name, shape) {
rob@100 3539 if (this.parent !== null) {
rob@100 3540 this.parent.addName( name, shape );
rob@100 3541 } else {
rob@100 3542 this.nameTable.push( [name, shape] );
rob@100 3543 }
rob@100 3544 },
rob@100 3545 /**
rob@100 3546 * @member PShape
rob@100 3547 * The translate() function specifies an amount to displace the shape. The <b>x</b> parameter specifies left/right translation, the <b>y</b> parameter specifies up/down translation, and the <b>z</b> parameter specifies translations toward/away from the screen.
rob@100 3548 * Subsequent calls to the method accumulates the effect. For example, calling <b>translate(50, 0)</b> and then <b>translate(20, 0)</b> is the same as <b>translate(70, 0)</b>.
rob@100 3549 * This transformation is applied directly to the shape, it's not refreshed each time <b>draw()</b> is run.
rob@100 3550 * <br><br>Using this method with the <b>z</b> parameter requires using the P3D or OPENGL parameter in combination with size.
rob@100 3551 *
rob@100 3552 * @param {int|float} x left/right translation
rob@100 3553 * @param {int|float} y up/down translation
rob@100 3554 * @param {int|float} z forward/back translation
rob@100 3555 *
rob@100 3556 * @see PMatrix2D#translate
rob@100 3557 * @see PMatrix3D#translate
rob@100 3558 */
rob@100 3559 translate: function() {
rob@100 3560 if(arguments.length === 2)
rob@100 3561 {
rob@100 3562 this.checkMatrix(2);
rob@100 3563 this.matrix.translate(arguments[0], arguments[1]);
rob@100 3564 } else {
rob@100 3565 this.checkMatrix(3);
rob@100 3566 this.matrix.translate(arguments[0], arguments[1], 0);
rob@100 3567 }
rob@100 3568 },
rob@100 3569 /**
rob@100 3570 * @member PShape
rob@100 3571 * The checkMatrix() function makes sure that the shape's matrix is 1) not null, and 2) has a matrix
rob@100 3572 * that can handle <em>at least</em> the specified number of dimensions.
rob@100 3573 *
rob@100 3574 * @param {int} dimensions the specified number of dimensions
rob@100 3575 */
rob@100 3576 checkMatrix: function(dimensions) {
rob@100 3577 if(this.matrix === null) {
rob@100 3578 if(dimensions === 2) {
rob@100 3579 this.matrix = new PMatrix2D();
rob@100 3580 } else {
rob@100 3581 this.matrix = new PMatrix3D();
rob@100 3582 }
rob@100 3583 }else if(dimensions === 3 && this.matrix instanceof PMatrix2D) {
rob@100 3584 this.matrix = new PMatrix3D();
rob@100 3585 }
rob@100 3586 },
rob@100 3587 /**
rob@100 3588 * @member PShape
rob@100 3589 * The rotateX() function rotates a shape around the x-axis the amount specified by the <b>angle</b> parameter. Angles should be specified in radians (values from 0 to TWO_PI) or converted to radians with the <b>radians()</b> method.
rob@100 3590 * <br><br>Shapes are always rotated around the upper-left corner of their bounding box. Positive numbers rotate objects in a clockwise direction.
rob@100 3591 * Subsequent calls to the method accumulates the effect. For example, calling <b>rotateX(HALF_PI)</b> and then <b>rotateX(HALF_PI)</b> is the same as <b>rotateX(PI)</b>.
rob@100 3592 * This transformation is applied directly to the shape, it's not refreshed each time <b>draw()</b> is run.
rob@100 3593 * <br><br>This method requires a 3D renderer. You need to pass P3D or OPENGL as a third parameter into the <b>size()</b> method as shown in the example above.
rob@100 3594 *
rob@100 3595 * @param {float}angle angle of rotation specified in radians
rob@100 3596 *
rob@100 3597 * @see PMatrix3D#rotateX
rob@100 3598 */
rob@100 3599 rotateX: function(angle) {
rob@100 3600 this.rotate(angle, 1, 0, 0);
rob@100 3601 },
rob@100 3602 /**
rob@100 3603 * @member PShape
rob@100 3604 * The rotateY() function rotates a shape around the y-axis the amount specified by the <b>angle</b> parameter. Angles should be specified in radians (values from 0 to TWO_PI) or converted to radians with the <b>radians()</b> method.
rob@100 3605 * <br><br>Shapes are always rotated around the upper-left corner of their bounding box. Positive numbers rotate objects in a clockwise direction.
rob@100 3606 * Subsequent calls to the method accumulates the effect. For example, calling <b>rotateY(HALF_PI)</b> and then <b>rotateY(HALF_PI)</b> is the same as <b>rotateY(PI)</b>.
rob@100 3607 * This transformation is applied directly to the shape, it's not refreshed each time <b>draw()</b> is run.
rob@100 3608 * <br><br>This method requires a 3D renderer. You need to pass P3D or OPENGL as a third parameter into the <b>size()</b> method as shown in the example above.
rob@100 3609 *
rob@100 3610 * @param {float}angle angle of rotation specified in radians
rob@100 3611 *
rob@100 3612 * @see PMatrix3D#rotateY
rob@100 3613 */
rob@100 3614 rotateY: function(angle) {
rob@100 3615 this.rotate(angle, 0, 1, 0);
rob@100 3616 },
rob@100 3617 /**
rob@100 3618 * @member PShape
rob@100 3619 * The rotateZ() function rotates a shape around the z-axis the amount specified by the <b>angle</b> parameter. Angles should be specified in radians (values from 0 to TWO_PI) or converted to radians with the <b>radians()</b> method.
rob@100 3620 * <br><br>Shapes are always rotated around the upper-left corner of their bounding box. Positive numbers rotate objects in a clockwise direction.
rob@100 3621 * Subsequent calls to the method accumulates the effect. For example, calling <b>rotateZ(HALF_PI)</b> and then <b>rotateZ(HALF_PI)</b> is the same as <b>rotateZ(PI)</b>.
rob@100 3622 * This transformation is applied directly to the shape, it's not refreshed each time <b>draw()</b> is run.
rob@100 3623 * <br><br>This method requires a 3D renderer. You need to pass P3D or OPENGL as a third parameter into the <b>size()</b> method as shown in the example above.
rob@100 3624 *
rob@100 3625 * @param {float}angle angle of rotation specified in radians
rob@100 3626 *
rob@100 3627 * @see PMatrix3D#rotateZ
rob@100 3628 */
rob@100 3629 rotateZ: function(angle) {
rob@100 3630 this.rotate(angle, 0, 0, 1);
rob@100 3631 },
rob@100 3632 /**
rob@100 3633 * @member PShape
rob@100 3634 * The rotate() function rotates a shape the amount specified by the <b>angle</b> parameter. Angles should be specified in radians (values from 0 to TWO_PI) or converted to radians with the <b>radians()</b> method.
rob@100 3635 * <br><br>Shapes are always rotated around the upper-left corner of their bounding box. Positive numbers rotate objects in a clockwise direction.
rob@100 3636 * Transformations apply to everything that happens after and subsequent calls to the method accumulates the effect.
rob@100 3637 * For example, calling <b>rotate(HALF_PI)</b> and then <b>rotate(HALF_PI)</b> is the same as <b>rotate(PI)</b>.
rob@100 3638 * This transformation is applied directly to the shape, it's not refreshed each time <b>draw()</b> is run.
rob@100 3639 * If optional parameters x,y,z are supplied, the rotate is about the point (x, y, z).
rob@100 3640 *
rob@100 3641 * @param {float}angle angle of rotation specified in radians
rob@100 3642 * @param {float}x x-coordinate of the point
rob@100 3643 * @param {float}y y-coordinate of the point
rob@100 3644 * @param {float}z z-coordinate of the point
rob@100 3645 * @see PMatrix2D#rotate
rob@100 3646 * @see PMatrix3D#rotate
rob@100 3647 */
rob@100 3648 rotate: function() {
rob@100 3649 if(arguments.length === 1){
rob@100 3650 this.checkMatrix(2);
rob@100 3651 this.matrix.rotate(arguments[0]);
rob@100 3652 } else {
rob@100 3653 this.checkMatrix(3);
rob@100 3654 this.matrix.rotate(arguments[0],
rob@100 3655 arguments[1],
rob@100 3656 arguments[2],
rob@100 3657 arguments[3]);
rob@100 3658 }
rob@100 3659 },
rob@100 3660 /**
rob@100 3661 * @member PShape
rob@100 3662 * The scale() function increases or decreases the size of a shape by expanding and contracting vertices. Shapes always scale from the relative origin of their bounding box.
rob@100 3663 * Scale values are specified as decimal percentages. For example, the method call <b>scale(2.0)</b> increases the dimension of a shape by 200%.
rob@100 3664 * Subsequent calls to the method multiply the effect. For example, calling <b>scale(2.0)</b> and then <b>scale(1.5)</b> is the same as <b>scale(3.0)</b>.
rob@100 3665 * This transformation is applied directly to the shape, it's not refreshed each time <b>draw()</b> is run.
rob@100 3666 * <br><br>Using this fuction with the <b>z</b> parameter requires passing P3D or OPENGL into the size() parameter.
rob@100 3667 *
rob@100 3668 * @param {float}s percentage to scale the object
rob@100 3669 * @param {float}x percentage to scale the object in the x-axis
rob@100 3670 * @param {float}y percentage to scale the object in the y-axis
rob@100 3671 * @param {float}z percentage to scale the object in the z-axis
rob@100 3672 *
rob@100 3673 * @see PMatrix2D#scale
rob@100 3674 * @see PMatrix3D#scale
rob@100 3675 */
rob@100 3676 scale: function() {
rob@100 3677 if(arguments.length === 2) {
rob@100 3678 this.checkMatrix(2);
rob@100 3679 this.matrix.scale(arguments[0], arguments[1]);
rob@100 3680 } else if (arguments.length === 3) {
rob@100 3681 this.checkMatrix(2);
rob@100 3682 this.matrix.scale(arguments[0], arguments[1], arguments[2]);
rob@100 3683 } else {
rob@100 3684 this.checkMatrix(2);
rob@100 3685 this.matrix.scale(arguments[0]);
rob@100 3686 }
rob@100 3687 },
rob@100 3688 /**
rob@100 3689 * @member PShape
rob@100 3690 * The resetMatrix() function resets the matrix
rob@100 3691 *
rob@100 3692 * @see PMatrix2D#reset
rob@100 3693 * @see PMatrix3D#reset
rob@100 3694 */
rob@100 3695 resetMatrix: function() {
rob@100 3696 this.checkMatrix(2);
rob@100 3697 this.matrix.reset();
rob@100 3698 },
rob@100 3699 /**
rob@100 3700 * @member PShape
rob@100 3701 * The applyMatrix() function multiplies this matrix by another matrix of type PMatrix3D or PMatrix2D.
rob@100 3702 * Individual elements can also be provided
rob@100 3703 *
rob@100 3704 * @param {PMatrix3D|PMatrix2D} matrix the matrix to multiply by
rob@100 3705 *
rob@100 3706 * @see PMatrix2D#apply
rob@100 3707 * @see PMatrix3D#apply
rob@100 3708 */
rob@100 3709 applyMatrix: function(matrix) {
rob@100 3710 if (arguments.length === 1) {
rob@100 3711 this.applyMatrix(matrix.elements[0],
rob@100 3712 matrix.elements[1], 0,
rob@100 3713 matrix.elements[2],
rob@100 3714 matrix.elements[3],
rob@100 3715 matrix.elements[4], 0,
rob@100 3716 matrix.elements[5],
rob@100 3717 0, 0, 1, 0,
rob@100 3718 0, 0, 0, 1);
rob@100 3719 } else if (arguments.length === 6) {
rob@100 3720 this.checkMatrix(2);
rob@100 3721 this.matrix.apply(arguments[0], arguments[1], arguments[2], 0,
rob@100 3722 arguments[3], arguments[4], arguments[5], 0,
rob@100 3723 0, 0, 1, 0,
rob@100 3724 0, 0, 0, 1);
rob@100 3725
rob@100 3726 } else if (arguments.length === 16) {
rob@100 3727 this.checkMatrix(3);
rob@100 3728 this.matrix.apply(arguments[0],
rob@100 3729 arguments[1],
rob@100 3730 arguments[2],
rob@100 3731 arguments[3],
rob@100 3732 arguments[4],
rob@100 3733 arguments[5],
rob@100 3734 arguments[6],
rob@100 3735 arguments[7],
rob@100 3736 arguments[8],
rob@100 3737 arguments[9],
rob@100 3738 arguments[10],
rob@100 3739 arguments[11],
rob@100 3740 arguments[12],
rob@100 3741 arguments[13],
rob@100 3742 arguments[14],
rob@100 3743 arguments[15]);
rob@100 3744 }
rob@100 3745 }
rob@100 3746 };
rob@100 3747
rob@100 3748 return PShape;
rob@100 3749 };
rob@100 3750 },{}],16:[function(require,module,exports){
rob@100 3751 /**
rob@100 3752 * SVG stands for Scalable Vector Graphics, a portable graphics format. It is
rob@100 3753 * a vector format so it allows for infinite resolution and relatively small
rob@100 3754 * file sizes. Most modern media software can view SVG files, including Adobe
rob@100 3755 * products, Firefox, etc. Illustrator and Inkscape can edit SVG files.
rob@100 3756 *
rob@100 3757 * @param {PApplet} parent typically use "this"
rob@100 3758 * @param {String} filename name of the SVG file to load
rob@100 3759 * @param {XMLElement} xml an XMLElement element
rob@100 3760 * @param {PShapeSVG} parent the parent PShapeSVG
rob@100 3761 *
rob@100 3762 * @see PShape
rob@100 3763 */
rob@100 3764 module.exports = function(options) {
rob@100 3765 var CommonFunctions = options.CommonFunctions,
rob@100 3766 PConstants = options.PConstants,
rob@100 3767 PShape = options.PShape,
rob@100 3768 XMLElement = options.XMLElement,
rob@100 3769 colors = options.colors;
rob@100 3770
rob@100 3771 var PShapeSVG = function() {
rob@100 3772 PShape.call(this); // PShape is the base class.
rob@100 3773 if (arguments.length === 1) { // xml element coming in
rob@100 3774 this.element = arguments[0];
rob@100 3775
rob@100 3776 // set values to their defaults according to the SVG spec
rob@100 3777 this.vertexCodes = [];
rob@100 3778 this.vertices = [];
rob@100 3779 this.opacity = 1;
rob@100 3780
rob@100 3781 this.stroke = false;
rob@100 3782 this.strokeColor = PConstants.ALPHA_MASK;
rob@100 3783 this.strokeWeight = 1;
rob@100 3784 this.strokeCap = PConstants.SQUARE; // BUTT in svg spec
rob@100 3785 this.strokeJoin = PConstants.MITER;
rob@100 3786 this.strokeGradient = null;
rob@100 3787 this.strokeGradientPaint = null;
rob@100 3788 this.strokeName = null;
rob@100 3789 this.strokeOpacity = 1;
rob@100 3790
rob@100 3791 this.fill = true;
rob@100 3792 this.fillColor = PConstants.ALPHA_MASK;
rob@100 3793 this.fillGradient = null;
rob@100 3794 this.fillGradientPaint = null;
rob@100 3795 this.fillName = null;
rob@100 3796 this.fillOpacity = 1;
rob@100 3797
rob@100 3798 if (this.element.getName() !== "svg") {
rob@100 3799 throw("root is not <svg>, it's <" + this.element.getName() + ">");
rob@100 3800 }
rob@100 3801 }
rob@100 3802 else if (arguments.length === 2) {
rob@100 3803 if (typeof arguments[1] === 'string') {
rob@100 3804 if (arguments[1].indexOf(".svg") > -1) { //its a filename
rob@100 3805 this.element = new XMLElement(true, arguments[1]);
rob@100 3806 // set values to their defaults according to the SVG spec
rob@100 3807 this.vertexCodes = [];
rob@100 3808 this.vertices = [];
rob@100 3809 this.opacity = 1;
rob@100 3810
rob@100 3811 this.stroke = false;
rob@100 3812 this.strokeColor = PConstants.ALPHA_MASK;
rob@100 3813 this.strokeWeight = 1;
rob@100 3814 this.strokeCap = PConstants.SQUARE; // BUTT in svg spec
rob@100 3815 this.strokeJoin = PConstants.MITER;
rob@100 3816 this.strokeGradient = "";
rob@100 3817 this.strokeGradientPaint = "";
rob@100 3818 this.strokeName = "";
rob@100 3819 this.strokeOpacity = 1;
rob@100 3820
rob@100 3821 this.fill = true;
rob@100 3822 this.fillColor = PConstants.ALPHA_MASK;
rob@100 3823 this.fillGradient = null;
rob@100 3824 this.fillGradientPaint = null;
rob@100 3825 this.fillOpacity = 1;
rob@100 3826
rob@100 3827 }
rob@100 3828 } else { // XMLElement
rob@100 3829 if (arguments[0]) { // PShapeSVG
rob@100 3830 this.element = arguments[1];
rob@100 3831 this.vertexCodes = arguments[0].vertexCodes.slice();
rob@100 3832 this.vertices = arguments[0].vertices.slice();
rob@100 3833
rob@100 3834 this.stroke = arguments[0].stroke;
rob@100 3835 this.strokeColor = arguments[0].strokeColor;
rob@100 3836 this.strokeWeight = arguments[0].strokeWeight;
rob@100 3837 this.strokeCap = arguments[0].strokeCap;
rob@100 3838 this.strokeJoin = arguments[0].strokeJoin;
rob@100 3839 this.strokeGradient = arguments[0].strokeGradient;
rob@100 3840 this.strokeGradientPaint = arguments[0].strokeGradientPaint;
rob@100 3841 this.strokeName = arguments[0].strokeName;
rob@100 3842
rob@100 3843 this.fill = arguments[0].fill;
rob@100 3844 this.fillColor = arguments[0].fillColor;
rob@100 3845 this.fillGradient = arguments[0].fillGradient;
rob@100 3846 this.fillGradientPaint = arguments[0].fillGradientPaint;
rob@100 3847 this.fillName = arguments[0].fillName;
rob@100 3848 this.strokeOpacity = arguments[0].strokeOpacity;
rob@100 3849 this.fillOpacity = arguments[0].fillOpacity;
rob@100 3850 this.opacity = arguments[0].opacity;
rob@100 3851 }
rob@100 3852 }
rob@100 3853 }
rob@100 3854
rob@100 3855 this.name = this.element.getStringAttribute("id");
rob@100 3856 var displayStr = this.element.getStringAttribute("display", "inline");
rob@100 3857 this.visible = displayStr !== "none";
rob@100 3858 var str = this.element.getAttribute("transform");
rob@100 3859 if (str) {
rob@100 3860 this.matrix = this.parseMatrix(str);
rob@100 3861 }
rob@100 3862 // not proper parsing of the viewBox, but will cover us for cases where
rob@100 3863 // the width and height of the object is not specified
rob@100 3864 var viewBoxStr = this.element.getStringAttribute("viewBox");
rob@100 3865 if ( viewBoxStr !== null ) {
rob@100 3866 var viewBox = viewBoxStr.split(" ");
rob@100 3867 this.width = viewBox[2];
rob@100 3868 this.height = viewBox[3];
rob@100 3869 }
rob@100 3870
rob@100 3871 // TODO if viewbox is not same as width/height, then use it to scale
rob@100 3872 // the original objects. for now, viewbox only used when width/height
rob@100 3873 // are empty values (which by the spec means w/h of "100%"
rob@100 3874 var unitWidth = this.element.getStringAttribute("width");
rob@100 3875 var unitHeight = this.element.getStringAttribute("height");
rob@100 3876 if (unitWidth !== null) {
rob@100 3877 this.width = this.parseUnitSize(unitWidth);
rob@100 3878 this.height = this.parseUnitSize(unitHeight);
rob@100 3879 } else {
rob@100 3880 if ((this.width === 0) || (this.height === 0)) {
rob@100 3881 // For the spec, the default is 100% and 100%. For purposes
rob@100 3882 // here, insert a dummy value because this is prolly just a
rob@100 3883 // font or something for which the w/h doesn't matter.
rob@100 3884 this.width = 1;
rob@100 3885 this.height = 1;
rob@100 3886
rob@100 3887 //show warning
rob@100 3888 throw("The width and/or height is not " +
rob@100 3889 "readable in the <svg> tag of this file.");
rob@100 3890 }
rob@100 3891 }
rob@100 3892 this.parseColors(this.element);
rob@100 3893 this.parseChildren(this.element);
rob@100 3894
rob@100 3895 };
rob@100 3896 /**
rob@100 3897 * PShapeSVG methods
rob@100 3898 * missing: getChild(), print(), parseStyleAttributes(), styles() - deals with strokeGradient and fillGradient
rob@100 3899 */
rob@100 3900 PShapeSVG.prototype = new PShape();
rob@100 3901 /**
rob@100 3902 * @member PShapeSVG
rob@100 3903 * The parseMatrix() function parses the specified SVG matrix into a PMatrix2D. Note that PMatrix2D
rob@100 3904 * is rotated relative to the SVG definition, so parameters are rearranged
rob@100 3905 * here. More about the transformation matrices in
rob@100 3906 * <a href="http://www.w3.org/TR/SVG/coords.html#TransformAttribute">this section</a>
rob@100 3907 * of the SVG documentation.
rob@100 3908 *
rob@100 3909 * @param {String} str text of the matrix param.
rob@100 3910 *
rob@100 3911 * @return {PMatrix2D} a PMatrix2D
rob@100 3912 */
rob@100 3913 PShapeSVG.prototype.parseMatrix = (function() {
rob@100 3914 function getCoords(s) {
rob@100 3915 var m = [];
rob@100 3916 s.replace(/\((.*?)\)/, (function() {
rob@100 3917 return function(all, params) {
rob@100 3918 // get the coordinates that can be separated by spaces or a comma
rob@100 3919 m = params.replace(/,+/g, " ").split(/\s+/);
rob@100 3920 };
rob@100 3921 }()));
rob@100 3922 return m;
rob@100 3923 }
rob@100 3924
rob@100 3925 return function(str) {
rob@100 3926 this.checkMatrix(2);
rob@100 3927 var pieces = [];
rob@100 3928 str.replace(/\s*(\w+)\((.*?)\)/g, function(all) {
rob@100 3929 // get a list of transform definitions
rob@100 3930 pieces.push(CommonFunctions.trim(all));
rob@100 3931 });
rob@100 3932 if (pieces.length === 0) {
rob@100 3933 return null;
rob@100 3934 }
rob@100 3935
rob@100 3936 for (var i = 0, j = pieces.length; i < j; i++) {
rob@100 3937 var m = getCoords(pieces[i]);
rob@100 3938
rob@100 3939 if (pieces[i].indexOf("matrix") !== -1) {
rob@100 3940 this.matrix.set(m[0], m[2], m[4], m[1], m[3], m[5]);
rob@100 3941 } else if (pieces[i].indexOf("translate") !== -1) {
rob@100 3942 var tx = m[0];
rob@100 3943 var ty = (m.length === 2) ? m[1] : 0;
rob@100 3944 this.matrix.translate(tx,ty);
rob@100 3945 } else if (pieces[i].indexOf("scale") !== -1) {
rob@100 3946 var sx = m[0];
rob@100 3947 var sy = (m.length === 2) ? m[1] : m[0];
rob@100 3948 this.matrix.scale(sx,sy);
rob@100 3949 } else if (pieces[i].indexOf("rotate") !== -1) {
rob@100 3950 var angle = m[0];
rob@100 3951 if (m.length === 1) {
rob@100 3952 this.matrix.rotate(CommonFunctions.radians(angle));
rob@100 3953 } else if (m.length === 3) {
rob@100 3954 this.matrix.translate(m[1], m[2]);
rob@100 3955 this.matrix.rotate(CommonFunctions.radians(m[0]));
rob@100 3956 this.matrix.translate(-m[1], -m[2]);
rob@100 3957 }
rob@100 3958 } else if (pieces[i].indexOf("skewX") !== -1) {
rob@100 3959 this.matrix.skewX(parseFloat(m[0]));
rob@100 3960 } else if (pieces[i].indexOf("skewY") !== -1) {
rob@100 3961 this.matrix.skewY(m[0]);
rob@100 3962 } else if (pieces[i].indexOf("shearX") !== -1) {
rob@100 3963 this.matrix.shearX(m[0]);
rob@100 3964 } else if (pieces[i].indexOf("shearY") !== -1) {
rob@100 3965 this.matrix.shearY(m[0]);
rob@100 3966 }
rob@100 3967 }
rob@100 3968 return this.matrix;
rob@100 3969 };
rob@100 3970 }());
rob@100 3971
rob@100 3972 /**
rob@100 3973 * @member PShapeSVG
rob@100 3974 * The parseChildren() function parses the specified XMLElement
rob@100 3975 *
rob@100 3976 * @param {XMLElement}element the XMLElement to parse
rob@100 3977 */
rob@100 3978 PShapeSVG.prototype.parseChildren = function(element) {
rob@100 3979 var newelement = element.getChildren();
rob@100 3980 var base = new PShape();
rob@100 3981 var i, j;
rob@100 3982 for (i = 0, j = newelement.length; i < j; i++) {
rob@100 3983 var kid = this.parseChild(newelement[i]);
rob@100 3984 if (kid) {
rob@100 3985 base.addChild(kid);
rob@100 3986 }
rob@100 3987 }
rob@100 3988 for (i = 0, j = base.children.length; i < j; i++) {
rob@100 3989 this.children.push(base.children[i]);
rob@100 3990 }
rob@100 3991 };
rob@100 3992 /**
rob@100 3993 * @member PShapeSVG
rob@100 3994 * The getName() function returns the name
rob@100 3995 *
rob@100 3996 * @return {String} the name
rob@100 3997 */
rob@100 3998 PShapeSVG.prototype.getName = function() {
rob@100 3999 return this.name;
rob@100 4000 };
rob@100 4001 /**
rob@100 4002 * @member PShapeSVG
rob@100 4003 * The parseChild() function parses a child XML element.
rob@100 4004 *
rob@100 4005 * @param {XMLElement} elem the element to parse
rob@100 4006 *
rob@100 4007 * @return {PShape} the newly created PShape
rob@100 4008 */
rob@100 4009 PShapeSVG.prototype.parseChild = function( elem ) {
rob@100 4010 var name = elem.getName();
rob@100 4011 var shape;
rob@100 4012 if (name === "g") {
rob@100 4013 shape = new PShapeSVG(this, elem);
rob@100 4014 } else if (name === "defs") {
rob@100 4015 // generally this will contain gradient info, so may
rob@100 4016 // as well just throw it into a group element for parsing
rob@100 4017 shape = new PShapeSVG(this, elem);
rob@100 4018 } else if (name === "line") {
rob@100 4019 shape = new PShapeSVG(this, elem);
rob@100 4020 shape.parseLine();
rob@100 4021 } else if (name === "circle") {
rob@100 4022 shape = new PShapeSVG(this, elem);
rob@100 4023 shape.parseEllipse(true);
rob@100 4024 } else if (name === "ellipse") {
rob@100 4025 shape = new PShapeSVG(this, elem);
rob@100 4026 shape.parseEllipse(false);
rob@100 4027 } else if (name === "rect") {
rob@100 4028 shape = new PShapeSVG(this, elem);
rob@100 4029 shape.parseRect();
rob@100 4030 } else if (name === "polygon") {
rob@100 4031 shape = new PShapeSVG(this, elem);
rob@100 4032 shape.parsePoly(true);
rob@100 4033 } else if (name === "polyline") {
rob@100 4034 shape = new PShapeSVG(this, elem);
rob@100 4035 shape.parsePoly(false);
rob@100 4036 } else if (name === "path") {
rob@100 4037 shape = new PShapeSVG(this, elem);
rob@100 4038 shape.parsePath();
rob@100 4039 } else if (name === "radialGradient") {
rob@100 4040 //return new RadialGradient(this, elem);
rob@100 4041 unimplemented('PShapeSVG.prototype.parseChild, name = radialGradient');
rob@100 4042 } else if (name === "linearGradient") {
rob@100 4043 //return new LinearGradient(this, elem);
rob@100 4044 unimplemented('PShapeSVG.prototype.parseChild, name = linearGradient');
rob@100 4045 } else if (name === "text") {
rob@100 4046 unimplemented('PShapeSVG.prototype.parseChild, name = text');
rob@100 4047 } else if (name === "filter") {
rob@100 4048 unimplemented('PShapeSVG.prototype.parseChild, name = filter');
rob@100 4049 } else if (name === "mask") {
rob@100 4050 unimplemented('PShapeSVG.prototype.parseChild, name = mask');
rob@100 4051 } else {
rob@100 4052 // ignoring
rob@100 4053 }
rob@100 4054 return shape;
rob@100 4055 };
rob@100 4056 /**
rob@100 4057 * @member PShapeSVG
rob@100 4058 * The parsePath() function parses the <path> element of the svg file
rob@100 4059 * A path is defined by including a path element which contains a d="(path data)" attribute, where the d attribute contains
rob@100 4060 * the moveto, line, curve (both cubic and quadratic Beziers), arc and closepath instructions.
rob@100 4061 **/
rob@100 4062 PShapeSVG.prototype.parsePath = function() {
rob@100 4063 this.family = PConstants.PATH;
rob@100 4064 this.kind = 0;
rob@100 4065 var pathDataChars = [];
rob@100 4066 var c;
rob@100 4067 //change multiple spaces and commas to single space
rob@100 4068 var pathData = CommonFunctions.trim(this.element.getStringAttribute("d").replace(/[\s,]+/g,' '));
rob@100 4069 if (pathData === null) {
rob@100 4070 return;
rob@100 4071 }
rob@100 4072 pathData = pathData.split('');
rob@100 4073 var cx = 0,
rob@100 4074 cy = 0,
rob@100 4075 ctrlX = 0,
rob@100 4076 ctrlY = 0,
rob@100 4077 ctrlX1 = 0,
rob@100 4078 ctrlX2 = 0,
rob@100 4079 ctrlY1 = 0,
rob@100 4080 ctrlY2 = 0,
rob@100 4081 endX = 0,
rob@100 4082 endY = 0,
rob@100 4083 ppx = 0,
rob@100 4084 ppy = 0,
rob@100 4085 px = 0,
rob@100 4086 py = 0,
rob@100 4087 i = 0,
rob@100 4088 valOf = 0;
rob@100 4089 var str = "";
rob@100 4090 var tmpArray = [];
rob@100 4091 var flag = false;
rob@100 4092 var lastInstruction;
rob@100 4093 var command;
rob@100 4094 var j, k;
rob@100 4095 while (i< pathData.length) {
rob@100 4096 valOf = pathData[i].charCodeAt(0);
rob@100 4097 if ((valOf >= 65 && valOf <= 90) || (valOf >= 97 && valOf <= 122)) {
rob@100 4098 // if it's a letter
rob@100 4099 // populate the tmpArray with coordinates
rob@100 4100 j = i;
rob@100 4101 i++;
rob@100 4102 if (i < pathData.length) { // don't go over boundary of array
rob@100 4103 tmpArray = [];
rob@100 4104 valOf = pathData[i].charCodeAt(0);
rob@100 4105 while (!((valOf >= 65 && valOf <= 90) ||
rob@100 4106 (valOf >= 97 && valOf <= 100) ||
rob@100 4107 (valOf >= 102 && valOf <= 122)) && flag === false) { // if its NOT a letter
rob@100 4108 if (valOf === 32) { //if its a space and the str isn't empty
rob@100 4109 // sometimes you get a space after the letter
rob@100 4110 if (str !== "") {
rob@100 4111 tmpArray.push(parseFloat(str));
rob@100 4112 str = "";
rob@100 4113 }
rob@100 4114 i++;
rob@100 4115 } else if (valOf === 45) { //if it's a -
rob@100 4116 // allow for 'e' notation in numbers, e.g. 2.10e-9
rob@100 4117 if (pathData[i-1].charCodeAt(0) === 101) {
rob@100 4118 str += pathData[i].toString();
rob@100 4119 i++;
rob@100 4120 } else {
rob@100 4121 // sometimes no space separator after (ex: 104.535-16.322)
rob@100 4122 if (str !== "") {
rob@100 4123 tmpArray.push(parseFloat(str));
rob@100 4124 }
rob@100 4125 str = pathData[i].toString();
rob@100 4126 i++;
rob@100 4127 }
rob@100 4128 } else {
rob@100 4129 str += pathData[i].toString();
rob@100 4130 i++;
rob@100 4131 }
rob@100 4132 if (i === pathData.length) { // don't go over boundary of array
rob@100 4133 flag = true;
rob@100 4134 } else {
rob@100 4135 valOf = pathData[i].charCodeAt(0);
rob@100 4136 }
rob@100 4137 }
rob@100 4138 }
rob@100 4139 if (str !== "") {
rob@100 4140 tmpArray.push(parseFloat(str));
rob@100 4141 str = "";
rob@100 4142 }
rob@100 4143 command = pathData[j];
rob@100 4144 valOf = command.charCodeAt(0);
rob@100 4145 if (valOf === 77) { // M - move to (absolute)
rob@100 4146 if (tmpArray.length >= 2 && tmpArray.length % 2 === 0) {
rob@100 4147 // need one+ pairs of co-ordinates
rob@100 4148 cx = tmpArray[0];
rob@100 4149 cy = tmpArray[1];
rob@100 4150 this.parsePathMoveto(cx, cy);
rob@100 4151 if (tmpArray.length > 2) {
rob@100 4152 for (j = 2, k = tmpArray.length; j < k; j+=2) {
rob@100 4153 // absolute line to
rob@100 4154 cx = tmpArray[j];
rob@100 4155 cy = tmpArray[j+1];
rob@100 4156 this.parsePathLineto(cx,cy);
rob@100 4157 }
rob@100 4158 }
rob@100 4159 }
rob@100 4160 } else if (valOf === 109) { // m - move to (relative)
rob@100 4161 if (tmpArray.length >= 2 && tmpArray.length % 2 === 0) {
rob@100 4162 // need one+ pairs of co-ordinates
rob@100 4163 cx += tmpArray[0];
rob@100 4164 cy += tmpArray[1];
rob@100 4165 this.parsePathMoveto(cx,cy);
rob@100 4166 if (tmpArray.length > 2) {
rob@100 4167 for (j = 2, k = tmpArray.length; j < k; j+=2) {
rob@100 4168 // relative line to
rob@100 4169 cx += tmpArray[j];
rob@100 4170 cy += tmpArray[j + 1];
rob@100 4171 this.parsePathLineto(cx,cy);
rob@100 4172 }
rob@100 4173 }
rob@100 4174 }
rob@100 4175 } else if (valOf === 76) { // L - lineto (absolute)
rob@100 4176 if (tmpArray.length >= 2 && tmpArray.length % 2 === 0) {
rob@100 4177 // need one+ pairs of co-ordinates
rob@100 4178 for (j = 0, k = tmpArray.length; j < k; j+=2) {
rob@100 4179 cx = tmpArray[j];
rob@100 4180 cy = tmpArray[j + 1];
rob@100 4181 this.parsePathLineto(cx,cy);
rob@100 4182 }
rob@100 4183 }
rob@100 4184 } else if (valOf === 108) { // l - lineto (relative)
rob@100 4185 if (tmpArray.length >= 2 && tmpArray.length % 2 === 0) {
rob@100 4186 // need one+ pairs of co-ordinates
rob@100 4187 for (j = 0, k = tmpArray.length; j < k; j+=2) {
rob@100 4188 cx += tmpArray[j];
rob@100 4189 cy += tmpArray[j+1];
rob@100 4190 this.parsePathLineto(cx,cy);
rob@100 4191 }
rob@100 4192 }
rob@100 4193 } else if (valOf === 72) { // H - horizontal lineto (absolute)
rob@100 4194 for (j = 0, k = tmpArray.length; j < k; j++) {
rob@100 4195 // multiple x co-ordinates can be provided
rob@100 4196 cx = tmpArray[j];
rob@100 4197 this.parsePathLineto(cx, cy);
rob@100 4198 }
rob@100 4199 } else if (valOf === 104) { // h - horizontal lineto (relative)
rob@100 4200 for (j = 0, k = tmpArray.length; j < k; j++) {
rob@100 4201 // multiple x co-ordinates can be provided
rob@100 4202 cx += tmpArray[j];
rob@100 4203 this.parsePathLineto(cx, cy);
rob@100 4204 }
rob@100 4205 } else if (valOf === 86) { // V - vertical lineto (absolute)
rob@100 4206 for (j = 0, k = tmpArray.length; j < k; j++) {
rob@100 4207 // multiple y co-ordinates can be provided
rob@100 4208 cy = tmpArray[j];
rob@100 4209 this.parsePathLineto(cx, cy);
rob@100 4210 }
rob@100 4211 } else if (valOf === 118) { // v - vertical lineto (relative)
rob@100 4212 for (j = 0, k = tmpArray.length; j < k; j++) {
rob@100 4213 // multiple y co-ordinates can be provided
rob@100 4214 cy += tmpArray[j];
rob@100 4215 this.parsePathLineto(cx, cy);
rob@100 4216 }
rob@100 4217 } else if (valOf === 67) { // C - curve to (absolute)
rob@100 4218 if (tmpArray.length >= 6 && tmpArray.length % 6 === 0) {
rob@100 4219 // need one+ multiples of 6 co-ordinates
rob@100 4220 for (j = 0, k = tmpArray.length; j < k; j+=6) {
rob@100 4221 ctrlX1 = tmpArray[j];
rob@100 4222 ctrlY1 = tmpArray[j + 1];
rob@100 4223 ctrlX2 = tmpArray[j + 2];
rob@100 4224 ctrlY2 = tmpArray[j + 3];
rob@100 4225 endX = tmpArray[j + 4];
rob@100 4226 endY = tmpArray[j + 5];
rob@100 4227 this.parsePathCurveto(ctrlX1,
rob@100 4228 ctrlY1,
rob@100 4229 ctrlX2,
rob@100 4230 ctrlY2,
rob@100 4231 endX,
rob@100 4232 endY);
rob@100 4233 cx = endX;
rob@100 4234 cy = endY;
rob@100 4235 }
rob@100 4236 }
rob@100 4237 } else if (valOf === 99) { // c - curve to (relative)
rob@100 4238 if (tmpArray.length >= 6 && tmpArray.length % 6 === 0) {
rob@100 4239 // need one+ multiples of 6 co-ordinates
rob@100 4240 for (j = 0, k = tmpArray.length; j < k; j+=6) {
rob@100 4241 ctrlX1 = cx + tmpArray[j];
rob@100 4242 ctrlY1 = cy + tmpArray[j + 1];
rob@100 4243 ctrlX2 = cx + tmpArray[j + 2];
rob@100 4244 ctrlY2 = cy + tmpArray[j + 3];
rob@100 4245 endX = cx + tmpArray[j + 4];
rob@100 4246 endY = cy + tmpArray[j + 5];
rob@100 4247 this.parsePathCurveto(ctrlX1,
rob@100 4248 ctrlY1,
rob@100 4249 ctrlX2,
rob@100 4250 ctrlY2,
rob@100 4251 endX,
rob@100 4252 endY);
rob@100 4253 cx = endX;
rob@100 4254 cy = endY;
rob@100 4255 }
rob@100 4256 }
rob@100 4257 } else if (valOf === 83) { // S - curve to shorthand (absolute)
rob@100 4258 if (tmpArray.length >= 4 && tmpArray.length % 4 === 0) {
rob@100 4259 // need one+ multiples of 4 co-ordinates
rob@100 4260 for (j = 0, k = tmpArray.length; j < k; j+=4) {
rob@100 4261 if (lastInstruction.toLowerCase() === "c" ||
rob@100 4262 lastInstruction.toLowerCase() === "s") {
rob@100 4263 ppx = this.vertices[ this.vertices.length-2 ][0];
rob@100 4264 ppy = this.vertices[ this.vertices.length-2 ][1];
rob@100 4265 px = this.vertices[ this.vertices.length-1 ][0];
rob@100 4266 py = this.vertices[ this.vertices.length-1 ][1];
rob@100 4267 ctrlX1 = px + (px - ppx);
rob@100 4268 ctrlY1 = py + (py - ppy);
rob@100 4269 } else {
rob@100 4270 //If there is no previous curve,
rob@100 4271 //the current point will be used as the first control point.
rob@100 4272 ctrlX1 = this.vertices[this.vertices.length-1][0];
rob@100 4273 ctrlY1 = this.vertices[this.vertices.length-1][1];
rob@100 4274 }
rob@100 4275 ctrlX2 = tmpArray[j];
rob@100 4276 ctrlY2 = tmpArray[j + 1];
rob@100 4277 endX = tmpArray[j + 2];
rob@100 4278 endY = tmpArray[j + 3];
rob@100 4279 this.parsePathCurveto(ctrlX1,
rob@100 4280 ctrlY1,
rob@100 4281 ctrlX2,
rob@100 4282 ctrlY2,
rob@100 4283 endX,
rob@100 4284 endY);
rob@100 4285 cx = endX;
rob@100 4286 cy = endY;
rob@100 4287 }
rob@100 4288 }
rob@100 4289 } else if (valOf === 115) { // s - curve to shorthand (relative)
rob@100 4290 if (tmpArray.length >= 4 && tmpArray.length % 4 === 0) {
rob@100 4291 // need one+ multiples of 4 co-ordinates
rob@100 4292 for (j = 0, k = tmpArray.length; j < k; j+=4) {
rob@100 4293 if (lastInstruction.toLowerCase() === "c" ||
rob@100 4294 lastInstruction.toLowerCase() === "s") {
rob@100 4295 ppx = this.vertices[this.vertices.length-2][0];
rob@100 4296 ppy = this.vertices[this.vertices.length-2][1];
rob@100 4297 px = this.vertices[this.vertices.length-1][0];
rob@100 4298 py = this.vertices[this.vertices.length-1][1];
rob@100 4299 ctrlX1 = px + (px - ppx);
rob@100 4300 ctrlY1 = py + (py - ppy);
rob@100 4301 } else {
rob@100 4302 //If there is no previous curve,
rob@100 4303 //the current point will be used as the first control point.
rob@100 4304 ctrlX1 = this.vertices[this.vertices.length-1][0];
rob@100 4305 ctrlY1 = this.vertices[this.vertices.length-1][1];
rob@100 4306 }
rob@100 4307 ctrlX2 = cx + tmpArray[j];
rob@100 4308 ctrlY2 = cy + tmpArray[j + 1];
rob@100 4309 endX = cx + tmpArray[j + 2];
rob@100 4310 endY = cy + tmpArray[j + 3];
rob@100 4311 this.parsePathCurveto(ctrlX1,
rob@100 4312 ctrlY1,
rob@100 4313 ctrlX2,
rob@100 4314 ctrlY2,
rob@100 4315 endX,
rob@100 4316 endY);
rob@100 4317 cx = endX;
rob@100 4318 cy = endY;
rob@100 4319 }
rob@100 4320 }
rob@100 4321 } else if (valOf === 81) { // Q - quadratic curve to (absolute)
rob@100 4322 if (tmpArray.length >= 4 && tmpArray.length % 4 === 0) {
rob@100 4323 // need one+ multiples of 4 co-ordinates
rob@100 4324 for (j = 0, k = tmpArray.length; j < k; j+=4) {
rob@100 4325 ctrlX = tmpArray[j];
rob@100 4326 ctrlY = tmpArray[j + 1];
rob@100 4327 endX = tmpArray[j + 2];
rob@100 4328 endY = tmpArray[j + 3];
rob@100 4329 this.parsePathQuadto(cx, cy, ctrlX, ctrlY, endX, endY);
rob@100 4330 cx = endX;
rob@100 4331 cy = endY;
rob@100 4332 }
rob@100 4333 }
rob@100 4334 } else if (valOf === 113) { // q - quadratic curve to (relative)
rob@100 4335 if (tmpArray.length >= 4 && tmpArray.length % 4 === 0) {
rob@100 4336 // need one+ multiples of 4 co-ordinates
rob@100 4337 for (j = 0, k = tmpArray.length; j < k; j+=4) {
rob@100 4338 ctrlX = cx + tmpArray[j];
rob@100 4339 ctrlY = cy + tmpArray[j + 1];
rob@100 4340 endX = cx + tmpArray[j + 2];
rob@100 4341 endY = cy + tmpArray[j + 3];
rob@100 4342 this.parsePathQuadto(cx, cy, ctrlX, ctrlY, endX, endY);
rob@100 4343 cx = endX;
rob@100 4344 cy = endY;
rob@100 4345 }
rob@100 4346 }
rob@100 4347 } else if (valOf === 84) {
rob@100 4348 // T - quadratic curve to shorthand (absolute)
rob@100 4349 if (tmpArray.length >= 2 && tmpArray.length % 2 === 0) {
rob@100 4350 // need one+ pairs of co-ordinates
rob@100 4351 for (j = 0, k = tmpArray.length; j < k; j+=2) {
rob@100 4352 if (lastInstruction.toLowerCase() === "q" ||
rob@100 4353 lastInstruction.toLowerCase() === "t") {
rob@100 4354 ppx = this.vertices[this.vertices.length-2][0];
rob@100 4355 ppy = this.vertices[this.vertices.length-2][1];
rob@100 4356 px = this.vertices[this.vertices.length-1][0];
rob@100 4357 py = this.vertices[this.vertices.length-1][1];
rob@100 4358 ctrlX = px + (px - ppx);
rob@100 4359 ctrlY = py + (py - ppy);
rob@100 4360 } else {
rob@100 4361 // If there is no previous command or if the previous command
rob@100 4362 // was not a Q, q, T or t, assume the control point is
rob@100 4363 // coincident with the current point.
rob@100 4364 ctrlX = cx;
rob@100 4365 ctrlY = cy;
rob@100 4366 }
rob@100 4367 endX = tmpArray[j];
rob@100 4368 endY = tmpArray[j + 1];
rob@100 4369 this.parsePathQuadto(cx, cy, ctrlX, ctrlY, endX, endY);
rob@100 4370 cx = endX;
rob@100 4371 cy = endY;
rob@100 4372 }
rob@100 4373 }
rob@100 4374 } else if (valOf === 116) {
rob@100 4375 // t - quadratic curve to shorthand (relative)
rob@100 4376 if (tmpArray.length >= 2 && tmpArray.length % 2 === 0) {
rob@100 4377 // need one+ pairs of co-ordinates
rob@100 4378 for (j = 0, k = tmpArray.length; j < k; j+=2) {
rob@100 4379 if (lastInstruction.toLowerCase() === "q" ||
rob@100 4380 lastInstruction.toLowerCase() === "t") {
rob@100 4381 ppx = this.vertices[this.vertices.length-2][0];
rob@100 4382 ppy = this.vertices[this.vertices.length-2][1];
rob@100 4383 px = this.vertices[this.vertices.length-1][0];
rob@100 4384 py = this.vertices[this.vertices.length-1][1];
rob@100 4385 ctrlX = px + (px - ppx);
rob@100 4386 ctrlY = py + (py - ppy);
rob@100 4387 } else {
rob@100 4388 // If there is no previous command or if the previous command
rob@100 4389 // was not a Q, q, T or t, assume the control point is
rob@100 4390 // coincident with the current point.
rob@100 4391 ctrlX = cx;
rob@100 4392 ctrlY = cy;
rob@100 4393 }
rob@100 4394 endX = cx + tmpArray[j];
rob@100 4395 endY = cy + tmpArray[j + 1];
rob@100 4396 this.parsePathQuadto(cx, cy, ctrlX, ctrlY, endX, endY);
rob@100 4397 cx = endX;
rob@100 4398 cy = endY;
rob@100 4399 }
rob@100 4400 }
rob@100 4401 } else if (valOf === 90 || valOf === 122) { // Z or z (these do the same thing)
rob@100 4402 this.close = true;
rob@100 4403 }
rob@100 4404 lastInstruction = command.toString();
rob@100 4405 } else { i++;}
rob@100 4406 }
rob@100 4407 };
rob@100 4408 /**
rob@100 4409 * @member PShapeSVG
rob@100 4410 * PShapeSVG.parsePath() helper function
rob@100 4411 *
rob@100 4412 * @see PShapeSVG#parsePath
rob@100 4413 */
rob@100 4414 PShapeSVG.prototype.parsePathQuadto = function(x1, y1, cx, cy, x2, y2) {
rob@100 4415 if (this.vertices.length > 0) {
rob@100 4416 this.parsePathCode(PConstants.BEZIER_VERTEX);
rob@100 4417 // x1/y1 already covered by last moveto, lineto, or curveto
rob@100 4418 this.parsePathVertex(x1 + ((cx-x1)*2/3), y1 + ((cy-y1)*2/3));
rob@100 4419 this.parsePathVertex(x2 + ((cx-x2)*2/3), y2 + ((cy-y2)*2/3));
rob@100 4420 this.parsePathVertex(x2, y2);
rob@100 4421 } else {
rob@100 4422 throw ("Path must start with M/m");
rob@100 4423 }
rob@100 4424 };
rob@100 4425 /**
rob@100 4426 * @member PShapeSVG
rob@100 4427 * PShapeSVG.parsePath() helper function
rob@100 4428 *
rob@100 4429 * @see PShapeSVG#parsePath
rob@100 4430 */
rob@100 4431 PShapeSVG.prototype.parsePathCurveto = function(x1, y1, x2, y2, x3, y3) {
rob@100 4432 if (this.vertices.length > 0) {
rob@100 4433 this.parsePathCode(PConstants.BEZIER_VERTEX );
rob@100 4434 this.parsePathVertex(x1, y1);
rob@100 4435 this.parsePathVertex(x2, y2);
rob@100 4436 this.parsePathVertex(x3, y3);
rob@100 4437 } else {
rob@100 4438 throw ("Path must start with M/m");
rob@100 4439 }
rob@100 4440 };
rob@100 4441 /**
rob@100 4442 * @member PShapeSVG
rob@100 4443 * PShapeSVG.parsePath() helper function
rob@100 4444 *
rob@100 4445 * @see PShapeSVG#parsePath
rob@100 4446 */
rob@100 4447 PShapeSVG.prototype.parsePathLineto = function(px, py) {
rob@100 4448 if (this.vertices.length > 0) {
rob@100 4449 this.parsePathCode(PConstants.VERTEX);
rob@100 4450 this.parsePathVertex(px, py);
rob@100 4451 // add property to distinguish between curContext.moveTo
rob@100 4452 // or curContext.lineTo
rob@100 4453 this.vertices[this.vertices.length-1].moveTo = false;
rob@100 4454 } else {
rob@100 4455 throw ("Path must start with M/m");
rob@100 4456 }
rob@100 4457 };
rob@100 4458
rob@100 4459 PShapeSVG.prototype.parsePathMoveto = function(px, py) {
rob@100 4460 if (this.vertices.length > 0) {
rob@100 4461 this.parsePathCode(PConstants.BREAK);
rob@100 4462 }
rob@100 4463 this.parsePathCode(PConstants.VERTEX);
rob@100 4464 this.parsePathVertex(px, py);
rob@100 4465 // add property to distinguish between curContext.moveTo
rob@100 4466 // or curContext.lineTo
rob@100 4467 this.vertices[this.vertices.length-1].moveTo = true;
rob@100 4468 };
rob@100 4469 /**
rob@100 4470 * @member PShapeSVG
rob@100 4471 * PShapeSVG.parsePath() helper function
rob@100 4472 *
rob@100 4473 * @see PShapeSVG#parsePath
rob@100 4474 */
rob@100 4475 PShapeSVG.prototype.parsePathVertex = function(x, y) {
rob@100 4476 var verts = [];
rob@100 4477 verts[0] = x;
rob@100 4478 verts[1] = y;
rob@100 4479 this.vertices.push(verts);
rob@100 4480 };
rob@100 4481 /**
rob@100 4482 * @member PShapeSVG
rob@100 4483 * PShapeSVG.parsePath() helper function
rob@100 4484 *
rob@100 4485 * @see PShapeSVG#parsePath
rob@100 4486 */
rob@100 4487 PShapeSVG.prototype.parsePathCode = function(what) {
rob@100 4488 this.vertexCodes.push(what);
rob@100 4489 };
rob@100 4490 /**
rob@100 4491 * @member PShapeSVG
rob@100 4492 * The parsePoly() function parses a polyline or polygon from an SVG file.
rob@100 4493 *
rob@100 4494 * @param {boolean}val true if shape is closed (polygon), false if not (polyline)
rob@100 4495 */
rob@100 4496 PShapeSVG.prototype.parsePoly = function(val) {
rob@100 4497 this.family = PConstants.PATH;
rob@100 4498 this.close = val;
rob@100 4499 var pointsAttr = CommonFunctions.trim(this.element.getStringAttribute("points").replace(/[,\s]+/g,' '));
rob@100 4500 if (pointsAttr !== null) {
rob@100 4501 //split into array
rob@100 4502 var pointsBuffer = pointsAttr.split(" ");
rob@100 4503 if (pointsBuffer.length % 2 === 0) {
rob@100 4504 for (var i = 0, j = pointsBuffer.length; i < j; i++) {
rob@100 4505 var verts = [];
rob@100 4506 verts[0] = pointsBuffer[i];
rob@100 4507 verts[1] = pointsBuffer[++i];
rob@100 4508 this.vertices.push(verts);
rob@100 4509 }
rob@100 4510 } else {
rob@100 4511 throw("Error parsing polygon points: odd number of coordinates provided");
rob@100 4512 }
rob@100 4513 }
rob@100 4514 };
rob@100 4515 /**
rob@100 4516 * @member PShapeSVG
rob@100 4517 * The parseRect() function parses a rect from an SVG file.
rob@100 4518 */
rob@100 4519 PShapeSVG.prototype.parseRect = function() {
rob@100 4520 this.kind = PConstants.RECT;
rob@100 4521 this.family = PConstants.PRIMITIVE;
rob@100 4522 this.params = [];
rob@100 4523 this.params[0] = this.element.getFloatAttribute("x");
rob@100 4524 this.params[1] = this.element.getFloatAttribute("y");
rob@100 4525 this.params[2] = this.element.getFloatAttribute("width");
rob@100 4526 this.params[3] = this.element.getFloatAttribute("height");
rob@100 4527 if (this.params[2] < 0 || this.params[3] < 0) {
rob@100 4528 throw("svg error: negative width or height found while parsing <rect>");
rob@100 4529 }
rob@100 4530 };
rob@100 4531 /**
rob@100 4532 * @member PShapeSVG
rob@100 4533 * The parseEllipse() function handles parsing ellipse and circle tags.
rob@100 4534 *
rob@100 4535 * @param {boolean}val true if this is a circle and not an ellipse
rob@100 4536 */
rob@100 4537 PShapeSVG.prototype.parseEllipse = function(val) {
rob@100 4538 this.kind = PConstants.ELLIPSE;
rob@100 4539 this.family = PConstants.PRIMITIVE;
rob@100 4540 this.params = [];
rob@100 4541
rob@100 4542 this.params[0] = this.element.getFloatAttribute("cx") | 0 ;
rob@100 4543 this.params[1] = this.element.getFloatAttribute("cy") | 0;
rob@100 4544
rob@100 4545 var rx, ry;
rob@100 4546 if (val) {
rob@100 4547 rx = ry = this.element.getFloatAttribute("r");
rob@100 4548 if (rx < 0) {
rob@100 4549 throw("svg error: negative radius found while parsing <circle>");
rob@100 4550 }
rob@100 4551 } else {
rob@100 4552 rx = this.element.getFloatAttribute("rx");
rob@100 4553 ry = this.element.getFloatAttribute("ry");
rob@100 4554 if (rx < 0 || ry < 0) {
rob@100 4555 throw("svg error: negative x-axis radius or y-axis radius found while parsing <ellipse>");
rob@100 4556 }
rob@100 4557 }
rob@100 4558 this.params[0] -= rx;
rob@100 4559 this.params[1] -= ry;
rob@100 4560
rob@100 4561 this.params[2] = rx*2;
rob@100 4562 this.params[3] = ry*2;
rob@100 4563 };
rob@100 4564 /**
rob@100 4565 * @member PShapeSVG
rob@100 4566 * The parseLine() function handles parsing line tags.
rob@100 4567 *
rob@100 4568 * @param {boolean}val true if this is a circle and not an ellipse
rob@100 4569 */
rob@100 4570 PShapeSVG.prototype.parseLine = function() {
rob@100 4571 this.kind = PConstants.LINE;
rob@100 4572 this.family = PConstants.PRIMITIVE;
rob@100 4573 this.params = [];
rob@100 4574 this.params[0] = this.element.getFloatAttribute("x1");
rob@100 4575 this.params[1] = this.element.getFloatAttribute("y1");
rob@100 4576 this.params[2] = this.element.getFloatAttribute("x2");
rob@100 4577 this.params[3] = this.element.getFloatAttribute("y2");
rob@100 4578 };
rob@100 4579 /**
rob@100 4580 * @member PShapeSVG
rob@100 4581 * The parseColors() function handles parsing the opacity, strijem stroke-width, stroke-linejoin,stroke-linecap, fill, and style attributes
rob@100 4582 *
rob@100 4583 * @param {XMLElement}element the element of which attributes to parse
rob@100 4584 */
rob@100 4585 PShapeSVG.prototype.parseColors = function(element) {
rob@100 4586 if (element.hasAttribute("opacity")) {
rob@100 4587 this.setOpacity(element.getAttribute("opacity"));
rob@100 4588 }
rob@100 4589 if (element.hasAttribute("stroke")) {
rob@100 4590 this.setStroke(element.getAttribute("stroke"));
rob@100 4591 }
rob@100 4592 if (element.hasAttribute("stroke-width")) {
rob@100 4593 // if NaN (i.e. if it's 'inherit') then default
rob@100 4594 // back to the inherit setting
rob@100 4595 this.setStrokeWeight(element.getAttribute("stroke-width"));
rob@100 4596 }
rob@100 4597 if (element.hasAttribute("stroke-linejoin") ) {
rob@100 4598 this.setStrokeJoin(element.getAttribute("stroke-linejoin"));
rob@100 4599 }
rob@100 4600 if (element.hasAttribute("stroke-linecap")) {
rob@100 4601 this.setStrokeCap(element.getStringAttribute("stroke-linecap"));
rob@100 4602 }
rob@100 4603 // fill defaults to black (though stroke defaults to "none")
rob@100 4604 // http://www.w3.org/TR/SVG/painting.html#FillProperties
rob@100 4605 if (element.hasAttribute("fill")) {
rob@100 4606 this.setFill(element.getStringAttribute("fill"));
rob@100 4607 }
rob@100 4608 if (element.hasAttribute("style")) {
rob@100 4609 var styleText = element.getStringAttribute("style");
rob@100 4610 var styleTokens = styleText.toString().split( ";" );
rob@100 4611
rob@100 4612 for (var i = 0, j = styleTokens.length; i < j; i++) {
rob@100 4613 var tokens = CommonFunctions.trim(styleTokens[i].split( ":" ));
rob@100 4614 if (tokens[0] === "fill") {
rob@100 4615 this.setFill(tokens[1]);
rob@100 4616 } else if (tokens[0] === "fill-opacity") {
rob@100 4617 this.setFillOpacity(tokens[1]);
rob@100 4618 } else if (tokens[0] === "stroke") {
rob@100 4619 this.setStroke(tokens[1]);
rob@100 4620 } else if (tokens[0] === "stroke-width") {
rob@100 4621 this.setStrokeWeight(tokens[1]);
rob@100 4622 } else if (tokens[0] === "stroke-linecap") {
rob@100 4623 this.setStrokeCap(tokens[1]);
rob@100 4624 } else if (tokens[0] === "stroke-linejoin") {
rob@100 4625 this.setStrokeJoin(tokens[1]);
rob@100 4626 } else if (tokens[0] === "stroke-opacity") {
rob@100 4627 this.setStrokeOpacity(tokens[1]);
rob@100 4628 } else if (tokens[0] === "opacity") {
rob@100 4629 this.setOpacity(tokens[1]);
rob@100 4630 } // Other attributes are not yet implemented
rob@100 4631 }
rob@100 4632 }
rob@100 4633 };
rob@100 4634 /**
rob@100 4635 * @member PShapeSVG
rob@100 4636 * PShapeSVG.parseColors() helper function
rob@100 4637 *
rob@100 4638 * @param {String} opacityText the value of fillOpacity
rob@100 4639 *
rob@100 4640 * @see PShapeSVG#parseColors
rob@100 4641 */
rob@100 4642 PShapeSVG.prototype.setFillOpacity = function(opacityText) {
rob@100 4643 this.fillOpacity = parseFloat(opacityText);
rob@100 4644 this.fillColor = this.fillOpacity * 255 << 24 |
rob@100 4645 this.fillColor & 0xFFFFFF;
rob@100 4646 };
rob@100 4647 /**
rob@100 4648 * @member PShapeSVG
rob@100 4649 * PShapeSVG.parseColors() helper function
rob@100 4650 *
rob@100 4651 * @param {String} fillText the value of fill
rob@100 4652 *
rob@100 4653 * @see PShapeSVG#parseColors
rob@100 4654 */
rob@100 4655 PShapeSVG.prototype.setFill = function (fillText) {
rob@100 4656 var opacityMask = this.fillColor & 0xFF000000;
rob@100 4657 if (fillText === "none") {
rob@100 4658 this.fill = false;
rob@100 4659 } else if (fillText.indexOf("#") === 0) {
rob@100 4660 this.fill = true;
rob@100 4661 if (fillText.length === 4) {
rob@100 4662 // convert #00F to #0000FF
rob@100 4663 fillText = fillText.replace(/#(.)(.)(.)/,"#$1$1$2$2$3$3");
rob@100 4664 }
rob@100 4665 this.fillColor = opacityMask |
rob@100 4666 (parseInt(fillText.substring(1), 16 )) &
rob@100 4667 0xFFFFFF;
rob@100 4668 } else if (fillText.indexOf("rgb") === 0) {
rob@100 4669 this.fill = true;
rob@100 4670 this.fillColor = opacityMask | this.parseRGB(fillText);
rob@100 4671 } else if (fillText.indexOf("url(#") === 0) {
rob@100 4672 this.fillName = fillText.substring(5, fillText.length - 1 );
rob@100 4673 } else if (colors[fillText]) {
rob@100 4674 this.fill = true;
rob@100 4675 this.fillColor = opacityMask |
rob@100 4676 (parseInt(colors[fillText].substring(1), 16)) &
rob@100 4677 0xFFFFFF;
rob@100 4678 }
rob@100 4679 };
rob@100 4680 /**
rob@100 4681 * @member PShapeSVG
rob@100 4682 * PShapeSVG.parseColors() helper function
rob@100 4683 *
rob@100 4684 * @param {String} opacity the value of opacity
rob@100 4685 *
rob@100 4686 * @see PShapeSVG#parseColors
rob@100 4687 */
rob@100 4688 PShapeSVG.prototype.setOpacity = function(opacity) {
rob@100 4689 this.strokeColor = parseFloat(opacity) * 255 << 24 |
rob@100 4690 this.strokeColor & 0xFFFFFF;
rob@100 4691 this.fillColor = parseFloat(opacity) * 255 << 24 |
rob@100 4692 this.fillColor & 0xFFFFFF;
rob@100 4693 };
rob@100 4694 /**
rob@100 4695 * @member PShapeSVG
rob@100 4696 * PShapeSVG.parseColors() helper function
rob@100 4697 *
rob@100 4698 * @param {String} strokeText the value to set stroke to
rob@100 4699 *
rob@100 4700 * @see PShapeSVG#parseColors
rob@100 4701 */
rob@100 4702 PShapeSVG.prototype.setStroke = function(strokeText) {
rob@100 4703 var opacityMask = this.strokeColor & 0xFF000000;
rob@100 4704 if (strokeText === "none") {
rob@100 4705 this.stroke = false;
rob@100 4706 } else if (strokeText.charAt( 0 ) === "#") {
rob@100 4707 this.stroke = true;
rob@100 4708 if (strokeText.length === 4) {
rob@100 4709 // convert #00F to #0000FF
rob@100 4710 strokeText = strokeText.replace(/#(.)(.)(.)/,"#$1$1$2$2$3$3");
rob@100 4711 }
rob@100 4712 this.strokeColor = opacityMask |
rob@100 4713 (parseInt( strokeText.substring( 1 ), 16 )) &
rob@100 4714 0xFFFFFF;
rob@100 4715 } else if (strokeText.indexOf( "rgb" ) === 0 ) {
rob@100 4716 this.stroke = true;
rob@100 4717 this.strokeColor = opacityMask | this.parseRGB(strokeText);
rob@100 4718 } else if (strokeText.indexOf( "url(#" ) === 0) {
rob@100 4719 this.strokeName = strokeText.substring(5, strokeText.length - 1);
rob@100 4720 } else if (colors[strokeText]) {
rob@100 4721 this.stroke = true;
rob@100 4722 this.strokeColor = opacityMask |
rob@100 4723 (parseInt(colors[strokeText].substring(1), 16)) &
rob@100 4724 0xFFFFFF;
rob@100 4725 }
rob@100 4726 };
rob@100 4727 /**
rob@100 4728 * @member PShapeSVG
rob@100 4729 * PShapeSVG.parseColors() helper function
rob@100 4730 *
rob@100 4731 * @param {String} weight the value to set strokeWeight to
rob@100 4732 *
rob@100 4733 * @see PShapeSVG#parseColors
rob@100 4734 */
rob@100 4735 PShapeSVG.prototype.setStrokeWeight = function(weight) {
rob@100 4736 this.strokeWeight = this.parseUnitSize(weight);
rob@100 4737 };
rob@100 4738 /**
rob@100 4739 * @member PShapeSVG
rob@100 4740 * PShapeSVG.parseColors() helper function
rob@100 4741 *
rob@100 4742 * @param {String} linejoin the value to set strokeJoin to
rob@100 4743 *
rob@100 4744 * @see PShapeSVG#parseColors
rob@100 4745 */
rob@100 4746 PShapeSVG.prototype.setStrokeJoin = function(linejoin) {
rob@100 4747 if (linejoin === "miter") {
rob@100 4748 this.strokeJoin = PConstants.MITER;
rob@100 4749
rob@100 4750 } else if (linejoin === "round") {
rob@100 4751 this.strokeJoin = PConstants.ROUND;
rob@100 4752
rob@100 4753 } else if (linejoin === "bevel") {
rob@100 4754 this.strokeJoin = PConstants.BEVEL;
rob@100 4755 }
rob@100 4756 };
rob@100 4757 /**
rob@100 4758 * @member PShapeSVG
rob@100 4759 * PShapeSVG.parseColors() helper function
rob@100 4760 *
rob@100 4761 * @param {String} linecap the value to set strokeCap to
rob@100 4762 *
rob@100 4763 * @see PShapeSVG#parseColors
rob@100 4764 */
rob@100 4765 PShapeSVG.prototype.setStrokeCap = function (linecap) {
rob@100 4766 if (linecap === "butt") {
rob@100 4767 this.strokeCap = PConstants.SQUARE;
rob@100 4768
rob@100 4769 } else if (linecap === "round") {
rob@100 4770 this.strokeCap = PConstants.ROUND;
rob@100 4771
rob@100 4772 } else if (linecap === "square") {
rob@100 4773 this.strokeCap = PConstants.PROJECT;
rob@100 4774 }
rob@100 4775 };
rob@100 4776 /**
rob@100 4777 * @member PShapeSVG
rob@100 4778 * PShapeSVG.parseColors() helper function
rob@100 4779 *
rob@100 4780 * @param {String} opacityText the value to set stroke opacity to
rob@100 4781 *
rob@100 4782 * @see PShapeSVG#parseColors
rob@100 4783 */
rob@100 4784 PShapeSVG.prototype.setStrokeOpacity = function (opacityText) {
rob@100 4785 this.strokeOpacity = parseFloat(opacityText);
rob@100 4786 this.strokeColor = this.strokeOpacity * 255 << 24 |
rob@100 4787 this.strokeColor &
rob@100 4788 0xFFFFFF;
rob@100 4789 };
rob@100 4790 /**
rob@100 4791 * @member PShapeSVG
rob@100 4792 * The parseRGB() function parses an rbg() color string and returns a color int
rob@100 4793 *
rob@100 4794 * @param {String} color the color to parse in rbg() format
rob@100 4795 *
rob@100 4796 * @return {int} the equivalent color int
rob@100 4797 */
rob@100 4798 PShapeSVG.prototype.parseRGB = function(color) {
rob@100 4799 var sub = color.substring(color.indexOf('(') + 1, color.indexOf(')'));
rob@100 4800 var values = sub.split(", ");
rob@100 4801 return (values[0] << 16) | (values[1] << 8) | (values[2]);
rob@100 4802 };
rob@100 4803 /**
rob@100 4804 * @member PShapeSVG
rob@100 4805 * The parseUnitSize() function parse a size that may have a suffix for its units.
rob@100 4806 * Ignoring cases where this could also be a percentage.
rob@100 4807 * The <A HREF="http://www.w3.org/TR/SVG/coords.html#Units">units</A> spec:
rob@100 4808 * <UL>
rob@100 4809 * <LI>"1pt" equals "1.25px" (and therefore 1.25 user units)
rob@100 4810 * <LI>"1pc" equals "15px" (and therefore 15 user units)
rob@100 4811 * <LI>"1mm" would be "3.543307px" (3.543307 user units)
rob@100 4812 * <LI>"1cm" equals "35.43307px" (and therefore 35.43307 user units)
rob@100 4813 * <LI>"1in" equals "90px" (and therefore 90 user units)
rob@100 4814 * </UL>
rob@100 4815 */
rob@100 4816 PShapeSVG.prototype.parseUnitSize = function (text) {
rob@100 4817 var len = text.length - 2;
rob@100 4818 if (len < 0) { return text; }
rob@100 4819 if (text.indexOf("pt") === len) {
rob@100 4820 return parseFloat(text.substring(0, len)) * 1.25;
rob@100 4821 }
rob@100 4822 if (text.indexOf("pc") === len) {
rob@100 4823 return parseFloat( text.substring( 0, len)) * 15;
rob@100 4824 }
rob@100 4825 if (text.indexOf("mm") === len) {
rob@100 4826 return parseFloat( text.substring(0, len)) * 3.543307;
rob@100 4827 }
rob@100 4828 if (text.indexOf("cm") === len) {
rob@100 4829 return parseFloat(text.substring(0, len)) * 35.43307;
rob@100 4830 }
rob@100 4831 if (text.indexOf("in") === len) {
rob@100 4832 return parseFloat(text.substring(0, len)) * 90;
rob@100 4833 }
rob@100 4834 if (text.indexOf("px") === len) {
rob@100 4835 return parseFloat(text.substring(0, len));
rob@100 4836 }
rob@100 4837 return parseFloat(text);
rob@100 4838 };
rob@100 4839
rob@100 4840 return PShapeSVG;
rob@100 4841 };
rob@100 4842
rob@100 4843 },{}],17:[function(require,module,exports){
rob@100 4844 module.exports = function(options, undef) {
rob@100 4845 var PConstants = options.PConstants;
rob@100 4846
rob@100 4847 function PVector(x, y, z) {
rob@100 4848 this.x = x || 0;
rob@100 4849 this.y = y || 0;
rob@100 4850 this.z = z || 0;
rob@100 4851 }
rob@100 4852
rob@100 4853 PVector.fromAngle = function(angle, v) {
rob@100 4854 if (v === undef || v === null) {
rob@100 4855 v = new PVector();
rob@100 4856 }
rob@100 4857 v.x = Math.cos(angle);
rob@100 4858 v.y = Math.sin(angle);
rob@100 4859 return v;
rob@100 4860 };
rob@100 4861
rob@100 4862 PVector.random2D = function(v) {
rob@100 4863 return PVector.fromAngle(Math.random() * PConstants.TWO_PI, v);
rob@100 4864 };
rob@100 4865
rob@100 4866 PVector.random3D = function(v) {
rob@100 4867 var angle = Math.random() * PConstants.TWO_PI;
rob@100 4868 var vz = Math.random() * 2 - 1;
rob@100 4869 var mult = Math.sqrt(1 - vz * vz);
rob@100 4870 var vx = mult * Math.cos(angle);
rob@100 4871 var vy = mult * Math.sin(angle);
rob@100 4872 if (v === undef || v === null) {
rob@100 4873 v = new PVector(vx, vy, vz);
rob@100 4874 } else {
rob@100 4875 v.set(vx, vy, vz);
rob@100 4876 }
rob@100 4877 return v;
rob@100 4878 };
rob@100 4879
rob@100 4880 PVector.dist = function(v1, v2) {
rob@100 4881 return v1.dist(v2);
rob@100 4882 };
rob@100 4883
rob@100 4884 PVector.dot = function(v1, v2) {
rob@100 4885 return v1.dot(v2);
rob@100 4886 };
rob@100 4887
rob@100 4888 PVector.cross = function(v1, v2) {
rob@100 4889 return v1.cross(v2);
rob@100 4890 };
rob@100 4891
rob@100 4892 PVector.sub = function(v1, v2) {
rob@100 4893 return new PVector(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z);
rob@100 4894 };
rob@100 4895
rob@100 4896 PVector.angleBetween = function(v1, v2) {
rob@100 4897 return Math.acos(v1.dot(v2) / (v1.mag() * v2.mag()));
rob@100 4898 };
rob@100 4899
rob@100 4900 PVector.lerp = function(v1, v2, amt) {
rob@100 4901 // non-static lerp mutates object, but this version returns a new vector
rob@100 4902 var retval = new PVector(v1.x, v1.y, v1.z);
rob@100 4903 retval.lerp(v2, amt);
rob@100 4904 return retval;
rob@100 4905 };
rob@100 4906
rob@100 4907 // Common vector operations for PVector
rob@100 4908 PVector.prototype = {
rob@100 4909 set: function(v, y, z) {
rob@100 4910 if (arguments.length === 1) {
rob@100 4911 this.set(v.x || v[0] || 0,
rob@100 4912 v.y || v[1] || 0,
rob@100 4913 v.z || v[2] || 0);
rob@100 4914 } else {
rob@100 4915 this.x = v;
rob@100 4916 this.y = y;
rob@100 4917 this.z = z;
rob@100 4918 }
rob@100 4919 },
rob@100 4920 get: function() {
rob@100 4921 return new PVector(this.x, this.y, this.z);
rob@100 4922 },
rob@100 4923 mag: function() {
rob@100 4924 var x = this.x,
rob@100 4925 y = this.y,
rob@100 4926 z = this.z;
rob@100 4927 return Math.sqrt(x * x + y * y + z * z);
rob@100 4928 },
rob@100 4929 magSq: function() {
rob@100 4930 var x = this.x,
rob@100 4931 y = this.y,
rob@100 4932 z = this.z;
rob@100 4933 return (x * x + y * y + z * z);
rob@100 4934 },
rob@100 4935 setMag: function(v_or_len, len) {
rob@100 4936 if (len === undef) {
rob@100 4937 len = v_or_len;
rob@100 4938 this.normalize();
rob@100 4939 this.mult(len);
rob@100 4940 } else {
rob@100 4941 var v = v_or_len;
rob@100 4942 v.normalize();
rob@100 4943 v.mult(len);
rob@100 4944 return v;
rob@100 4945 }
rob@100 4946 },
rob@100 4947 add: function(v, y, z) {
rob@100 4948 if (arguments.length === 1) {
rob@100 4949 this.x += v.x;
rob@100 4950 this.y += v.y;
rob@100 4951 this.z += v.z;
rob@100 4952 } else {
rob@100 4953 this.x += v;
rob@100 4954 this.y += y;
rob@100 4955 this.z += z;
rob@100 4956 }
rob@100 4957 },
rob@100 4958 sub: function(v, y, z) {
rob@100 4959 if (arguments.length === 1) {
rob@100 4960 this.x -= v.x;
rob@100 4961 this.y -= v.y;
rob@100 4962 this.z -= v.z;
rob@100 4963 } else {
rob@100 4964 this.x -= v;
rob@100 4965 this.y -= y;
rob@100 4966 this.z -= z;
rob@100 4967 }
rob@100 4968 },
rob@100 4969 mult: function(v) {
rob@100 4970 if (typeof v === 'number') {
rob@100 4971 this.x *= v;
rob@100 4972 this.y *= v;
rob@100 4973 this.z *= v;
rob@100 4974 } else {
rob@100 4975 this.x *= v.x;
rob@100 4976 this.y *= v.y;
rob@100 4977 this.z *= v.z;
rob@100 4978 }
rob@100 4979 },
rob@100 4980 div: function(v) {
rob@100 4981 if (typeof v === 'number') {
rob@100 4982 this.x /= v;
rob@100 4983 this.y /= v;
rob@100 4984 this.z /= v;
rob@100 4985 } else {
rob@100 4986 this.x /= v.x;
rob@100 4987 this.y /= v.y;
rob@100 4988 this.z /= v.z;
rob@100 4989 }
rob@100 4990 },
rob@100 4991 rotate: function(angle) {
rob@100 4992 var prev_x = this.x;
rob@100 4993 var c = Math.cos(angle);
rob@100 4994 var s = Math.sin(angle);
rob@100 4995 this.x = c * this.x - s * this.y;
rob@100 4996 this.y = s * prev_x + c * this.y;
rob@100 4997 },
rob@100 4998 dist: function(v) {
rob@100 4999 var dx = this.x - v.x,
rob@100 5000 dy = this.y - v.y,
rob@100 5001 dz = this.z - v.z;
rob@100 5002 return Math.sqrt(dx * dx + dy * dy + dz * dz);
rob@100 5003 },
rob@100 5004 dot: function(v, y, z) {
rob@100 5005 if (arguments.length === 1) {
rob@100 5006 return (this.x * v.x + this.y * v.y + this.z * v.z);
rob@100 5007 }
rob@100 5008 return (this.x * v + this.y * y + this.z * z);
rob@100 5009 },
rob@100 5010 cross: function(v) {
rob@100 5011 var x = this.x,
rob@100 5012 y = this.y,
rob@100 5013 z = this.z;
rob@100 5014 return new PVector(y * v.z - v.y * z,
rob@100 5015 z * v.x - v.z * x,
rob@100 5016 x * v.y - v.x * y);
rob@100 5017 },
rob@100 5018 lerp: function(v_or_x, amt_or_y, z, amt) {
rob@100 5019 var lerp_val = function(start, stop, amt) {
rob@100 5020 return start + (stop - start) * amt;
rob@100 5021 };
rob@100 5022 var x, y;
rob@100 5023 if (arguments.length === 2) {
rob@100 5024 // given vector and amt
rob@100 5025 amt = amt_or_y;
rob@100 5026 x = v_or_x.x;
rob@100 5027 y = v_or_x.y;
rob@100 5028 z = v_or_x.z;
rob@100 5029 } else {
rob@100 5030 // given x, y, z and amt
rob@100 5031 x = v_or_x;
rob@100 5032 y = amt_or_y;
rob@100 5033 }
rob@100 5034 this.x = lerp_val(this.x, x, amt);
rob@100 5035 this.y = lerp_val(this.y, y, amt);
rob@100 5036 this.z = lerp_val(this.z, z, amt);
rob@100 5037 },
rob@100 5038 normalize: function() {
rob@100 5039 var m = this.mag();
rob@100 5040 if (m > 0) {
rob@100 5041 this.div(m);
rob@100 5042 }
rob@100 5043 },
rob@100 5044 limit: function(high) {
rob@100 5045 if (this.mag() > high) {
rob@100 5046 this.normalize();
rob@100 5047 this.mult(high);
rob@100 5048 }
rob@100 5049 },
rob@100 5050 heading: function() {
rob@100 5051 return (-Math.atan2(-this.y, this.x));
rob@100 5052 },
rob@100 5053 heading2D: function() {
rob@100 5054 return this.heading();
rob@100 5055 },
rob@100 5056 toString: function() {
rob@100 5057 return "[" + this.x + ", " + this.y + ", " + this.z + "]";
rob@100 5058 },
rob@100 5059 array: function() {
rob@100 5060 return [this.x, this.y, this.z];
rob@100 5061 }
rob@100 5062 };
rob@100 5063
rob@100 5064 function createPVectorMethod(method) {
rob@100 5065 return function(v1, v2) {
rob@100 5066 var v = v1.get();
rob@100 5067 v[method](v2);
rob@100 5068 return v;
rob@100 5069 };
rob@100 5070 }
rob@100 5071
rob@100 5072 for (var method in PVector.prototype) {
rob@100 5073 if (PVector.prototype.hasOwnProperty(method) && !PVector.hasOwnProperty(method)) {
rob@100 5074 PVector[method] = createPVectorMethod(method);
rob@100 5075 }
rob@100 5076 }
rob@100 5077
rob@100 5078 return PVector;
rob@100 5079 };
rob@100 5080
rob@100 5081
rob@100 5082 },{}],18:[function(require,module,exports){
rob@100 5083 /**
rob@100 5084 * XMLAttribute is an attribute of a XML element.
rob@100 5085 *
rob@100 5086 * @param {String} fname the full name of the attribute
rob@100 5087 * @param {String} n the short name of the attribute
rob@100 5088 * @param {String} namespace the namespace URI of the attribute
rob@100 5089 * @param {String} v the value of the attribute
rob@100 5090 * @param {String }t the type of the attribute
rob@100 5091 *
rob@100 5092 * @see XMLElement
rob@100 5093 */
rob@100 5094 module.exports = function() {
rob@100 5095
rob@100 5096 var XMLAttribute = function (fname, n, nameSpace, v, t){
rob@100 5097 this.fullName = fname || "";
rob@100 5098 this.name = n || "";
rob@100 5099 this.namespace = nameSpace || "";
rob@100 5100 this.value = v;
rob@100 5101 this.type = t;
rob@100 5102 };
rob@100 5103
rob@100 5104 XMLAttribute.prototype = {
rob@100 5105 /**
rob@100 5106 * @member XMLAttribute
rob@100 5107 * The getName() function returns the short name of the attribute
rob@100 5108 *
rob@100 5109 * @return {String} the short name of the attribute
rob@100 5110 */
rob@100 5111 getName: function() {
rob@100 5112 return this.name;
rob@100 5113 },
rob@100 5114 /**
rob@100 5115 * @member XMLAttribute
rob@100 5116 * The getFullName() function returns the full name of the attribute
rob@100 5117 *
rob@100 5118 * @return {String} the full name of the attribute
rob@100 5119 */
rob@100 5120 getFullName: function() {
rob@100 5121 return this.fullName;
rob@100 5122 },
rob@100 5123 /**
rob@100 5124 * @member XMLAttribute
rob@100 5125 * The getNamespace() function returns the namespace of the attribute
rob@100 5126 *
rob@100 5127 * @return {String} the namespace of the attribute
rob@100 5128 */
rob@100 5129 getNamespace: function() {
rob@100 5130 return this.namespace;
rob@100 5131 },
rob@100 5132 /**
rob@100 5133 * @member XMLAttribute
rob@100 5134 * The getValue() function returns the value of the attribute
rob@100 5135 *
rob@100 5136 * @return {String} the value of the attribute
rob@100 5137 */
rob@100 5138 getValue: function() {
rob@100 5139 return this.value;
rob@100 5140 },
rob@100 5141 /**
rob@100 5142 * @member XMLAttribute
rob@100 5143 * The getValue() function returns the type of the attribute
rob@100 5144 *
rob@100 5145 * @return {String} the type of the attribute
rob@100 5146 */
rob@100 5147 getType: function() {
rob@100 5148 return this.type;
rob@100 5149 },
rob@100 5150 /**
rob@100 5151 * @member XMLAttribute
rob@100 5152 * The setValue() function sets the value of the attribute
rob@100 5153 *
rob@100 5154 * @param {String} newval the new value
rob@100 5155 */
rob@100 5156 setValue: function(newval) {
rob@100 5157 this.value = newval;
rob@100 5158 }
rob@100 5159 };
rob@100 5160
rob@100 5161 return XMLAttribute;
rob@100 5162 };
rob@100 5163
rob@100 5164 },{}],19:[function(require,module,exports){
rob@100 5165 /**
rob@100 5166 * XMLElement is a representation of an XML object. The object is able to parse XML code
rob@100 5167 *
rob@100 5168 * @param {PApplet} parent typically use "this"
rob@100 5169 * @param {String} filename name of the XML/SVG file to load
rob@100 5170 * @param {String} xml the xml/svg string
rob@100 5171 * @param {String} fullname the full name of the element
rob@100 5172 * @param {String} namespace the namespace of the URI
rob@100 5173 * @param {String} systemID the system ID of the XML data where the element starts
rob@100 5174 * @param {Integer }lineNr the line in the XML data where the element starts
rob@100 5175 */
rob@100 5176 module.exports = function(options, undef) {
rob@100 5177
rob@100 5178 var Browser = options.Browser,
rob@100 5179 ajax = Browser.ajax,
rob@100 5180 window = Browser.window,
rob@100 5181 XMLHttpRequest = window.XMLHttpRequest,
rob@100 5182 DOMParser = window.DOMParser,
rob@100 5183 XMLAttribute = options. XMLAttribute;
rob@100 5184
rob@100 5185 var XMLElement = function(selector, uri, sysid, line) {
rob@100 5186 this.attributes = [];
rob@100 5187 this.children = [];
rob@100 5188 this.fullName = null;
rob@100 5189 this.name = null;
rob@100 5190 this.namespace = "";
rob@100 5191 this.content = null;
rob@100 5192 this.parent = null;
rob@100 5193 this.lineNr = "";
rob@100 5194 this.systemID = "";
rob@100 5195 this.type = "ELEMENT";
rob@100 5196
rob@100 5197 if (selector) {
rob@100 5198 if (typeof selector === "string") {
rob@100 5199 if (uri === undef && selector.indexOf("<") > -1) {
rob@100 5200 // load XML from text string
rob@100 5201 this.parse(selector);
rob@100 5202 } else {
rob@100 5203 // XMLElement(fullname, namespace, sysid, line) format
rob@100 5204 this.fullName = selector;
rob@100 5205 this.namespace = uri;
rob@100 5206 this.systemId = sysid;
rob@100 5207 this.lineNr = line;
rob@100 5208 }
rob@100 5209 } else {
rob@100 5210 // XMLElement(this,file) format
rob@100 5211 this.parse(uri);
rob@100 5212 }
rob@100 5213 }
rob@100 5214 };
rob@100 5215 /**
rob@100 5216 * XMLElement methods
rob@100 5217 * missing: enumerateAttributeNames(), enumerateChildren(),
rob@100 5218 * NOTE: parse does not work when a url is passed in
rob@100 5219 */
rob@100 5220 XMLElement.prototype = {
rob@100 5221 /**
rob@100 5222 * @member XMLElement
rob@100 5223 * The parse() function retrieves the file via ajax() and uses DOMParser()
rob@100 5224 * parseFromString method to make an XML document
rob@100 5225 * @addon
rob@100 5226 *
rob@100 5227 * @param {String} filename name of the XML/SVG file to load
rob@100 5228 *
rob@100 5229 * @throws ExceptionType Error loading document
rob@100 5230 *
rob@100 5231 * @see XMLElement#parseChildrenRecursive
rob@100 5232 */
rob@100 5233 parse: function(textstring) {
rob@100 5234 var xmlDoc;
rob@100 5235 try {
rob@100 5236 var extension = textstring.substring(textstring.length-4);
rob@100 5237 if (extension === ".xml" || extension === ".svg") {
rob@100 5238 textstring = ajax(textstring);
rob@100 5239 }
rob@100 5240 xmlDoc = new DOMParser().parseFromString(textstring, "text/xml");
rob@100 5241 var elements = xmlDoc.documentElement;
rob@100 5242 if (elements) {
rob@100 5243 this.parseChildrenRecursive(null, elements);
rob@100 5244 } else {
rob@100 5245 throw ("Error loading document");
rob@100 5246 }
rob@100 5247 return this;
rob@100 5248 } catch(e) {
rob@100 5249 throw(e);
rob@100 5250 }
rob@100 5251 },
rob@100 5252 /**
rob@100 5253 * @member XMLElement
rob@100 5254 * Internal helper function for parse().
rob@100 5255 * Loops through the
rob@100 5256 * @addon
rob@100 5257 *
rob@100 5258 * @param {XMLElement} parent the parent node
rob@100 5259 * @param {XML document childNodes} elementpath the remaining nodes that need parsing
rob@100 5260 *
rob@100 5261 * @return {XMLElement} the new element and its children elements
rob@100 5262 */
rob@100 5263 parseChildrenRecursive: function (parent, elementpath){
rob@100 5264 var xmlelement,
rob@100 5265 xmlattribute,
rob@100 5266 tmpattrib,
rob@100 5267 l, m,
rob@100 5268 child;
rob@100 5269 if (!parent) { // this element is the root element
rob@100 5270 this.fullName = elementpath.localName;
rob@100 5271 this.name = elementpath.nodeName;
rob@100 5272 xmlelement = this;
rob@100 5273 } else { // this element has a parent
rob@100 5274 xmlelement = new XMLElement(elementpath.nodeName);
rob@100 5275 xmlelement.parent = parent;
rob@100 5276 }
rob@100 5277
rob@100 5278 // if this is a text node, return a PCData element (parsed character data)
rob@100 5279 if (elementpath.nodeType === 3 && elementpath.textContent !== "") {
rob@100 5280 return this.createPCDataElement(elementpath.textContent);
rob@100 5281 }
rob@100 5282
rob@100 5283 // if this is a CDATA node, return a CData element (unparsed character data)
rob@100 5284 if (elementpath.nodeType === 4) {
rob@100 5285 return this.createCDataElement(elementpath.textContent);
rob@100 5286 }
rob@100 5287
rob@100 5288 // bind all attributes, if there are any
rob@100 5289 if (elementpath.attributes) {
rob@100 5290 for (l = 0, m = elementpath.attributes.length; l < m; l++) {
rob@100 5291 tmpattrib = elementpath.attributes[l];
rob@100 5292 xmlattribute = new XMLAttribute(tmpattrib.getname,
rob@100 5293 tmpattrib.nodeName,
rob@100 5294 tmpattrib.namespaceURI,
rob@100 5295 tmpattrib.nodeValue,
rob@100 5296 tmpattrib.nodeType);
rob@100 5297 xmlelement.attributes.push(xmlattribute);
rob@100 5298 }
rob@100 5299 }
rob@100 5300
rob@100 5301 // bind all children, if there are any
rob@100 5302 if (elementpath.childNodes) {
rob@100 5303 for (l = 0, m = elementpath.childNodes.length; l < m; l++) {
rob@100 5304 var node = elementpath.childNodes[l];
rob@100 5305 child = xmlelement.parseChildrenRecursive(xmlelement, node);
rob@100 5306 if (child !== null) {
rob@100 5307 xmlelement.children.push(child);
rob@100 5308 }
rob@100 5309 }
rob@100 5310 }
rob@100 5311
rob@100 5312 return xmlelement;
rob@100 5313 },
rob@100 5314 /**
rob@100 5315 * @member XMLElement
rob@100 5316 * The createElement() function Creates an empty element
rob@100 5317 *
rob@100 5318 * @param {String} fullName the full name of the element
rob@100 5319 * @param {String} namespace the namespace URI
rob@100 5320 * @param {String} systemID the system ID of the XML data where the element starts
rob@100 5321 * @param {int} lineNr the line in the XML data where the element starts
rob@100 5322 */
rob@100 5323 createElement: function (fullname, namespaceuri, sysid, line) {
rob@100 5324 if (sysid === undef) {
rob@100 5325 return new XMLElement(fullname, namespaceuri);
rob@100 5326 }
rob@100 5327 return new XMLElement(fullname, namespaceuri, sysid, line);
rob@100 5328 },
rob@100 5329 /**
rob@100 5330 * @member XMLElement
rob@100 5331 * The createPCDataElement() function creates an element to be used for #PCDATA content.
rob@100 5332 * Because Processing discards whitespace TEXT nodes, this method will not build an element
rob@100 5333 * if the passed content is empty after trimming for whitespace.
rob@100 5334 *
rob@100 5335 * @return {XMLElement} new "pcdata" XMLElement, or null if content consists only of whitespace
rob@100 5336 */
rob@100 5337 createPCDataElement: function (content, isCDATA) {
rob@100 5338 if (content.replace(/^\s+$/g,"") === "") {
rob@100 5339 return null;
rob@100 5340 }
rob@100 5341 var pcdata = new XMLElement();
rob@100 5342 pcdata.type = "TEXT";
rob@100 5343 pcdata.content = content;
rob@100 5344 return pcdata;
rob@100 5345 },
rob@100 5346 /**
rob@100 5347 * @member XMLElement
rob@100 5348 * The createCDataElement() function creates an element to be used for CDATA content.
rob@100 5349 *
rob@100 5350 * @return {XMLElement} new "cdata" XMLElement, or null if content consists only of whitespace
rob@100 5351 */
rob@100 5352 createCDataElement: function (content) {
rob@100 5353 var cdata = this.createPCDataElement(content);
rob@100 5354 if (cdata === null) {
rob@100 5355 return null;
rob@100 5356 }
rob@100 5357
rob@100 5358 cdata.type = "CDATA";
rob@100 5359 var htmlentities = {"<": "&lt;", ">": "&gt;", "'": "&apos;", '"': "&quot;"},
rob@100 5360 entity;
rob@100 5361 for (entity in htmlentities) {
rob@100 5362 if (!Object.hasOwnProperty(htmlentities,entity)) {
rob@100 5363 content = content.replace(new RegExp(entity, "g"), htmlentities[entity]);
rob@100 5364 }
rob@100 5365 }
rob@100 5366 cdata.cdata = content;
rob@100 5367 return cdata;
rob@100 5368 },
rob@100 5369 /**
rob@100 5370 * @member XMLElement
rob@100 5371 * The hasAttribute() function returns whether an attribute exists
rob@100 5372 *
rob@100 5373 * @param {String} name name of the attribute
rob@100 5374 * @param {String} namespace the namespace URI of the attribute
rob@100 5375 *
rob@100 5376 * @return {boolean} true if the attribute exists
rob@100 5377 */
rob@100 5378 hasAttribute: function () {
rob@100 5379 if (arguments.length === 1) {
rob@100 5380 return this.getAttribute(arguments[0]) !== null;
rob@100 5381 }
rob@100 5382 if (arguments.length === 2) {
rob@100 5383 return this.getAttribute(arguments[0],arguments[1]) !== null;
rob@100 5384 }
rob@100 5385 },
rob@100 5386 /**
rob@100 5387 * @member XMLElement
rob@100 5388 * The equals() function checks to see if the XMLElement being passed in equals another XMLElement
rob@100 5389 *
rob@100 5390 * @param {XMLElement} rawElement the element to compare to
rob@100 5391 *
rob@100 5392 * @return {boolean} true if the element equals another element
rob@100 5393 */
rob@100 5394 equals: function(other) {
rob@100 5395 if (!(other instanceof XMLElement)) {
rob@100 5396 return false;
rob@100 5397 }
rob@100 5398 var i, j;
rob@100 5399 if (this.fullName !== other.fullName) { return false; }
rob@100 5400 if (this.attributes.length !== other.getAttributeCount()) { return false; }
rob@100 5401 // attributes may be ordered differently
rob@100 5402 if (this.attributes.length !== other.attributes.length) { return false; }
rob@100 5403 var attr_name, attr_ns, attr_value, attr_type, attr_other;
rob@100 5404 for (i = 0, j = this.attributes.length; i < j; i++) {
rob@100 5405 attr_name = this.attributes[i].getName();
rob@100 5406 attr_ns = this.attributes[i].getNamespace();
rob@100 5407 attr_other = other.findAttribute(attr_name, attr_ns);
rob@100 5408 if (attr_other === null) { return false; }
rob@100 5409 if (this.attributes[i].getValue() !== attr_other.getValue()) { return false; }
rob@100 5410 if (this.attributes[i].getType() !== attr_other.getType()) { return false; }
rob@100 5411 }
rob@100 5412 // children must be ordered identically
rob@100 5413 if (this.children.length !== other.getChildCount()) { return false; }
rob@100 5414 if (this.children.length>0) {
rob@100 5415 var child1, child2;
rob@100 5416 for (i = 0, j = this.children.length; i < j; i++) {
rob@100 5417 child1 = this.getChild(i);
rob@100 5418 child2 = other.getChild(i);
rob@100 5419 if (!child1.equals(child2)) { return false; }
rob@100 5420 }
rob@100 5421 return true;
rob@100 5422 }
rob@100 5423 return (this.content === other.content);
rob@100 5424 },
rob@100 5425 /**
rob@100 5426 * @member XMLElement
rob@100 5427 * The getContent() function returns the content of an element. If there is no such content, null is returned
rob@100 5428 *
rob@100 5429 * @return {String} the (possibly null) content
rob@100 5430 */
rob@100 5431 getContent: function(){
rob@100 5432 if (this.type === "TEXT" || this.type === "CDATA") {
rob@100 5433 return this.content;
rob@100 5434 }
rob@100 5435 var children = this.children;
rob@100 5436 if (children.length === 1 && (children[0].type === "TEXT" || children[0].type === "CDATA")) {
rob@100 5437 return children[0].content;
rob@100 5438 }
rob@100 5439 return null;
rob@100 5440 },
rob@100 5441 /**
rob@100 5442 * @member XMLElement
rob@100 5443 * The getAttribute() function returns the value of an attribute
rob@100 5444 *
rob@100 5445 * @param {String} name the non-null full name of the attribute
rob@100 5446 * @param {String} namespace the namespace URI, which may be null
rob@100 5447 * @param {String} defaultValue the default value of the attribute
rob@100 5448 *
rob@100 5449 * @return {String} the value, or defaultValue if the attribute does not exist
rob@100 5450 */
rob@100 5451 getAttribute: function (){
rob@100 5452 var attribute;
rob@100 5453 if (arguments.length === 2) {
rob@100 5454 attribute = this.findAttribute(arguments[0]);
rob@100 5455 if (attribute) {
rob@100 5456 return attribute.getValue();
rob@100 5457 }
rob@100 5458 return arguments[1];
rob@100 5459 } else if (arguments.length === 1) {
rob@100 5460 attribute = this.findAttribute(arguments[0]);
rob@100 5461 if (attribute) {
rob@100 5462 return attribute.getValue();
rob@100 5463 }
rob@100 5464 return null;
rob@100 5465 } else if (arguments.length === 3) {
rob@100 5466 attribute = this.findAttribute(arguments[0],arguments[1]);
rob@100 5467 if (attribute) {
rob@100 5468 return attribute.getValue();
rob@100 5469 }
rob@100 5470 return arguments[2];
rob@100 5471 }
rob@100 5472 },
rob@100 5473 /**
rob@100 5474 * @member XMLElement
rob@100 5475 * The getStringAttribute() function returns the string attribute of the element
rob@100 5476 * If the <b>defaultValue</b> parameter is used and the attribute doesn't exist, the <b>defaultValue</b> value is returned.
rob@100 5477 * When calling the function without the <b>defaultValue</b> parameter, if the attribute doesn't exist, the value 0 is returned.
rob@100 5478 *
rob@100 5479 * @param name the name of the attribute
rob@100 5480 * @param defaultValue value returned if the attribute is not found
rob@100 5481 *
rob@100 5482 * @return {String} the value, or defaultValue if the attribute does not exist
rob@100 5483 */
rob@100 5484 getStringAttribute: function() {
rob@100 5485 if (arguments.length === 1) {
rob@100 5486 return this.getAttribute(arguments[0]);
rob@100 5487 }
rob@100 5488 if (arguments.length === 2) {
rob@100 5489 return this.getAttribute(arguments[0], arguments[1]);
rob@100 5490 }
rob@100 5491 return this.getAttribute(arguments[0], arguments[1],arguments[2]);
rob@100 5492 },
rob@100 5493 /**
rob@100 5494 * Processing 1.5 XML API wrapper for the generic String
rob@100 5495 * attribute getter. This may only take one argument.
rob@100 5496 */
rob@100 5497 getString: function(attributeName) {
rob@100 5498 return this.getStringAttribute(attributeName);
rob@100 5499 },
rob@100 5500 /**
rob@100 5501 * @member XMLElement
rob@100 5502 * The getFloatAttribute() function returns the float attribute of the element.
rob@100 5503 * If the <b>defaultValue</b> parameter is used and the attribute doesn't exist, the <b>defaultValue</b> value is returned.
rob@100 5504 * When calling the function without the <b>defaultValue</b> parameter, if the attribute doesn't exist, the value 0 is returned.
rob@100 5505 *
rob@100 5506 * @param name the name of the attribute
rob@100 5507 * @param defaultValue value returned if the attribute is not found
rob@100 5508 *
rob@100 5509 * @return {float} the value, or defaultValue if the attribute does not exist
rob@100 5510 */
rob@100 5511 getFloatAttribute: function() {
rob@100 5512 if (arguments.length === 1 ) {
rob@100 5513 return parseFloat(this.getAttribute(arguments[0], 0));
rob@100 5514 }
rob@100 5515 if (arguments.length === 2 ) {
rob@100 5516 return this.getAttribute(arguments[0], arguments[1]);
rob@100 5517 }
rob@100 5518 return this.getAttribute(arguments[0], arguments[1],arguments[2]);
rob@100 5519 },
rob@100 5520 /**
rob@100 5521 * Processing 1.5 XML API wrapper for the generic float
rob@100 5522 * attribute getter. This may only take one argument.
rob@100 5523 */
rob@100 5524 getFloat: function(attributeName) {
rob@100 5525 return this.getFloatAttribute(attributeName);
rob@100 5526 },
rob@100 5527 /**
rob@100 5528 * @member XMLElement
rob@100 5529 * The getIntAttribute() function returns the integer attribute of the element.
rob@100 5530 * If the <b>defaultValue</b> parameter is used and the attribute doesn't exist, the <b>defaultValue</b> value is returned.
rob@100 5531 * When calling the function without the <b>defaultValue</b> parameter, if the attribute doesn't exist, the value 0 is returned.
rob@100 5532 *
rob@100 5533 * @param name the name of the attribute
rob@100 5534 * @param defaultValue value returned if the attribute is not found
rob@100 5535 *
rob@100 5536 * @return {int} the value, or defaultValue if the attribute does not exist
rob@100 5537 */
rob@100 5538 getIntAttribute: function () {
rob@100 5539 if (arguments.length === 1) {
rob@100 5540 return this.getAttribute( arguments[0], 0 );
rob@100 5541 }
rob@100 5542 if (arguments.length === 2) {
rob@100 5543 return this.getAttribute(arguments[0], arguments[1]);
rob@100 5544 }
rob@100 5545 return this.getAttribute(arguments[0], arguments[1],arguments[2]);
rob@100 5546 },
rob@100 5547 /**
rob@100 5548 * Processing 1.5 XML API wrapper for the generic int
rob@100 5549 * attribute getter. This may only take one argument.
rob@100 5550 */
rob@100 5551 getInt: function(attributeName) {
rob@100 5552 return this.getIntAttribute(attributeName);
rob@100 5553 },
rob@100 5554 /**
rob@100 5555 * @member XMLElement
rob@100 5556 * The hasChildren() function returns whether the element has children.
rob@100 5557 *
rob@100 5558 * @return {boolean} true if the element has children.
rob@100 5559 */
rob@100 5560 hasChildren: function () {
rob@100 5561 return this.children.length > 0 ;
rob@100 5562 },
rob@100 5563 /**
rob@100 5564 * @member XMLElement
rob@100 5565 * The addChild() function adds a child element
rob@100 5566 *
rob@100 5567 * @param {XMLElement} child the non-null child to add.
rob@100 5568 */
rob@100 5569 addChild: function (child) {
rob@100 5570 if (child !== null) {
rob@100 5571 child.parent = this;
rob@100 5572 this.children.push(child);
rob@100 5573 }
rob@100 5574 },
rob@100 5575 /**
rob@100 5576 * @member XMLElement
rob@100 5577 * The insertChild() function inserts a child element at the index provided
rob@100 5578 *
rob@100 5579 * @param {XMLElement} child the non-null child to add.
rob@100 5580 * @param {int} index where to put the child.
rob@100 5581 */
rob@100 5582 insertChild: function (child, index) {
rob@100 5583 if (child) {
rob@100 5584 if ((child.getLocalName() === null) && (! this.hasChildren())) {
rob@100 5585 var lastChild = this.children[this.children.length -1];
rob@100 5586 if (lastChild.getLocalName() === null) {
rob@100 5587 lastChild.setContent(lastChild.getContent() + child.getContent());
rob@100 5588 return;
rob@100 5589 }
rob@100 5590 }
rob@100 5591 child.parent = this;
rob@100 5592 this.children.splice(index,0,child);
rob@100 5593 }
rob@100 5594 },
rob@100 5595 /**
rob@100 5596 * @member XMLElement
rob@100 5597 * The getChild() returns the child XMLElement as specified by the <b>index</b> parameter.
rob@100 5598 * The value of the <b>index</b> parameter must be less than the total number of children to avoid going out of the array storing the child elements.
rob@100 5599 * When the <b>path</b> parameter is specified, then it will return all children that match that path. The path is a series of elements and sub-elements, separated by slashes.
rob@100 5600 *
rob@100 5601 * @param {int} index where to put the child.
rob@100 5602 * @param {String} path path to a particular element
rob@100 5603 *
rob@100 5604 * @return {XMLElement} the element
rob@100 5605 */
rob@100 5606 getChild: function (selector) {
rob@100 5607 if (typeof selector === "number") {
rob@100 5608 return this.children[selector];
rob@100 5609 }
rob@100 5610 if (selector.indexOf('/') !== -1) {
rob@100 5611 // path traversal is required
rob@100 5612 return this.getChildRecursive(selector.split("/"), 0);
rob@100 5613 }
rob@100 5614 var kid, kidName;
rob@100 5615 for (var i = 0, j = this.getChildCount(); i < j; i++) {
rob@100 5616 kid = this.getChild(i);
rob@100 5617 kidName = kid.getName();
rob@100 5618 if (kidName !== null && kidName === selector) {
rob@100 5619 return kid;
rob@100 5620 }
rob@100 5621 }
rob@100 5622 return null;
rob@100 5623 },
rob@100 5624 /**
rob@100 5625 * @member XMLElement
rob@100 5626 * The getChildren() returns all of the children as an XMLElement array.
rob@100 5627 * When the <b>path</b> parameter is specified, then it will return all children that match that path.
rob@100 5628 * The path is a series of elements and sub-elements, separated by slashes.
rob@100 5629 *
rob@100 5630 * @param {String} path element name or path/to/element
rob@100 5631 *
rob@100 5632 * @return {XMLElement} array of child elements that match
rob@100 5633 *
rob@100 5634 * @see XMLElement#getChildCount()
rob@100 5635 * @see XMLElement#getChild()
rob@100 5636 */
rob@100 5637 getChildren: function(){
rob@100 5638 if (arguments.length === 1) {
rob@100 5639 if (typeof arguments[0] === "number") {
rob@100 5640 return this.getChild( arguments[0]);
rob@100 5641 }
rob@100 5642 if (arguments[0].indexOf('/') !== -1) { // path was given
rob@100 5643 return this.getChildrenRecursive( arguments[0].split("/"), 0);
rob@100 5644 }
rob@100 5645 var matches = [];
rob@100 5646 var kid, kidName;
rob@100 5647 for (var i = 0, j = this.getChildCount(); i < j; i++) {
rob@100 5648 kid = this.getChild(i);
rob@100 5649 kidName = kid.getName();
rob@100 5650 if (kidName !== null && kidName === arguments[0]) {
rob@100 5651 matches.push(kid);
rob@100 5652 }
rob@100 5653 }
rob@100 5654 return matches;
rob@100 5655 }
rob@100 5656 return this.children;
rob@100 5657 },
rob@100 5658 /**
rob@100 5659 * @member XMLElement
rob@100 5660 * The getChildCount() returns the number of children for the element.
rob@100 5661 *
rob@100 5662 * @return {int} the count
rob@100 5663 *
rob@100 5664 * @see XMLElement#getChild()
rob@100 5665 * @see XMLElement#getChildren()
rob@100 5666 */
rob@100 5667 getChildCount: function() {
rob@100 5668 return this.children.length;
rob@100 5669 },
rob@100 5670 /**
rob@100 5671 * @member XMLElement
rob@100 5672 * Internal helper function for getChild().
rob@100 5673 *
rob@100 5674 * @param {String[]} items result of splitting the query on slashes
rob@100 5675 * @param {int} offset where in the items[] array we're currently looking
rob@100 5676 *
rob@100 5677 * @return {XMLElement} matching element or null if no match
rob@100 5678 */
rob@100 5679 getChildRecursive: function (items, offset) {
rob@100 5680 // terminating clause: we are the requested candidate
rob@100 5681 if (offset === items.length) {
rob@100 5682 return this;
rob@100 5683 }
rob@100 5684 // continuation clause
rob@100 5685 var kid, kidName, matchName = items[offset];
rob@100 5686 for(var i = 0, j = this.getChildCount(); i < j; i++) {
rob@100 5687 kid = this.getChild(i);
rob@100 5688 kidName = kid.getName();
rob@100 5689 if (kidName !== null && kidName === matchName) {
rob@100 5690 return kid.getChildRecursive(items, offset+1);
rob@100 5691 }
rob@100 5692 }
rob@100 5693 return null;
rob@100 5694 },
rob@100 5695 /**
rob@100 5696 * @member XMLElement
rob@100 5697 * Internal helper function for getChildren().
rob@100 5698 *
rob@100 5699 * @param {String[]} items result of splitting the query on slashes
rob@100 5700 * @param {int} offset where in the items[] array we're currently looking
rob@100 5701 *
rob@100 5702 * @return {XMLElement[]} matching elements or empty array if no match
rob@100 5703 */
rob@100 5704 getChildrenRecursive: function (items, offset) {
rob@100 5705 if (offset === items.length-1) {
rob@100 5706 return this.getChildren(items[offset]);
rob@100 5707 }
rob@100 5708 var matches = this.getChildren(items[offset]);
rob@100 5709 var kidMatches = [];
rob@100 5710 for (var i = 0; i < matches.length; i++) {
rob@100 5711 kidMatches = kidMatches.concat(matches[i].getChildrenRecursive(items, offset+1));
rob@100 5712 }
rob@100 5713 return kidMatches;
rob@100 5714 },
rob@100 5715 /**
rob@100 5716 * @member XMLElement
rob@100 5717 * The isLeaf() function returns whether the element is a leaf element.
rob@100 5718 *
rob@100 5719 * @return {boolean} true if the element has no children.
rob@100 5720 */
rob@100 5721 isLeaf: function() {
rob@100 5722 return !this.hasChildren();
rob@100 5723 },
rob@100 5724 /**
rob@100 5725 * @member XMLElement
rob@100 5726 * The listChildren() function put the names of all children into an array. Same as looping through
rob@100 5727 * each child and calling getName() on each XMLElement.
rob@100 5728 *
rob@100 5729 * @return {String[]} a list of element names.
rob@100 5730 */
rob@100 5731 listChildren: function() {
rob@100 5732 var arr = [];
rob@100 5733 for (var i = 0, j = this.children.length; i < j; i++) {
rob@100 5734 arr.push( this.getChild(i).getName());
rob@100 5735 }
rob@100 5736 return arr;
rob@100 5737 },
rob@100 5738 /**
rob@100 5739 * @member XMLElement
rob@100 5740 * The removeAttribute() function removes an attribute
rob@100 5741 *
rob@100 5742 * @param {String} name the non-null name of the attribute.
rob@100 5743 * @param {String} namespace the namespace URI of the attribute, which may be null.
rob@100 5744 */
rob@100 5745 removeAttribute: function (name , namespace) {
rob@100 5746 this.namespace = namespace || "";
rob@100 5747 for (var i = 0, j = this.attributes.length; i < j; i++) {
rob@100 5748 if (this.attributes[i].getName() === name && this.attributes[i].getNamespace() === this.namespace) {
rob@100 5749 this.attributes.splice(i, 1);
rob@100 5750 break;
rob@100 5751 }
rob@100 5752 }
rob@100 5753 },
rob@100 5754 /**
rob@100 5755 * @member XMLElement
rob@100 5756 * The removeChild() removes a child element.
rob@100 5757 *
rob@100 5758 * @param {XMLElement} child the the non-null child to be renoved
rob@100 5759 */
rob@100 5760 removeChild: function(child) {
rob@100 5761 if (child) {
rob@100 5762 for (var i = 0, j = this.children.length; i < j; i++) {
rob@100 5763 if (this.children[i].equals(child)) {
rob@100 5764 this.children.splice(i, 1);
rob@100 5765 break;
rob@100 5766 }
rob@100 5767 }
rob@100 5768 }
rob@100 5769 },
rob@100 5770 /**
rob@100 5771 * @member XMLElement
rob@100 5772 * The removeChildAtIndex() removes the child located at a certain index
rob@100 5773 *
rob@100 5774 * @param {int} index the index of the child, where the first child has index 0
rob@100 5775 */
rob@100 5776 removeChildAtIndex: function(index) {
rob@100 5777 if (this.children.length > index) { //make sure its not outofbounds
rob@100 5778 this.children.splice(index, 1);
rob@100 5779 }
rob@100 5780 },
rob@100 5781 /**
rob@100 5782 * @member XMLElement
rob@100 5783 * The findAttribute() function searches an attribute
rob@100 5784 *
rob@100 5785 * @param {String} name fullName the non-null full name of the attribute
rob@100 5786 * @param {String} namespace the name space, which may be null
rob@100 5787 *
rob@100 5788 * @return {XMLAttribute} the attribute, or null if the attribute does not exist.
rob@100 5789 */
rob@100 5790 findAttribute: function (name, namespace) {
rob@100 5791 this.namespace = namespace || "";
rob@100 5792 for (var i = 0, j = this.attributes.length; i < j; i++) {
rob@100 5793 if (this.attributes[i].getName() === name && this.attributes[i].getNamespace() === this.namespace) {
rob@100 5794 return this.attributes[i];
rob@100 5795 }
rob@100 5796 }
rob@100 5797 return null;
rob@100 5798 },
rob@100 5799 /**
rob@100 5800 * @member XMLElement
rob@100 5801 * The setAttribute() function sets an attribute.
rob@100 5802 *
rob@100 5803 * @param {String} name the non-null full name of the attribute
rob@100 5804 * @param {String} namespace the non-null value of the attribute
rob@100 5805 */
rob@100 5806 setAttribute: function() {
rob@100 5807 var attr;
rob@100 5808 if (arguments.length === 3) {
rob@100 5809 var index = arguments[0].indexOf(':');
rob@100 5810 var name = arguments[0].substring(index + 1);
rob@100 5811 attr = this.findAttribute(name, arguments[1]);
rob@100 5812 if (attr) {
rob@100 5813 attr.setValue(arguments[2]);
rob@100 5814 } else {
rob@100 5815 attr = new XMLAttribute(arguments[0], name, arguments[1], arguments[2], "CDATA");
rob@100 5816 this.attributes.push(attr);
rob@100 5817 }
rob@100 5818 } else {
rob@100 5819 attr = this.findAttribute(arguments[0]);
rob@100 5820 if (attr) {
rob@100 5821 attr.setValue(arguments[1]);
rob@100 5822 } else {
rob@100 5823 attr = new XMLAttribute(arguments[0], arguments[0], null, arguments[1], "CDATA");
rob@100 5824 this.attributes.push(attr);
rob@100 5825 }
rob@100 5826 }
rob@100 5827 },
rob@100 5828 /**
rob@100 5829 * Processing 1.5 XML API wrapper for the generic String
rob@100 5830 * attribute setter. This must take two arguments.
rob@100 5831 */
rob@100 5832 setString: function(attribute, value) {
rob@100 5833 this.setAttribute(attribute, value);
rob@100 5834 },
rob@100 5835 /**
rob@100 5836 * Processing 1.5 XML API wrapper for the generic int
rob@100 5837 * attribute setter. This must take two arguments.
rob@100 5838 */
rob@100 5839 setInt: function(attribute, value) {
rob@100 5840 this.setAttribute(attribute, value);
rob@100 5841 },
rob@100 5842 /**
rob@100 5843 * Processing 1.5 XML API wrapper for the generic float
rob@100 5844 * attribute setter. This must take two arguments.
rob@100 5845 */
rob@100 5846 setFloat: function(attribute, value) {
rob@100 5847 this.setAttribute(attribute, value);
rob@100 5848 },
rob@100 5849 /**
rob@100 5850 * @member XMLElement
rob@100 5851 * The setContent() function sets the #PCDATA content. It is an error to call this method with a
rob@100 5852 * non-null value if there are child objects.
rob@100 5853 *
rob@100 5854 * @param {String} content the (possibly null) content
rob@100 5855 */
rob@100 5856 setContent: function(content) {
rob@100 5857 if (this.children.length > 0) {
rob@100 5858 Processing.debug("Tried to set content for XMLElement with children"); }
rob@100 5859 this.content = content;
rob@100 5860 },
rob@100 5861 /**
rob@100 5862 * @member XMLElement
rob@100 5863 * The setName() function sets the full name. This method also sets the short name and clears the
rob@100 5864 * namespace URI.
rob@100 5865 *
rob@100 5866 * @param {String} name the non-null name
rob@100 5867 * @param {String} namespace the namespace URI, which may be null.
rob@100 5868 */
rob@100 5869 setName: function() {
rob@100 5870 if (arguments.length === 1) {
rob@100 5871 this.name = arguments[0];
rob@100 5872 this.fullName = arguments[0];
rob@100 5873 this.namespace = null;
rob@100 5874 } else {
rob@100 5875 var index = arguments[0].indexOf(':');
rob@100 5876 if ((arguments[1] === null) || (index < 0)) {
rob@100 5877 this.name = arguments[0];
rob@100 5878 } else {
rob@100 5879 this.name = arguments[0].substring(index + 1);
rob@100 5880 }
rob@100 5881 this.fullName = arguments[0];
rob@100 5882 this.namespace = arguments[1];
rob@100 5883 }
rob@100 5884 },
rob@100 5885 /**
rob@100 5886 * @member XMLElement
rob@100 5887 * The getName() function returns the full name (i.e. the name including an eventual namespace
rob@100 5888 * prefix) of the element.
rob@100 5889 *
rob@100 5890 * @return {String} the name, or null if the element only contains #PCDATA.
rob@100 5891 */
rob@100 5892 getName: function() {
rob@100 5893 return this.fullName;
rob@100 5894 },
rob@100 5895 /**
rob@100 5896 * @member XMLElement
rob@100 5897 * The getLocalName() function returns the local name (i.e. the name excluding an eventual namespace
rob@100 5898 * prefix) of the element.
rob@100 5899 *
rob@100 5900 * @return {String} the name, or null if the element only contains #PCDATA.
rob@100 5901 */
rob@100 5902 getLocalName: function() {
rob@100 5903 return this.name;
rob@100 5904 },
rob@100 5905 /**
rob@100 5906 * @member XMLElement
rob@100 5907 * The getAttributeCount() function returns the number of attributes for the node
rob@100 5908 * that this XMLElement represents.
rob@100 5909 *
rob@100 5910 * @return {int} the number of attributes in this XMLElement
rob@100 5911 */
rob@100 5912 getAttributeCount: function() {
rob@100 5913 return this.attributes.length;
rob@100 5914 },
rob@100 5915 /**
rob@100 5916 * @member XMLElement
rob@100 5917 * The toString() function returns the XML definition of an XMLElement.
rob@100 5918 *
rob@100 5919 * @return {String} the XML definition of this XMLElement
rob@100 5920 */
rob@100 5921 toString: function() {
rob@100 5922 // shortcut for text and cdata nodes
rob@100 5923 if (this.type === "TEXT") {
rob@100 5924 return this.content;
rob@100 5925 }
rob@100 5926
rob@100 5927 if (this.type === "CDATA") {
rob@100 5928 return this.cdata;
rob@100 5929 }
rob@100 5930
rob@100 5931 // real XMLElements
rob@100 5932 var tagstring = this.fullName;
rob@100 5933 var xmlstring = "<" + tagstring;
rob@100 5934 var a,c;
rob@100 5935
rob@100 5936 // serialize the attributes to XML string
rob@100 5937 for (a = 0; a<this.attributes.length; a++) {
rob@100 5938 var attr = this.attributes[a];
rob@100 5939 xmlstring += " " + attr.getName() + "=" + '"' + attr.getValue() + '"';
rob@100 5940 }
rob@100 5941
rob@100 5942 // serialize all children to XML string
rob@100 5943 if (this.children.length === 0) {
rob@100 5944 if (this.content==="") {
rob@100 5945 xmlstring += "/>";
rob@100 5946 } else {
rob@100 5947 xmlstring += ">" + this.content + "</"+tagstring+">";
rob@100 5948 }
rob@100 5949 } else {
rob@100 5950 xmlstring += ">";
rob@100 5951 for (c = 0; c<this.children.length; c++) {
rob@100 5952 xmlstring += this.children[c].toString();
rob@100 5953 }
rob@100 5954 xmlstring += "</" + tagstring + ">";
rob@100 5955 }
rob@100 5956 return xmlstring;
rob@100 5957 }
rob@100 5958 };
rob@100 5959
rob@100 5960 /**
rob@100 5961 * static Processing 1.5 XML API wrapper for the
rob@100 5962 * parse method. This may only take one argument.
rob@100 5963 */
rob@100 5964 XMLElement.parse = function(xmlstring) {
rob@100 5965 var element = new XMLElement();
rob@100 5966 element.parse(xmlstring);
rob@100 5967 return element;
rob@100 5968 };
rob@100 5969
rob@100 5970 return XMLElement;
rob@100 5971 };
rob@100 5972
rob@100 5973 },{}],20:[function(require,module,exports){
rob@100 5974 /**
rob@100 5975 * web colors, by name
rob@100 5976 */
rob@100 5977 module.exports = {
rob@100 5978 aliceblue: "#f0f8ff",
rob@100 5979 antiquewhite: "#faebd7",
rob@100 5980 aqua: "#00ffff",
rob@100 5981 aquamarine: "#7fffd4",
rob@100 5982 azure: "#f0ffff",
rob@100 5983 beige: "#f5f5dc",
rob@100 5984 bisque: "#ffe4c4",
rob@100 5985 black: "#000000",
rob@100 5986 blanchedalmond: "#ffebcd",
rob@100 5987 blue: "#0000ff",
rob@100 5988 blueviolet: "#8a2be2",
rob@100 5989 brown: "#a52a2a",
rob@100 5990 burlywood: "#deb887",
rob@100 5991 cadetblue: "#5f9ea0",
rob@100 5992 chartreuse: "#7fff00",
rob@100 5993 chocolate: "#d2691e",
rob@100 5994 coral: "#ff7f50",
rob@100 5995 cornflowerblue: "#6495ed",
rob@100 5996 cornsilk: "#fff8dc",
rob@100 5997 crimson: "#dc143c",
rob@100 5998 cyan: "#00ffff",
rob@100 5999 darkblue: "#00008b",
rob@100 6000 darkcyan: "#008b8b",
rob@100 6001 darkgoldenrod: "#b8860b",
rob@100 6002 darkgray: "#a9a9a9",
rob@100 6003 darkgreen: "#006400",
rob@100 6004 darkkhaki: "#bdb76b",
rob@100 6005 darkmagenta: "#8b008b",
rob@100 6006 darkolivegreen: "#556b2f",
rob@100 6007 darkorange: "#ff8c00",
rob@100 6008 darkorchid: "#9932cc",
rob@100 6009 darkred: "#8b0000",
rob@100 6010 darksalmon: "#e9967a",
rob@100 6011 darkseagreen: "#8fbc8f",
rob@100 6012 darkslateblue: "#483d8b",
rob@100 6013 darkslategray: "#2f4f4f",
rob@100 6014 darkturquoise: "#00ced1",
rob@100 6015 darkviolet: "#9400d3",
rob@100 6016 deeppink: "#ff1493",
rob@100 6017 deepskyblue: "#00bfff",
rob@100 6018 dimgray: "#696969",
rob@100 6019 dodgerblue: "#1e90ff",
rob@100 6020 firebrick: "#b22222",
rob@100 6021 floralwhite: "#fffaf0",
rob@100 6022 forestgreen: "#228b22",
rob@100 6023 fuchsia: "#ff00ff",
rob@100 6024 gainsboro: "#dcdcdc",
rob@100 6025 ghostwhite: "#f8f8ff",
rob@100 6026 gold: "#ffd700",
rob@100 6027 goldenrod: "#daa520",
rob@100 6028 gray: "#808080",
rob@100 6029 green: "#008000",
rob@100 6030 greenyellow: "#adff2f",
rob@100 6031 honeydew: "#f0fff0",
rob@100 6032 hotpink: "#ff69b4",
rob@100 6033 indianred: "#cd5c5c",
rob@100 6034 indigo: "#4b0082",
rob@100 6035 ivory: "#fffff0",
rob@100 6036 khaki: "#f0e68c",
rob@100 6037 lavender: "#e6e6fa",
rob@100 6038 lavenderblush: "#fff0f5",
rob@100 6039 lawngreen: "#7cfc00",
rob@100 6040 lemonchiffon: "#fffacd",
rob@100 6041 lightblue: "#add8e6",
rob@100 6042 lightcoral: "#f08080",
rob@100 6043 lightcyan: "#e0ffff",
rob@100 6044 lightgoldenrodyellow: "#fafad2",
rob@100 6045 lightgrey: "#d3d3d3",
rob@100 6046 lightgreen: "#90ee90",
rob@100 6047 lightpink: "#ffb6c1",
rob@100 6048 lightsalmon: "#ffa07a",
rob@100 6049 lightseagreen: "#20b2aa",
rob@100 6050 lightskyblue: "#87cefa",
rob@100 6051 lightslategray: "#778899",
rob@100 6052 lightsteelblue: "#b0c4de",
rob@100 6053 lightyellow: "#ffffe0",
rob@100 6054 lime: "#00ff00",
rob@100 6055 limegreen: "#32cd32",
rob@100 6056 linen: "#faf0e6",
rob@100 6057 magenta: "#ff00ff",
rob@100 6058 maroon: "#800000",
rob@100 6059 mediumaquamarine: "#66cdaa",
rob@100 6060 mediumblue: "#0000cd",
rob@100 6061 mediumorchid: "#ba55d3",
rob@100 6062 mediumpurple: "#9370d8",
rob@100 6063 mediumseagreen: "#3cb371",
rob@100 6064 mediumslateblue: "#7b68ee",
rob@100 6065 mediumspringgreen: "#00fa9a",
rob@100 6066 mediumturquoise: "#48d1cc",
rob@100 6067 mediumvioletred: "#c71585",
rob@100 6068 midnightblue: "#191970",
rob@100 6069 mintcream: "#f5fffa",
rob@100 6070 mistyrose: "#ffe4e1",
rob@100 6071 moccasin: "#ffe4b5",
rob@100 6072 navajowhite: "#ffdead",
rob@100 6073 navy: "#000080",
rob@100 6074 oldlace: "#fdf5e6",
rob@100 6075 olive: "#808000",
rob@100 6076 olivedrab: "#6b8e23",
rob@100 6077 orange: "#ffa500",
rob@100 6078 orangered: "#ff4500",
rob@100 6079 orchid: "#da70d6",
rob@100 6080 palegoldenrod: "#eee8aa",
rob@100 6081 palegreen: "#98fb98",
rob@100 6082 paleturquoise: "#afeeee",
rob@100 6083 palevioletred: "#d87093",
rob@100 6084 papayawhip: "#ffefd5",
rob@100 6085 peachpuff: "#ffdab9",
rob@100 6086 peru: "#cd853f",
rob@100 6087 pink: "#ffc0cb",
rob@100 6088 plum: "#dda0dd",
rob@100 6089 powderblue: "#b0e0e6",
rob@100 6090 purple: "#800080",
rob@100 6091 red: "#ff0000",
rob@100 6092 rosybrown: "#bc8f8f",
rob@100 6093 royalblue: "#4169e1",
rob@100 6094 saddlebrown: "#8b4513",
rob@100 6095 salmon: "#fa8072",
rob@100 6096 sandybrown: "#f4a460",
rob@100 6097 seagreen: "#2e8b57",
rob@100 6098 seashell: "#fff5ee",
rob@100 6099 sienna: "#a0522d",
rob@100 6100 silver: "#c0c0c0",
rob@100 6101 skyblue: "#87ceeb",
rob@100 6102 slateblue: "#6a5acd",
rob@100 6103 slategray: "#708090",
rob@100 6104 snow: "#fffafa",
rob@100 6105 springgreen: "#00ff7f",
rob@100 6106 steelblue: "#4682b4",
rob@100 6107 tan: "#d2b48c",
rob@100 6108 teal: "#008080",
rob@100 6109 thistle: "#d8bfd8",
rob@100 6110 tomato: "#ff6347",
rob@100 6111 turquoise: "#40e0d0",
rob@100 6112 violet: "#ee82ee",
rob@100 6113 wheat: "#f5deb3",
rob@100 6114 white: "#ffffff",
rob@100 6115 whitesmoke: "#f5f5f5",
rob@100 6116 yellow: "#ffff00",
rob@100 6117 yellowgreen: "#9acd32"
rob@100 6118 };
rob@100 6119
rob@100 6120 },{}],21:[function(require,module,exports){
rob@100 6121 module.exports = function(virtHashCode, virtEquals, undef) {
rob@100 6122
rob@100 6123 return function withProxyFunctions(p, removeFirstArgument) {
rob@100 6124 /**
rob@100 6125 * The contains(string) function returns true if the string passed in the parameter
rob@100 6126 * is a substring of this string. It returns false if the string passed
rob@100 6127 * in the parameter is not a substring of this string.
rob@100 6128 *
rob@100 6129 * @param {String} The string to look for in the current string
rob@100 6130 *
rob@100 6131 * @return {boolean} returns true if this string contains
rob@100 6132 * the string passed as parameter. returns false, otherwise.
rob@100 6133 *
rob@100 6134 */
rob@100 6135 p.__contains = function (subject, subStr) {
rob@100 6136 if (typeof subject !== "string") {
rob@100 6137 return subject.contains.apply(subject, removeFirstArgument(arguments));
rob@100 6138 }
rob@100 6139 //Parameter is not null AND
rob@100 6140 //The type of the parameter is the same as this object (string)
rob@100 6141 //The javascript function that finds a substring returns 0 or higher
rob@100 6142 return (
rob@100 6143 (subject !== null) &&
rob@100 6144 (subStr !== null) &&
rob@100 6145 (typeof subStr === "string") &&
rob@100 6146 (subject.indexOf(subStr) > -1)
rob@100 6147 );
rob@100 6148 };
rob@100 6149
rob@100 6150 /**
rob@100 6151 * The __replaceAll() function searches all matches between a substring (or regular expression) and a string,
rob@100 6152 * and replaces the matched substring with a new substring
rob@100 6153 *
rob@100 6154 * @param {String} subject a substring
rob@100 6155 * @param {String} regex a substring or a regular expression
rob@100 6156 * @param {String} replace the string to replace the found value
rob@100 6157 *
rob@100 6158 * @return {String} returns result
rob@100 6159 *
rob@100 6160 * @see #match
rob@100 6161 */
rob@100 6162 p.__replaceAll = function(subject, regex, replacement) {
rob@100 6163 if (typeof subject !== "string") {
rob@100 6164 return subject.replaceAll.apply(subject, removeFirstArgument(arguments));
rob@100 6165 }
rob@100 6166
rob@100 6167 return subject.replace(new RegExp(regex, "g"), replacement);
rob@100 6168 };
rob@100 6169
rob@100 6170 /**
rob@100 6171 * The __replaceFirst() function searches first matche between a substring (or regular expression) and a string,
rob@100 6172 * and replaces the matched substring with a new substring
rob@100 6173 *
rob@100 6174 * @param {String} subject a substring
rob@100 6175 * @param {String} regex a substring or a regular expression
rob@100 6176 * @param {String} replace the string to replace the found value
rob@100 6177 *
rob@100 6178 * @return {String} returns result
rob@100 6179 *
rob@100 6180 * @see #match
rob@100 6181 */
rob@100 6182 p.__replaceFirst = function(subject, regex, replacement) {
rob@100 6183 if (typeof subject !== "string") {
rob@100 6184 return subject.replaceFirst.apply(subject, removeFirstArgument(arguments));
rob@100 6185 }
rob@100 6186
rob@100 6187 return subject.replace(new RegExp(regex, ""), replacement);
rob@100 6188 };
rob@100 6189
rob@100 6190 /**
rob@100 6191 * The __replace() function searches all matches between a substring and a string,
rob@100 6192 * and replaces the matched substring with a new substring
rob@100 6193 *
rob@100 6194 * @param {String} subject a substring
rob@100 6195 * @param {String} what a substring to find
rob@100 6196 * @param {String} replacement the string to replace the found value
rob@100 6197 *
rob@100 6198 * @return {String} returns result
rob@100 6199 */
rob@100 6200 p.__replace = function(subject, what, replacement) {
rob@100 6201 if (typeof subject !== "string") {
rob@100 6202 return subject.replace.apply(subject, removeFirstArgument(arguments));
rob@100 6203 }
rob@100 6204 if (what instanceof RegExp) {
rob@100 6205 return subject.replace(what, replacement);
rob@100 6206 }
rob@100 6207
rob@100 6208 if (typeof what !== "string") {
rob@100 6209 what = what.toString();
rob@100 6210 }
rob@100 6211 if (what === "") {
rob@100 6212 return subject;
rob@100 6213 }
rob@100 6214
rob@100 6215 var i = subject.indexOf(what);
rob@100 6216 if (i < 0) {
rob@100 6217 return subject;
rob@100 6218 }
rob@100 6219
rob@100 6220 var j = 0, result = "";
rob@100 6221 do {
rob@100 6222 result += subject.substring(j, i) + replacement;
rob@100 6223 j = i + what.length;
rob@100 6224 } while ( (i = subject.indexOf(what, j)) >= 0);
rob@100 6225 return result + subject.substring(j);
rob@100 6226 };
rob@100 6227
rob@100 6228 /**
rob@100 6229 * The __equals() function compares two strings (or objects) to see if they are the same.
rob@100 6230 * This method is necessary because it's not possible to compare strings using the equality operator (==).
rob@100 6231 * Returns true if the strings are the same and false if they are not.
rob@100 6232 *
rob@100 6233 * @param {String} subject a string used for comparison
rob@100 6234 * @param {String} other a string used for comparison with
rob@100 6235 *
rob@100 6236 * @return {boolean} true is the strings are the same false otherwise
rob@100 6237 */
rob@100 6238 p.__equals = function(subject, other) {
rob@100 6239 if (subject.equals instanceof Function) {
rob@100 6240 return subject.equals.apply(subject, removeFirstArgument(arguments));
rob@100 6241 }
rob@100 6242
rob@100 6243 return virtEquals(subject, other);
rob@100 6244 };
rob@100 6245
rob@100 6246 /**
rob@100 6247 * The __equalsIgnoreCase() function compares two strings to see if they are the same.
rob@100 6248 * Returns true if the strings are the same, either when forced to all lower case or
rob@100 6249 * all upper case.
rob@100 6250 *
rob@100 6251 * @param {String} subject a string used for comparison
rob@100 6252 * @param {String} other a string used for comparison with
rob@100 6253 *
rob@100 6254 * @return {boolean} true is the strings are the same, ignoring case. false otherwise
rob@100 6255 */
rob@100 6256 p.__equalsIgnoreCase = function(subject, other) {
rob@100 6257 if (typeof subject !== "string") {
rob@100 6258 return subject.equalsIgnoreCase.apply(subject, removeFirstArgument(arguments));
rob@100 6259 }
rob@100 6260
rob@100 6261 return subject.toLowerCase() === other.toLowerCase();
rob@100 6262 };
rob@100 6263
rob@100 6264 /**
rob@100 6265 * The __toCharArray() function splits the string into a char array.
rob@100 6266 *
rob@100 6267 * @param {String} subject The string
rob@100 6268 *
rob@100 6269 * @return {Char[]} a char array
rob@100 6270 */
rob@100 6271 p.__toCharArray = function(subject) {
rob@100 6272 if (typeof subject !== "string") {
rob@100 6273 return subject.toCharArray.apply(subject, removeFirstArgument(arguments));
rob@100 6274 }
rob@100 6275
rob@100 6276 var chars = [];
rob@100 6277 for (var i = 0, len = subject.length; i < len; ++i) {
rob@100 6278 chars[i] = new Char(subject.charAt(i));
rob@100 6279 }
rob@100 6280 return chars;
rob@100 6281 };
rob@100 6282
rob@100 6283 /**
rob@100 6284 * The __split() function splits a string using the regex delimiter
rob@100 6285 * specified. If limit is specified, the resultant array will have number
rob@100 6286 * of elements equal to or less than the limit.
rob@100 6287 *
rob@100 6288 * @param {String} subject string to be split
rob@100 6289 * @param {String} regexp regex string used to split the subject
rob@100 6290 * @param {int} limit max number of tokens to be returned
rob@100 6291 *
rob@100 6292 * @return {String[]} an array of tokens from the split string
rob@100 6293 */
rob@100 6294 p.__split = function(subject, regex, limit) {
rob@100 6295 if (typeof subject !== "string") {
rob@100 6296 return subject.split.apply(subject, removeFirstArgument(arguments));
rob@100 6297 }
rob@100 6298
rob@100 6299 var pattern = new RegExp(regex);
rob@100 6300
rob@100 6301 // If limit is not specified, use JavaScript's built-in String.split.
rob@100 6302 if ((limit === undef) || (limit < 1)) {
rob@100 6303 return subject.split(pattern);
rob@100 6304 }
rob@100 6305
rob@100 6306 // If limit is specified, JavaScript's built-in String.split has a
rob@100 6307 // different behaviour than Java's. A Java-compatible implementation is
rob@100 6308 // provided here.
rob@100 6309 var result = [], currSubject = subject, pos;
rob@100 6310 while (((pos = currSubject.search(pattern)) !== -1) && (result.length < (limit - 1))) {
rob@100 6311 var match = pattern.exec(currSubject).toString();
rob@100 6312 result.push(currSubject.substring(0, pos));
rob@100 6313 currSubject = currSubject.substring(pos + match.length);
rob@100 6314 }
rob@100 6315 if ((pos !== -1) || (currSubject !== "")) {
rob@100 6316 result.push(currSubject);
rob@100 6317 }
rob@100 6318 return result;
rob@100 6319 };
rob@100 6320
rob@100 6321 /**
rob@100 6322 * The codePointAt() function returns the unicode value of the character at a given index of a string.
rob@100 6323 *
rob@100 6324 * @param {int} idx the index of the character
rob@100 6325 *
rob@100 6326 * @return {String} code the String containing the unicode value of the character
rob@100 6327 */
rob@100 6328 p.__codePointAt = function(subject, idx) {
rob@100 6329 var code = subject.charCodeAt(idx),
rob@100 6330 hi,
rob@100 6331 low;
rob@100 6332 if (0xD800 <= code && code <= 0xDBFF) {
rob@100 6333 hi = code;
rob@100 6334 low = subject.charCodeAt(idx + 1);
rob@100 6335 return ((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000;
rob@100 6336 }
rob@100 6337 return code;
rob@100 6338 };
rob@100 6339
rob@100 6340 /**
rob@100 6341 * The matches() function checks whether or not a string matches a given regular expression.
rob@100 6342 *
rob@100 6343 * @param {String} str the String on which the match is tested
rob@100 6344 * @param {String} regexp the regexp for which a match is tested
rob@100 6345 *
rob@100 6346 * @return {boolean} true if the string fits the regexp, false otherwise
rob@100 6347 */
rob@100 6348 p.__matches = function(str, regexp) {
rob@100 6349 return (new RegExp(regexp)).test(str);
rob@100 6350 };
rob@100 6351
rob@100 6352 /**
rob@100 6353 * The startsWith() function tests if a string starts with the specified prefix. If the prefix
rob@100 6354 * is the empty String or equal to the subject String, startsWith() will also return true.
rob@100 6355 *
rob@100 6356 * @param {String} prefix the String used to compare against the start of the subject String.
rob@100 6357 * @param {int} toffset (optional) an offset into the subject String where searching should begin.
rob@100 6358 *
rob@100 6359 * @return {boolean} true if the subject String starts with the prefix.
rob@100 6360 */
rob@100 6361 p.__startsWith = function(subject, prefix, toffset) {
rob@100 6362 if (typeof subject !== "string") {
rob@100 6363 return subject.startsWith.apply(subject, removeFirstArgument(arguments));
rob@100 6364 }
rob@100 6365
rob@100 6366 toffset = toffset || 0;
rob@100 6367 if (toffset < 0 || toffset > subject.length) {
rob@100 6368 return false;
rob@100 6369 }
rob@100 6370 return (prefix === '' || prefix === subject) ? true : (subject.indexOf(prefix) === toffset);
rob@100 6371 };
rob@100 6372
rob@100 6373 /**
rob@100 6374 * The endsWith() function tests if a string ends with the specified suffix. If the suffix
rob@100 6375 * is the empty String, endsWith() will also return true.
rob@100 6376 *
rob@100 6377 * @param {String} suffix the String used to compare against the end of the subject String.
rob@100 6378 *
rob@100 6379 * @return {boolean} true if the subject String starts with the prefix.
rob@100 6380 */
rob@100 6381 p.__endsWith = function(subject, suffix) {
rob@100 6382 if (typeof subject !== "string") {
rob@100 6383 return subject.endsWith.apply(subject, removeFirstArgument(arguments));
rob@100 6384 }
rob@100 6385
rob@100 6386 var suffixLen = suffix ? suffix.length : 0;
rob@100 6387 return (suffix === '' || suffix === subject) ? true :
rob@100 6388 (subject.indexOf(suffix) === subject.length - suffixLen);
rob@100 6389 };
rob@100 6390
rob@100 6391 /**
rob@100 6392 * The returns hash code of the.
rob@100 6393 *
rob@100 6394 * @param {Object} subject The string
rob@100 6395 *
rob@100 6396 * @return {int} a hash code
rob@100 6397 */
rob@100 6398 p.__hashCode = function(subject) {
rob@100 6399 if (subject.hashCode instanceof Function) {
rob@100 6400 return subject.hashCode.apply(subject, removeFirstArgument(arguments));
rob@100 6401 }
rob@100 6402 return virtHashCode(subject);
rob@100 6403 };
rob@100 6404
rob@100 6405 /**
rob@100 6406 * The __printStackTrace() prints stack trace to the console.
rob@100 6407 *
rob@100 6408 * @param {Exception} subject The error
rob@100 6409 */
rob@100 6410 p.__printStackTrace = function(subject) {
rob@100 6411 p.println("Exception: " + subject.toString() );
rob@100 6412 };
rob@100 6413 };
rob@100 6414
rob@100 6415 };
rob@100 6416
rob@100 6417 },{}],22:[function(require,module,exports){
rob@100 6418 /**
rob@100 6419 * For many "math" functions, we can delegate
rob@100 6420 * to the Math object. For others, we can't.
rob@100 6421 */
rob@100 6422 module.exports = function withMath(p, undef) {
rob@100 6423 var internalRandomGenerator = function() { return Math.random(); };
rob@100 6424
rob@100 6425 /**
rob@100 6426 * Calculates the absolute value (magnitude) of a number. The absolute value of a number is always positive.
rob@100 6427 *
rob@100 6428 * @param {int|float} value int or float
rob@100 6429 *
rob@100 6430 * @returns {int|float}
rob@100 6431 */
rob@100 6432 p.abs = Math.abs;
rob@100 6433
rob@100 6434 /**
rob@100 6435 * Calculates the closest int value that is greater than or equal to the value of the parameter.
rob@100 6436 * For example, ceil(9.03) returns the value 10.
rob@100 6437 *
rob@100 6438 * @param {float} value float
rob@100 6439 *
rob@100 6440 * @returns {int}
rob@100 6441 *
rob@100 6442 * @see floor
rob@100 6443 * @see round
rob@100 6444 */
rob@100 6445 p.ceil = Math.ceil;
rob@100 6446
rob@100 6447 /**
rob@100 6448 * Returns Euler's number e (2.71828...) raised to the power of the value parameter.
rob@100 6449 *
rob@100 6450 * @param {int|float} value int or float: the exponent to raise e to
rob@100 6451 *
rob@100 6452 * @returns {float}
rob@100 6453 */
rob@100 6454 p.exp = Math.exp;
rob@100 6455
rob@100 6456 /**
rob@100 6457 * Calculates the closest int value that is less than or equal to the value of the parameter.
rob@100 6458 *
rob@100 6459 * @param {int|float} value the value to floor
rob@100 6460 *
rob@100 6461 * @returns {int|float}
rob@100 6462 *
rob@100 6463 * @see ceil
rob@100 6464 * @see round
rob@100 6465 */
rob@100 6466 p.floor = Math.floor;
rob@100 6467
rob@100 6468 /**
rob@100 6469 * Calculates the natural logarithm (the base-e logarithm) of a number. This function
rob@100 6470 * expects the values greater than 0.0.
rob@100 6471 *
rob@100 6472 * @param {int|float} value int or float: number must be greater then 0.0
rob@100 6473 *
rob@100 6474 * @returns {float}
rob@100 6475 */
rob@100 6476 p.log = Math.log;
rob@100 6477
rob@100 6478 /**
rob@100 6479 * Facilitates exponential expressions. The pow() function is an efficient way of
rob@100 6480 * multiplying numbers by themselves (or their reciprocal) in large quantities.
rob@100 6481 * For example, pow(3, 5) is equivalent to the expression 3*3*3*3*3 and pow(3, -5)
rob@100 6482 * is equivalent to 1 / 3*3*3*3*3.
rob@100 6483 *
rob@100 6484 * @param {int|float} num base of the exponential expression
rob@100 6485 * @param {int|float} exponent power of which to raise the base
rob@100 6486 *
rob@100 6487 * @returns {float}
rob@100 6488 *
rob@100 6489 * @see sqrt
rob@100 6490 */
rob@100 6491 p.pow = Math.pow;
rob@100 6492
rob@100 6493 /**
rob@100 6494 * Calculates the integer closest to the value parameter. For example, round(9.2) returns the value 9.
rob@100 6495 *
rob@100 6496 * @param {float} value number to round
rob@100 6497 *
rob@100 6498 * @returns {int}
rob@100 6499 *
rob@100 6500 * @see floor
rob@100 6501 * @see ceil
rob@100 6502 */
rob@100 6503 p.round = Math.round;
rob@100 6504 /**
rob@100 6505 * Calculates the square root of a number. The square root of a number is always positive,
rob@100 6506 * even though there may be a valid negative root. The square root s of number a is such
rob@100 6507 * that s*s = a. It is the opposite of squaring.
rob@100 6508 *
rob@100 6509 * @param {float} value int or float, non negative
rob@100 6510 *
rob@100 6511 * @returns {float}
rob@100 6512 *
rob@100 6513 * @see pow
rob@100 6514 * @see sq
rob@100 6515 */
rob@100 6516
rob@100 6517 p.sqrt = Math.sqrt;
rob@100 6518
rob@100 6519 // Trigonometry
rob@100 6520 /**
rob@100 6521 * The inverse of cos(), returns the arc cosine of a value. This function expects the
rob@100 6522 * values in the range of -1 to 1 and values are returned in the range 0 to PI (3.1415927).
rob@100 6523 *
rob@100 6524 * @param {float} value the value whose arc cosine is to be returned
rob@100 6525 *
rob@100 6526 * @returns {float}
rob@100 6527 *
rob@100 6528 * @see cos
rob@100 6529 * @see asin
rob@100 6530 * @see atan
rob@100 6531 */
rob@100 6532 p.acos = Math.acos;
rob@100 6533
rob@100 6534 /**
rob@100 6535 * The inverse of sin(), returns the arc sine of a value. This function expects the values
rob@100 6536 * in the range of -1 to 1 and values are returned in the range -PI/2 to PI/2.
rob@100 6537 *
rob@100 6538 * @param {float} value the value whose arc sine is to be returned
rob@100 6539 *
rob@100 6540 * @returns {float}
rob@100 6541 *
rob@100 6542 * @see sin
rob@100 6543 * @see acos
rob@100 6544 * @see atan
rob@100 6545 */
rob@100 6546 p.asin = Math.asin;
rob@100 6547
rob@100 6548 /**
rob@100 6549 * The inverse of tan(), returns the arc tangent of a value. This function expects the values
rob@100 6550 * in the range of -Infinity to Infinity (exclusive) and values are returned in the range -PI/2 to PI/2 .
rob@100 6551 *
rob@100 6552 * @param {float} value -Infinity to Infinity (exclusive)
rob@100 6553 *
rob@100 6554 * @returns {float}
rob@100 6555 *
rob@100 6556 * @see tan
rob@100 6557 * @see asin
rob@100 6558 * @see acos
rob@100 6559 */
rob@100 6560 p.atan = Math.atan;
rob@100 6561
rob@100 6562 /**
rob@100 6563 * Calculates the angle (in radians) from a specified point to the coordinate origin as measured from
rob@100 6564 * the positive x-axis. Values are returned as a float in the range from PI to -PI. The atan2() function
rob@100 6565 * is most often used for orienting geometry to the position of the cursor. Note: The y-coordinate of the
rob@100 6566 * point is the first parameter and the x-coordinate is the second due the the structure of calculating the tangent.
rob@100 6567 *
rob@100 6568 * @param {float} y y-coordinate of the point
rob@100 6569 * @param {float} x x-coordinate of the point
rob@100 6570 *
rob@100 6571 * @returns {float}
rob@100 6572 *
rob@100 6573 * @see tan
rob@100 6574 */
rob@100 6575 p.atan2 = Math.atan2;
rob@100 6576
rob@100 6577 /**
rob@100 6578 * Calculates the cosine of an angle. This function expects the values of the angle parameter to be provided
rob@100 6579 * in radians (values from 0 to PI*2). Values are returned in the range -1 to 1.
rob@100 6580 *
rob@100 6581 * @param {float} value an angle in radians
rob@100 6582 *
rob@100 6583 * @returns {float}
rob@100 6584 *
rob@100 6585 * @see tan
rob@100 6586 * @see sin
rob@100 6587 */
rob@100 6588 p.cos = Math.cos;
rob@100 6589
rob@100 6590 /**
rob@100 6591 * Calculates the sine of an angle. This function expects the values of the angle parameter to be provided in
rob@100 6592 * radians (values from 0 to 6.28). Values are returned in the range -1 to 1.
rob@100 6593 *
rob@100 6594 * @param {float} value an angle in radians
rob@100 6595 *
rob@100 6596 * @returns {float}
rob@100 6597 *
rob@100 6598 * @see cos
rob@100 6599 * @see radians
rob@100 6600 */
rob@100 6601 p.sin = Math.sin;
rob@100 6602
rob@100 6603 /**
rob@100 6604 * Calculates the ratio of the sine and cosine of an angle. This function expects the values of the angle
rob@100 6605 * parameter to be provided in radians (values from 0 to PI*2). Values are returned in the range infinity to -infinity.
rob@100 6606 *
rob@100 6607 * @param {float} value an angle in radians
rob@100 6608 *
rob@100 6609 * @returns {float}
rob@100 6610 *
rob@100 6611 * @see cos
rob@100 6612 * @see sin
rob@100 6613 * @see radians
rob@100 6614 */
rob@100 6615 p.tan = Math.tan;
rob@100 6616
rob@100 6617 /**
rob@100 6618 * Constrains a value to not exceed a maximum and minimum value.
rob@100 6619 *
rob@100 6620 * @param {int|float} value the value to constrain
rob@100 6621 * @param {int|float} value minimum limit
rob@100 6622 * @param {int|float} value maximum limit
rob@100 6623 *
rob@100 6624 * @returns {int|float}
rob@100 6625 *
rob@100 6626 * @see max
rob@100 6627 * @see min
rob@100 6628 */
rob@100 6629 p.constrain = function(aNumber, aMin, aMax) {
rob@100 6630 return aNumber > aMax ? aMax : aNumber < aMin ? aMin : aNumber;
rob@100 6631 };
rob@100 6632
rob@100 6633 /**
rob@100 6634 * Calculates the distance between two points.
rob@100 6635 *
rob@100 6636 * @param {int|float} x1 int or float: x-coordinate of the first point
rob@100 6637 * @param {int|float} y1 int or float: y-coordinate of the first point
rob@100 6638 * @param {int|float} z1 int or float: z-coordinate of the first point
rob@100 6639 * @param {int|float} x2 int or float: x-coordinate of the second point
rob@100 6640 * @param {int|float} y2 int or float: y-coordinate of the second point
rob@100 6641 * @param {int|float} z2 int or float: z-coordinate of the second point
rob@100 6642 *
rob@100 6643 * @returns {float}
rob@100 6644 */
rob@100 6645 p.dist = function() {
rob@100 6646 var dx, dy, dz;
rob@100 6647 if (arguments.length === 4) {
rob@100 6648 dx = arguments[0] - arguments[2];
rob@100 6649 dy = arguments[1] - arguments[3];
rob@100 6650 return Math.sqrt(dx * dx + dy * dy);
rob@100 6651 }
rob@100 6652 if (arguments.length === 6) {
rob@100 6653 dx = arguments[0] - arguments[3];
rob@100 6654 dy = arguments[1] - arguments[4];
rob@100 6655 dz = arguments[2] - arguments[5];
rob@100 6656 return Math.sqrt(dx * dx + dy * dy + dz * dz);
rob@100 6657 }
rob@100 6658 };
rob@100 6659
rob@100 6660 /**
rob@100 6661 * Calculates a number between two numbers at a specific increment. The amt parameter is the
rob@100 6662 * amount to interpolate between the two values where 0.0 equal to the first point, 0.1 is very
rob@100 6663 * near the first point, 0.5 is half-way in between, etc. The lerp function is convenient for
rob@100 6664 * creating motion along a straight path and for drawing dotted lines.
rob@100 6665 *
rob@100 6666 * @param {int|float} value1 float or int: first value
rob@100 6667 * @param {int|float} value2 float or int: second value
rob@100 6668 * @param {int|float} amt float: between 0.0 and 1.0
rob@100 6669 *
rob@100 6670 * @returns {float}
rob@100 6671 *
rob@100 6672 * @see curvePoint
rob@100 6673 * @see bezierPoint
rob@100 6674 */
rob@100 6675 p.lerp = function(value1, value2, amt) {
rob@100 6676 return ((value2 - value1) * amt) + value1;
rob@100 6677 };
rob@100 6678
rob@100 6679 /**
rob@100 6680 * Calculates the magnitude (or length) of a vector. A vector is a direction in space commonly
rob@100 6681 * used in computer graphics and linear algebra. Because it has no "start" position, the magnitude
rob@100 6682 * of a vector can be thought of as the distance from coordinate (0,0) to its (x,y) value.
rob@100 6683 * Therefore, mag() is a shortcut for writing "dist(0, 0, x, y)".
rob@100 6684 *
rob@100 6685 * @param {int|float} a float or int: first value
rob@100 6686 * @param {int|float} b float or int: second value
rob@100 6687 * @param {int|float} c float or int: third value
rob@100 6688 *
rob@100 6689 * @returns {float}
rob@100 6690 *
rob@100 6691 * @see dist
rob@100 6692 */
rob@100 6693 p.mag = function(a, b, c) {
rob@100 6694 if (c) {
rob@100 6695 return Math.sqrt(a * a + b * b + c * c);
rob@100 6696 }
rob@100 6697
rob@100 6698 return Math.sqrt(a * a + b * b);
rob@100 6699 };
rob@100 6700
rob@100 6701 /**
rob@100 6702 * Re-maps a number from one range to another. In the example above, the number '25' is converted from
rob@100 6703 * a value in the range 0..100 into a value that ranges from the left edge (0) to the right edge (width) of the screen.
rob@100 6704 * Numbers outside the range are not clamped to 0 and 1, because out-of-range values are often intentional and useful.
rob@100 6705 *
rob@100 6706 * @param {float} value The incoming value to be converted
rob@100 6707 * @param {float} istart Lower bound of the value's current range
rob@100 6708 * @param {float} istop Upper bound of the value's current range
rob@100 6709 * @param {float} ostart Lower bound of the value's target range
rob@100 6710 * @param {float} ostop Upper bound of the value's target range
rob@100 6711 *
rob@100 6712 * @returns {float}
rob@100 6713 *
rob@100 6714 * @see norm
rob@100 6715 * @see lerp
rob@100 6716 */
rob@100 6717 p.map = function(value, istart, istop, ostart, ostop) {
rob@100 6718 return ostart + (ostop - ostart) * ((value - istart) / (istop - istart));
rob@100 6719 };
rob@100 6720
rob@100 6721 /**
rob@100 6722 * Determines the largest value in a sequence of numbers.
rob@100 6723 *
rob@100 6724 * @param {int|float} value1 int or float
rob@100 6725 * @param {int|float} value2 int or float
rob@100 6726 * @param {int|float} value3 int or float
rob@100 6727 * @param {int|float} array int or float array
rob@100 6728 *
rob@100 6729 * @returns {int|float}
rob@100 6730 *
rob@100 6731 * @see min
rob@100 6732 */
rob@100 6733 p.max = function() {
rob@100 6734 if (arguments.length === 2) {
rob@100 6735 return arguments[0] < arguments[1] ? arguments[1] : arguments[0];
rob@100 6736 }
rob@100 6737 var numbers = arguments.length === 1 ? arguments[0] : arguments; // if single argument, array is used
rob@100 6738 if (! ("length" in numbers && numbers.length > 0)) {
rob@100 6739 throw "Non-empty array is expected";
rob@100 6740 }
rob@100 6741 var max = numbers[0],
rob@100 6742 count = numbers.length;
rob@100 6743 for (var i = 1; i < count; ++i) {
rob@100 6744 if (max < numbers[i]) {
rob@100 6745 max = numbers[i];
rob@100 6746 }
rob@100 6747 }
rob@100 6748 return max;
rob@100 6749 };
rob@100 6750
rob@100 6751 /**
rob@100 6752 * Determines the smallest value in a sequence of numbers.
rob@100 6753 *
rob@100 6754 * @param {int|float} value1 int or float
rob@100 6755 * @param {int|float} value2 int or float
rob@100 6756 * @param {int|float} value3 int or float
rob@100 6757 * @param {int|float} array int or float array
rob@100 6758 *
rob@100 6759 * @returns {int|float}
rob@100 6760 *
rob@100 6761 * @see max
rob@100 6762 */
rob@100 6763 p.min = function() {
rob@100 6764 if (arguments.length === 2) {
rob@100 6765 return arguments[0] < arguments[1] ? arguments[0] : arguments[1];
rob@100 6766 }
rob@100 6767 var numbers = arguments.length === 1 ? arguments[0] : arguments; // if single argument, array is used
rob@100 6768 if (! ("length" in numbers && numbers.length > 0)) {
rob@100 6769 throw "Non-empty array is expected";
rob@100 6770 }
rob@100 6771 var min = numbers[0],
rob@100 6772 count = numbers.length;
rob@100 6773 for (var i = 1; i < count; ++i) {
rob@100 6774 if (min > numbers[i]) {
rob@100 6775 min = numbers[i];
rob@100 6776 }
rob@100 6777 }
rob@100 6778 return min;
rob@100 6779 };
rob@100 6780
rob@100 6781 /**
rob@100 6782 * Normalizes a number from another range into a value between 0 and 1.
rob@100 6783 * Identical to map(value, low, high, 0, 1);
rob@100 6784 * Numbers outside the range are not clamped to 0 and 1, because out-of-range
rob@100 6785 * values are often intentional and useful.
rob@100 6786 *
rob@100 6787 * @param {float} aNumber The incoming value to be converted
rob@100 6788 * @param {float} low Lower bound of the value's current range
rob@100 6789 * @param {float} high Upper bound of the value's current range
rob@100 6790 *
rob@100 6791 * @returns {float}
rob@100 6792 *
rob@100 6793 * @see map
rob@100 6794 * @see lerp
rob@100 6795 */
rob@100 6796 p.norm = function(aNumber, low, high) {
rob@100 6797 return (aNumber - low) / (high - low);
rob@100 6798 };
rob@100 6799
rob@100 6800 /**
rob@100 6801 * Squares a number (multiplies a number by itself). The result is always a positive number,
rob@100 6802 * as multiplying two negative numbers always yields a positive result. For example, -1 * -1 = 1.
rob@100 6803 *
rob@100 6804 * @param {float} value int or float
rob@100 6805 *
rob@100 6806 * @returns {float}
rob@100 6807 *
rob@100 6808 * @see sqrt
rob@100 6809 */
rob@100 6810 p.sq = function(aNumber) {
rob@100 6811 return aNumber * aNumber;
rob@100 6812 };
rob@100 6813
rob@100 6814 /**
rob@100 6815 * Converts a radian measurement to its corresponding value in degrees. Radians and degrees are two ways of
rob@100 6816 * measuring the same thing. There are 360 degrees in a circle and 2*PI radians in a circle. For example,
rob@100 6817 * 90 degrees = PI/2 = 1.5707964. All trigonometric methods in Processing require their parameters to be specified in radians.
rob@100 6818 *
rob@100 6819 * @param {int|float} value an angle in radians
rob@100 6820 *
rob@100 6821 * @returns {float}
rob@100 6822 *
rob@100 6823 * @see radians
rob@100 6824 */
rob@100 6825 p.degrees = function(aAngle) {
rob@100 6826 return (aAngle * 180) / Math.PI;
rob@100 6827 };
rob@100 6828
rob@100 6829 /**
rob@100 6830 * Generates random numbers. Each time the random() function is called, it returns an unexpected value within
rob@100 6831 * the specified range. If one parameter is passed to the function it will return a float between zero and the
rob@100 6832 * value of the high parameter. The function call random(5) returns values between 0 and 5 (starting at zero,
rob@100 6833 * up to but not including 5). If two parameters are passed, it will return a float with a value between the
rob@100 6834 * parameters. The function call random(-5, 10.2) returns values starting at -5 up to (but not including) 10.2.
rob@100 6835 * To convert a floating-point random number to an integer, use the int() function.
rob@100 6836 *
rob@100 6837 * @param {int|float} value1 if one parameter is used, the top end to random from, if two params the low end
rob@100 6838 * @param {int|float} value2 the top end of the random range
rob@100 6839 *
rob@100 6840 * @returns {float}
rob@100 6841 *
rob@100 6842 * @see randomSeed
rob@100 6843 * @see noise
rob@100 6844 */
rob@100 6845 p.random = function() {
rob@100 6846 if(arguments.length === 0) {
rob@100 6847 return internalRandomGenerator();
rob@100 6848 }
rob@100 6849 if(arguments.length === 1) {
rob@100 6850 return internalRandomGenerator() * arguments[0];
rob@100 6851 }
rob@100 6852 var aMin = arguments[0], aMax = arguments[1];
rob@100 6853 return internalRandomGenerator() * (aMax - aMin) + aMin;
rob@100 6854 };
rob@100 6855
rob@100 6856 // Pseudo-random generator
rob@100 6857 function Marsaglia(i1, i2) {
rob@100 6858 // from http://www.math.uni-bielefeld.de/~sillke/ALGORITHMS/random/marsaglia-c
rob@100 6859 var z=i1 || 362436069, w= i2 || 521288629;
rob@100 6860 var intGenerator = function() {
rob@100 6861 z=(36969*(z&65535)+(z>>>16)) & 0xFFFFFFFF;
rob@100 6862 w=(18000*(w&65535)+(w>>>16)) & 0xFFFFFFFF;
rob@100 6863 return (((z&0xFFFF)<<16) | (w&0xFFFF)) & 0xFFFFFFFF;
rob@100 6864 };
rob@100 6865
rob@100 6866 this.doubleGenerator = function() {
rob@100 6867 var i = intGenerator() / 4294967296;
rob@100 6868 return i < 0 ? 1 + i : i;
rob@100 6869 };
rob@100 6870 this.intGenerator = intGenerator;
rob@100 6871 }
rob@100 6872
rob@100 6873 Marsaglia.createRandomized = function() {
rob@100 6874 var now = new Date();
rob@100 6875 return new Marsaglia((now / 60000) & 0xFFFFFFFF, now & 0xFFFFFFFF);
rob@100 6876 };
rob@100 6877
rob@100 6878 /**
rob@100 6879 * Sets the seed value for random(). By default, random() produces different results each time the
rob@100 6880 * program is run. Set the value parameter to a constant to return the same pseudo-random numbers
rob@100 6881 * each time the software is run.
rob@100 6882 *
rob@100 6883 * @param {int|float} seed int
rob@100 6884 *
rob@100 6885 * @see random
rob@100 6886 * @see noise
rob@100 6887 * @see noiseSeed
rob@100 6888 */
rob@100 6889 p.randomSeed = function(seed) {
rob@100 6890 internalRandomGenerator = (new Marsaglia(seed)).doubleGenerator;
rob@100 6891 this.haveNextNextGaussian = false;
rob@100 6892 };
rob@100 6893
rob@100 6894 /**
rob@100 6895 * Returns a float from a random series of numbers having a mean of 0 and standard deviation of 1. Each time
rob@100 6896 * the randomGaussian() function is called, it returns a number fitting a Gaussian, or normal, distribution.
rob@100 6897 * There is theoretically no minimum or maximum value that randomGaussian() might return. Rather, there is just a
rob@100 6898 * very low probability that values far from the mean will be returned; and a higher probability that numbers
rob@100 6899 * near the mean will be returned.
rob@100 6900 *
rob@100 6901 * @returns {float}
rob@100 6902 *
rob@100 6903 * @see random
rob@100 6904 * @see noise
rob@100 6905 */
rob@100 6906 p.randomGaussian = function() {
rob@100 6907 if (this.haveNextNextGaussian) {
rob@100 6908 this.haveNextNextGaussian = false;
rob@100 6909 return this.nextNextGaussian;
rob@100 6910 }
rob@100 6911 var v1, v2, s;
rob@100 6912 do {
rob@100 6913 v1 = 2 * internalRandomGenerator() - 1; // between -1.0 and 1.0
rob@100 6914 v2 = 2 * internalRandomGenerator() - 1; // between -1.0 and 1.0
rob@100 6915 s = v1 * v1 + v2 * v2;
rob@100 6916 }
rob@100 6917 while (s >= 1 || s === 0);
rob@100 6918
rob@100 6919 var multiplier = Math.sqrt(-2 * Math.log(s) / s);
rob@100 6920 this.nextNextGaussian = v2 * multiplier;
rob@100 6921 this.haveNextNextGaussian = true;
rob@100 6922
rob@100 6923 return v1 * multiplier;
rob@100 6924 };
rob@100 6925
rob@100 6926 // Noise functions and helpers
rob@100 6927 function PerlinNoise(seed) {
rob@100 6928 var rnd = seed !== undef ? new Marsaglia(seed) : Marsaglia.createRandomized();
rob@100 6929 var i, j;
rob@100 6930 // http://www.noisemachine.com/talk1/17b.html
rob@100 6931 // http://mrl.nyu.edu/~perlin/noise/
rob@100 6932 // generate permutation
rob@100 6933 var perm = new Uint8Array(512);
rob@100 6934 for(i=0;i<256;++i) { perm[i] = i; }
rob@100 6935 for(i=0;i<256;++i) { var t = perm[j = rnd.intGenerator() & 0xFF]; perm[j] = perm[i]; perm[i] = t; }
rob@100 6936 // copy to avoid taking mod in perm[0];
rob@100 6937 for(i=0;i<256;++i) { perm[i + 256] = perm[i]; }
rob@100 6938
rob@100 6939 function grad3d(i,x,y,z) {
rob@100 6940 var h = i & 15; // convert into 12 gradient directions
rob@100 6941 var u = h<8 ? x : y,
rob@100 6942 v = h<4 ? y : h===12||h===14 ? x : z;
rob@100 6943 return ((h&1) === 0 ? u : -u) + ((h&2) === 0 ? v : -v);
rob@100 6944 }
rob@100 6945
rob@100 6946 function grad2d(i,x,y) {
rob@100 6947 var v = (i & 1) === 0 ? x : y;
rob@100 6948 return (i&2) === 0 ? -v : v;
rob@100 6949 }
rob@100 6950
rob@100 6951 function grad1d(i,x) {
rob@100 6952 return (i&1) === 0 ? -x : x;
rob@100 6953 }
rob@100 6954
rob@100 6955 function lerp(t,a,b) { return a + t * (b - a); }
rob@100 6956
rob@100 6957 this.noise3d = function(x, y, z) {
rob@100 6958 var X = Math.floor(x)&255, Y = Math.floor(y)&255, Z = Math.floor(z)&255;
rob@100 6959 x -= Math.floor(x); y -= Math.floor(y); z -= Math.floor(z);
rob@100 6960 var fx = (3-2*x)*x*x, fy = (3-2*y)*y*y, fz = (3-2*z)*z*z;
rob@100 6961 var p0 = perm[X]+Y, p00 = perm[p0] + Z, p01 = perm[p0 + 1] + Z,
rob@100 6962 p1 = perm[X + 1] + Y, p10 = perm[p1] + Z, p11 = perm[p1 + 1] + Z;
rob@100 6963 return lerp(fz,
rob@100 6964 lerp(fy, lerp(fx, grad3d(perm[p00], x, y, z), grad3d(perm[p10], x-1, y, z)),
rob@100 6965 lerp(fx, grad3d(perm[p01], x, y-1, z), grad3d(perm[p11], x-1, y-1,z))),
rob@100 6966 lerp(fy, lerp(fx, grad3d(perm[p00 + 1], x, y, z-1), grad3d(perm[p10 + 1], x-1, y, z-1)),
rob@100 6967 lerp(fx, grad3d(perm[p01 + 1], x, y-1, z-1), grad3d(perm[p11 + 1], x-1, y-1,z-1))));
rob@100 6968 };
rob@100 6969
rob@100 6970 this.noise2d = function(x, y) {
rob@100 6971 var X = Math.floor(x)&255, Y = Math.floor(y)&255;
rob@100 6972 x -= Math.floor(x); y -= Math.floor(y);
rob@100 6973 var fx = (3-2*x)*x*x, fy = (3-2*y)*y*y;
rob@100 6974 var p0 = perm[X]+Y, p1 = perm[X + 1] + Y;
rob@100 6975 return lerp(fy,
rob@100 6976 lerp(fx, grad2d(perm[p0], x, y), grad2d(perm[p1], x-1, y)),
rob@100 6977 lerp(fx, grad2d(perm[p0 + 1], x, y-1), grad2d(perm[p1 + 1], x-1, y-1)));
rob@100 6978 };
rob@100 6979
rob@100 6980 this.noise1d = function(x) {
rob@100 6981 var X = Math.floor(x)&255;
rob@100 6982 x -= Math.floor(x);
rob@100 6983 var fx = (3-2*x)*x*x;
rob@100 6984 return lerp(fx, grad1d(perm[X], x), grad1d(perm[X+1], x-1));
rob@100 6985 };
rob@100 6986 }
rob@100 6987
rob@100 6988 // processing defaults
rob@100 6989 var noiseProfile = { generator: undef, octaves: 4, fallout: 0.5, seed: undef};
rob@100 6990
rob@100 6991 /**
rob@100 6992 * Returns the Perlin noise value at specified coordinates. Perlin noise is a random sequence
rob@100 6993 * generator producing a more natural ordered, harmonic succession of numbers compared to the
rob@100 6994 * standard random() function. It was invented by Ken Perlin in the 1980s and been used since
rob@100 6995 * in graphical applications to produce procedural textures, natural motion, shapes, terrains etc.
rob@100 6996 * The main difference to the random() function is that Perlin noise is defined in an infinite
rob@100 6997 * n-dimensional space where each pair of coordinates corresponds to a fixed semi-random value
rob@100 6998 * (fixed only for the lifespan of the program). The resulting value will always be between 0.0
rob@100 6999 * and 1.0. Processing can compute 1D, 2D and 3D noise, depending on the number of coordinates
rob@100 7000 * given. The noise value can be animated by moving through the noise space as demonstrated in
rob@100 7001 * the example above. The 2nd and 3rd dimension can also be interpreted as time.
rob@100 7002 * The actual noise is structured similar to an audio signal, in respect to the function's use
rob@100 7003 * of frequencies. Similar to the concept of harmonics in physics, perlin noise is computed over
rob@100 7004 * several octaves which are added together for the final result.
rob@100 7005 * Another way to adjust the character of the resulting sequence is the scale of the input
rob@100 7006 * coordinates. As the function works within an infinite space the value of the coordinates
rob@100 7007 * doesn't matter as such, only the distance between successive coordinates does (eg. when using
rob@100 7008 * noise() within a loop). As a general rule the smaller the difference between coordinates, the
rob@100 7009 * smoother the resulting noise sequence will be. Steps of 0.005-0.03 work best for most applications,
rob@100 7010 * but this will differ depending on use.
rob@100 7011 *
rob@100 7012 * @param {float} x x coordinate in noise space
rob@100 7013 * @param {float} y y coordinate in noise space
rob@100 7014 * @param {float} z z coordinate in noise space
rob@100 7015 *
rob@100 7016 * @returns {float}
rob@100 7017 *
rob@100 7018 * @see random
rob@100 7019 * @see noiseDetail
rob@100 7020 */
rob@100 7021 p.noise = function(x, y, z) {
rob@100 7022 if(noiseProfile.generator === undef) {
rob@100 7023 // caching
rob@100 7024 noiseProfile.generator = new PerlinNoise(noiseProfile.seed);
rob@100 7025 }
rob@100 7026 var generator = noiseProfile.generator;
rob@100 7027 var effect = 1, k = 1, sum = 0;
rob@100 7028 for(var i=0; i<noiseProfile.octaves; ++i) {
rob@100 7029 effect *= noiseProfile.fallout;
rob@100 7030 switch (arguments.length) {
rob@100 7031 case 1:
rob@100 7032 sum += effect * (1 + generator.noise1d(k*x))/2; break;
rob@100 7033 case 2:
rob@100 7034 sum += effect * (1 + generator.noise2d(k*x, k*y))/2; break;
rob@100 7035 case 3:
rob@100 7036 sum += effect * (1 + generator.noise3d(k*x, k*y, k*z))/2; break;
rob@100 7037 }
rob@100 7038 k *= 2;
rob@100 7039 }
rob@100 7040 return sum;
rob@100 7041 };
rob@100 7042
rob@100 7043 /**
rob@100 7044 * Adjusts the character and level of detail produced by the Perlin noise function.
rob@100 7045 * Similar to harmonics in physics, noise is computed over several octaves. Lower octaves
rob@100 7046 * contribute more to the output signal and as such define the overal intensity of the noise,
rob@100 7047 * whereas higher octaves create finer grained details in the noise sequence. By default,
rob@100 7048 * noise is computed over 4 octaves with each octave contributing exactly half than its
rob@100 7049 * predecessor, starting at 50% strength for the 1st octave. This falloff amount can be
rob@100 7050 * changed by adding an additional function parameter. Eg. a falloff factor of 0.75 means
rob@100 7051 * each octave will now have 75% impact (25% less) of the previous lower octave. Any value
rob@100 7052 * between 0.0 and 1.0 is valid, however note that values greater than 0.5 might result in
rob@100 7053 * greater than 1.0 values returned by noise(). By changing these parameters, the signal
rob@100 7054 * created by the noise() function can be adapted to fit very specific needs and characteristics.
rob@100 7055 *
rob@100 7056 * @param {int} octaves number of octaves to be used by the noise() function
rob@100 7057 * @param {float} falloff falloff factor for each octave
rob@100 7058 *
rob@100 7059 * @see noise
rob@100 7060 */
rob@100 7061 p.noiseDetail = function(octaves, fallout) {
rob@100 7062 noiseProfile.octaves = octaves;
rob@100 7063 if(fallout !== undef) {
rob@100 7064 noiseProfile.fallout = fallout;
rob@100 7065 }
rob@100 7066 };
rob@100 7067
rob@100 7068 /**
rob@100 7069 * Sets the seed value for noise(). By default, noise() produces different results each
rob@100 7070 * time the program is run. Set the value parameter to a constant to return the same
rob@100 7071 * pseudo-random numbers each time the software is run.
rob@100 7072 *
rob@100 7073 * @param {int} seed int
rob@100 7074 *
rob@100 7075 * @returns {float}
rob@100 7076 *
rob@100 7077 * @see random
rob@100 7078 * @see radomSeed
rob@100 7079 * @see noise
rob@100 7080 * @see noiseDetail
rob@100 7081 */
rob@100 7082 p.noiseSeed = function(seed) {
rob@100 7083 noiseProfile.seed = seed;
rob@100 7084 noiseProfile.generator = undef;
rob@100 7085 };
rob@100 7086 };
rob@100 7087
rob@100 7088 },{}],23:[function(require,module,exports){
rob@100 7089 /**
rob@100 7090 * Common functions traditionally on "p" that should be class functions
rob@100 7091 * that get bound to "p" when an instance is actually built, instead.
rob@100 7092 */
rob@100 7093 module.exports = (function commonFunctions(undef) {
rob@100 7094
rob@100 7095 var CommonFunctions = {
rob@100 7096 /**
rob@100 7097 * Remove whitespace characters from the beginning and ending
rob@100 7098 * of a String or a String array. Works like String.trim() but includes the
rob@100 7099 * unicode nbsp character as well. If an array is passed in the function will return a new array not effecting the array passed in.
rob@100 7100 *
rob@100 7101 * @param {String} str the string to trim
rob@100 7102 * @param {String[]} str the string array to trim
rob@100 7103 *
rob@100 7104 * @return {String|String[]} retrurns a string or an array will removed whitespaces
rob@100 7105 */
rob@100 7106 trim: function(str) {
rob@100 7107 if (str instanceof Array) {
rob@100 7108 var arr = [];
rob@100 7109 for (var i = 0; i < str.length; i++) {
rob@100 7110 arr.push(str[i].replace(/^\s*/, '').replace(/\s*$/, '').replace(/\r*$/, ''));
rob@100 7111 }
rob@100 7112 return arr;
rob@100 7113 }
rob@100 7114 return str.replace(/^\s*/, '').replace(/\s*$/, '').replace(/\r*$/, '');
rob@100 7115 },
rob@100 7116
rob@100 7117 /**
rob@100 7118 * Converts a degree measurement to its corresponding value in radians. Radians and degrees are two ways of
rob@100 7119 * measuring the same thing. There are 360 degrees in a circle and 2*PI radians in a circle. For example,
rob@100 7120 * 90 degrees = PI/2 = 1.5707964. All trigonometric methods in Processing require their parameters to be specified in radians.
rob@100 7121 *
rob@100 7122 * @param {int|float} value an angle in radians
rob@100 7123 *
rob@100 7124 * @returns {float}
rob@100 7125 *
rob@100 7126 * @see degrees
rob@100 7127 */
rob@100 7128 radians: function(aAngle) {
rob@100 7129 return (aAngle / 180) * Math.PI;
rob@100 7130 },
rob@100 7131
rob@100 7132 /**
rob@100 7133 * Number-to-String formatting function. Prepends "plus" or "minus" depending
rob@100 7134 * on whether the value is positive or negative, respectively, after padding
rob@100 7135 * the value with zeroes on the left and right, the number of zeroes used dictated
rob@100 7136 * by the values 'leftDigits' and 'rightDigits'. 'value' cannot be an array.
rob@100 7137 *
rob@100 7138 * @param {int|float} value the number to format
rob@100 7139 * @param {String} plus the prefix for positive numbers
rob@100 7140 * @param {String} minus the prefix for negative numbers
rob@100 7141 * @param {int} left number of digits to the left of the decimal point
rob@100 7142 * @param {int} right number of digits to the right of the decimal point
rob@100 7143 * @param {String} group string delimited for groups, such as the comma in "1,000"
rob@100 7144 *
rob@100 7145 * @returns {String or String[]}
rob@100 7146 *
rob@100 7147 * @see nfCore
rob@100 7148 */
rob@100 7149 nfCoreScalar: function (value, plus, minus, leftDigits, rightDigits, group) {
rob@100 7150 var sign = (value < 0) ? minus : plus;
rob@100 7151 var autoDetectDecimals = rightDigits === 0;
rob@100 7152 var rightDigitsOfDefault = (rightDigits === undef || rightDigits < 0) ? 0 : rightDigits;
rob@100 7153
rob@100 7154 var absValue = Math.abs(value);
rob@100 7155 if (autoDetectDecimals) {
rob@100 7156 rightDigitsOfDefault = 1;
rob@100 7157 absValue *= 10;
rob@100 7158 while (Math.abs(Math.round(absValue) - absValue) > 1e-6 && rightDigitsOfDefault < 7) {
rob@100 7159 ++rightDigitsOfDefault;
rob@100 7160 absValue *= 10;
rob@100 7161 }
rob@100 7162 } else if (rightDigitsOfDefault !== 0) {
rob@100 7163 absValue *= Math.pow(10, rightDigitsOfDefault);
rob@100 7164 }
rob@100 7165
rob@100 7166 // Using Java's default rounding policy HALF_EVEN. This policy is based
rob@100 7167 // on the idea that 0.5 values round to the nearest even number, and
rob@100 7168 // everything else is rounded normally.
rob@100 7169 var number, doubled = absValue * 2;
rob@100 7170 if (Math.floor(absValue) === absValue) {
rob@100 7171 number = absValue;
rob@100 7172 } else if (Math.floor(doubled) === doubled) {
rob@100 7173 var floored = Math.floor(absValue);
rob@100 7174 number = floored + (floored % 2);
rob@100 7175 } else {
rob@100 7176 number = Math.round(absValue);
rob@100 7177 }
rob@100 7178
rob@100 7179 var buffer = "";
rob@100 7180 var totalDigits = leftDigits + rightDigitsOfDefault;
rob@100 7181 while (totalDigits > 0 || number > 0) {
rob@100 7182 totalDigits--;
rob@100 7183 buffer = "" + (number % 10) + buffer;
rob@100 7184 number = Math.floor(number / 10);
rob@100 7185 }
rob@100 7186 if (group !== undef) {
rob@100 7187 var i = buffer.length - 3 - rightDigitsOfDefault;
rob@100 7188 while(i > 0) {
rob@100 7189 buffer = buffer.substring(0,i) + group + buffer.substring(i);
rob@100 7190 i-=3;
rob@100 7191 }
rob@100 7192 }
rob@100 7193 if (rightDigitsOfDefault > 0) {
rob@100 7194 return sign + buffer.substring(0, buffer.length - rightDigitsOfDefault) +
rob@100 7195 "." + buffer.substring(buffer.length - rightDigitsOfDefault, buffer.length);
rob@100 7196 }
rob@100 7197 return sign + buffer;
rob@100 7198 },
rob@100 7199
rob@100 7200 /**
rob@100 7201 * Number-to-String formatting function. Prepends "plus" or "minus" depending
rob@100 7202 * on whether the value is positive or negative, respectively, after padding
rob@100 7203 * the value with zeroes on the left and right, the number of zeroes used dictated
rob@100 7204 * by the values 'leftDigits' and 'rightDigits'. 'value' can be an array;
rob@100 7205 * if the input is an array, each value in it is formatted separately, and
rob@100 7206 * an array with formatted values is returned.
rob@100 7207 *
rob@100 7208 * @param {int|int[]|float|float[]} value the number(s) to format
rob@100 7209 * @param {String} plus the prefix for positive numbers
rob@100 7210 * @param {String} minus the prefix for negative numbers
rob@100 7211 * @param {int} left number of digits to the left of the decimal point
rob@100 7212 * @param {int} right number of digits to the right of the decimal point
rob@100 7213 * @param {String} group string delimited for groups, such as the comma in "1,000"
rob@100 7214 *
rob@100 7215 * @returns {String or String[]}
rob@100 7216 *
rob@100 7217 * @see nfCoreScalar
rob@100 7218 */
rob@100 7219 nfCore: function(value, plus, minus, leftDigits, rightDigits, group) {
rob@100 7220 if (value instanceof Array) {
rob@100 7221 var arr = [];
rob@100 7222 for (var i = 0, len = value.length; i < len; i++) {
rob@100 7223 arr.push(CommonFunctions.nfCoreScalar(value[i], plus, minus, leftDigits, rightDigits, group));
rob@100 7224 }
rob@100 7225 return arr;
rob@100 7226 }
rob@100 7227 return CommonFunctions.nfCoreScalar(value, plus, minus, leftDigits, rightDigits, group);
rob@100 7228 },
rob@100 7229
rob@100 7230 /**
rob@100 7231 * Utility function for formatting numbers into strings. There are two versions, one for
rob@100 7232 * formatting floats and one for formatting ints. The values for the digits, left, and
rob@100 7233 * right parameters should always be positive integers.
rob@100 7234 * As shown in the above example, nf() is used to add zeros to the left and/or right
rob@100 7235 * of a number. This is typically for aligning a list of numbers. To remove digits from
rob@100 7236 * a floating-point number, use the int(), ceil(), floor(), or round() functions.
rob@100 7237 *
rob@100 7238 * @param {int|int[]|float|float[]} value the number(s) to format
rob@100 7239 * @param {int} left number of digits to the left of the decimal point
rob@100 7240 * @param {int} right number of digits to the right of the decimal point
rob@100 7241 *
rob@100 7242 * @returns {String or String[]}
rob@100 7243 *
rob@100 7244 * @see nfs
rob@100 7245 * @see nfp
rob@100 7246 * @see nfc
rob@100 7247 */
rob@100 7248 nf: function(value, leftDigits, rightDigits) {
rob@100 7249 return CommonFunctions.nfCore(value, "", "-", leftDigits, rightDigits);
rob@100 7250 },
rob@100 7251
rob@100 7252 /**
rob@100 7253 * Utility function for formatting numbers into strings. Similar to nf() but leaves a blank space in front
rob@100 7254 * of positive numbers so they align with negative numbers in spite of the minus symbol. There are two
rob@100 7255 * versions, one for formatting floats and one for formatting ints. The values for the digits, left,
rob@100 7256 * and right parameters should always be positive integers.
rob@100 7257 *
rob@100 7258 * @param {int|int[]|float|float[]} value the number(s) to format
rob@100 7259 * @param {int} left number of digits to the left of the decimal point
rob@100 7260 * @param {int} right number of digits to the right of the decimal point
rob@100 7261 *
rob@100 7262 * @returns {String or String[]}
rob@100 7263 *
rob@100 7264 * @see nf
rob@100 7265 * @see nfp
rob@100 7266 * @see nfc
rob@100 7267 */
rob@100 7268 nfs: function(value, leftDigits, rightDigits) {
rob@100 7269 return CommonFunctions.nfCore(value, " ", "-", leftDigits, rightDigits);
rob@100 7270 },
rob@100 7271
rob@100 7272 /**
rob@100 7273 * Utility function for formatting numbers into strings. Similar to nf() but puts a "+" in front of
rob@100 7274 * positive numbers and a "-" in front of negative numbers. There are two versions, one for formatting
rob@100 7275 * floats and one for formatting ints. The values for the digits, left, and right parameters should
rob@100 7276 * always be positive integers.
rob@100 7277 *
rob@100 7278 * @param {int|int[]|float|float[]} value the number(s) to format
rob@100 7279 * @param {int} left number of digits to the left of the decimal point
rob@100 7280 * @param {int} right number of digits to the right of the decimal point
rob@100 7281 *
rob@100 7282 * @returns {String or String[]}
rob@100 7283 *
rob@100 7284 * @see nfs
rob@100 7285 * @see nf
rob@100 7286 * @see nfc
rob@100 7287 */
rob@100 7288 nfp: function(value, leftDigits, rightDigits) {
rob@100 7289 return CommonFunctions.nfCore(value, "+", "-", leftDigits, rightDigits);
rob@100 7290 },
rob@100 7291
rob@100 7292 /**
rob@100 7293 * Utility function for formatting numbers into strings and placing appropriate commas to mark
rob@100 7294 * units of 1000. There are two versions, one for formatting ints and one for formatting an array
rob@100 7295 * of ints. The value for the digits parameter should always be a positive integer.
rob@100 7296 *
rob@100 7297 * @param {int|int[]|float|float[]} value the number(s) to format
rob@100 7298 * @param {int} left number of digits to the left of the decimal point
rob@100 7299 * @param {int} right number of digits to the right of the decimal point
rob@100 7300 *
rob@100 7301 * @returns {String or String[]}
rob@100 7302 *
rob@100 7303 * @see nf
rob@100 7304 * @see nfs
rob@100 7305 * @see nfp
rob@100 7306 */
rob@100 7307 nfc: function(value, rightDigits) {
rob@100 7308 return CommonFunctions.nfCore(value, "", "-", 0, rightDigits, ",");
rob@100 7309 },
rob@100 7310
rob@100 7311 // used to bind all common functions to "p"
rob@100 7312 withCommonFunctions: function withCommonFunctions(p) {
rob@100 7313 ["trim", "radians", "nf", "nfs", "nfp", "nfc"].forEach(function(f){
rob@100 7314 p[f] = CommonFunctions[f];
rob@100 7315 });
rob@100 7316 }
rob@100 7317 };
rob@100 7318
rob@100 7319 return CommonFunctions;
rob@100 7320 }());
rob@100 7321
rob@100 7322 },{}],24:[function(require,module,exports){
rob@100 7323 /**
rob@100 7324 * Touch and Mouse event handling
rob@100 7325 */
rob@100 7326 module.exports = function withTouch(p, curElement, attachEventHandler, document, PConstants, undef) {
rob@100 7327
rob@100 7328 /**
rob@100 7329 * Determine the location of the (mouse) pointer.
rob@100 7330 */
rob@100 7331 function calculateOffset(curElement, event) {
rob@100 7332 var element = curElement,
rob@100 7333 offsetX = 0,
rob@100 7334 offsetY = 0;
rob@100 7335
rob@100 7336 p.pmouseX = p.mouseX;
rob@100 7337 p.pmouseY = p.mouseY;
rob@100 7338
rob@100 7339 // Find element offset
rob@100 7340 if (element.offsetParent) {
rob@100 7341 do {
rob@100 7342 offsetX += element.offsetLeft;
rob@100 7343 offsetY += element.offsetTop;
rob@100 7344 } while (!!(element = element.offsetParent));
rob@100 7345 }
rob@100 7346
rob@100 7347 // Find Scroll offset
rob@100 7348 element = curElement;
rob@100 7349 do {
rob@100 7350 offsetX -= element.scrollLeft || 0;
rob@100 7351 offsetY -= element.scrollTop || 0;
rob@100 7352 } while (!!(element = element.parentNode));
rob@100 7353
rob@100 7354 // Get padding and border style widths for mouse offsets
rob@100 7355 var stylePaddingLeft, stylePaddingTop, styleBorderLeft, styleBorderTop;
rob@100 7356 if (document.defaultView && document.defaultView.getComputedStyle) {
rob@100 7357 stylePaddingLeft = parseInt(document.defaultView.getComputedStyle(curElement, null).paddingLeft, 10) || 0;
rob@100 7358 stylePaddingTop = parseInt(document.defaultView.getComputedStyle(curElement, null).paddingTop, 10) || 0;
rob@100 7359 styleBorderLeft = parseInt(document.defaultView.getComputedStyle(curElement, null).borderLeftWidth, 10) || 0;
rob@100 7360 styleBorderTop = parseInt(document.defaultView.getComputedStyle(curElement, null).borderTopWidth, 10) || 0;
rob@100 7361 }
rob@100 7362
rob@100 7363 // Add padding and border style widths to offset
rob@100 7364 offsetX += stylePaddingLeft;
rob@100 7365 offsetY += stylePaddingTop;
rob@100 7366
rob@100 7367 offsetX += styleBorderLeft;
rob@100 7368 offsetY += styleBorderTop;
rob@100 7369
rob@100 7370 // Take into account any scrolling done
rob@100 7371 offsetX += window.pageXOffset;
rob@100 7372 offsetY += window.pageYOffset;
rob@100 7373
rob@100 7374 return {'X':offsetX,'Y':offsetY};
rob@100 7375 }
rob@100 7376
rob@100 7377 // simple relative position
rob@100 7378 function updateMousePosition(curElement, event) {
rob@100 7379 var offset = calculateOffset(curElement, event);
rob@100 7380 // Dropping support for IE clientX and clientY, switching to pageX and pageY
rob@100 7381 // so we don't have to calculate scroll offset.
rob@100 7382 // Removed in ticket #184. See rev: 2f106d1c7017fed92d045ba918db47d28e5c16f4
rob@100 7383 p.mouseX = event.pageX - offset.X;
rob@100 7384 p.mouseY = event.pageY - offset.Y;
rob@100 7385 }
rob@100 7386
rob@100 7387 /**
rob@100 7388 * Return a TouchEvent with canvas-specific x/y co-ordinates
rob@100 7389 */
rob@100 7390 function addTouchEventOffset(t) {
rob@100 7391 var offset = calculateOffset(t.changedTouches[0].target, t.changedTouches[0]),
rob@100 7392 i;
rob@100 7393
rob@100 7394 for (i = 0; i < t.touches.length; i++) {
rob@100 7395 var touch = t.touches[i];
rob@100 7396 touch.offsetX = touch.pageX - offset.X;
rob@100 7397 touch.offsetY = touch.pageY - offset.Y;
rob@100 7398 }
rob@100 7399 for (i = 0; i < t.targetTouches.length; i++) {
rob@100 7400 var targetTouch = t.targetTouches[i];
rob@100 7401 targetTouch.offsetX = targetTouch.pageX - offset.X;
rob@100 7402 targetTouch.offsetY = targetTouch.pageY - offset.Y;
rob@100 7403 }
rob@100 7404 for (i = 0; i < t.changedTouches.length; i++) {
rob@100 7405 var changedTouch = t.changedTouches[i];
rob@100 7406 changedTouch.offsetX = changedTouch.pageX - offset.X;
rob@100 7407 changedTouch.offsetY = changedTouch.pageY - offset.Y;
rob@100 7408 }
rob@100 7409
rob@100 7410 return t;
rob@100 7411 }
rob@100 7412
rob@100 7413 /**
rob@100 7414 * Touch event support.
rob@100 7415 */
rob@100 7416 attachEventHandler(curElement, "touchstart", function (t) {
rob@100 7417 // Removes unwanted behaviour of the canvas when touching canvas
rob@100 7418 curElement.setAttribute("style","-webkit-user-select: none");
rob@100 7419 curElement.setAttribute("onclick","void(0)");
rob@100 7420 curElement.setAttribute("style","-webkit-tap-highlight-color:rgba(0,0,0,0)");
rob@100 7421 // Loop though eventHandlers and remove mouse listeners
rob@100 7422 for (var i=0, ehl=eventHandlers.length; i<ehl; i++) {
rob@100 7423 var type = eventHandlers[i].type;
rob@100 7424 // Have this function remove itself from the eventHandlers list too
rob@100 7425 if (type === "mouseout" || type === "mousemove" ||
rob@100 7426 type === "mousedown" || type === "mouseup" ||
rob@100 7427 type === "DOMMouseScroll" || type === "mousewheel" || type === "touchstart") {
rob@100 7428 detachEventHandler(eventHandlers[i]);
rob@100 7429 }
rob@100 7430 }
rob@100 7431
rob@100 7432 // If there are any native touch events defined in the sketch, connect all of them
rob@100 7433 // Otherwise, connect all of the emulated mouse events
rob@100 7434 if (p.touchStart !== undef || p.touchMove !== undef ||
rob@100 7435 p.touchEnd !== undef || p.touchCancel !== undef) {
rob@100 7436 attachEventHandler(curElement, "touchstart", function(t) {
rob@100 7437 if (p.touchStart !== undef) {
rob@100 7438 t = addTouchEventOffset(t);
rob@100 7439 p.touchStart(t);
rob@100 7440 }
rob@100 7441 });
rob@100 7442
rob@100 7443 attachEventHandler(curElement, "touchmove", function(t) {
rob@100 7444 if (p.touchMove !== undef) {
rob@100 7445 t.preventDefault(); // Stop the viewport from scrolling
rob@100 7446 t = addTouchEventOffset(t);
rob@100 7447 p.touchMove(t);
rob@100 7448 }
rob@100 7449 });
rob@100 7450
rob@100 7451 attachEventHandler(curElement, "touchend", function(t) {
rob@100 7452 if (p.touchEnd !== undef) {
rob@100 7453 t = addTouchEventOffset(t);
rob@100 7454 p.touchEnd(t);
rob@100 7455 }
rob@100 7456 });
rob@100 7457
rob@100 7458 attachEventHandler(curElement, "touchcancel", function(t) {
rob@100 7459 if (p.touchCancel !== undef) {
rob@100 7460 t = addTouchEventOffset(t);
rob@100 7461 p.touchCancel(t);
rob@100 7462 }
rob@100 7463 });
rob@100 7464
rob@100 7465 } else {
rob@100 7466 // Emulated touch start/mouse down event
rob@100 7467 attachEventHandler(curElement, "touchstart", function(e) {
rob@100 7468 updateMousePosition(curElement, e.touches[0]);
rob@100 7469
rob@100 7470 p.__mousePressed = true;
rob@100 7471 p.mouseDragging = false;
rob@100 7472 p.mouseButton = PConstants.LEFT;
rob@100 7473
rob@100 7474 if (typeof p.mousePressed === "function") {
rob@100 7475 p.mousePressed();
rob@100 7476 }
rob@100 7477 });
rob@100 7478
rob@100 7479 // Emulated touch move/mouse move event
rob@100 7480 attachEventHandler(curElement, "touchmove", function(e) {
rob@100 7481 e.preventDefault();
rob@100 7482 updateMousePosition(curElement, e.touches[0]);
rob@100 7483
rob@100 7484 if (typeof p.mouseMoved === "function" && !p.__mousePressed) {
rob@100 7485 p.mouseMoved();
rob@100 7486 }
rob@100 7487 if (typeof p.mouseDragged === "function" && p.__mousePressed) {
rob@100 7488 p.mouseDragged();
rob@100 7489 p.mouseDragging = true;
rob@100 7490 }
rob@100 7491 });
rob@100 7492
rob@100 7493 // Emulated touch up/mouse up event
rob@100 7494 attachEventHandler(curElement, "touchend", function(e) {
rob@100 7495 p.__mousePressed = false;
rob@100 7496
rob@100 7497 if (typeof p.mouseClicked === "function" && !p.mouseDragging) {
rob@100 7498 p.mouseClicked();
rob@100 7499 }
rob@100 7500
rob@100 7501 if (typeof p.mouseReleased === "function") {
rob@100 7502 p.mouseReleased();
rob@100 7503 }
rob@100 7504 });
rob@100 7505 }
rob@100 7506
rob@100 7507 // Refire the touch start event we consumed in this function
rob@100 7508 curElement.dispatchEvent(t);
rob@100 7509 });
rob@100 7510
rob@100 7511 /**
rob@100 7512 * Context menu toggles. Most often you will not want the
rob@100 7513 * browser's context menu to show on a right click, but
rob@100 7514 * sometimes, you do, so we add two unofficial functions
rob@100 7515 * that can be used to trigger context menu behaviour.
rob@100 7516 */
rob@100 7517 (function() {
rob@100 7518 var enabled = true,
rob@100 7519 contextMenu = function(e) {
rob@100 7520 e.preventDefault();
rob@100 7521 e.stopPropagation();
rob@100 7522 };
rob@100 7523
rob@100 7524 p.disableContextMenu = function() {
rob@100 7525 if (!enabled) {
rob@100 7526 return;
rob@100 7527 }
rob@100 7528 attachEventHandler(curElement, 'contextmenu', contextMenu);
rob@100 7529 enabled = false;
rob@100 7530 };
rob@100 7531
rob@100 7532 p.enableContextMenu = function() {
rob@100 7533 if (enabled) {
rob@100 7534 return;
rob@100 7535 }
rob@100 7536 detachEventHandler({elem: curElement, type: 'contextmenu', fn: contextMenu});
rob@100 7537 enabled = true;
rob@100 7538 };
rob@100 7539 }());
rob@100 7540
rob@100 7541 /**
rob@100 7542 * Mouse moved or dragged
rob@100 7543 */
rob@100 7544 attachEventHandler(curElement, "mousemove", function(e) {
rob@100 7545 updateMousePosition(curElement, e);
rob@100 7546 if (typeof p.mouseMoved === "function" && !p.__mousePressed) {
rob@100 7547 p.mouseMoved();
rob@100 7548 }
rob@100 7549 if (typeof p.mouseDragged === "function" && p.__mousePressed) {
rob@100 7550 p.mouseDragged();
rob@100 7551 p.mouseDragging = true;
rob@100 7552 }
rob@100 7553 });
rob@100 7554
rob@100 7555 /**
rob@100 7556 * Unofficial mouse-out handling
rob@100 7557 */
rob@100 7558 attachEventHandler(curElement, "mouseout", function(e) {
rob@100 7559 if (typeof p.mouseOut === "function") {
rob@100 7560 p.mouseOut();
rob@100 7561 }
rob@100 7562 });
rob@100 7563
rob@100 7564 /**
rob@100 7565 * Mouse over
rob@100 7566 */
rob@100 7567 attachEventHandler(curElement, "mouseover", function(e) {
rob@100 7568 updateMousePosition(curElement, e);
rob@100 7569 if (typeof p.mouseOver === "function") {
rob@100 7570 p.mouseOver();
rob@100 7571 }
rob@100 7572 });
rob@100 7573
rob@100 7574 /**
rob@100 7575 * Disable browser's default handling for click-drag of a canvas.
rob@100 7576 */
rob@100 7577 curElement.onmousedown = function () {
rob@100 7578 // make sure focus happens, but nothing else
rob@100 7579 curElement.focus();
rob@100 7580 return false;
rob@100 7581 };
rob@100 7582
rob@100 7583 /**
rob@100 7584 * Mouse pressed or drag
rob@100 7585 */
rob@100 7586 attachEventHandler(curElement, "mousedown", function(e) {
rob@100 7587 p.__mousePressed = true;
rob@100 7588 p.mouseDragging = false;
rob@100 7589 switch (e.which) {
rob@100 7590 case 1:
rob@100 7591 p.mouseButton = PConstants.LEFT;
rob@100 7592 break;
rob@100 7593 case 2:
rob@100 7594 p.mouseButton = PConstants.CENTER;
rob@100 7595 break;
rob@100 7596 case 3:
rob@100 7597 p.mouseButton = PConstants.RIGHT;
rob@100 7598 break;
rob@100 7599 }
rob@100 7600
rob@100 7601 if (typeof p.mousePressed === "function") {
rob@100 7602 p.mousePressed();
rob@100 7603 }
rob@100 7604 });
rob@100 7605
rob@100 7606 /**
rob@100 7607 * Mouse clicked or released
rob@100 7608 */
rob@100 7609 attachEventHandler(curElement, "mouseup", function(e) {
rob@100 7610 p.__mousePressed = false;
rob@100 7611
rob@100 7612 if (typeof p.mouseClicked === "function" && !p.mouseDragging) {
rob@100 7613 p.mouseClicked();
rob@100 7614 }
rob@100 7615
rob@100 7616 if (typeof p.mouseReleased === "function") {
rob@100 7617 p.mouseReleased();
rob@100 7618 }
rob@100 7619 });
rob@100 7620
rob@100 7621 /**
rob@100 7622 * Unofficial scroll wheel handling.
rob@100 7623 */
rob@100 7624 var mouseWheelHandler = function(e) {
rob@100 7625 var delta = 0;
rob@100 7626
rob@100 7627 if (e.wheelDelta) {
rob@100 7628 delta = e.wheelDelta / 120;
rob@100 7629 if (window.opera) {
rob@100 7630 delta = -delta;
rob@100 7631 }
rob@100 7632 } else if (e.detail) {
rob@100 7633 delta = -e.detail / 3;
rob@100 7634 }
rob@100 7635
rob@100 7636 p.mouseScroll = delta;
rob@100 7637
rob@100 7638 if (delta && typeof p.mouseScrolled === 'function') {
rob@100 7639 p.mouseScrolled();
rob@100 7640 }
rob@100 7641 };
rob@100 7642
rob@100 7643 // Support Gecko and non-Gecko scroll events
rob@100 7644 attachEventHandler(document, 'DOMMouseScroll', mouseWheelHandler);
rob@100 7645 attachEventHandler(document, 'mousewheel', mouseWheelHandler);
rob@100 7646
rob@100 7647 };
rob@100 7648
rob@100 7649 },{}],25:[function(require,module,exports){
rob@100 7650 /**
rob@100 7651 * The parser for turning Processing syntax into Pjs JavaScript.
rob@100 7652 * This code is not trivial; unless you know what you're doing,
rob@100 7653 * you shouldn't be changing things in here =)
rob@100 7654 */
rob@100 7655 module.exports = function setupParser(Processing, options) {
rob@100 7656
rob@100 7657 var defaultScope = options.defaultScope,
rob@100 7658 PConstants = defaultScope.PConstants,
rob@100 7659 aFunctions = options.aFunctions,
rob@100 7660 Browser = options.Browser,
rob@100 7661 document = Browser.document,
rob@100 7662 undef;
rob@100 7663
rob@100 7664 // Processing global methods and constants for the parser
rob@100 7665 function getGlobalMembers() {
rob@100 7666 // The names array contains the names of everything that is inside "p."
rob@100 7667 // When something new is added to "p." it must also be added to this list.
rob@100 7668 var names = [ /* this code is generated by jsglobals.js */
rob@100 7669 "abs", "acos", "alpha", "ambient", "ambientLight", "append", "applyMatrix",
rob@100 7670 "arc", "arrayCopy", "asin", "atan", "atan2", "background", "beginCamera",
rob@100 7671 "beginDraw", "beginShape", "bezier", "bezierDetail", "bezierPoint",
rob@100 7672 "bezierTangent", "bezierVertex", "binary", "blend", "blendColor",
rob@100 7673 "blit_resize", "blue", "box", "breakShape", "brightness",
rob@100 7674 "camera", "ceil", "Character", "color", "colorMode",
rob@100 7675 "concat", "constrain", "copy", "cos", "createFont",
rob@100 7676 "createGraphics", "createImage", "cursor", "curve", "curveDetail",
rob@100 7677 "curvePoint", "curveTangent", "curveTightness", "curveVertex", "day",
rob@100 7678 "degrees", "directionalLight", "disableContextMenu",
rob@100 7679 "dist", "draw", "ellipse", "ellipseMode", "emissive", "enableContextMenu",
rob@100 7680 "endCamera", "endDraw", "endShape", "exit", "exp", "expand", "externals",
rob@100 7681 "fill", "filter", "floor", "focused", "frameCount", "frameRate", "frustum",
rob@100 7682 "get", "glyphLook", "glyphTable", "green", "height", "hex", "hint", "hour",
rob@100 7683 "hue", "image", "imageMode", "intersect", "join", "key",
rob@100 7684 "keyCode", "keyPressed", "keyReleased", "keyTyped", "lerp", "lerpColor",
rob@100 7685 "lightFalloff", "lights", "lightSpecular", "line", "link", "loadBytes",
rob@100 7686 "loadFont", "loadGlyphs", "loadImage", "loadPixels", "loadShape", "loadXML",
rob@100 7687 "loadStrings", "log", "loop", "mag", "map", "match", "matchAll", "max",
rob@100 7688 "millis", "min", "minute", "mix", "modelX", "modelY", "modelZ", "modes",
rob@100 7689 "month", "mouseButton", "mouseClicked", "mouseDragged", "mouseMoved",
rob@100 7690 "mouseOut", "mouseOver", "mousePressed", "mouseReleased", "mouseScroll",
rob@100 7691 "mouseScrolled", "mouseX", "mouseY", "name", "nf", "nfc", "nfp", "nfs",
rob@100 7692 "noCursor", "noFill", "noise", "noiseDetail", "noiseSeed", "noLights",
rob@100 7693 "noLoop", "norm", "normal", "noSmooth", "noStroke", "noTint", "ortho",
rob@100 7694 "param", "parseBoolean", "parseByte", "parseChar", "parseFloat",
rob@100 7695 "parseInt", "peg", "perspective", "PImage", "pixels", "PMatrix2D",
rob@100 7696 "PMatrix3D", "PMatrixStack", "pmouseX", "pmouseY", "point",
rob@100 7697 "pointLight", "popMatrix", "popStyle", "pow", "print", "printCamera",
rob@100 7698 "println", "printMatrix", "printProjection", "PShape", "PShapeSVG",
rob@100 7699 "pushMatrix", "pushStyle", "quad", "radians", "random", "randomGaussian",
rob@100 7700 "randomSeed", "rect", "rectMode", "red", "redraw", "requestImage",
rob@100 7701 "resetMatrix", "reverse", "rotate", "rotateX", "rotateY", "rotateZ",
rob@100 7702 "round", "saturation", "save", "saveFrame", "saveStrings", "scale",
rob@100 7703 "screenX", "screenY", "screenZ", "second", "set", "setup", "shape",
rob@100 7704 "shapeMode", "shared", "shearX", "shearY", "shininess", "shorten", "sin", "size", "smooth",
rob@100 7705 "sort", "specular", "sphere", "sphereDetail", "splice", "split",
rob@100 7706 "splitTokens", "spotLight", "sq", "sqrt", "status", "str", "stroke",
rob@100 7707 "strokeCap", "strokeJoin", "strokeWeight", "subset", "tan", "text",
rob@100 7708 "textAlign", "textAscent", "textDescent", "textFont", "textLeading",
rob@100 7709 "textMode", "textSize", "texture", "textureMode", "textWidth", "tint", "toImageData",
rob@100 7710 "touchCancel", "touchEnd", "touchMove", "touchStart", "translate", "transform",
rob@100 7711 "triangle", "trim", "unbinary", "unhex", "updatePixels", "use3DContext",
rob@100 7712 "vertex", "width", "XMLElement", "XML", "year", "__contains", "__equals",
rob@100 7713 "__equalsIgnoreCase", "__frameRate", "__hashCode", "__int_cast",
rob@100 7714 "__instanceof", "__keyPressed", "__mousePressed", "__printStackTrace",
rob@100 7715 "__replace", "__replaceAll", "__replaceFirst", "__toCharArray", "__split",
rob@100 7716 "__codePointAt", "__startsWith", "__endsWith", "__matches"];
rob@100 7717
rob@100 7718 // custom functions and properties are added here
rob@100 7719 if(aFunctions) {
rob@100 7720 Object.keys(aFunctions).forEach(function(name) {
rob@100 7721 names.push(name);
rob@100 7722 });
rob@100 7723 }
rob@100 7724
rob@100 7725 // custom libraries that were attached to Processing
rob@100 7726 var members = {};
rob@100 7727 var i, l;
rob@100 7728 for (i = 0, l = names.length; i < l ; ++i) {
rob@100 7729 members[names[i]] = null;
rob@100 7730 }
rob@100 7731 for (var lib in Processing.lib) {
rob@100 7732 if (Processing.lib.hasOwnProperty(lib)) {
rob@100 7733 if (Processing.lib[lib].exports) {
rob@100 7734 var exportedNames = Processing.lib[lib].exports;
rob@100 7735 for (i = 0, l = exportedNames.length; i < l; ++i) {
rob@100 7736 members[exportedNames[i]] = null;
rob@100 7737 }
rob@100 7738 }
rob@100 7739 }
rob@100 7740 }
rob@100 7741 return members;
rob@100 7742 }
rob@100 7743
rob@100 7744 /*
rob@100 7745
rob@100 7746 Parser converts Java-like syntax into JavaScript.
rob@100 7747 Creates an Abstract Syntax Tree -- "Light AST" from the Java-like code.
rob@100 7748
rob@100 7749 It is an object tree. The root object is created from the AstRoot class, which contains statements.
rob@100 7750
rob@100 7751 A statement object can be of type: AstForStatement, AstCatchStatement, AstPrefixStatement, AstMethod, AstClass,
rob@100 7752 AstInterface, AstFunction, AstStatementBlock and AstLabel.
rob@100 7753
rob@100 7754 AstPrefixStatement can be a statement of type: if, switch, while, with, do, else, finally, return, throw, try, break, and continue.
rob@100 7755
rob@100 7756 These object's toString function returns the JavaScript code for the statement.
rob@100 7757
rob@100 7758 Any processing calls need "processing." prepended to them.
rob@100 7759
rob@100 7760 Similarly, calls from inside classes need "$this_1.", prepended to them,
rob@100 7761 with 1 being the depth level for inner classes.
rob@100 7762 This includes members passed down from inheritance.
rob@100 7763
rob@100 7764 The resulting code is then eval'd and run.
rob@100 7765
rob@100 7766 */
rob@100 7767
rob@100 7768 function parseProcessing(code) {
rob@100 7769 var globalMembers = getGlobalMembers();
rob@100 7770
rob@100 7771 // masks parentheses, brackets and braces with '"A5"'
rob@100 7772 // where A is the bracket type, and 5 is the index in an array containing all brackets split into atoms
rob@100 7773 // 'while(true){}' -> 'while"B1""A2"'
rob@100 7774 // parentheses() = B, brackets[] = C and braces{} = A
rob@100 7775 function splitToAtoms(code) {
rob@100 7776 var atoms = [];
rob@100 7777 var items = code.split(/([\{\[\(\)\]\}])/);
rob@100 7778 var result = items[0];
rob@100 7779
rob@100 7780 var stack = [];
rob@100 7781 for(var i=1; i < items.length; i += 2) {
rob@100 7782 var item = items[i];
rob@100 7783 if(item === '[' || item === '{' || item === '(') {
rob@100 7784 stack.push(result); result = item;
rob@100 7785 } else if(item === ']' || item === '}' || item === ')') {
rob@100 7786 var kind = item === '}' ? 'A' : item === ')' ? 'B' : 'C';
rob@100 7787 var index = atoms.length; atoms.push(result + item);
rob@100 7788 result = stack.pop() + '"' + kind + (index + 1) + '"';
rob@100 7789 }
rob@100 7790 result += items[i + 1];
rob@100 7791 }
rob@100 7792 atoms.unshift(result);
rob@100 7793 return atoms;
rob@100 7794 }
rob@100 7795
rob@100 7796 // replaces strings and regexs keyed by index with an array of strings
rob@100 7797 function injectStrings(code, strings) {
rob@100 7798 return code.replace(/'(\d+)'/g, function(all, index) {
rob@100 7799 var val = strings[index];
rob@100 7800 if(val.charAt(0) === "/") {
rob@100 7801 return val;
rob@100 7802 }
rob@100 7803 return (/^'((?:[^'\\\n])|(?:\\.[0-9A-Fa-f]*))'$/).test(val) ? "(new $p.Character(" + val + "))" : val;
rob@100 7804 });
rob@100 7805 }
rob@100 7806
rob@100 7807 // trims off leading and trailing spaces
rob@100 7808 // returns an object. object.left, object.middle, object.right, object.untrim
rob@100 7809 function trimSpaces(string) {
rob@100 7810 var m1 = /^\s*/.exec(string), result;
rob@100 7811 if(m1[0].length === string.length) {
rob@100 7812 result = {left: m1[0], middle: "", right: ""};
rob@100 7813 } else {
rob@100 7814 var m2 = /\s*$/.exec(string);
rob@100 7815 result = {left: m1[0], middle: string.substring(m1[0].length, m2.index), right: m2[0]};
rob@100 7816 }
rob@100 7817 result.untrim = function(t) { return this.left + t + this.right; };
rob@100 7818 return result;
rob@100 7819 }
rob@100 7820
rob@100 7821 // simple trim of leading and trailing spaces
rob@100 7822 function trim(string) {
rob@100 7823 return string.replace(/^\s+/,'').replace(/\s+$/,'');
rob@100 7824 }
rob@100 7825
rob@100 7826 function appendToLookupTable(table, array) {
rob@100 7827 for(var i=0,l=array.length;i<l;++i) {
rob@100 7828 table[array[i]] = null;
rob@100 7829 }
rob@100 7830 return table;
rob@100 7831 }
rob@100 7832
rob@100 7833 function isLookupTableEmpty(table) {
rob@100 7834 for(var i in table) {
rob@100 7835 if(table.hasOwnProperty(i)) {
rob@100 7836 return false;
rob@100 7837 }
rob@100 7838 }
rob@100 7839 return true;
rob@100 7840 }
rob@100 7841
rob@100 7842 function getAtomIndex(templ) { return templ.substring(2, templ.length - 1); }
rob@100 7843
rob@100 7844 // remove carriage returns "\r"
rob@100 7845 var codeWoExtraCr = code.replace(/\r\n?|\n\r/g, "\n");
rob@100 7846
rob@100 7847 // masks strings and regexs with "'5'", where 5 is the index in an array containing all strings and regexs
rob@100 7848 // also removes all comments
rob@100 7849 var strings = [];
rob@100 7850 var codeWoStrings = codeWoExtraCr.replace(/("(?:[^"\\\n]|\\.)*")|('(?:[^'\\\n]|\\.)*')|(([\[\(=|&!\^:?]\s*)(\/(?![*\/])(?:[^\/\\\n]|\\.)*\/[gim]*)\b)|(\/\/[^\n]*\n)|(\/\*(?:(?!\*\/)(?:.|\n))*\*\/)/g,
rob@100 7851 function(all, quoted, aposed, regexCtx, prefix, regex, singleComment, comment) {
rob@100 7852 var index;
rob@100 7853 if(quoted || aposed) { // replace strings
rob@100 7854 index = strings.length; strings.push(all);
rob@100 7855 return "'" + index + "'";
rob@100 7856 }
rob@100 7857 if(regexCtx) { // replace RegExps
rob@100 7858 index = strings.length; strings.push(regex);
rob@100 7859 return prefix + "'" + index + "'";
rob@100 7860 }
rob@100 7861 // kill comments
rob@100 7862 return comment !== "" ? " " : "\n";
rob@100 7863 });
rob@100 7864
rob@100 7865 // protect character codes from namespace collision
rob@100 7866 codeWoStrings = codeWoStrings.replace(/__x([0-9A-F]{4})/g, function(all, hexCode) {
rob@100 7867 // $ = __x0024
rob@100 7868 // _ = __x005F
rob@100 7869 // this protects existing character codes from conversion
rob@100 7870 // __x0024 = __x005F_x0024
rob@100 7871 return "__x005F_x" + hexCode;
rob@100 7872 });
rob@100 7873
rob@100 7874 // convert dollar sign to character code
rob@100 7875 codeWoStrings = codeWoStrings.replace(/\$/g, "__x0024");
rob@100 7876
rob@100 7877 // Remove newlines after return statements
rob@100 7878 codeWoStrings = codeWoStrings.replace(/return\s*[\n\r]+/g, "return ");
rob@100 7879
rob@100 7880 // removes generics
rob@100 7881 var genericsWereRemoved;
rob@100 7882 var codeWoGenerics = codeWoStrings;
rob@100 7883 var replaceFunc = function(all, before, types, after) {
rob@100 7884 if(!!before || !!after) {
rob@100 7885 return all;
rob@100 7886 }
rob@100 7887 genericsWereRemoved = true;
rob@100 7888 return "";
rob@100 7889 };
rob@100 7890
rob@100 7891 do {
rob@100 7892 genericsWereRemoved = false;
rob@100 7893 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);
rob@100 7894 } while (genericsWereRemoved);
rob@100 7895
rob@100 7896 var atoms = splitToAtoms(codeWoGenerics);
rob@100 7897 var replaceContext;
rob@100 7898 var declaredClasses = {}, currentClassId, classIdSeed = 0;
rob@100 7899
rob@100 7900 function addAtom(text, type) {
rob@100 7901 var lastIndex = atoms.length;
rob@100 7902 atoms.push(text);
rob@100 7903 return '"' + type + lastIndex + '"';
rob@100 7904 }
rob@100 7905
rob@100 7906 function generateClassId() {
rob@100 7907 return "class" + (++classIdSeed);
rob@100 7908 }
rob@100 7909
rob@100 7910 function appendClass(class_, classId, scopeId) {
rob@100 7911 class_.classId = classId;
rob@100 7912 class_.scopeId = scopeId;
rob@100 7913 declaredClasses[classId] = class_;
rob@100 7914 }
rob@100 7915
rob@100 7916 // functions defined below
rob@100 7917 var transformClassBody, transformInterfaceBody, transformStatementsBlock, transformStatements, transformMain, transformExpression;
rob@100 7918
rob@100 7919 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;
rob@100 7920 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;
rob@100 7921 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*)*([=,]|$)/;
rob@100 7922 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;
rob@100 7923 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*/;
rob@100 7924 var functionsRegex = /\bfunction(?:\s+([A-Za-z_$][\w$]*))?\s*("B\d+")\s*("A\d+")/g;
rob@100 7925
rob@100 7926 // This converts classes, methods and functions into atoms, and adds them to the atoms array.
rob@100 7927 // classes = E, methods = D and functions = H
rob@100 7928 function extractClassesAndMethods(code) {
rob@100 7929 var s = code;
rob@100 7930 s = s.replace(classesRegex, function(all) {
rob@100 7931 return addAtom(all, 'E');
rob@100 7932 });
rob@100 7933 s = s.replace(methodsRegex, function(all) {
rob@100 7934 return addAtom(all, 'D');
rob@100 7935 });
rob@100 7936 s = s.replace(functionsRegex, function(all) {
rob@100 7937 return addAtom(all, 'H');
rob@100 7938 });
rob@100 7939 return s;
rob@100 7940 }
rob@100 7941
rob@100 7942 // This converts constructors into atoms, and adds them to the atoms array.
rob@100 7943 // constructors = G
rob@100 7944 function extractConstructors(code, className) {
rob@100 7945 var result = code.replace(cstrsRegex, function(all, attr, name, params, throws_, body) {
rob@100 7946 if(name !== className) {
rob@100 7947 return all;
rob@100 7948 }
rob@100 7949 return addAtom(all, 'G');
rob@100 7950 });
rob@100 7951 return result;
rob@100 7952 }
rob@100 7953
rob@100 7954 // AstParam contains the name of a parameter inside a function declaration
rob@100 7955 function AstParam(name) {
rob@100 7956 this.name = name;
rob@100 7957 }
rob@100 7958 AstParam.prototype.toString = function() {
rob@100 7959 return this.name;
rob@100 7960 };
rob@100 7961 // AstParams contains an array of AstParam objects
rob@100 7962 function AstParams(params, methodArgsParam) {
rob@100 7963 this.params = params;
rob@100 7964 this.methodArgsParam = methodArgsParam;
rob@100 7965 }
rob@100 7966 AstParams.prototype.getNames = function() {
rob@100 7967 var names = [];
rob@100 7968 for(var i=0,l=this.params.length;i<l;++i) {
rob@100 7969 names.push(this.params[i].name);
rob@100 7970 }
rob@100 7971 return names;
rob@100 7972 };
rob@100 7973 AstParams.prototype.prependMethodArgs = function(body) {
rob@100 7974 if (!this.methodArgsParam) {
rob@100 7975 return body;
rob@100 7976 }
rob@100 7977 return "{\nvar " + this.methodArgsParam.name +
rob@100 7978 " = Array.prototype.slice.call(arguments, " +
rob@100 7979 this.params.length + ");\n" + body.substring(1);
rob@100 7980 };
rob@100 7981 AstParams.prototype.toString = function() {
rob@100 7982 if(this.params.length === 0) {
rob@100 7983 return "()";
rob@100 7984 }
rob@100 7985 var result = "(";
rob@100 7986 for(var i=0,l=this.params.length;i<l;++i) {
rob@100 7987 result += this.params[i] + ", ";
rob@100 7988 }
rob@100 7989 return result.substring(0, result.length - 2) + ")";
rob@100 7990 };
rob@100 7991
rob@100 7992 function transformParams(params) {
rob@100 7993 var paramsWoPars = trim(params.substring(1, params.length - 1));
rob@100 7994 var result = [], methodArgsParam = null;
rob@100 7995 if(paramsWoPars !== "") {
rob@100 7996 var paramList = paramsWoPars.split(",");
rob@100 7997 for(var i=0; i < paramList.length; ++i) {
rob@100 7998 var param = /\b([A-Za-z_$][\w$]*\b)(\s*"[ABC][\d]*")*\s*$/.exec(paramList[i]);
rob@100 7999 if (i === paramList.length - 1 && paramList[i].indexOf('...') >= 0) {
rob@100 8000 methodArgsParam = new AstParam(param[1]);
rob@100 8001 break;
rob@100 8002 }
rob@100 8003 result.push(new AstParam(param[1]));
rob@100 8004 }
rob@100 8005 }
rob@100 8006 return new AstParams(result, methodArgsParam);
rob@100 8007 }
rob@100 8008
rob@100 8009 function preExpressionTransform(expr) {
rob@100 8010 var s = expr;
rob@100 8011 // new type[] {...} --> {...}
rob@100 8012 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) {
rob@100 8013 return init;
rob@100 8014 });
rob@100 8015 // new Runnable() {...} --> "F???"
rob@100 8016 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) {
rob@100 8017 return addAtom(all, 'F');
rob@100 8018 });
rob@100 8019 // function(...) { } --> "H???"
rob@100 8020 s = s.replace(functionsRegex, function(all) {
rob@100 8021 return addAtom(all, 'H');
rob@100 8022 });
rob@100 8023 // new type[?] --> createJavaArray('type', [?])
rob@100 8024 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) {
rob@100 8025 var args = index.replace(/"C(\d+)"/g, function(all, j) { return atoms[j]; })
rob@100 8026 .replace(/\[\s*\]/g, "[null]").replace(/\s*\]\s*\[\s*/g, ", ");
rob@100 8027 var arrayInitializer = "{" + args.substring(1, args.length - 1) + "}";
rob@100 8028 var createArrayArgs = "('" + type + "', " + addAtom(arrayInitializer, 'A') + ")";
rob@100 8029 return '$p.createJavaArray' + addAtom(createArrayArgs, 'B');
rob@100 8030 });
rob@100 8031 // .length() --> .length
rob@100 8032 s = s.replace(/(\.\s*length)\s*"B\d+"/g, "$1");
rob@100 8033 // #000000 --> 0x000000
rob@100 8034 s = s.replace(/#([0-9A-Fa-f]{6})\b/g, function(all, digits) {
rob@100 8035 return "0xFF" + digits;
rob@100 8036 });
rob@100 8037 // delete (type)???, except (int)???
rob@100 8038 s = s.replace(/"B(\d+)"(\s*(?:[\w$']|"B))/g, function(all, index, next) {
rob@100 8039 var atom = atoms[index];
rob@100 8040 if(!/^\(\s*[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*\s*(?:"C\d+"\s*)*\)$/.test(atom)) {
rob@100 8041 return all;
rob@100 8042 }
rob@100 8043 if(/^\(\s*int\s*\)$/.test(atom)) {
rob@100 8044 return "(int)" + next;
rob@100 8045 }
rob@100 8046 var indexParts = atom.split(/"C(\d+)"/g);
rob@100 8047 if(indexParts.length > 1) {
rob@100 8048 // even items contains atom numbers, can check only first
rob@100 8049 if(! /^\[\s*\]$/.test(atoms[indexParts[1]])) {
rob@100 8050 return all; // fallback - not a cast
rob@100 8051 }
rob@100 8052 }
rob@100 8053 return "" + next;
rob@100 8054 });
rob@100 8055 // (int)??? -> __int_cast(???)
rob@100 8056 s = s.replace(/\(int\)([^,\]\)\}\?\:\*\+\-\/\^\|\%\&\~<\>\=]+)/g, function(all, arg) {
rob@100 8057 var trimmed = trimSpaces(arg);
rob@100 8058 return trimmed.untrim("__int_cast(" + trimmed.middle + ")");
rob@100 8059 });
rob@100 8060 // super() -> $superCstr(), super. -> $super.;
rob@100 8061 s = s.replace(/\bsuper(\s*"B\d+")/g, "$$superCstr$1").replace(/\bsuper(\s*\.)/g, "$$super$1");
rob@100 8062 // 000.43->0.43 and 0010f->10, but not 0010
rob@100 8063 s = s.replace(/\b0+((\d*)(?:\.[\d*])?(?:[eE][\-\+]?\d+)?[fF]?)\b/, function(all, numberWo0, intPart) {
rob@100 8064 if( numberWo0 === intPart) {
rob@100 8065 return all;
rob@100 8066 }
rob@100 8067 return intPart === "" ? "0" + numberWo0 : numberWo0;
rob@100 8068 });
rob@100 8069 // 3.0f -> 3.0
rob@100 8070 s = s.replace(/\b(\.?\d+\.?)[fF]\b/g, "$1");
rob@100 8071 // Weird (?) parsing errors with %
rob@100 8072 s = s.replace(/([^\s])%([^=\s])/g, "$1 % $2");
rob@100 8073 // Since frameRate() and frameRate are different things,
rob@100 8074 // we need to differentiate them somehow. So when we parse
rob@100 8075 // the Processing.js source, replace frameRate so it isn't
rob@100 8076 // confused with frameRate(), as well as keyPressed and mousePressed
rob@100 8077 s = s.replace(/\b(frameRate|keyPressed|mousePressed)\b(?!\s*"B)/g, "__$1");
rob@100 8078 // "boolean", "byte", "int", etc. => "parseBoolean", "parseByte", "parseInt", etc.
rob@100 8079 s = s.replace(/\b(boolean|byte|char|float|int)\s*"B/g, function(all, name) {
rob@100 8080 return "parse" + name.substring(0, 1).toUpperCase() + name.substring(1) + "\"B";
rob@100 8081 });
rob@100 8082 // "pixels" replacements:
rob@100 8083 // pixels[i] = c => pixels.setPixel(i,c) | pixels[i] => pixels.getPixel(i)
rob@100 8084 // pixels.length => pixels.getLength()
rob@100 8085 // pixels = ar => pixels.set(ar) | pixels => pixels.toArray()
rob@100 8086 s = s.replace(/\bpixels\b\s*(("C(\d+)")|\.length)?(\s*=(?!=)([^,\]\)\}]+))?/g,
rob@100 8087 function(all, indexOrLength, index, atomIndex, equalsPart, rightSide) {
rob@100 8088 if(index) {
rob@100 8089 var atom = atoms[atomIndex];
rob@100 8090 if(equalsPart) {
rob@100 8091 return "pixels.setPixel" + addAtom("(" +atom.substring(1, atom.length - 1) +
rob@100 8092 "," + rightSide + ")", 'B');
rob@100 8093 }
rob@100 8094 return "pixels.getPixel" + addAtom("(" + atom.substring(1, atom.length - 1) +
rob@100 8095 ")", 'B');
rob@100 8096 }
rob@100 8097 if(indexOrLength) {
rob@100 8098 // length
rob@100 8099 return "pixels.getLength" + addAtom("()", 'B');
rob@100 8100 }
rob@100 8101 if(equalsPart) {
rob@100 8102 return "pixels.set" + addAtom("(" + rightSide + ")", 'B');
rob@100 8103 }
rob@100 8104 return "pixels.toArray" + addAtom("()", 'B');
rob@100 8105 });
rob@100 8106 // Java method replacements for: replace, replaceAll, replaceFirst, equals, hashCode, etc.
rob@100 8107 // xxx.replace(yyy) -> __replace(xxx, yyy)
rob@100 8108 // "xx".replace(yyy) -> __replace("xx", yyy)
rob@100 8109 var repeatJavaReplacement;
rob@100 8110 function replacePrototypeMethods(all, subject, method, atomIndex) {
rob@100 8111 var atom = atoms[atomIndex];
rob@100 8112 repeatJavaReplacement = true;
rob@100 8113 var trimmed = trimSpaces(atom.substring(1, atom.length - 1));
rob@100 8114 return "__" + method + ( trimmed.middle === "" ? addAtom("(" + subject.replace(/\.\s*$/, "") + ")", 'B') :
rob@100 8115 addAtom("(" + subject.replace(/\.\s*$/, "") + "," + trimmed.middle + ")", 'B') );
rob@100 8116 }
rob@100 8117 do {
rob@100 8118 repeatJavaReplacement = false;
rob@100 8119 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,
rob@100 8120 replacePrototypeMethods);
rob@100 8121 } while (repeatJavaReplacement);
rob@100 8122 // xxx instanceof yyy -> __instanceof(xxx, yyy)
rob@100 8123 function replaceInstanceof(all, subject, type) {
rob@100 8124 repeatJavaReplacement = true;
rob@100 8125 return "__instanceof" + addAtom("(" + subject + ", " + type + ")", 'B');
rob@100 8126 }
rob@100 8127 do {
rob@100 8128 repeatJavaReplacement = false;
rob@100 8129 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,
rob@100 8130 replaceInstanceof);
rob@100 8131 } while (repeatJavaReplacement);
rob@100 8132 // this() -> $constr()
rob@100 8133 s = s.replace(/\bthis(\s*"B\d+")/g, "$$constr$1");
rob@100 8134
rob@100 8135 return s;
rob@100 8136 }
rob@100 8137
rob@100 8138 function AstInlineClass(baseInterfaceName, body) {
rob@100 8139 this.baseInterfaceName = baseInterfaceName;
rob@100 8140 this.body = body;
rob@100 8141 body.owner = this;
rob@100 8142 }
rob@100 8143 AstInlineClass.prototype.toString = function() {
rob@100 8144 return "new (" + this.body + ")";
rob@100 8145 };
rob@100 8146
rob@100 8147 function transformInlineClass(class_) {
rob@100 8148 var m = new RegExp(/\bnew\s*([A-Za-z_$][\w$]*\s*(?:\.\s*[A-Za-z_$][\w$]*)*)\s*"B\d+"\s*"A(\d+)"/).exec(class_);
rob@100 8149 var oldClassId = currentClassId, newClassId = generateClassId();
rob@100 8150 currentClassId = newClassId;
rob@100 8151 var uniqueClassName = m[1] + "$" + newClassId;
rob@100 8152 var inlineClass = new AstInlineClass(uniqueClassName,
rob@100 8153 transformClassBody(atoms[m[2]], uniqueClassName, "", "implements " + m[1]));
rob@100 8154 appendClass(inlineClass, newClassId, oldClassId);
rob@100 8155 currentClassId = oldClassId;
rob@100 8156 return inlineClass;
rob@100 8157 }
rob@100 8158
rob@100 8159 function AstFunction(name, params, body) {
rob@100 8160 this.name = name;
rob@100 8161 this.params = params;
rob@100 8162 this.body = body;
rob@100 8163 }
rob@100 8164 AstFunction.prototype.toString = function() {
rob@100 8165 var oldContext = replaceContext;
rob@100 8166 // saving "this." and parameters
rob@100 8167 var names = appendToLookupTable({"this":null}, this.params.getNames());
rob@100 8168 replaceContext = function (subject) {
rob@100 8169 return names.hasOwnProperty(subject.name) ? subject.name : oldContext(subject);
rob@100 8170 };
rob@100 8171 var result = "function";
rob@100 8172 if(this.name) {
rob@100 8173 result += " " + this.name;
rob@100 8174 }
rob@100 8175 var body = this.params.prependMethodArgs(this.body.toString());
rob@100 8176 result += this.params + " " + body;
rob@100 8177 replaceContext = oldContext;
rob@100 8178 return result;
rob@100 8179 };
rob@100 8180
rob@100 8181 function transformFunction(class_) {
rob@100 8182 var m = new RegExp(/\b([A-Za-z_$][\w$]*)\s*"B(\d+)"\s*"A(\d+)"/).exec(class_);
rob@100 8183 return new AstFunction( m[1] !== "function" ? m[1] : null,
rob@100 8184 transformParams(atoms[m[2]]), transformStatementsBlock(atoms[m[3]]));
rob@100 8185 }
rob@100 8186
rob@100 8187 function AstInlineObject(members) {
rob@100 8188 this.members = members;
rob@100 8189 }
rob@100 8190 AstInlineObject.prototype.toString = function() {
rob@100 8191 var oldContext = replaceContext;
rob@100 8192 replaceContext = function (subject) {
rob@100 8193 return subject.name === "this" ? "this" : oldContext(subject); // saving "this."
rob@100 8194 };
rob@100 8195 var result = "";
rob@100 8196 for(var i=0,l=this.members.length;i<l;++i) {
rob@100 8197 if(this.members[i].label) {
rob@100 8198 result += this.members[i].label + ": ";
rob@100 8199 }
rob@100 8200 result += this.members[i].value.toString() + ", ";
rob@100 8201 }
rob@100 8202 replaceContext = oldContext;
rob@100 8203 return result.substring(0, result.length - 2);
rob@100 8204 };
rob@100 8205
rob@100 8206 function transformInlineObject(obj) {
rob@100 8207 var members = obj.split(',');
rob@100 8208 for(var i=0; i < members.length; ++i) {
rob@100 8209 var label = members[i].indexOf(':');
rob@100 8210 if(label < 0) {
rob@100 8211 members[i] = { value: transformExpression(members[i]) };
rob@100 8212 } else {
rob@100 8213 members[i] = { label: trim(members[i].substring(0, label)),
rob@100 8214 value: transformExpression( trim(members[i].substring(label + 1)) ) };
rob@100 8215 }
rob@100 8216 }
rob@100 8217 return new AstInlineObject(members);
rob@100 8218 }
rob@100 8219
rob@100 8220 function expandExpression(expr) {
rob@100 8221 if(expr.charAt(0) === '(' || expr.charAt(0) === '[') {
rob@100 8222 return expr.charAt(0) + expandExpression(expr.substring(1, expr.length - 1)) + expr.charAt(expr.length - 1);
rob@100 8223 }
rob@100 8224 if(expr.charAt(0) === '{') {
rob@100 8225 if(/^\{\s*(?:[A-Za-z_$][\w$]*|'\d+')\s*:/.test(expr)) {
rob@100 8226 return "{" + addAtom(expr.substring(1, expr.length - 1), 'I') + "}";
rob@100 8227 }
rob@100 8228 return "[" + expandExpression(expr.substring(1, expr.length - 1)) + "]";
rob@100 8229 }
rob@100 8230 var trimmed = trimSpaces(expr);
rob@100 8231 var result = preExpressionTransform(trimmed.middle);
rob@100 8232 result = result.replace(/"[ABC](\d+)"/g, function(all, index) {
rob@100 8233 return expandExpression(atoms[index]);
rob@100 8234 });
rob@100 8235 return trimmed.untrim(result);
rob@100 8236 }
rob@100 8237
rob@100 8238 function replaceContextInVars(expr) {
rob@100 8239 return expr.replace(/(\.\s*)?((?:\b[A-Za-z_]|\$)[\w$]*)(\s*\.\s*([A-Za-z_$][\w$]*)(\s*\()?)?/g,
rob@100 8240 function(all, memberAccessSign, identifier, suffix, subMember, callSign) {
rob@100 8241 if(memberAccessSign) {
rob@100 8242 return all;
rob@100 8243 }
rob@100 8244 var subject = { name: identifier, member: subMember, callSign: !!callSign };
rob@100 8245 return replaceContext(subject) + (suffix === undef ? "" : suffix);
rob@100 8246 });
rob@100 8247 }
rob@100 8248
rob@100 8249 function AstExpression(expr, transforms) {
rob@100 8250 this.expr = expr;
rob@100 8251 this.transforms = transforms;
rob@100 8252 }
rob@100 8253 AstExpression.prototype.toString = function() {
rob@100 8254 var transforms = this.transforms;
rob@100 8255 var expr = replaceContextInVars(this.expr);
rob@100 8256 return expr.replace(/"!(\d+)"/g, function(all, index) {
rob@100 8257 return transforms[index].toString();
rob@100 8258 });
rob@100 8259 };
rob@100 8260
rob@100 8261 transformExpression = function(expr) {
rob@100 8262 var transforms = [];
rob@100 8263 var s = expandExpression(expr);
rob@100 8264 s = s.replace(/"H(\d+)"/g, function(all, index) {
rob@100 8265 transforms.push(transformFunction(atoms[index]));
rob@100 8266 return '"!' + (transforms.length - 1) + '"';
rob@100 8267 });
rob@100 8268 s = s.replace(/"F(\d+)"/g, function(all, index) {
rob@100 8269 transforms.push(transformInlineClass(atoms[index]));
rob@100 8270 return '"!' + (transforms.length - 1) + '"';
rob@100 8271 });
rob@100 8272 s = s.replace(/"I(\d+)"/g, function(all, index) {
rob@100 8273 transforms.push(transformInlineObject(atoms[index]));
rob@100 8274 return '"!' + (transforms.length - 1) + '"';
rob@100 8275 });
rob@100 8276
rob@100 8277 return new AstExpression(s, transforms);
rob@100 8278 };
rob@100 8279
rob@100 8280 function AstVarDefinition(name, value, isDefault) {
rob@100 8281 this.name = name;
rob@100 8282 this.value = value;
rob@100 8283 this.isDefault = isDefault;
rob@100 8284 }
rob@100 8285 AstVarDefinition.prototype.toString = function() {
rob@100 8286 return this.name + ' = ' + this.value;
rob@100 8287 };
rob@100 8288
rob@100 8289 function transformVarDefinition(def, defaultTypeValue) {
rob@100 8290 var eqIndex = def.indexOf("=");
rob@100 8291 var name, value, isDefault;
rob@100 8292 if(eqIndex < 0) {
rob@100 8293 name = def;
rob@100 8294 value = defaultTypeValue;
rob@100 8295 isDefault = true;
rob@100 8296 } else {
rob@100 8297 name = def.substring(0, eqIndex);
rob@100 8298 value = transformExpression(def.substring(eqIndex + 1));
rob@100 8299 isDefault = false;
rob@100 8300 }
rob@100 8301 return new AstVarDefinition( trim(name.replace(/(\s*"C\d+")+/g, "")),
rob@100 8302 value, isDefault);
rob@100 8303 }
rob@100 8304
rob@100 8305 function getDefaultValueForType(type) {
rob@100 8306 if(type === "int" || type === "float") {
rob@100 8307 return "0";
rob@100 8308 }
rob@100 8309 if(type === "boolean") {
rob@100 8310 return "false";
rob@100 8311 }
rob@100 8312 if(type === "color") {
rob@100 8313 return "0x00000000";
rob@100 8314 }
rob@100 8315 return "null";
rob@100 8316 }
rob@100 8317
rob@100 8318 function AstVar(definitions, varType) {
rob@100 8319 this.definitions = definitions;
rob@100 8320 this.varType = varType;
rob@100 8321 }
rob@100 8322 AstVar.prototype.getNames = function() {
rob@100 8323 var names = [];
rob@100 8324 for(var i=0,l=this.definitions.length;i<l;++i) {
rob@100 8325 names.push(this.definitions[i].name);
rob@100 8326 }
rob@100 8327 return names;
rob@100 8328 };
rob@100 8329 AstVar.prototype.toString = function() {
rob@100 8330 return "var " + this.definitions.join(",");
rob@100 8331 };
rob@100 8332 function AstStatement(expression) {
rob@100 8333 this.expression = expression;
rob@100 8334 }
rob@100 8335 AstStatement.prototype.toString = function() {
rob@100 8336 return this.expression.toString();
rob@100 8337 };
rob@100 8338
rob@100 8339 function transformStatement(statement) {
rob@100 8340 if(fieldTest.test(statement)) {
rob@100 8341 var attrAndType = attrAndTypeRegex.exec(statement);
rob@100 8342 var definitions = statement.substring(attrAndType[0].length).split(",");
rob@100 8343 var defaultTypeValue = getDefaultValueForType(attrAndType[2]);
rob@100 8344 for(var i=0; i < definitions.length; ++i) {
rob@100 8345 definitions[i] = transformVarDefinition(definitions[i], defaultTypeValue);
rob@100 8346 }
rob@100 8347 return new AstVar(definitions, attrAndType[2]);
rob@100 8348 }
rob@100 8349 return new AstStatement(transformExpression(statement));
rob@100 8350 }
rob@100 8351
rob@100 8352 function AstForExpression(initStatement, condition, step) {
rob@100 8353 this.initStatement = initStatement;
rob@100 8354 this.condition = condition;
rob@100 8355 this.step = step;
rob@100 8356 }
rob@100 8357 AstForExpression.prototype.toString = function() {
rob@100 8358 return "(" + this.initStatement + "; " + this.condition + "; " + this.step + ")";
rob@100 8359 };
rob@100 8360
rob@100 8361 function AstForInExpression(initStatement, container) {
rob@100 8362 this.initStatement = initStatement;
rob@100 8363 this.container = container;
rob@100 8364 }
rob@100 8365 AstForInExpression.prototype.toString = function() {
rob@100 8366 var init = this.initStatement.toString();
rob@100 8367 if(init.indexOf("=") >= 0) { // can be without var declaration
rob@100 8368 init = init.substring(0, init.indexOf("="));
rob@100 8369 }
rob@100 8370 return "(" + init + " in " + this.container + ")";
rob@100 8371 };
rob@100 8372
rob@100 8373 function AstForEachExpression(initStatement, container) {
rob@100 8374 this.initStatement = initStatement;
rob@100 8375 this.container = container;
rob@100 8376 }
rob@100 8377 AstForEachExpression.iteratorId = 0;
rob@100 8378 AstForEachExpression.prototype.toString = function() {
rob@100 8379 var init = this.initStatement.toString();
rob@100 8380 var iterator = "$it" + (AstForEachExpression.iteratorId++);
rob@100 8381 var variableName = init.replace(/^\s*var\s*/, "").split("=")[0];
rob@100 8382 var initIteratorAndVariable = "var " + iterator + " = new $p.ObjectIterator(" + this.container + "), " +
rob@100 8383 variableName + " = void(0)";
rob@100 8384 var nextIterationCondition = iterator + ".hasNext() && ((" +
rob@100 8385 variableName + " = " + iterator + ".next()) || true)";
rob@100 8386 return "(" + initIteratorAndVariable + "; " + nextIterationCondition + ";)";
rob@100 8387 };
rob@100 8388
rob@100 8389 function transformForExpression(expr) {
rob@100 8390 var content;
rob@100 8391 if (/\bin\b/.test(expr)) {
rob@100 8392 content = expr.substring(1, expr.length - 1).split(/\bin\b/g);
rob@100 8393 return new AstForInExpression( transformStatement(trim(content[0])),
rob@100 8394 transformExpression(content[1]));
rob@100 8395 }
rob@100 8396 if (expr.indexOf(":") >= 0 && expr.indexOf(";") < 0) {
rob@100 8397 content = expr.substring(1, expr.length - 1).split(":");
rob@100 8398 return new AstForEachExpression( transformStatement(trim(content[0])),
rob@100 8399 transformExpression(content[1]));
rob@100 8400 }
rob@100 8401 content = expr.substring(1, expr.length - 1).split(";");
rob@100 8402 return new AstForExpression( transformStatement(trim(content[0])),
rob@100 8403 transformExpression(content[1]), transformExpression(content[2]));
rob@100 8404 }
rob@100 8405
rob@100 8406 function sortByWeight(array) {
rob@100 8407 array.sort(function (a,b) {
rob@100 8408 return b.weight - a.weight;
rob@100 8409 });
rob@100 8410 }
rob@100 8411
rob@100 8412 function AstInnerInterface(name, body, isStatic) {
rob@100 8413 this.name = name;
rob@100 8414 this.body = body;
rob@100 8415 this.isStatic = isStatic;
rob@100 8416 body.owner = this;
rob@100 8417 }
rob@100 8418 AstInnerInterface.prototype.toString = function() {
rob@100 8419 return "" + this.body;
rob@100 8420 };
rob@100 8421 function AstInnerClass(name, body, isStatic) {
rob@100 8422 this.name = name;
rob@100 8423 this.body = body;
rob@100 8424 this.isStatic = isStatic;
rob@100 8425 body.owner = this;
rob@100 8426 }
rob@100 8427 AstInnerClass.prototype.toString = function() {
rob@100 8428 return "" + this.body;
rob@100 8429 };
rob@100 8430
rob@100 8431 function transformInnerClass(class_) {
rob@100 8432 var m = classesRegex.exec(class_); // 1 - attr, 2 - class|int, 3 - name, 4 - extends, 5 - implements, 6 - body
rob@100 8433 classesRegex.lastIndex = 0;
rob@100 8434 var isStatic = m[1].indexOf("static") >= 0;
rob@100 8435 var body = atoms[getAtomIndex(m[6])], innerClass;
rob@100 8436 var oldClassId = currentClassId, newClassId = generateClassId();
rob@100 8437 currentClassId = newClassId;
rob@100 8438 if(m[2] === "interface") {
rob@100 8439 innerClass = new AstInnerInterface(m[3], transformInterfaceBody(body, m[3], m[4]), isStatic);
rob@100 8440 } else {
rob@100 8441 innerClass = new AstInnerClass(m[3], transformClassBody(body, m[3], m[4], m[5]), isStatic);
rob@100 8442 }
rob@100 8443 appendClass(innerClass, newClassId, oldClassId);
rob@100 8444 currentClassId = oldClassId;
rob@100 8445 return innerClass;
rob@100 8446 }
rob@100 8447
rob@100 8448 function AstClassMethod(name, params, body, isStatic) {
rob@100 8449 this.name = name;
rob@100 8450 this.params = params;
rob@100 8451 this.body = body;
rob@100 8452 this.isStatic = isStatic;
rob@100 8453 }
rob@100 8454 AstClassMethod.prototype.toString = function(){
rob@100 8455 var paramNames = appendToLookupTable({}, this.params.getNames());
rob@100 8456 var oldContext = replaceContext;
rob@100 8457 replaceContext = function (subject) {
rob@100 8458 return paramNames.hasOwnProperty(subject.name) ? subject.name : oldContext(subject);
rob@100 8459 };
rob@100 8460 var body = this.params.prependMethodArgs(this.body.toString());
rob@100 8461 var result = "function " + this.methodId + this.params + " " + body +"\n";
rob@100 8462 replaceContext = oldContext;
rob@100 8463 return result;
rob@100 8464 };
rob@100 8465
rob@100 8466 function transformClassMethod(method) {
rob@100 8467 var m = methodsRegex.exec(method);
rob@100 8468 methodsRegex.lastIndex = 0;
rob@100 8469 var isStatic = m[1].indexOf("static") >= 0;
rob@100 8470 var body = m[6] !== ';' ? atoms[getAtomIndex(m[6])] : "{}";
rob@100 8471 return new AstClassMethod(m[3], transformParams(atoms[getAtomIndex(m[4])]),
rob@100 8472 transformStatementsBlock(body), isStatic );
rob@100 8473 }
rob@100 8474
rob@100 8475 function AstClassField(definitions, fieldType, isStatic) {
rob@100 8476 this.definitions = definitions;
rob@100 8477 this.fieldType = fieldType;
rob@100 8478 this.isStatic = isStatic;
rob@100 8479 }
rob@100 8480 AstClassField.prototype.getNames = function() {
rob@100 8481 var names = [];
rob@100 8482 for(var i=0,l=this.definitions.length;i<l;++i) {
rob@100 8483 names.push(this.definitions[i].name);
rob@100 8484 }
rob@100 8485 return names;
rob@100 8486 };
rob@100 8487 AstClassField.prototype.toString = function() {
rob@100 8488 var thisPrefix = replaceContext({ name: "[this]" });
rob@100 8489 if(this.isStatic) {
rob@100 8490 var className = this.owner.name;
rob@100 8491 var staticDeclarations = [];
rob@100 8492 for(var i=0,l=this.definitions.length;i<l;++i) {
rob@100 8493 var definition = this.definitions[i];
rob@100 8494 var name = definition.name, staticName = className + "." + name;
rob@100 8495 var declaration = "if(" + staticName + " === void(0)) {\n" +
rob@100 8496 " " + staticName + " = " + definition.value + "; }\n" +
rob@100 8497 "$p.defineProperty(" + thisPrefix + ", " +
rob@100 8498 "'" + name + "', { get: function(){return " + staticName + ";}, " +
rob@100 8499 "set: function(val){" + staticName + " = val;} });\n";
rob@100 8500 staticDeclarations.push(declaration);
rob@100 8501 }
rob@100 8502 return staticDeclarations.join("");
rob@100 8503 }
rob@100 8504 return thisPrefix + "." + this.definitions.join("; " + thisPrefix + ".");
rob@100 8505 };
rob@100 8506
rob@100 8507 function transformClassField(statement) {
rob@100 8508 var attrAndType = attrAndTypeRegex.exec(statement);
rob@100 8509 var isStatic = attrAndType[1].indexOf("static") >= 0;
rob@100 8510 var definitions = statement.substring(attrAndType[0].length).split(/,\s*/g);
rob@100 8511 var defaultTypeValue = getDefaultValueForType(attrAndType[2]);
rob@100 8512 for(var i=0; i < definitions.length; ++i) {
rob@100 8513 definitions[i] = transformVarDefinition(definitions[i], defaultTypeValue);
rob@100 8514 }
rob@100 8515 return new AstClassField(definitions, attrAndType[2], isStatic);
rob@100 8516 }
rob@100 8517
rob@100 8518 function AstConstructor(params, body) {
rob@100 8519 this.params = params;
rob@100 8520 this.body = body;
rob@100 8521 }
rob@100 8522 AstConstructor.prototype.toString = function() {
rob@100 8523 var paramNames = appendToLookupTable({}, this.params.getNames());
rob@100 8524 var oldContext = replaceContext;
rob@100 8525 replaceContext = function (subject) {
rob@100 8526 return paramNames.hasOwnProperty(subject.name) ? subject.name : oldContext(subject);
rob@100 8527 };
rob@100 8528 var prefix = "function $constr_" + this.params.params.length + this.params.toString();
rob@100 8529 var body = this.params.prependMethodArgs(this.body.toString());
rob@100 8530 if(!/\$(superCstr|constr)\b/.test(body)) {
rob@100 8531 body = "{\n$superCstr();\n" + body.substring(1);
rob@100 8532 }
rob@100 8533 replaceContext = oldContext;
rob@100 8534 return prefix + body + "\n";
rob@100 8535 };
rob@100 8536
rob@100 8537 function transformConstructor(cstr) {
rob@100 8538 var m = new RegExp(/"B(\d+)"\s*"A(\d+)"/).exec(cstr);
rob@100 8539 var params = transformParams(atoms[m[1]]);
rob@100 8540
rob@100 8541 return new AstConstructor(params, transformStatementsBlock(atoms[m[2]]));
rob@100 8542 }
rob@100 8543
rob@100 8544 function AstInterfaceBody(name, interfacesNames, methodsNames, fields, innerClasses, misc) {
rob@100 8545 var i,l;
rob@100 8546 this.name = name;
rob@100 8547 this.interfacesNames = interfacesNames;
rob@100 8548 this.methodsNames = methodsNames;
rob@100 8549 this.fields = fields;
rob@100 8550 this.innerClasses = innerClasses;
rob@100 8551 this.misc = misc;
rob@100 8552 for(i=0,l=fields.length; i<l; ++i) {
rob@100 8553 fields[i].owner = this;
rob@100 8554 }
rob@100 8555 }
rob@100 8556 AstInterfaceBody.prototype.getMembers = function(classFields, classMethods, classInners) {
rob@100 8557 if(this.owner.base) {
rob@100 8558 this.owner.base.body.getMembers(classFields, classMethods, classInners);
rob@100 8559 }
rob@100 8560 var i, j, l, m;
rob@100 8561 for(i=0,l=this.fields.length;i<l;++i) {
rob@100 8562 var fieldNames = this.fields[i].getNames();
rob@100 8563 for(j=0,m=fieldNames.length;j<m;++j) {
rob@100 8564 classFields[fieldNames[j]] = this.fields[i];
rob@100 8565 }
rob@100 8566 }
rob@100 8567 for(i=0,l=this.methodsNames.length;i<l;++i) {
rob@100 8568 var methodName = this.methodsNames[i];
rob@100 8569 classMethods[methodName] = true;
rob@100 8570 }
rob@100 8571 for(i=0,l=this.innerClasses.length;i<l;++i) {
rob@100 8572 var innerClass = this.innerClasses[i];
rob@100 8573 classInners[innerClass.name] = innerClass;
rob@100 8574 }
rob@100 8575 };
rob@100 8576 AstInterfaceBody.prototype.toString = function() {
rob@100 8577 function getScopeLevel(p) {
rob@100 8578 var i = 0;
rob@100 8579 while(p) {
rob@100 8580 ++i;
rob@100 8581 p=p.scope;
rob@100 8582 }
rob@100 8583 return i;
rob@100 8584 }
rob@100 8585
rob@100 8586 var scopeLevel = getScopeLevel(this.owner);
rob@100 8587
rob@100 8588 var className = this.name;
rob@100 8589 var staticDefinitions = "";
rob@100 8590 var metadata = "";
rob@100 8591
rob@100 8592 var thisClassFields = {}, thisClassMethods = {}, thisClassInners = {};
rob@100 8593 this.getMembers(thisClassFields, thisClassMethods, thisClassInners);
rob@100 8594
rob@100 8595 var i, l, j, m;
rob@100 8596
rob@100 8597 if (this.owner.interfaces) {
rob@100 8598 // interface name can be present, but interface is not
rob@100 8599 var resolvedInterfaces = [], resolvedInterface;
rob@100 8600 for (i = 0, l = this.interfacesNames.length; i < l; ++i) {
rob@100 8601 if (!this.owner.interfaces[i]) {
rob@100 8602 continue;
rob@100 8603 }
rob@100 8604 resolvedInterface = replaceContext({name: this.interfacesNames[i]});
rob@100 8605 resolvedInterfaces.push(resolvedInterface);
rob@100 8606 staticDefinitions += "$p.extendInterfaceMembers(" + className + ", " + resolvedInterface + ");\n";
rob@100 8607 }
rob@100 8608 metadata += className + ".$interfaces = [" + resolvedInterfaces.join(", ") + "];\n";
rob@100 8609 }
rob@100 8610 metadata += className + ".$isInterface = true;\n";
rob@100 8611 metadata += className + ".$methods = [\'" + this.methodsNames.join("\', \'") + "\'];\n";
rob@100 8612
rob@100 8613 sortByWeight(this.innerClasses);
rob@100 8614 for (i = 0, l = this.innerClasses.length; i < l; ++i) {
rob@100 8615 var innerClass = this.innerClasses[i];
rob@100 8616 if (innerClass.isStatic) {
rob@100 8617 staticDefinitions += className + "." + innerClass.name + " = " + innerClass + ";\n";
rob@100 8618 }
rob@100 8619 }
rob@100 8620
rob@100 8621 for (i = 0, l = this.fields.length; i < l; ++i) {
rob@100 8622 var field = this.fields[i];
rob@100 8623 if (field.isStatic) {
rob@100 8624 staticDefinitions += className + "." + field.definitions.join(";\n" + className + ".") + ";\n";
rob@100 8625 }
rob@100 8626 }
rob@100 8627
rob@100 8628 return "(function() {\n" +
rob@100 8629 "function " + className + "() { throw \'Unable to create the interface\'; }\n" +
rob@100 8630 staticDefinitions +
rob@100 8631 metadata +
rob@100 8632 "return " + className + ";\n" +
rob@100 8633 "})()";
rob@100 8634 };
rob@100 8635
rob@100 8636 transformInterfaceBody = function(body, name, baseInterfaces) {
rob@100 8637 var declarations = body.substring(1, body.length - 1);
rob@100 8638 declarations = extractClassesAndMethods(declarations);
rob@100 8639 declarations = extractConstructors(declarations, name);
rob@100 8640 var methodsNames = [], classes = [];
rob@100 8641 declarations = declarations.replace(/"([DE])(\d+)"/g, function(all, type, index) {
rob@100 8642 if(type === 'D') { methodsNames.push(index); }
rob@100 8643 else if(type === 'E') { classes.push(index); }
rob@100 8644 return "";
rob@100 8645 });
rob@100 8646 var fields = declarations.split(/;(?:\s*;)*/g);
rob@100 8647 var baseInterfaceNames;
rob@100 8648 var i, l;
rob@100 8649
rob@100 8650 if(baseInterfaces !== undef) {
rob@100 8651 baseInterfaceNames = baseInterfaces.replace(/^\s*extends\s+(.+?)\s*$/g, "$1").split(/\s*,\s*/g);
rob@100 8652 }
rob@100 8653
rob@100 8654 for(i = 0, l = methodsNames.length; i < l; ++i) {
rob@100 8655 var method = transformClassMethod(atoms[methodsNames[i]]);
rob@100 8656 methodsNames[i] = method.name;
rob@100 8657 }
rob@100 8658 for(i = 0, l = fields.length - 1; i < l; ++i) {
rob@100 8659 var field = trimSpaces(fields[i]);
rob@100 8660 fields[i] = transformClassField(field.middle);
rob@100 8661 }
rob@100 8662 var tail = fields.pop();
rob@100 8663 for(i = 0, l = classes.length; i < l; ++i) {
rob@100 8664 classes[i] = transformInnerClass(atoms[classes[i]]);
rob@100 8665 }
rob@100 8666
rob@100 8667 return new AstInterfaceBody(name, baseInterfaceNames, methodsNames, fields, classes, { tail: tail });
rob@100 8668 };
rob@100 8669
rob@100 8670 function AstClassBody(name, baseClassName, interfacesNames, functions, methods, fields, cstrs, innerClasses, misc) {
rob@100 8671 var i,l;
rob@100 8672 this.name = name;
rob@100 8673 this.baseClassName = baseClassName;
rob@100 8674 this.interfacesNames = interfacesNames;
rob@100 8675 this.functions = functions;
rob@100 8676 this.methods = methods;
rob@100 8677 this.fields = fields;
rob@100 8678 this.cstrs = cstrs;
rob@100 8679 this.innerClasses = innerClasses;
rob@100 8680 this.misc = misc;
rob@100 8681 for(i=0,l=fields.length; i<l; ++i) {
rob@100 8682 fields[i].owner = this;
rob@100 8683 }
rob@100 8684 }
rob@100 8685 AstClassBody.prototype.getMembers = function(classFields, classMethods, classInners) {
rob@100 8686 if(this.owner.base) {
rob@100 8687 this.owner.base.body.getMembers(classFields, classMethods, classInners);
rob@100 8688 }
rob@100 8689 var i, j, l, m;
rob@100 8690 for(i=0,l=this.fields.length;i<l;++i) {
rob@100 8691 var fieldNames = this.fields[i].getNames();
rob@100 8692 for(j=0,m=fieldNames.length;j<m;++j) {
rob@100 8693 classFields[fieldNames[j]] = this.fields[i];
rob@100 8694 }
rob@100 8695 }
rob@100 8696 for(i=0,l=this.methods.length;i<l;++i) {
rob@100 8697 var method = this.methods[i];
rob@100 8698 classMethods[method.name] = method;
rob@100 8699 }
rob@100 8700 for(i=0,l=this.innerClasses.length;i<l;++i) {
rob@100 8701 var innerClass = this.innerClasses[i];
rob@100 8702 classInners[innerClass.name] = innerClass;
rob@100 8703 }
rob@100 8704 };
rob@100 8705 AstClassBody.prototype.toString = function() {
rob@100 8706 function getScopeLevel(p) {
rob@100 8707 var i = 0;
rob@100 8708 while(p) {
rob@100 8709 ++i;
rob@100 8710 p=p.scope;
rob@100 8711 }
rob@100 8712 return i;
rob@100 8713 }
rob@100 8714
rob@100 8715 var scopeLevel = getScopeLevel(this.owner);
rob@100 8716
rob@100 8717 var selfId = "$this_" + scopeLevel;
rob@100 8718 var className = this.name;
rob@100 8719 var result = "var " + selfId + " = this;\n";
rob@100 8720 var staticDefinitions = "";
rob@100 8721 var metadata = "";
rob@100 8722
rob@100 8723 var thisClassFields = {}, thisClassMethods = {}, thisClassInners = {};
rob@100 8724 this.getMembers(thisClassFields, thisClassMethods, thisClassInners);
rob@100 8725
rob@100 8726 var oldContext = replaceContext;
rob@100 8727 replaceContext = function (subject) {
rob@100 8728 var name = subject.name;
rob@100 8729 if(name === "this") {
rob@100 8730 // returns "$this_N.$self" pointer instead of "this" in cases:
rob@100 8731 // "this()", "this.XXX()", "this", but not for "this.XXX"
rob@100 8732 return subject.callSign || !subject.member ? selfId + ".$self" : selfId;
rob@100 8733 }
rob@100 8734 if(thisClassFields.hasOwnProperty(name)) {
rob@100 8735 return thisClassFields[name].isStatic ? className + "." + name : selfId + "." + name;
rob@100 8736 }
rob@100 8737 if(thisClassInners.hasOwnProperty(name)) {
rob@100 8738 return selfId + "." + name;
rob@100 8739 }
rob@100 8740 if(thisClassMethods.hasOwnProperty(name)) {
rob@100 8741 return thisClassMethods[name].isStatic ? className + "." + name : selfId + ".$self." + name;
rob@100 8742 }
rob@100 8743 return oldContext(subject);
rob@100 8744 };
rob@100 8745
rob@100 8746 var resolvedBaseClassName;
rob@100 8747 if (this.baseClassName) {
rob@100 8748 resolvedBaseClassName = oldContext({name: this.baseClassName});
rob@100 8749 result += "var $super = { $upcast: " + selfId + " };\n";
rob@100 8750 result += "function $superCstr(){" + resolvedBaseClassName +
rob@100 8751 ".apply($super,arguments);if(!('$self' in $super)) $p.extendClassChain($super)}\n";
rob@100 8752 metadata += className + ".$base = " + resolvedBaseClassName + ";\n";
rob@100 8753 } else {
rob@100 8754 result += "function $superCstr(){$p.extendClassChain("+ selfId +")}\n";
rob@100 8755 }
rob@100 8756
rob@100 8757 if (this.owner.base) {
rob@100 8758 // base class name can be present, but class is not
rob@100 8759 staticDefinitions += "$p.extendStaticMembers(" + className + ", " + resolvedBaseClassName + ");\n";
rob@100 8760 }
rob@100 8761
rob@100 8762 var i, l, j, m;
rob@100 8763
rob@100 8764 if (this.owner.interfaces) {
rob@100 8765 // interface name can be present, but interface is not
rob@100 8766 var resolvedInterfaces = [], resolvedInterface;
rob@100 8767 for (i = 0, l = this.interfacesNames.length; i < l; ++i) {
rob@100 8768 if (!this.owner.interfaces[i]) {
rob@100 8769 continue;
rob@100 8770 }
rob@100 8771 resolvedInterface = oldContext({name: this.interfacesNames[i]});
rob@100 8772 resolvedInterfaces.push(resolvedInterface);
rob@100 8773 staticDefinitions += "$p.extendInterfaceMembers(" + className + ", " + resolvedInterface + ");\n";
rob@100 8774 }
rob@100 8775 metadata += className + ".$interfaces = [" + resolvedInterfaces.join(", ") + "];\n";
rob@100 8776 }
rob@100 8777
rob@100 8778 if (this.functions.length > 0) {
rob@100 8779 result += this.functions.join('\n') + '\n';
rob@100 8780 }
rob@100 8781
rob@100 8782 sortByWeight(this.innerClasses);
rob@100 8783 for (i = 0, l = this.innerClasses.length; i < l; ++i) {
rob@100 8784 var innerClass = this.innerClasses[i];
rob@100 8785 if (innerClass.isStatic) {
rob@100 8786 staticDefinitions += className + "." + innerClass.name + " = " + innerClass + ";\n";
rob@100 8787 result += selfId + "." + innerClass.name + " = " + className + "." + innerClass.name + ";\n";
rob@100 8788 } else {
rob@100 8789 result += selfId + "." + innerClass.name + " = " + innerClass + ";\n";
rob@100 8790 }
rob@100 8791 }
rob@100 8792
rob@100 8793 for (i = 0, l = this.fields.length; i < l; ++i) {
rob@100 8794 var field = this.fields[i];
rob@100 8795 if (field.isStatic) {
rob@100 8796 staticDefinitions += className + "." + field.definitions.join(";\n" + className + ".") + ";\n";
rob@100 8797 for (j = 0, m = field.definitions.length; j < m; ++j) {
rob@100 8798 var fieldName = field.definitions[j].name, staticName = className + "." + fieldName;
rob@100 8799 result += "$p.defineProperty(" + selfId + ", '" + fieldName + "', {" +
rob@100 8800 "get: function(){return " + staticName + "}, " +
rob@100 8801 "set: function(val){" + staticName + " = val}});\n";
rob@100 8802 }
rob@100 8803 } else {
rob@100 8804 result += selfId + "." + field.definitions.join(";\n" + selfId + ".") + ";\n";
rob@100 8805 }
rob@100 8806 }
rob@100 8807 var methodOverloads = {};
rob@100 8808 for (i = 0, l = this.methods.length; i < l; ++i) {
rob@100 8809 var method = this.methods[i];
rob@100 8810 var overload = methodOverloads[method.name];
rob@100 8811 var methodId = method.name + "$" + method.params.params.length;
rob@100 8812 var hasMethodArgs = !!method.params.methodArgsParam;
rob@100 8813 if (overload) {
rob@100 8814 ++overload;
rob@100 8815 methodId += "_" + overload;
rob@100 8816 } else {
rob@100 8817 overload = 1;
rob@100 8818 }
rob@100 8819 method.methodId = methodId;
rob@100 8820 methodOverloads[method.name] = overload;
rob@100 8821 if (method.isStatic) {
rob@100 8822 staticDefinitions += method;
rob@100 8823 staticDefinitions += "$p.addMethod(" + className + ", '" + method.name + "', " + methodId + ", " + hasMethodArgs + ");\n";
rob@100 8824 result += "$p.addMethod(" + selfId + ", '" + method.name + "', " + methodId + ", " + hasMethodArgs + ");\n";
rob@100 8825 } else {
rob@100 8826 result += method;
rob@100 8827 result += "$p.addMethod(" + selfId + ", '" + method.name + "', " + methodId + ", " + hasMethodArgs + ");\n";
rob@100 8828 }
rob@100 8829 }
rob@100 8830 result += trim(this.misc.tail);
rob@100 8831
rob@100 8832 if (this.cstrs.length > 0) {
rob@100 8833 result += this.cstrs.join('\n') + '\n';
rob@100 8834 }
rob@100 8835
rob@100 8836 result += "function $constr() {\n";
rob@100 8837 var cstrsIfs = [];
rob@100 8838 for (i = 0, l = this.cstrs.length; i < l; ++i) {
rob@100 8839 var paramsLength = this.cstrs[i].params.params.length;
rob@100 8840 var methodArgsPresent = !!this.cstrs[i].params.methodArgsParam;
rob@100 8841 cstrsIfs.push("if(arguments.length " + (methodArgsPresent ? ">=" : "===") +
rob@100 8842 " " + paramsLength + ") { " +
rob@100 8843 "$constr_" + paramsLength + ".apply(" + selfId + ", arguments); }");
rob@100 8844 }
rob@100 8845 if(cstrsIfs.length > 0) {
rob@100 8846 result += cstrsIfs.join(" else ") + " else ";
rob@100 8847 }
rob@100 8848 // ??? add check if length is 0, otherwise fail
rob@100 8849 result += "$superCstr();\n}\n";
rob@100 8850 result += "$constr.apply(null, arguments);\n";
rob@100 8851
rob@100 8852 replaceContext = oldContext;
rob@100 8853 return "(function() {\n" +
rob@100 8854 "function " + className + "() {\n" + result + "}\n" +
rob@100 8855 staticDefinitions +
rob@100 8856 metadata +
rob@100 8857 "return " + className + ";\n" +
rob@100 8858 "})()";
rob@100 8859 };
rob@100 8860
rob@100 8861 transformClassBody = function(body, name, baseName, interfaces) {
rob@100 8862 var declarations = body.substring(1, body.length - 1);
rob@100 8863 declarations = extractClassesAndMethods(declarations);
rob@100 8864 declarations = extractConstructors(declarations, name);
rob@100 8865 var methods = [], classes = [], cstrs = [], functions = [];
rob@100 8866 declarations = declarations.replace(/"([DEGH])(\d+)"/g, function(all, type, index) {
rob@100 8867 if(type === 'D') { methods.push(index); }
rob@100 8868 else if(type === 'E') { classes.push(index); }
rob@100 8869 else if(type === 'H') { functions.push(index); }
rob@100 8870 else { cstrs.push(index); }
rob@100 8871 return "";
rob@100 8872 });
rob@100 8873 var fields = declarations.replace(/^(?:\s*;)+/, "").split(/;(?:\s*;)*/g);
rob@100 8874 var baseClassName, interfacesNames;
rob@100 8875 var i;
rob@100 8876
rob@100 8877 if(baseName !== undef) {
rob@100 8878 baseClassName = baseName.replace(/^\s*extends\s+([A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*)\s*$/g, "$1");
rob@100 8879 }
rob@100 8880
rob@100 8881 if(interfaces !== undef) {
rob@100 8882 interfacesNames = interfaces.replace(/^\s*implements\s+(.+?)\s*$/g, "$1").split(/\s*,\s*/g);
rob@100 8883 }
rob@100 8884
rob@100 8885 for(i = 0; i < functions.length; ++i) {
rob@100 8886 functions[i] = transformFunction(atoms[functions[i]]);
rob@100 8887 }
rob@100 8888 for(i = 0; i < methods.length; ++i) {
rob@100 8889 methods[i] = transformClassMethod(atoms[methods[i]]);
rob@100 8890 }
rob@100 8891 for(i = 0; i < fields.length - 1; ++i) {
rob@100 8892 var field = trimSpaces(fields[i]);
rob@100 8893 fields[i] = transformClassField(field.middle);
rob@100 8894 }
rob@100 8895 var tail = fields.pop();
rob@100 8896 for(i = 0; i < cstrs.length; ++i) {
rob@100 8897 cstrs[i] = transformConstructor(atoms[cstrs[i]]);
rob@100 8898 }
rob@100 8899 for(i = 0; i < classes.length; ++i) {
rob@100 8900 classes[i] = transformInnerClass(atoms[classes[i]]);
rob@100 8901 }
rob@100 8902
rob@100 8903 return new AstClassBody(name, baseClassName, interfacesNames, functions, methods, fields, cstrs,
rob@100 8904 classes, { tail: tail });
rob@100 8905 };
rob@100 8906
rob@100 8907 function AstInterface(name, body) {
rob@100 8908 this.name = name;
rob@100 8909 this.body = body;
rob@100 8910 body.owner = this;
rob@100 8911 }
rob@100 8912 AstInterface.prototype.toString = function() {
rob@100 8913 return "var " + this.name + " = " + this.body + ";\n" +
rob@100 8914 "$p." + this.name + " = " + this.name + ";\n";
rob@100 8915 };
rob@100 8916 function AstClass(name, body) {
rob@100 8917 this.name = name;
rob@100 8918 this.body = body;
rob@100 8919 body.owner = this;
rob@100 8920 }
rob@100 8921 AstClass.prototype.toString = function() {
rob@100 8922 return "var " + this.name + " = " + this.body + ";\n" +
rob@100 8923 "$p." + this.name + " = " + this.name + ";\n";
rob@100 8924 };
rob@100 8925
rob@100 8926 function transformGlobalClass(class_) {
rob@100 8927 var m = classesRegex.exec(class_); // 1 - attr, 2 - class|int, 3 - name, 4 - extends, 5 - implements, 6 - body
rob@100 8928 classesRegex.lastIndex = 0;
rob@100 8929 var body = atoms[getAtomIndex(m[6])];
rob@100 8930 var oldClassId = currentClassId, newClassId = generateClassId();
rob@100 8931 currentClassId = newClassId;
rob@100 8932 var globalClass;
rob@100 8933 if(m[2] === "interface") {
rob@100 8934 globalClass = new AstInterface(m[3], transformInterfaceBody(body, m[3], m[4]) );
rob@100 8935 } else {
rob@100 8936 globalClass = new AstClass(m[3], transformClassBody(body, m[3], m[4], m[5]) );
rob@100 8937 }
rob@100 8938 appendClass(globalClass, newClassId, oldClassId);
rob@100 8939 currentClassId = oldClassId;
rob@100 8940 return globalClass;
rob@100 8941 }
rob@100 8942
rob@100 8943 function AstMethod(name, params, body) {
rob@100 8944 this.name = name;
rob@100 8945 this.params = params;
rob@100 8946 this.body = body;
rob@100 8947 }
rob@100 8948 AstMethod.prototype.toString = function(){
rob@100 8949 var paramNames = appendToLookupTable({}, this.params.getNames());
rob@100 8950 var oldContext = replaceContext;
rob@100 8951 replaceContext = function (subject) {
rob@100 8952 return paramNames.hasOwnProperty(subject.name) ? subject.name : oldContext(subject);
rob@100 8953 };
rob@100 8954 var body = this.params.prependMethodArgs(this.body.toString());
rob@100 8955 var result = "function " + this.name + this.params + " " + body + "\n" +
rob@100 8956 "$p." + this.name + " = " + this.name + ";\n" +
rob@100 8957 this.name + " = " + this.name + ".bind($p);";
rob@100 8958 // "$p." + this.name + " = " + this.name + ";";
rob@100 8959 replaceContext = oldContext;
rob@100 8960 return result;
rob@100 8961 };
rob@100 8962
rob@100 8963 function transformGlobalMethod(method) {
rob@100 8964 var m = methodsRegex.exec(method);
rob@100 8965 var result =
rob@100 8966 methodsRegex.lastIndex = 0;
rob@100 8967 return new AstMethod(m[3], transformParams(atoms[getAtomIndex(m[4])]),
rob@100 8968 transformStatementsBlock(atoms[getAtomIndex(m[6])]));
rob@100 8969 }
rob@100 8970
rob@100 8971 function preStatementsTransform(statements) {
rob@100 8972 var s = statements;
rob@100 8973 // turns multiple catch blocks into one, because we have no way to properly get into them anyway.
rob@100 8974 s = s.replace(/\b(catch\s*"B\d+"\s*"A\d+")(\s*catch\s*"B\d+"\s*"A\d+")+/g, "$1");
rob@100 8975 return s;
rob@100 8976 }
rob@100 8977
rob@100 8978 function AstForStatement(argument, misc) {
rob@100 8979 this.argument = argument;
rob@100 8980 this.misc = misc;
rob@100 8981 }
rob@100 8982 AstForStatement.prototype.toString = function() {
rob@100 8983 return this.misc.prefix + this.argument.toString();
rob@100 8984 };
rob@100 8985 function AstCatchStatement(argument, misc) {
rob@100 8986 this.argument = argument;
rob@100 8987 this.misc = misc;
rob@100 8988 }
rob@100 8989 AstCatchStatement.prototype.toString = function() {
rob@100 8990 return this.misc.prefix + this.argument.toString();
rob@100 8991 };
rob@100 8992 function AstPrefixStatement(name, argument, misc) {
rob@100 8993 this.name = name;
rob@100 8994 this.argument = argument;
rob@100 8995 this.misc = misc;
rob@100 8996 }
rob@100 8997 AstPrefixStatement.prototype.toString = function() {
rob@100 8998 var result = this.misc.prefix;
rob@100 8999 if(this.argument !== undef) {
rob@100 9000 result += this.argument.toString();
rob@100 9001 }
rob@100 9002 return result;
rob@100 9003 };
rob@100 9004 function AstSwitchCase(expr) {
rob@100 9005 this.expr = expr;
rob@100 9006 }
rob@100 9007 AstSwitchCase.prototype.toString = function() {
rob@100 9008 return "case " + this.expr + ":";
rob@100 9009 };
rob@100 9010 function AstLabel(label) {
rob@100 9011 this.label = label;
rob@100 9012 }
rob@100 9013 AstLabel.prototype.toString = function() {
rob@100 9014 return this.label;
rob@100 9015 };
rob@100 9016
rob@100 9017 transformStatements = function(statements, transformMethod, transformClass) {
rob@100 9018 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);
rob@100 9019 var res = [];
rob@100 9020 statements = preStatementsTransform(statements);
rob@100 9021 var lastIndex = 0, m, space;
rob@100 9022 // m contains the matches from the nextStatement regexp, null if there are no matches.
rob@100 9023 // nextStatement.exec starts searching at nextStatement.lastIndex.
rob@100 9024 while((m = nextStatement.exec(statements)) !== null) {
rob@100 9025 if(m[1] !== undef) { // catch, for ...
rob@100 9026 var i = statements.lastIndexOf('"B', nextStatement.lastIndex);
rob@100 9027 var statementsPrefix = statements.substring(lastIndex, i);
rob@100 9028 if(m[1] === "for") {
rob@100 9029 res.push(new AstForStatement(transformForExpression(atoms[m[2]]),
rob@100 9030 { prefix: statementsPrefix }) );
rob@100 9031 } else if(m[1] === "catch") {
rob@100 9032 res.push(new AstCatchStatement(transformParams(atoms[m[2]]),
rob@100 9033 { prefix: statementsPrefix }) );
rob@100 9034 } else {
rob@100 9035 res.push(new AstPrefixStatement(m[1], transformExpression(atoms[m[2]]),
rob@100 9036 { prefix: statementsPrefix }) );
rob@100 9037 }
rob@100 9038 } else if(m[3] !== undef) { // do, else, ...
rob@100 9039 res.push(new AstPrefixStatement(m[3], undef,
rob@100 9040 { prefix: statements.substring(lastIndex, nextStatement.lastIndex) }) );
rob@100 9041 } else if(m[4] !== undef) { // block, class and methods
rob@100 9042 space = statements.substring(lastIndex, nextStatement.lastIndex - m[4].length);
rob@100 9043 if(trim(space).length !== 0) { continue; } // avoiding new type[] {} construct
rob@100 9044 res.push(space);
rob@100 9045 var kind = m[4].charAt(1), atomIndex = m[5];
rob@100 9046 if(kind === 'D') {
rob@100 9047 res.push(transformMethod(atoms[atomIndex]));
rob@100 9048 } else if(kind === 'E') {
rob@100 9049 res.push(transformClass(atoms[atomIndex]));
rob@100 9050 } else if(kind === 'H') {
rob@100 9051 res.push(transformFunction(atoms[atomIndex]));
rob@100 9052 } else {
rob@100 9053 res.push(transformStatementsBlock(atoms[atomIndex]));
rob@100 9054 }
rob@100 9055 } else if(m[6] !== undef) { // switch case
rob@100 9056 res.push(new AstSwitchCase(transformExpression(trim(m[7]))));
rob@100 9057 } else if(m[8] !== undef) { // label
rob@100 9058 space = statements.substring(lastIndex, nextStatement.lastIndex - m[8].length);
rob@100 9059 if(trim(space).length !== 0) { continue; } // avoiding ?: construct
rob@100 9060 res.push(new AstLabel(statements.substring(lastIndex, nextStatement.lastIndex)) );
rob@100 9061 } else { // semicolon
rob@100 9062 var statement = trimSpaces(statements.substring(lastIndex, nextStatement.lastIndex - 1));
rob@100 9063 res.push(statement.left);
rob@100 9064 res.push(transformStatement(statement.middle));
rob@100 9065 res.push(statement.right + ";");
rob@100 9066 }
rob@100 9067 lastIndex = nextStatement.lastIndex;
rob@100 9068 }
rob@100 9069 var statementsTail = trimSpaces(statements.substring(lastIndex));
rob@100 9070 res.push(statementsTail.left);
rob@100 9071 if(statementsTail.middle !== "") {
rob@100 9072 res.push(transformStatement(statementsTail.middle));
rob@100 9073 res.push(";" + statementsTail.right);
rob@100 9074 }
rob@100 9075 return res;
rob@100 9076 };
rob@100 9077
rob@100 9078 function getLocalNames(statements) {
rob@100 9079 var localNames = [];
rob@100 9080 for(var i=0,l=statements.length;i<l;++i) {
rob@100 9081 var statement = statements[i];
rob@100 9082 if(statement instanceof AstVar) {
rob@100 9083 localNames = localNames.concat(statement.getNames());
rob@100 9084 } else if(statement instanceof AstForStatement &&
rob@100 9085 statement.argument.initStatement instanceof AstVar) {
rob@100 9086 localNames = localNames.concat(statement.argument.initStatement.getNames());
rob@100 9087 } else if(statement instanceof AstInnerInterface || statement instanceof AstInnerClass ||
rob@100 9088 statement instanceof AstInterface || statement instanceof AstClass ||
rob@100 9089 statement instanceof AstMethod || statement instanceof AstFunction) {
rob@100 9090 localNames.push(statement.name);
rob@100 9091 }
rob@100 9092 }
rob@100 9093 return appendToLookupTable({}, localNames);
rob@100 9094 }
rob@100 9095
rob@100 9096 function AstStatementsBlock(statements) {
rob@100 9097 this.statements = statements;
rob@100 9098 }
rob@100 9099 AstStatementsBlock.prototype.toString = function() {
rob@100 9100 var localNames = getLocalNames(this.statements);
rob@100 9101 var oldContext = replaceContext;
rob@100 9102
rob@100 9103 // replacing context only when necessary
rob@100 9104 if(!isLookupTableEmpty(localNames)) {
rob@100 9105 replaceContext = function (subject) {
rob@100 9106 return localNames.hasOwnProperty(subject.name) ? subject.name : oldContext(subject);
rob@100 9107 };
rob@100 9108 }
rob@100 9109
rob@100 9110 var result = "{\n" + this.statements.join('') + "\n}";
rob@100 9111 replaceContext = oldContext;
rob@100 9112 return result;
rob@100 9113 };
rob@100 9114
rob@100 9115 transformStatementsBlock = function(block) {
rob@100 9116 var content = trimSpaces(block.substring(1, block.length - 1));
rob@100 9117 return new AstStatementsBlock(transformStatements(content.middle));
rob@100 9118 };
rob@100 9119
rob@100 9120 function AstRoot(statements) {
rob@100 9121 this.statements = statements;
rob@100 9122 }
rob@100 9123 AstRoot.prototype.toString = function() {
rob@100 9124 var classes = [], otherStatements = [], statement;
rob@100 9125 for (var i = 0, len = this.statements.length; i < len; ++i) {
rob@100 9126 statement = this.statements[i];
rob@100 9127 if (statement instanceof AstClass || statement instanceof AstInterface) {
rob@100 9128 classes.push(statement);
rob@100 9129 } else {
rob@100 9130 otherStatements.push(statement);
rob@100 9131 }
rob@100 9132 }
rob@100 9133 sortByWeight(classes);
rob@100 9134
rob@100 9135 var localNames = getLocalNames(this.statements);
rob@100 9136 replaceContext = function (subject) {
rob@100 9137 var name = subject.name;
rob@100 9138 if(localNames.hasOwnProperty(name)) {
rob@100 9139 return name;
rob@100 9140 }
rob@100 9141 if(globalMembers.hasOwnProperty(name) ||
rob@100 9142 PConstants.hasOwnProperty(name) ||
rob@100 9143 defaultScope.hasOwnProperty(name)) {
rob@100 9144 return "$p." + name;
rob@100 9145 }
rob@100 9146 return name;
rob@100 9147 };
rob@100 9148 var result = "// this code was autogenerated from PJS\n" +
rob@100 9149 "(function($p) {\n" +
rob@100 9150 classes.join('') + "\n" +
rob@100 9151 otherStatements.join('') + "\n})";
rob@100 9152 replaceContext = null;
rob@100 9153 return result;
rob@100 9154 };
rob@100 9155
rob@100 9156 transformMain = function() {
rob@100 9157 var statements = extractClassesAndMethods(atoms[0]);
rob@100 9158 statements = statements.replace(/\bimport\s+[^;]+;/g, "");
rob@100 9159 return new AstRoot( transformStatements(statements,
rob@100 9160 transformGlobalMethod, transformGlobalClass) );
rob@100 9161 };
rob@100 9162
rob@100 9163 function generateMetadata(ast) {
rob@100 9164 var globalScope = {};
rob@100 9165 var id, class_;
rob@100 9166 for(id in declaredClasses) {
rob@100 9167 if(declaredClasses.hasOwnProperty(id)) {
rob@100 9168 class_ = declaredClasses[id];
rob@100 9169 var scopeId = class_.scopeId, name = class_.name;
rob@100 9170 if(scopeId) {
rob@100 9171 var scope = declaredClasses[scopeId];
rob@100 9172 class_.scope = scope;
rob@100 9173 if(scope.inScope === undef) {
rob@100 9174 scope.inScope = {};
rob@100 9175 }
rob@100 9176 scope.inScope[name] = class_;
rob@100 9177 } else {
rob@100 9178 globalScope[name] = class_;
rob@100 9179 }
rob@100 9180 }
rob@100 9181 }
rob@100 9182
rob@100 9183 function findInScopes(class_, name) {
rob@100 9184 var parts = name.split('.');
rob@100 9185 var currentScope = class_.scope, found;
rob@100 9186 while(currentScope) {
rob@100 9187 if(currentScope.hasOwnProperty(parts[0])) {
rob@100 9188 found = currentScope[parts[0]]; break;
rob@100 9189 }
rob@100 9190 currentScope = currentScope.scope;
rob@100 9191 }
rob@100 9192 if(found === undef) {
rob@100 9193 found = globalScope[parts[0]];
rob@100 9194 }
rob@100 9195 for(var i=1,l=parts.length;i<l && found;++i) {
rob@100 9196 found = found.inScope[parts[i]];
rob@100 9197 }
rob@100 9198 return found;
rob@100 9199 }
rob@100 9200
rob@100 9201 for(id in declaredClasses) {
rob@100 9202 if(declaredClasses.hasOwnProperty(id)) {
rob@100 9203 class_ = declaredClasses[id];
rob@100 9204 var baseClassName = class_.body.baseClassName;
rob@100 9205 if(baseClassName) {
rob@100 9206 var parent = findInScopes(class_, baseClassName);
rob@100 9207 if (parent) {
rob@100 9208 class_.base = parent;
rob@100 9209 if (!parent.derived) {
rob@100 9210 parent.derived = [];
rob@100 9211 }
rob@100 9212 parent.derived.push(class_);
rob@100 9213 }
rob@100 9214 }
rob@100 9215 var interfacesNames = class_.body.interfacesNames,
rob@100 9216 interfaces = [], i, l;
rob@100 9217 if (interfacesNames && interfacesNames.length > 0) {
rob@100 9218 for (i = 0, l = interfacesNames.length; i < l; ++i) {
rob@100 9219 var interface_ = findInScopes(class_, interfacesNames[i]);
rob@100 9220 interfaces.push(interface_);
rob@100 9221 if (!interface_) {
rob@100 9222 continue;
rob@100 9223 }
rob@100 9224 if (!interface_.derived) {
rob@100 9225 interface_.derived = [];
rob@100 9226 }
rob@100 9227 interface_.derived.push(class_);
rob@100 9228 }
rob@100 9229 if (interfaces.length > 0) {
rob@100 9230 class_.interfaces = interfaces;
rob@100 9231 }
rob@100 9232 }
rob@100 9233 }
rob@100 9234 }
rob@100 9235 }
rob@100 9236
rob@100 9237 function setWeight(ast) {
rob@100 9238 var queue = [], tocheck = {};
rob@100 9239 var id, scopeId, class_;
rob@100 9240 // queue most inner and non-inherited
rob@100 9241 for (id in declaredClasses) {
rob@100 9242 if (declaredClasses.hasOwnProperty(id)) {
rob@100 9243 class_ = declaredClasses[id];
rob@100 9244 if (!class_.inScope && !class_.derived) {
rob@100 9245 queue.push(id);
rob@100 9246 class_.weight = 0;
rob@100 9247 } else {
rob@100 9248 var dependsOn = [];
rob@100 9249 if (class_.inScope) {
rob@100 9250 for (scopeId in class_.inScope) {
rob@100 9251 if (class_.inScope.hasOwnProperty(scopeId)) {
rob@100 9252 dependsOn.push(class_.inScope[scopeId]);
rob@100 9253 }
rob@100 9254 }
rob@100 9255 }
rob@100 9256 if (class_.derived) {
rob@100 9257 dependsOn = dependsOn.concat(class_.derived);
rob@100 9258 }
rob@100 9259 tocheck[id] = dependsOn;
rob@100 9260 }
rob@100 9261 }
rob@100 9262 }
rob@100 9263 function removeDependentAndCheck(targetId, from) {
rob@100 9264 var dependsOn = tocheck[targetId];
rob@100 9265 if (!dependsOn) {
rob@100 9266 return false; // no need to process
rob@100 9267 }
rob@100 9268 var i = dependsOn.indexOf(from);
rob@100 9269 if (i < 0) {
rob@100 9270 return false;
rob@100 9271 }
rob@100 9272 dependsOn.splice(i, 1);
rob@100 9273 if (dependsOn.length > 0) {
rob@100 9274 return false;
rob@100 9275 }
rob@100 9276 delete tocheck[targetId];
rob@100 9277 return true;
rob@100 9278 }
rob@100 9279 while (queue.length > 0) {
rob@100 9280 id = queue.shift();
rob@100 9281 class_ = declaredClasses[id];
rob@100 9282 if (class_.scopeId && removeDependentAndCheck(class_.scopeId, class_)) {
rob@100 9283 queue.push(class_.scopeId);
rob@100 9284 declaredClasses[class_.scopeId].weight = class_.weight + 1;
rob@100 9285 }
rob@100 9286 if (class_.base && removeDependentAndCheck(class_.base.classId, class_)) {
rob@100 9287 queue.push(class_.base.classId);
rob@100 9288 class_.base.weight = class_.weight + 1;
rob@100 9289 }
rob@100 9290 if (class_.interfaces) {
rob@100 9291 var i, l;
rob@100 9292 for (i = 0, l = class_.interfaces.length; i < l; ++i) {
rob@100 9293 if (!class_.interfaces[i] ||
rob@100 9294 !removeDependentAndCheck(class_.interfaces[i].classId, class_)) {
rob@100 9295 continue;
rob@100 9296 }
rob@100 9297 queue.push(class_.interfaces[i].classId);
rob@100 9298 class_.interfaces[i].weight = class_.weight + 1;
rob@100 9299 }
rob@100 9300 }
rob@100 9301 }
rob@100 9302 }
rob@100 9303
rob@100 9304 var transformed = transformMain();
rob@100 9305 generateMetadata(transformed);
rob@100 9306 setWeight(transformed);
rob@100 9307
rob@100 9308 var redendered = transformed.toString();
rob@100 9309
rob@100 9310 // remove empty extra lines with space
rob@100 9311 redendered = redendered.replace(/\s*\n(?:[\t ]*\n)+/g, "\n\n");
rob@100 9312
rob@100 9313 // convert character codes to characters
rob@100 9314 redendered = redendered.replace(/__x([0-9A-F]{4})/g, function(all, hexCode) {
rob@100 9315 return String.fromCharCode(parseInt(hexCode,16));
rob@100 9316 });
rob@100 9317
rob@100 9318 return injectStrings(redendered, strings);
rob@100 9319 }// Parser ends
rob@100 9320
rob@100 9321 function preprocessCode(aCode, sketch) {
rob@100 9322 // Parse out @pjs directive, if any.
rob@100 9323 var dm = new RegExp(/\/\*\s*@pjs\s+((?:[^\*]|\*+[^\*\/])*)\*\//g).exec(aCode);
rob@100 9324 if (dm && dm.length === 2) {
rob@100 9325 // masks contents of a JSON to be replaced later
rob@100 9326 // to protect the contents from further parsing
rob@100 9327 var jsonItems = [],
rob@100 9328 directives = dm.splice(1, 2)[0].replace(/\{([\s\S]*?)\}/g, (function() {
rob@100 9329 return function(all, item) {
rob@100 9330 jsonItems.push(item);
rob@100 9331 return "{" + (jsonItems.length-1) + "}";
rob@100 9332 };
rob@100 9333 }())).replace('\n', '').replace('\r', '').split(";");
rob@100 9334
rob@100 9335 // We'll L/RTrim, and also remove any surrounding double quotes (e.g., just take string contents)
rob@100 9336 var clean = function(s) {
rob@100 9337 return s.replace(/^\s*["']?/, '').replace(/["']?\s*$/, '');
rob@100 9338 };
rob@100 9339
rob@100 9340 for (var i = 0, dl = directives.length; i < dl; i++) {
rob@100 9341 var pair = directives[i].split('=');
rob@100 9342 if (pair && pair.length === 2) {
rob@100 9343 var key = clean(pair[0]),
rob@100 9344 value = clean(pair[1]),
rob@100 9345 list = [];
rob@100 9346 // A few directives require work beyond storying key/value pairings
rob@100 9347 if (key === "preload") {
rob@100 9348 list = value.split(',');
rob@100 9349 // All pre-loaded images will get put in imageCache, keyed on filename
rob@100 9350 for (var j = 0, jl = list.length; j < jl; j++) {
rob@100 9351 var imageName = clean(list[j]);
rob@100 9352 sketch.imageCache.add(imageName);
rob@100 9353 }
rob@100 9354 // fonts can be declared as a string containing a url,
rob@100 9355 // or a JSON object, containing a font name, and a url
rob@100 9356 } else if (key === "font") {
rob@100 9357 list = value.split(",");
rob@100 9358 for (var x = 0, xl = list.length; x < xl; x++) {
rob@100 9359 var fontName = clean(list[x]),
rob@100 9360 index = /^\{(\d*?)\}$/.exec(fontName);
rob@100 9361 // if index is not null, send JSON, otherwise, send string
rob@100 9362 PFont.preloading.add(index ? JSON.parse("{" + jsonItems[index[1]] + "}") : fontName);
rob@100 9363 }
rob@100 9364 } else if (key === "pauseOnBlur") {
rob@100 9365 sketch.options.pauseOnBlur = value === "true";
rob@100 9366 } else if (key === "globalKeyEvents") {
rob@100 9367 sketch.options.globalKeyEvents = value === "true";
rob@100 9368 } else if (key.substring(0, 6) === "param-") {
rob@100 9369 sketch.params[key.substring(6)] = value;
rob@100 9370 } else {
rob@100 9371 sketch.options[key] = value;
rob@100 9372 }
rob@100 9373 }
rob@100 9374 }
rob@100 9375 }
rob@100 9376 return aCode;
rob@100 9377 }
rob@100 9378
rob@100 9379 // Parse/compiles Processing (Java-like) syntax to JavaScript syntax
rob@100 9380 Processing.compile = function(pdeCode) {
rob@100 9381 var sketch = new Processing.Sketch();
rob@100 9382 var code = preprocessCode(pdeCode, sketch);
rob@100 9383 var compiledPde = parseProcessing(code);
rob@100 9384 sketch.sourceCode = compiledPde;
rob@100 9385 return sketch;
rob@100 9386 };
rob@100 9387
rob@100 9388 // the logger for println()
rob@100 9389 var PjsConsole = function (document) {
rob@100 9390 var e = {}, added = false;
rob@100 9391 e.BufferMax = 200;
rob@100 9392 e.wrapper = document.createElement("div");
rob@100 9393 e.wrapper.setAttribute("style", "opacity:.75;display:block;position:fixed;bottom:0px;left:0px;right:0px;height:50px;background-color:#aaa");
rob@100 9394 e.dragger = document.createElement("div");
rob@100 9395 e.dragger.setAttribute("style", "display:block;border:3px black raised;cursor:n-resize;position:absolute;top:0px;left:0px;right:0px;height:5px;background-color:#333");
rob@100 9396 e.closer = document.createElement("div");
rob@100 9397 e.closer.onmouseover = function () {
rob@100 9398 e.closer.style.setProperty("background-color", "#ccc");
rob@100 9399 };
rob@100 9400 e.closer.onmouseout = function () {
rob@100 9401 e.closer.style.setProperty("background-color", "#ddd");
rob@100 9402 };
rob@100 9403 e.closer.innerHTML = "&#10006;";
rob@100 9404 e.closer.setAttribute("style", "opacity:.5;display:block;border:3px black raised;position:absolute;top:10px;right:30px;height:20px;width:20px;background-color:#ddd;color:#000;line-height:20px;text-align:center;cursor:pointer;");
rob@100 9405 e.javaconsole = document.createElement("div");
rob@100 9406 e.javaconsole.setAttribute("style", "overflow-x: auto;display:block;position:absolute;left:10px;right:0px;bottom:5px;top:10px;overflow-y:scroll;height:40px;");
rob@100 9407 e.wrapper.appendChild(e.dragger);
rob@100 9408 e.wrapper.appendChild(e.javaconsole);
rob@100 9409 e.wrapper.appendChild(e.closer);
rob@100 9410 e.dragger.onmousedown = function (t) {
rob@100 9411 e.divheight = e.wrapper.style.height;
rob@100 9412 if (document.selection) document.selection.empty();
rob@100 9413 else window.getSelection().removeAllRanges();
rob@100 9414 var n = t.screenY;
rob@100 9415 window.onmousemove = function (t) {
rob@100 9416 e.wrapper.style.height = parseFloat(e.divheight) + (n - t.screenY) + "px";
rob@100 9417 e.javaconsole.style.height = parseFloat(e.divheight) + (n - t.screenY) - 10 + "px";
rob@100 9418 };
rob@100 9419 window.onmouseup = function (t) {
rob@100 9420 if (document.selection) document.selection.empty();
rob@100 9421 else window.getSelection().removeAllRanges();
rob@100 9422 e.wrapper.style.height = parseFloat(e.divheight) + (n - t.screenY) + "px";
rob@100 9423 e.javaconsole.style.height = parseFloat(e.divheight) + (n - t.screenY) - 10 + "px";
rob@100 9424 window.onmousemove = null;
rob@100 9425 window.onmouseup = null;
rob@100 9426 };
rob@100 9427 };
rob@100 9428 e.BufferArray = [];
rob@100 9429 e.print = e.log = function (t) {
rob@100 9430 // var oldheight = e.javaconsole.scrollHeight-e.javaconsole.scrollTop;
rob@100 9431 if (e.BufferArray[e.BufferArray.length - 1]) e.BufferArray[e.BufferArray.length - 1] += (t) + "";
rob@100 9432 else e.BufferArray.push(t);
rob@100 9433 e.javaconsole.innerHTML = e.BufferArray.join('');
rob@100 9434 if (e.wrapper.style.visibility === "hidden") {
rob@100 9435 e.wrapper.style.visibility = "visible";
rob@100 9436 }
rob@100 9437 //if (e.BufferArray.length > e.BufferMax) e.BufferArray.splice(0, 1);
rob@100 9438 //else e.javaconsole.scrollTop = oldheight;
rob@100 9439 if (e.wrapper.style.visibility === "hidden") {
rob@100 9440 e.wrapper.style.visibility = "visible";
rob@100 9441 }
rob@100 9442 };
rob@100 9443 e.println = function (t) {
rob@100 9444 if(!added) { document.body.appendChild(e.wrapper); }
rob@100 9445 e.print(t);
rob@100 9446 e.BufferArray.push('<br/>');
rob@100 9447 e.javaconsole.innerHTML = e.BufferArray.join('');
rob@100 9448 if (e.wrapper.style.visibility === "hidden") {
rob@100 9449 e.wrapper.style.visibility = "visible";
rob@100 9450 }
rob@100 9451 if (e.BufferArray.length > e.BufferMax) e.BufferArray.splice(0, 1);
rob@100 9452 else e.javaconsole.scrollTop = e.javaconsole.scrollHeight;
rob@100 9453 if (e.wrapper.style.visibility === "hidden") {
rob@100 9454 e.wrapper.style.visibility = "visible";
rob@100 9455 }
rob@100 9456 };
rob@100 9457 e.showconsole = function () {
rob@100 9458 e.wrapper.style.visibility = "visible";
rob@100 9459 };
rob@100 9460 e.hideconsole = function () {
rob@100 9461 e.wrapper.style.visibility = "hidden";
rob@100 9462 };
rob@100 9463 e.closer.onclick = function () {
rob@100 9464 e.hideconsole();
rob@100 9465 };
rob@100 9466 e.hideconsole();
rob@100 9467 return e;
rob@100 9468 };
rob@100 9469
rob@100 9470 Processing.logger = new PjsConsole(document);
rob@100 9471
rob@100 9472 // done
rob@100 9473 return Processing;
rob@100 9474 };
rob@100 9475
rob@100 9476 },{}],26:[function(require,module,exports){
rob@100 9477 /**
rob@100 9478 * Processing.js object
rob@100 9479 */
rob@100 9480 module.exports = function(options, undef) {
rob@100 9481 var defaultScope = options.defaultScope,
rob@100 9482 extend = options.extend,
rob@100 9483 Browser = options.Browser,
rob@100 9484 ajax = Browser.ajax,
rob@100 9485 navigator = Browser.navigator,
rob@100 9486 window = Browser.window,
rob@100 9487 XMLHttpRequest = window.XMLHttpRequest,
rob@100 9488 document = Browser.document,
rob@100 9489 noop = options.noop,
rob@100 9490
rob@100 9491 PConstants = defaultScope.PConstants;
rob@100 9492 PFont = defaultScope.PFont,
rob@100 9493 PShapeSVG = defaultScope.PShapeSVG,
rob@100 9494 PVector = defaultScope.PVector,
rob@100 9495 Char = Character = defaultScope.Char,
rob@100 9496 ObjectIterator = defaultScope.ObjectIterator,
rob@100 9497 XMLElement = defaultScope.XMLElement,
rob@100 9498 XML = defaultScope.XML;
rob@100 9499
rob@100 9500 // fascinating "read only" jshint error if we don't start a new var block here.
rob@100 9501 var HTMLCanvasElement = window.HTMLCanvasElement,
rob@100 9502 HTMLImageElement = window.HTMLImageElement,
rob@100 9503 localStorage = window.localStorage;
rob@100 9504
rob@100 9505 var isDOMPresent = ("document" in this) && !("fake" in this.document);
rob@100 9506
rob@100 9507 // document.head polyfill for the benefit of Firefox 3.6
rob@100 9508 if (!document.head) {
rob@100 9509 document.head = document.getElementsByTagName('head')[0];
rob@100 9510 }
rob@100 9511
rob@100 9512 var Float32Array = setupTypedArray("Float32Array", "WebGLFloatArray"),
rob@100 9513 Int32Array = setupTypedArray("Int32Array", "WebGLIntArray"),
rob@100 9514 Uint16Array = setupTypedArray("Uint16Array", "WebGLUnsignedShortArray"),
rob@100 9515 Uint8Array = setupTypedArray("Uint8Array", "WebGLUnsignedByteArray");
rob@100 9516
rob@100 9517 // Typed Arrays: fallback to WebGL arrays or Native JS arrays if unavailable
rob@100 9518 function setupTypedArray(name, fallback) {
rob@100 9519 // Check if TypedArray exists, and use if so.
rob@100 9520 if (name in window) {
rob@100 9521 return window[name];
rob@100 9522 }
rob@100 9523
rob@100 9524 // Check if WebGLArray exists
rob@100 9525 if (typeof window[fallback] === "function") {
rob@100 9526 return window[fallback];
rob@100 9527 }
rob@100 9528
rob@100 9529 // Use Native JS array
rob@100 9530 return function(obj) {
rob@100 9531 if (obj instanceof Array) {
rob@100 9532 return obj;
rob@100 9533 }
rob@100 9534 if (typeof obj === "number") {
rob@100 9535 var arr = [];
rob@100 9536 arr.length = obj;
rob@100 9537 return arr;
rob@100 9538 }
rob@100 9539 };
rob@100 9540 }
rob@100 9541
rob@100 9542 /* IE9+ quirks mode check - ticket #1606 */
rob@100 9543 if (document.documentMode >= 9 && !document.doctype) {
rob@100 9544 throw("The doctype directive is missing. The recommended doctype in Internet Explorer is the HTML5 doctype: <!DOCTYPE html>");
rob@100 9545 }
rob@100 9546
rob@100 9547 // Manage multiple Processing instances
rob@100 9548 var processingInstances = [];
rob@100 9549 var processingInstanceIds = {};
rob@100 9550
rob@100 9551 /**
rob@100 9552 * instance tracking - adding new instances
rob@100 9553 */
rob@100 9554 var addInstance = function(processing) {
rob@100 9555 if (processing.externals.canvas.id === undef || !processing.externals.canvas.id.length) {
rob@100 9556 processing.externals.canvas.id = "__processing" + processingInstances.length;
rob@100 9557 }
rob@100 9558 processingInstanceIds[processing.externals.canvas.id] = processingInstances.length;
rob@100 9559 processingInstances.push(processing);
rob@100 9560 };
rob@100 9561
rob@100 9562 /**
rob@100 9563 * instance tracking - removal
rob@100 9564 */
rob@100 9565 var removeInstance = function(id) {
rob@100 9566 processingInstances.splice(processingInstanceIds[id], 1);
rob@100 9567 delete processingInstanceIds[id];
rob@100 9568 };
rob@100 9569
rob@100 9570
rob@100 9571 /**
rob@100 9572 * The Processing object
rob@100 9573 */
rob@100 9574 var Processing = this.Processing = function(aCanvas, aCode, aFunctions) {
rob@100 9575
rob@100 9576 if (!(this instanceof Processing)) {
rob@100 9577 throw("called Processing constructor as if it were a function: missing 'new'.");
rob@100 9578 }
rob@100 9579
rob@100 9580 var curElement = {},
rob@100 9581 pgraphicsMode = (aCanvas === undef && aCode === undef);
rob@100 9582
rob@100 9583 if (pgraphicsMode) {
rob@100 9584 curElement = document.createElement("canvas");
rob@100 9585 } else {
rob@100 9586 // We'll take a canvas element or a string for a canvas element's id
rob@100 9587 curElement = typeof aCanvas === "string" ? document.getElementById(aCanvas) : aCanvas;
rob@100 9588 }
rob@100 9589
rob@100 9590 if (!('getContext' in curElement)) {
rob@100 9591 throw("called Processing constructor without passing canvas element reference or id.");
rob@100 9592 }
rob@100 9593
rob@100 9594 function unimplemented(s) {
rob@100 9595 Processing.debug('Unimplemented - ' + s);
rob@100 9596 }
rob@100 9597
rob@100 9598 ////////////////////////////////////////////////////////////////////////////
rob@100 9599 // JavaScript event binding and releasing
rob@100 9600 ////////////////////////////////////////////////////////////////////////////
rob@100 9601
rob@100 9602 var eventHandlers = [];
rob@100 9603
rob@100 9604 function attachEventHandler(elem, type, fn) {
rob@100 9605 if (elem.addEventListener) {
rob@100 9606 elem.addEventListener(type, fn, false);
rob@100 9607 } else {
rob@100 9608 elem.attachEvent("on" + type, fn);
rob@100 9609 }
rob@100 9610 eventHandlers.push({elem: elem, type: type, fn: fn});
rob@100 9611 }
rob@100 9612
rob@100 9613 function detachEventHandler(eventHandler) {
rob@100 9614 var elem = eventHandler.elem,
rob@100 9615 type = eventHandler.type,
rob@100 9616 fn = eventHandler.fn;
rob@100 9617 if (elem.removeEventListener) {
rob@100 9618 elem.removeEventListener(type, fn, false);
rob@100 9619 } else if (elem.detachEvent) {
rob@100 9620 elem.detachEvent("on" + type, fn);
rob@100 9621 }
rob@100 9622 }
rob@100 9623
rob@100 9624 function removeFirstArgument(args) {
rob@100 9625 return Array.prototype.slice.call(args, 1);
rob@100 9626 }
rob@100 9627
rob@100 9628 // When something new is added to "p." it must also be added to the "names" array.
rob@100 9629 // The names array contains the names of everything that is inside "p."
rob@100 9630 var p = this;
rob@100 9631
rob@100 9632 p.Char = p.Character = Char;
rob@100 9633
rob@100 9634 // add in the Processing API functions
rob@100 9635 extend.withCommonFunctions(p);
rob@100 9636 extend.withMath(p);
rob@100 9637 extend.withProxyFunctions(p, removeFirstArgument);
rob@100 9638 extend.withTouch(p, curElement, attachEventHandler, document, PConstants);
rob@100 9639
rob@100 9640 // custom functions and properties are added here
rob@100 9641 if(aFunctions) {
rob@100 9642 Object.keys(aFunctions).forEach(function(name) {
rob@100 9643 p[name] = aFunctions[name];
rob@100 9644 });
rob@100 9645 }
rob@100 9646
rob@100 9647 // PJS specific (non-p5) methods and properties to externalize
rob@100 9648 p.externals = {
rob@100 9649 canvas: curElement,
rob@100 9650 context: undef,
rob@100 9651 sketch: undef,
rob@100 9652 window: window
rob@100 9653 };
rob@100 9654
rob@100 9655 p.name = 'Processing.js Instance'; // Set Processing defaults / environment variables
rob@100 9656 p.use3DContext = false; // default '2d' canvas context
rob@100 9657
rob@100 9658 /**
rob@100 9659 * Confirms if a Processing program is "focused", meaning that it is
rob@100 9660 * active and will accept input from mouse or keyboard. This variable
rob@100 9661 * is "true" if it is focused and "false" if not. This variable is
rob@100 9662 * often used when you want to warn people they need to click on the
rob@100 9663 * browser before it will work.
rob@100 9664 */
rob@100 9665 p.focused = false;
rob@100 9666 p.breakShape = false;
rob@100 9667
rob@100 9668 // Glyph path storage for textFonts
rob@100 9669 p.glyphTable = {};
rob@100 9670
rob@100 9671 // Global vars for tracking mouse position
rob@100 9672 p.pmouseX = 0;
rob@100 9673 p.pmouseY = 0;
rob@100 9674 p.mouseX = 0;
rob@100 9675 p.mouseY = 0;
rob@100 9676 p.mouseButton = 0;
rob@100 9677 p.mouseScroll = 0;
rob@100 9678
rob@100 9679 // Undefined event handlers to be replaced by user when needed
rob@100 9680 p.mouseClicked = undef;
rob@100 9681 p.mouseDragged = undef;
rob@100 9682 p.mouseMoved = undef;
rob@100 9683 p.mousePressed = undef;
rob@100 9684 p.mouseReleased = undef;
rob@100 9685 p.mouseScrolled = undef;
rob@100 9686 p.mouseOver = undef;
rob@100 9687 p.mouseOut = undef;
rob@100 9688 p.touchStart = undef;
rob@100 9689 p.touchEnd = undef;
rob@100 9690 p.touchMove = undef;
rob@100 9691 p.touchCancel = undef;
rob@100 9692 p.key = undef;
rob@100 9693 p.keyCode = undef;
rob@100 9694 p.keyPressed = noop; // needed to remove function checks
rob@100 9695 p.keyReleased = noop;
rob@100 9696 p.keyTyped = noop;
rob@100 9697 p.draw = undef;
rob@100 9698 p.setup = undef;
rob@100 9699
rob@100 9700 // Remapped vars
rob@100 9701 p.__mousePressed = false;
rob@100 9702 p.__keyPressed = false;
rob@100 9703 p.__frameRate = 60;
rob@100 9704
rob@100 9705 // The current animation frame
rob@100 9706 p.frameCount = 0;
rob@100 9707
rob@100 9708 // The height/width of the canvas
rob@100 9709 p.width = 100;
rob@100 9710 p.height = 100;
rob@100 9711
rob@100 9712 // "Private" variables used to maintain state
rob@100 9713 var curContext,
rob@100 9714 curSketch,
rob@100 9715 drawing, // hold a Drawing2D or Drawing3D object
rob@100 9716 doFill = true,
rob@100 9717 fillStyle = [1.0, 1.0, 1.0, 1.0],
rob@100 9718 currentFillColor = 0xFFFFFFFF,
rob@100 9719 isFillDirty = true,
rob@100 9720 doStroke = true,
rob@100 9721 strokeStyle = [0.0, 0.0, 0.0, 1.0],
rob@100 9722 currentStrokeColor = 0xFF000000,
rob@100 9723 isStrokeDirty = true,
rob@100 9724 lineWidth = 1,
rob@100 9725 loopStarted = false,
rob@100 9726 renderSmooth = false,
rob@100 9727 doLoop = true,
rob@100 9728 looping = 0,
rob@100 9729 curRectMode = PConstants.CORNER,
rob@100 9730 curEllipseMode = PConstants.CENTER,
rob@100 9731 normalX = 0,
rob@100 9732 normalY = 0,
rob@100 9733 normalZ = 0,
rob@100 9734 normalMode = PConstants.NORMAL_MODE_AUTO,
rob@100 9735 curFrameRate = 60,
rob@100 9736 curMsPerFrame = 1000/curFrameRate,
rob@100 9737 curCursor = PConstants.ARROW,
rob@100 9738 oldCursor = curElement.style.cursor,
rob@100 9739 curShape = PConstants.POLYGON,
rob@100 9740 curShapeCount = 0,
rob@100 9741 curvePoints = [],
rob@100 9742 curTightness = 0,
rob@100 9743 curveDet = 20,
rob@100 9744 curveInited = false,
rob@100 9745 backgroundObj = -3355444, // rgb(204, 204, 204) is the default gray background colour
rob@100 9746 bezDetail = 20,
rob@100 9747 colorModeA = 255,
rob@100 9748 colorModeX = 255,
rob@100 9749 colorModeY = 255,
rob@100 9750 colorModeZ = 255,
rob@100 9751 pathOpen = false,
rob@100 9752 mouseDragging = false,
rob@100 9753 pmouseXLastFrame = 0,
rob@100 9754 pmouseYLastFrame = 0,
rob@100 9755 curColorMode = PConstants.RGB,
rob@100 9756 curTint = null,
rob@100 9757 curTint3d = null,
rob@100 9758 getLoaded = false,
rob@100 9759 start = Date.now(),
rob@100 9760 timeSinceLastFPS = start,
rob@100 9761 framesSinceLastFPS = 0,
rob@100 9762 textcanvas,
rob@100 9763 curveBasisMatrix,
rob@100 9764 curveToBezierMatrix,
rob@100 9765 curveDrawMatrix,
rob@100 9766 bezierDrawMatrix,
rob@100 9767 bezierBasisInverse,
rob@100 9768 bezierBasisMatrix,
rob@100 9769 curContextCache = { attributes: {}, locations: {} },
rob@100 9770 // Shaders
rob@100 9771 programObject3D,
rob@100 9772 programObject2D,
rob@100 9773 programObjectUnlitShape,
rob@100 9774 boxBuffer,
rob@100 9775 boxNormBuffer,
rob@100 9776 boxOutlineBuffer,
rob@100 9777 rectBuffer,
rob@100 9778 rectNormBuffer,
rob@100 9779 sphereBuffer,
rob@100 9780 lineBuffer,
rob@100 9781 fillBuffer,
rob@100 9782 fillColorBuffer,
rob@100 9783 strokeColorBuffer,
rob@100 9784 pointBuffer,
rob@100 9785 shapeTexVBO,
rob@100 9786 canTex, // texture for createGraphics
rob@100 9787 textTex, // texture for 3d tex
rob@100 9788 curTexture = {width:0,height:0},
rob@100 9789 curTextureMode = PConstants.IMAGE,
rob@100 9790 usingTexture = false,
rob@100 9791 textBuffer,
rob@100 9792 textureBuffer,
rob@100 9793 indexBuffer,
rob@100 9794 // Text alignment
rob@100 9795 horizontalTextAlignment = PConstants.LEFT,
rob@100 9796 verticalTextAlignment = PConstants.BASELINE,
rob@100 9797 textMode = PConstants.MODEL,
rob@100 9798 // Font state
rob@100 9799 curFontName = "Arial",
rob@100 9800 curTextSize = 12,
rob@100 9801 curTextAscent = 9,
rob@100 9802 curTextDescent = 2,
rob@100 9803 curTextLeading = 14,
rob@100 9804 curTextFont = PFont.get(curFontName, curTextSize),
rob@100 9805 // Pixels cache
rob@100 9806 originalContext,
rob@100 9807 proxyContext = null,
rob@100 9808 isContextReplaced = false,
rob@100 9809 setPixelsCached,
rob@100 9810 maxPixelsCached = 1000,
rob@100 9811 pressedKeysMap = [],
rob@100 9812 lastPressedKeyCode = null,
rob@100 9813 codedKeys = [ PConstants.SHIFT, PConstants.CONTROL, PConstants.ALT, PConstants.CAPSLK, PConstants.PGUP, PConstants.PGDN,
rob@100 9814 PConstants.END, PConstants.HOME, PConstants.LEFT, PConstants.UP, PConstants.RIGHT, PConstants.DOWN, PConstants.NUMLK,
rob@100 9815 PConstants.INSERT, PConstants.F1, PConstants.F2, PConstants.F3, PConstants.F4, PConstants.F5, PConstants.F6, PConstants.F7,
rob@100 9816 PConstants.F8, PConstants.F9, PConstants.F10, PConstants.F11, PConstants.F12, PConstants.META ];
rob@100 9817
rob@100 9818 // User can only have MAX_LIGHTS lights
rob@100 9819 var lightCount = 0;
rob@100 9820
rob@100 9821 //sphere stuff
rob@100 9822 var sphereDetailV = 0,
rob@100 9823 sphereDetailU = 0,
rob@100 9824 sphereX = [],
rob@100 9825 sphereY = [],
rob@100 9826 sphereZ = [],
rob@100 9827 sinLUT = new Float32Array(PConstants.SINCOS_LENGTH),
rob@100 9828 cosLUT = new Float32Array(PConstants.SINCOS_LENGTH),
rob@100 9829 sphereVerts,
rob@100 9830 sphereNorms;
rob@100 9831
rob@100 9832 // Camera defaults and settings
rob@100 9833 var cam,
rob@100 9834 cameraInv,
rob@100 9835 modelView,
rob@100 9836 modelViewInv,
rob@100 9837 userMatrixStack,
rob@100 9838 userReverseMatrixStack,
rob@100 9839 inverseCopy,
rob@100 9840 projection,
rob@100 9841 manipulatingCamera = false,
rob@100 9842 frustumMode = false,
rob@100 9843 cameraFOV = 60 * (Math.PI / 180),
rob@100 9844 cameraX = p.width / 2,
rob@100 9845 cameraY = p.height / 2,
rob@100 9846 cameraZ = cameraY / Math.tan(cameraFOV / 2),
rob@100 9847 cameraNear = cameraZ / 10,
rob@100 9848 cameraFar = cameraZ * 10,
rob@100 9849 cameraAspect = p.width / p.height;
rob@100 9850
rob@100 9851 var vertArray = [],
rob@100 9852 curveVertArray = [],
rob@100 9853 curveVertCount = 0,
rob@100 9854 isCurve = false,
rob@100 9855 isBezier = false,
rob@100 9856 firstVert = true;
rob@100 9857
rob@100 9858 //PShape stuff
rob@100 9859 var curShapeMode = PConstants.CORNER;
rob@100 9860
rob@100 9861 // Stores states for pushStyle() and popStyle().
rob@100 9862 var styleArray = [];
rob@100 9863
rob@100 9864 // The vertices for the box cannot be specified using a triangle strip since each
rob@100 9865 // side of the cube must have its own set of normals.
rob@100 9866 // Vertices are specified in a counter-clockwise order.
rob@100 9867 // Triangles are in this order: back, front, right, bottom, left, top.
rob@100 9868 var boxVerts = new Float32Array([
rob@100 9869 0.5, 0.5, -0.5, 0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5, 0.5, -0.5, 0.5, 0.5, -0.5,
rob@100 9870 0.5, 0.5, 0.5, -0.5, 0.5, 0.5, -0.5, -0.5, 0.5, -0.5, -0.5, 0.5, 0.5, -0.5, 0.5, 0.5, 0.5, 0.5,
rob@100 9871 0.5, 0.5, -0.5, 0.5, 0.5, 0.5, 0.5, -0.5, 0.5, 0.5, -0.5, 0.5, 0.5, -0.5, -0.5, 0.5, 0.5, -0.5,
rob@100 9872 0.5, -0.5, -0.5, 0.5, -0.5, 0.5, -0.5, -0.5, 0.5, -0.5, -0.5, 0.5, -0.5, -0.5, -0.5, 0.5, -0.5, -0.5,
rob@100 9873 -0.5, -0.5, -0.5, -0.5, -0.5, 0.5, -0.5, 0.5, 0.5, -0.5, 0.5, 0.5, -0.5, 0.5, -0.5, -0.5, -0.5, -0.5,
rob@100 9874 0.5, 0.5, 0.5, 0.5, 0.5, -0.5, -0.5, 0.5, -0.5, -0.5, 0.5, -0.5, -0.5, 0.5, 0.5, 0.5, 0.5, 0.5]);
rob@100 9875
rob@100 9876 var boxOutlineVerts = new Float32Array([
rob@100 9877 0.5, 0.5, 0.5, 0.5, -0.5, 0.5, 0.5, 0.5, -0.5, 0.5, -0.5, -0.5,
rob@100 9878 -0.5, 0.5, -0.5, -0.5, -0.5, -0.5, -0.5, 0.5, 0.5, -0.5, -0.5, 0.5,
rob@100 9879 0.5, 0.5, 0.5, 0.5, 0.5, -0.5, 0.5, 0.5, -0.5, -0.5, 0.5, -0.5,
rob@100 9880 -0.5, 0.5, -0.5, -0.5, 0.5, 0.5, -0.5, 0.5, 0.5, 0.5, 0.5, 0.5,
rob@100 9881 0.5, -0.5, 0.5, 0.5, -0.5, -0.5, 0.5, -0.5, -0.5, -0.5, -0.5, -0.5,
rob@100 9882 -0.5, -0.5, -0.5, -0.5, -0.5, 0.5, -0.5, -0.5, 0.5, 0.5, -0.5, 0.5]);
rob@100 9883
rob@100 9884 var boxNorms = new Float32Array([
rob@100 9885 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1,
rob@100 9886 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1,
rob@100 9887 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0,
rob@100 9888 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0,
rob@100 9889 -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0,
rob@100 9890 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0]);
rob@100 9891
rob@100 9892 // These verts are used for the fill and stroke using TRIANGLE_FAN and LINE_LOOP.
rob@100 9893 var rectVerts = new Float32Array([0,0,0, 0,1,0, 1,1,0, 1,0,0]);
rob@100 9894
rob@100 9895 var rectNorms = new Float32Array([0,0,1, 0,0,1, 0,0,1, 0,0,1]);
rob@100 9896
rob@100 9897 // Shader for points and lines in begin/endShape.
rob@100 9898 var vertexShaderSrcUnlitShape =
rob@100 9899 "varying vec4 vFrontColor;" +
rob@100 9900
rob@100 9901 "attribute vec3 aVertex;" +
rob@100 9902 "attribute vec4 aColor;" +
rob@100 9903
rob@100 9904 "uniform mat4 uView;" +
rob@100 9905 "uniform mat4 uProjection;" +
rob@100 9906 "uniform float uPointSize;" +
rob@100 9907
rob@100 9908 "void main(void) {" +
rob@100 9909 " vFrontColor = aColor;" +
rob@100 9910 " gl_PointSize = uPointSize;" +
rob@100 9911 " gl_Position = uProjection * uView * vec4(aVertex, 1.0);" +
rob@100 9912 "}";
rob@100 9913
rob@100 9914 var fragmentShaderSrcUnlitShape =
rob@100 9915 "#ifdef GL_ES\n" +
rob@100 9916 "precision highp float;\n" +
rob@100 9917 "#endif\n" +
rob@100 9918
rob@100 9919 "varying vec4 vFrontColor;" +
rob@100 9920 "uniform bool uSmooth;" +
rob@100 9921
rob@100 9922 "void main(void){" +
rob@100 9923 " if(uSmooth == true){" +
rob@100 9924 " float dist = distance(gl_PointCoord, vec2(0.5));" +
rob@100 9925 " if(dist > 0.5){" +
rob@100 9926 " discard;" +
rob@100 9927 " }" +
rob@100 9928 " }" +
rob@100 9929 " gl_FragColor = vFrontColor;" +
rob@100 9930 "}";
rob@100 9931
rob@100 9932 // Shader for rect, text, box outlines, sphere outlines, point() and line().
rob@100 9933 var vertexShaderSrc2D =
rob@100 9934 "varying vec4 vFrontColor;" +
rob@100 9935
rob@100 9936 "attribute vec3 aVertex;" +
rob@100 9937 "attribute vec2 aTextureCoord;" +
rob@100 9938 "uniform vec4 uColor;" +
rob@100 9939
rob@100 9940 "uniform mat4 uModel;" +
rob@100 9941 "uniform mat4 uView;" +
rob@100 9942 "uniform mat4 uProjection;" +
rob@100 9943 "uniform float uPointSize;" +
rob@100 9944 "varying vec2 vTextureCoord;"+
rob@100 9945
rob@100 9946 "void main(void) {" +
rob@100 9947 " gl_PointSize = uPointSize;" +
rob@100 9948 " vFrontColor = uColor;" +
rob@100 9949 " gl_Position = uProjection * uView * uModel * vec4(aVertex, 1.0);" +
rob@100 9950 " vTextureCoord = aTextureCoord;" +
rob@100 9951 "}";
rob@100 9952
rob@100 9953 var fragmentShaderSrc2D =
rob@100 9954 "#ifdef GL_ES\n" +
rob@100 9955 "precision highp float;\n" +
rob@100 9956 "#endif\n" +
rob@100 9957
rob@100 9958 "varying vec4 vFrontColor;" +
rob@100 9959 "varying vec2 vTextureCoord;"+
rob@100 9960
rob@100 9961 "uniform sampler2D uSampler;"+
rob@100 9962 "uniform int uIsDrawingText;"+
rob@100 9963 "uniform bool uSmooth;" +
rob@100 9964
rob@100 9965 "void main(void){" +
rob@100 9966 // WebGL does not support POINT_SMOOTH, so we do it ourselves
rob@100 9967 " if(uSmooth == true){" +
rob@100 9968 " float dist = distance(gl_PointCoord, vec2(0.5));" +
rob@100 9969 " if(dist > 0.5){" +
rob@100 9970 " discard;" +
rob@100 9971 " }" +
rob@100 9972 " }" +
rob@100 9973
rob@100 9974 " if(uIsDrawingText == 1){" +
rob@100 9975 " float alpha = texture2D(uSampler, vTextureCoord).a;"+
rob@100 9976 " gl_FragColor = vec4(vFrontColor.rgb * alpha, alpha);"+
rob@100 9977 " }" +
rob@100 9978 " else{" +
rob@100 9979 " gl_FragColor = vFrontColor;" +
rob@100 9980 " }" +
rob@100 9981 "}";
rob@100 9982
rob@100 9983 var webglMaxTempsWorkaround = /Windows/.test(navigator.userAgent);
rob@100 9984
rob@100 9985 // Vertex shader for boxes and spheres.
rob@100 9986 var vertexShaderSrc3D =
rob@100 9987 "varying vec4 vFrontColor;" +
rob@100 9988
rob@100 9989 "attribute vec3 aVertex;" +
rob@100 9990 "attribute vec3 aNormal;" +
rob@100 9991 "attribute vec4 aColor;" +
rob@100 9992 "attribute vec2 aTexture;" +
rob@100 9993 "varying vec2 vTexture;" +
rob@100 9994
rob@100 9995 "uniform vec4 uColor;" +
rob@100 9996
rob@100 9997 "uniform bool uUsingMat;" +
rob@100 9998 "uniform vec3 uSpecular;" +
rob@100 9999 "uniform vec3 uMaterialEmissive;" +
rob@100 10000 "uniform vec3 uMaterialAmbient;" +
rob@100 10001 "uniform vec3 uMaterialSpecular;" +
rob@100 10002 "uniform float uShininess;" +
rob@100 10003
rob@100 10004 "uniform mat4 uModel;" +
rob@100 10005 "uniform mat4 uView;" +
rob@100 10006 "uniform mat4 uProjection;" +
rob@100 10007 "uniform mat4 uNormalTransform;" +
rob@100 10008
rob@100 10009 "uniform int uLightCount;" +
rob@100 10010 "uniform vec3 uFalloff;" +
rob@100 10011
rob@100 10012 // Careful changing the order of these fields. Some cards
rob@100 10013 // have issues with memory alignment.
rob@100 10014 "struct Light {" +
rob@100 10015 " int type;" +
rob@100 10016 " vec3 color;" +
rob@100 10017 " vec3 position;" +
rob@100 10018 " vec3 direction;" +
rob@100 10019 " float angle;" +
rob@100 10020 " vec3 halfVector;" +
rob@100 10021 " float concentration;" +
rob@100 10022 "};" +
rob@100 10023
rob@100 10024 // nVidia cards have issues with arrays of structures
rob@100 10025 // so instead we create 8 instances of Light.
rob@100 10026 "uniform Light uLights0;" +
rob@100 10027 "uniform Light uLights1;" +
rob@100 10028 "uniform Light uLights2;" +
rob@100 10029 "uniform Light uLights3;" +
rob@100 10030 "uniform Light uLights4;" +
rob@100 10031 "uniform Light uLights5;" +
rob@100 10032 "uniform Light uLights6;" +
rob@100 10033 "uniform Light uLights7;" +
rob@100 10034
rob@100 10035 // GLSL does not support switch.
rob@100 10036 "Light getLight(int index){" +
rob@100 10037 " if(index == 0) return uLights0;" +
rob@100 10038 " if(index == 1) return uLights1;" +
rob@100 10039 " if(index == 2) return uLights2;" +
rob@100 10040 " if(index == 3) return uLights3;" +
rob@100 10041 " if(index == 4) return uLights4;" +
rob@100 10042 " if(index == 5) return uLights5;" +
rob@100 10043 " if(index == 6) return uLights6;" +
rob@100 10044 // Do not use a conditional for the last return statement
rob@100 10045 // because some video cards will fail and complain that
rob@100 10046 // "not all paths return".
rob@100 10047 " return uLights7;" +
rob@100 10048 "}" +
rob@100 10049
rob@100 10050 "void AmbientLight( inout vec3 totalAmbient, in vec3 ecPos, in Light light ) {" +
rob@100 10051 // Get the vector from the light to the vertex and
rob@100 10052 // get the distance from the current vector to the light position.
rob@100 10053 " float d = length( light.position - ecPos );" +
rob@100 10054 " float attenuation = 1.0 / ( uFalloff[0] + ( uFalloff[1] * d ) + ( uFalloff[2] * d * d ));" +
rob@100 10055 " totalAmbient += light.color * attenuation;" +
rob@100 10056 "}" +
rob@100 10057
rob@100 10058 /*
rob@100 10059 col - accumulated color
rob@100 10060 spec - accumulated specular highlight
rob@100 10061 vertNormal - Normal of the vertex
rob@100 10062 ecPos - eye coordinate position
rob@100 10063 light - light structure
rob@100 10064 */
rob@100 10065 "void DirectionalLight( inout vec3 col, inout vec3 spec, in vec3 vertNormal, in vec3 ecPos, in Light light ) {" +
rob@100 10066 " float powerFactor = 0.0;" +
rob@100 10067 " float nDotVP = max(0.0, dot( vertNormal, normalize(-light.position) ));" +
rob@100 10068 " float nDotVH = max(0.0, dot( vertNormal, normalize(-light.position-normalize(ecPos) )));" +
rob@100 10069
rob@100 10070 " if( nDotVP != 0.0 ){" +
rob@100 10071 " powerFactor = pow( nDotVH, uShininess );" +
rob@100 10072 " }" +
rob@100 10073
rob@100 10074 " col += light.color * nDotVP;" +
rob@100 10075 " spec += uSpecular * powerFactor;" +
rob@100 10076 "}" +
rob@100 10077
rob@100 10078 /*
rob@100 10079 col - accumulated color
rob@100 10080 spec - accumulated specular highlight
rob@100 10081 vertNormal - Normal of the vertex
rob@100 10082 ecPos - eye coordinate position
rob@100 10083 light - light structure
rob@100 10084 */
rob@100 10085 "void PointLight( inout vec3 col, inout vec3 spec, in vec3 vertNormal, in vec3 ecPos, in Light light ) {" +
rob@100 10086 " float powerFactor;" +
rob@100 10087
rob@100 10088 // Get the vector from the light to the vertex.
rob@100 10089 " vec3 VP = light.position - ecPos;" +
rob@100 10090
rob@100 10091 // Get the distance from the current vector to the light position.
rob@100 10092 " float d = length( VP ); " +
rob@100 10093
rob@100 10094 // Normalize the light ray so it can be used in the dot product operation.
rob@100 10095 " VP = normalize( VP );" +
rob@100 10096
rob@100 10097 " float attenuation = 1.0 / ( uFalloff[0] + ( uFalloff[1] * d ) + ( uFalloff[2] * d * d ));" +
rob@100 10098
rob@100 10099 " float nDotVP = max( 0.0, dot( vertNormal, VP ));" +
rob@100 10100 " vec3 halfVector = normalize( VP - normalize(ecPos) );" +
rob@100 10101 " float nDotHV = max( 0.0, dot( vertNormal, halfVector ));" +
rob@100 10102
rob@100 10103 " if( nDotVP == 0.0 ) {" +
rob@100 10104 " powerFactor = 0.0;" +
rob@100 10105 " }" +
rob@100 10106 " else {" +
rob@100 10107 " powerFactor = pow( nDotHV, uShininess );" +
rob@100 10108 " }" +
rob@100 10109
rob@100 10110 " spec += uSpecular * powerFactor * attenuation;" +
rob@100 10111 " col += light.color * nDotVP * attenuation;" +
rob@100 10112 "}" +
rob@100 10113
rob@100 10114 /*
rob@100 10115 col - accumulated color
rob@100 10116 spec - accumulated specular highlight
rob@100 10117 vertNormal - Normal of the vertex
rob@100 10118 ecPos - eye coordinate position
rob@100 10119 light - light structure
rob@100 10120 */
rob@100 10121 "void SpotLight( inout vec3 col, inout vec3 spec, in vec3 vertNormal, in vec3 ecPos, in Light light ) {" +
rob@100 10122 " float spotAttenuation;" +
rob@100 10123 " float powerFactor = 0.0;" +
rob@100 10124
rob@100 10125 // Calculate the vector from the current vertex to the light.
rob@100 10126 " vec3 VP = light.position - ecPos;" +
rob@100 10127 " vec3 ldir = normalize( -light.direction );" +
rob@100 10128
rob@100 10129 // Get the distance from the spotlight and the vertex
rob@100 10130 " float d = length( VP );" +
rob@100 10131 " VP = normalize( VP );" +
rob@100 10132
rob@100 10133 " float attenuation = 1.0 / ( uFalloff[0] + ( uFalloff[1] * d ) + ( uFalloff[2] * d * d ) );" +
rob@100 10134
rob@100 10135 // Dot product of the vector from vertex to light and light direction.
rob@100 10136 " float spotDot = dot( VP, ldir );" +
rob@100 10137
rob@100 10138 // If the vertex falls inside the cone
rob@100 10139 (webglMaxTempsWorkaround ? // Windows reports max temps error if light.angle is used
rob@100 10140 " spotAttenuation = 1.0; " :
rob@100 10141 " if( spotDot > cos( light.angle ) ) {" +
rob@100 10142 " spotAttenuation = pow( spotDot, light.concentration );" +
rob@100 10143 " }" +
rob@100 10144 " else{" +
rob@100 10145 " spotAttenuation = 0.0;" +
rob@100 10146 " }" +
rob@100 10147 " attenuation *= spotAttenuation;" +
rob@100 10148 "") +
rob@100 10149
rob@100 10150 " float nDotVP = max( 0.0, dot( vertNormal, VP ) );" +
rob@100 10151 " vec3 halfVector = normalize( VP - normalize(ecPos) );" +
rob@100 10152 " float nDotHV = max( 0.0, dot( vertNormal, halfVector ) );" +
rob@100 10153
rob@100 10154 " if( nDotVP != 0.0 ) {" +
rob@100 10155 " powerFactor = pow( nDotHV, uShininess );" +
rob@100 10156 " }" +
rob@100 10157
rob@100 10158 " spec += uSpecular * powerFactor * attenuation;" +
rob@100 10159 " col += light.color * nDotVP * attenuation;" +
rob@100 10160 "}" +
rob@100 10161
rob@100 10162 "void main(void) {" +
rob@100 10163 " vec3 finalAmbient = vec3( 0.0 );" +
rob@100 10164 " vec3 finalDiffuse = vec3( 0.0 );" +
rob@100 10165 " vec3 finalSpecular = vec3( 0.0 );" +
rob@100 10166
rob@100 10167 " vec4 col = uColor;" +
rob@100 10168
rob@100 10169 " if ( uColor[0] == -1.0 ){" +
rob@100 10170 " col = aColor;" +
rob@100 10171 " }" +
rob@100 10172
rob@100 10173 // We use the sphere vertices as the normals when we create the sphere buffer.
rob@100 10174 // But this only works if the sphere vertices are unit length, so we
rob@100 10175 // have to normalize the normals here. Since this is only required for spheres
rob@100 10176 // we could consider placing this in a conditional later on.
rob@100 10177 " vec3 norm = normalize(vec3( uNormalTransform * vec4( aNormal, 0.0 ) ));" +
rob@100 10178
rob@100 10179 " vec4 ecPos4 = uView * uModel * vec4(aVertex, 1.0);" +
rob@100 10180 " vec3 ecPos = (vec3(ecPos4))/ecPos4.w;" +
rob@100 10181
rob@100 10182 // If there were no lights this draw call, just use the
rob@100 10183 // assigned fill color of the shape and the specular value.
rob@100 10184 " if( uLightCount == 0 ) {" +
rob@100 10185 " vFrontColor = col + vec4(uMaterialSpecular, 1.0);" +
rob@100 10186 " }" +
rob@100 10187 " else {" +
rob@100 10188 // WebGL forces us to iterate over a constant value
rob@100 10189 // so we can't iterate using lightCount.
rob@100 10190 " for( int i = 0; i < 8; i++ ) {" +
rob@100 10191 " Light l = getLight(i);" +
rob@100 10192
rob@100 10193 // We can stop iterating if we know we have gone past
rob@100 10194 // the number of lights which are actually on. This gives us a
rob@100 10195 // significant performance increase with high vertex counts.
rob@100 10196 " if( i >= uLightCount ){" +
rob@100 10197 " break;" +
rob@100 10198 " }" +
rob@100 10199
rob@100 10200 " if( l.type == 0 ) {" +
rob@100 10201 " AmbientLight( finalAmbient, ecPos, l );" +
rob@100 10202 " }" +
rob@100 10203 " else if( l.type == 1 ) {" +
rob@100 10204 " DirectionalLight( finalDiffuse, finalSpecular, norm, ecPos, l );" +
rob@100 10205 " }" +
rob@100 10206 " else if( l.type == 2 ) {" +
rob@100 10207 " PointLight( finalDiffuse, finalSpecular, norm, ecPos, l );" +
rob@100 10208 " }" +
rob@100 10209 " else {" +
rob@100 10210 " SpotLight( finalDiffuse, finalSpecular, norm, ecPos, l );" +
rob@100 10211 " }" +
rob@100 10212 " }" +
rob@100 10213
rob@100 10214 " if( uUsingMat == false ) {" +
rob@100 10215 " vFrontColor = vec4(" +
rob@100 10216 " vec3( col ) * finalAmbient +" +
rob@100 10217 " vec3( col ) * finalDiffuse +" +
rob@100 10218 " vec3( col ) * finalSpecular," +
rob@100 10219 " col[3] );" +
rob@100 10220 " }" +
rob@100 10221 " else{" +
rob@100 10222 " vFrontColor = vec4( " +
rob@100 10223 " uMaterialEmissive + " +
rob@100 10224 " (vec3(col) * uMaterialAmbient * finalAmbient ) + " +
rob@100 10225 " (vec3(col) * finalDiffuse) + " +
rob@100 10226 " (uMaterialSpecular * finalSpecular), " +
rob@100 10227 " col[3] );" +
rob@100 10228 " }" +
rob@100 10229 " }" +
rob@100 10230
rob@100 10231 " vTexture.xy = aTexture.xy;" +
rob@100 10232 " gl_Position = uProjection * uView * uModel * vec4( aVertex, 1.0 );" +
rob@100 10233 "}";
rob@100 10234
rob@100 10235 var fragmentShaderSrc3D =
rob@100 10236 "#ifdef GL_ES\n" +
rob@100 10237 "precision highp float;\n" +
rob@100 10238 "#endif\n" +
rob@100 10239
rob@100 10240 "varying vec4 vFrontColor;" +
rob@100 10241
rob@100 10242 "uniform sampler2D uSampler;" +
rob@100 10243 "uniform bool uUsingTexture;" +
rob@100 10244 "varying vec2 vTexture;" +
rob@100 10245
rob@100 10246 // In Processing, when a texture is used, the fill color is ignored
rob@100 10247 // vec4(1.0,1.0,1.0,0.5)
rob@100 10248 "void main(void){" +
rob@100 10249 " if( uUsingTexture ){" +
rob@100 10250 " gl_FragColor = vec4(texture2D(uSampler, vTexture.xy)) * vFrontColor;" +
rob@100 10251 " }"+
rob@100 10252 " else{" +
rob@100 10253 " gl_FragColor = vFrontColor;" +
rob@100 10254 " }" +
rob@100 10255 "}";
rob@100 10256
rob@100 10257 ////////////////////////////////////////////////////////////////////////////
rob@100 10258 // 3D Functions
rob@100 10259 ////////////////////////////////////////////////////////////////////////////
rob@100 10260
rob@100 10261 /*
rob@100 10262 * Sets a uniform variable in a program object to a particular
rob@100 10263 * value. Before calling this function, ensure the correct
rob@100 10264 * program object has been installed as part of the current
rob@100 10265 * rendering state by calling useProgram.
rob@100 10266 *
rob@100 10267 * On some systems, if the variable exists in the shader but isn't used,
rob@100 10268 * the compiler will optimize it out and this function will fail.
rob@100 10269 *
rob@100 10270 * @param {String} cacheId
rob@100 10271 * @param {WebGLProgram} programObj program object returned from
rob@100 10272 * createProgramObject
rob@100 10273 * @param {String} varName the name of the variable in the shader
rob@100 10274 * @param {float | Array} varValue either a scalar value or an Array
rob@100 10275 *
rob@100 10276 * @returns none
rob@100 10277 *
rob@100 10278 * @see uniformi
rob@100 10279 * @see uniformMatrix
rob@100 10280 */
rob@100 10281 function uniformf(cacheId, programObj, varName, varValue) {
rob@100 10282 var varLocation = curContextCache.locations[cacheId];
rob@100 10283 if(varLocation === undef) {
rob@100 10284 varLocation = curContext.getUniformLocation(programObj, varName);
rob@100 10285 curContextCache.locations[cacheId] = varLocation;
rob@100 10286 }
rob@100 10287 // the variable won't be found if it was optimized out.
rob@100 10288 if (varLocation !== null) {
rob@100 10289 if (varValue.length === 4) {
rob@100 10290 curContext.uniform4fv(varLocation, varValue);
rob@100 10291 } else if (varValue.length === 3) {
rob@100 10292 curContext.uniform3fv(varLocation, varValue);
rob@100 10293 } else if (varValue.length === 2) {
rob@100 10294 curContext.uniform2fv(varLocation, varValue);
rob@100 10295 } else {
rob@100 10296 curContext.uniform1f(varLocation, varValue);
rob@100 10297 }
rob@100 10298 }
rob@100 10299 }
rob@100 10300
rob@100 10301 /**
rob@100 10302 * Sets a uniform int or int array in a program object to a particular
rob@100 10303 * value. Before calling this function, ensure the correct
rob@100 10304 * program object has been installed as part of the current
rob@100 10305 * rendering state.
rob@100 10306 *
rob@100 10307 * On some systems, if the variable exists in the shader but isn't used,
rob@100 10308 * the compiler will optimize it out and this function will fail.
rob@100 10309 *
rob@100 10310 * @param {String} cacheId
rob@100 10311 * @param {WebGLProgram} programObj program object returned from
rob@100 10312 * createProgramObject
rob@100 10313 * @param {String} varName the name of the variable in the shader
rob@100 10314 * @param {int | Array} varValue either a scalar value or an Array
rob@100 10315 *
rob@100 10316 * @returns none
rob@100 10317 *
rob@100 10318 * @see uniformf
rob@100 10319 * @see uniformMatrix
rob@100 10320 */
rob@100 10321 function uniformi(cacheId, programObj, varName, varValue) {
rob@100 10322 var varLocation = curContextCache.locations[cacheId];
rob@100 10323 if(varLocation === undef) {
rob@100 10324 varLocation = curContext.getUniformLocation(programObj, varName);
rob@100 10325 curContextCache.locations[cacheId] = varLocation;
rob@100 10326 }
rob@100 10327 // the variable won't be found if it was optimized out.
rob@100 10328 if (varLocation !== null) {
rob@100 10329 if (varValue.length === 4) {
rob@100 10330 curContext.uniform4iv(varLocation, varValue);
rob@100 10331 } else if (varValue.length === 3) {
rob@100 10332 curContext.uniform3iv(varLocation, varValue);
rob@100 10333 } else if (varValue.length === 2) {
rob@100 10334 curContext.uniform2iv(varLocation, varValue);
rob@100 10335 } else {
rob@100 10336 curContext.uniform1i(varLocation, varValue);
rob@100 10337 }
rob@100 10338 }
rob@100 10339 }
rob@100 10340
rob@100 10341 /**
rob@100 10342 * Sets the value of a uniform matrix variable in a program
rob@100 10343 * object. Before calling this function, ensure the correct
rob@100 10344 * program object has been installed as part of the current
rob@100 10345 * rendering state.
rob@100 10346 *
rob@100 10347 * On some systems, if the variable exists in the shader but
rob@100 10348 * isn't used, the compiler will optimize it out and this
rob@100 10349 * function will fail.
rob@100 10350 *
rob@100 10351 * @param {String} cacheId
rob@100 10352 * @param {WebGLProgram} programObj program object returned from
rob@100 10353 * createProgramObject
rob@100 10354 * @param {String} varName the name of the variable in the shader
rob@100 10355 * @param {boolean} transpose must be false
rob@100 10356 * @param {Array} matrix an array of 4, 9 or 16 values
rob@100 10357 *
rob@100 10358 * @returns none
rob@100 10359 *
rob@100 10360 * @see uniformi
rob@100 10361 * @see uniformf
rob@100 10362 */
rob@100 10363 function uniformMatrix(cacheId, programObj, varName, transpose, matrix) {
rob@100 10364 var varLocation = curContextCache.locations[cacheId];
rob@100 10365 if(varLocation === undef) {
rob@100 10366 varLocation = curContext.getUniformLocation(programObj, varName);
rob@100 10367 curContextCache.locations[cacheId] = varLocation;
rob@100 10368 }
rob@100 10369 // The variable won't be found if it was optimized out.
rob@100 10370 if (varLocation !== -1) {
rob@100 10371 if (matrix.length === 16) {
rob@100 10372 curContext.uniformMatrix4fv(varLocation, transpose, matrix);
rob@100 10373 } else if (matrix.length === 9) {
rob@100 10374 curContext.uniformMatrix3fv(varLocation, transpose, matrix);
rob@100 10375 } else {
rob@100 10376 curContext.uniformMatrix2fv(varLocation, transpose, matrix);
rob@100 10377 }
rob@100 10378 }
rob@100 10379 }
rob@100 10380
rob@100 10381 /**
rob@100 10382 * Binds the VBO, sets the vertex attribute data for the program
rob@100 10383 * object and enables the attribute.
rob@100 10384 *
rob@100 10385 * On some systems, if the attribute exists in the shader but
rob@100 10386 * isn't used, the compiler will optimize it out and this
rob@100 10387 * function will fail.
rob@100 10388 *
rob@100 10389 * @param {String} cacheId
rob@100 10390 * @param {WebGLProgram} programObj program object returned from
rob@100 10391 * createProgramObject
rob@100 10392 * @param {String} varName the name of the variable in the shader
rob@100 10393 * @param {int} size the number of components per vertex attribute
rob@100 10394 * @param {WebGLBuffer} VBO Vertex Buffer Object
rob@100 10395 *
rob@100 10396 * @returns none
rob@100 10397 *
rob@100 10398 * @see disableVertexAttribPointer
rob@100 10399 */
rob@100 10400 function vertexAttribPointer(cacheId, programObj, varName, size, VBO) {
rob@100 10401 var varLocation = curContextCache.attributes[cacheId];
rob@100 10402 if(varLocation === undef) {
rob@100 10403 varLocation = curContext.getAttribLocation(programObj, varName);
rob@100 10404 curContextCache.attributes[cacheId] = varLocation;
rob@100 10405 }
rob@100 10406 if (varLocation !== -1) {
rob@100 10407 curContext.bindBuffer(curContext.ARRAY_BUFFER, VBO);
rob@100 10408 curContext.vertexAttribPointer(varLocation, size, curContext.FLOAT, false, 0, 0);
rob@100 10409 curContext.enableVertexAttribArray(varLocation);
rob@100 10410 }
rob@100 10411 }
rob@100 10412
rob@100 10413 /**
rob@100 10414 * Disables a program object attribute from being sent to WebGL.
rob@100 10415 *
rob@100 10416 * @param {String} cacheId
rob@100 10417 * @param {WebGLProgram} programObj program object returned from
rob@100 10418 * createProgramObject
rob@100 10419 * @param {String} varName name of the attribute
rob@100 10420 *
rob@100 10421 * @returns none
rob@100 10422 *
rob@100 10423 * @see vertexAttribPointer
rob@100 10424 */
rob@100 10425 function disableVertexAttribPointer(cacheId, programObj, varName){
rob@100 10426 var varLocation = curContextCache.attributes[cacheId];
rob@100 10427 if(varLocation === undef) {
rob@100 10428 varLocation = curContext.getAttribLocation(programObj, varName);
rob@100 10429 curContextCache.attributes[cacheId] = varLocation;
rob@100 10430 }
rob@100 10431 if (varLocation !== -1) {
rob@100 10432 curContext.disableVertexAttribArray(varLocation);
rob@100 10433 }
rob@100 10434 }
rob@100 10435
rob@100 10436 /**
rob@100 10437 * Creates a WebGL program object.
rob@100 10438 *
rob@100 10439 * @param {String} vetexShaderSource
rob@100 10440 * @param {String} fragmentShaderSource
rob@100 10441 *
rob@100 10442 * @returns {WebGLProgram} A program object
rob@100 10443 */
rob@100 10444 var createProgramObject = function(curContext, vetexShaderSource, fragmentShaderSource) {
rob@100 10445 var vertexShaderObject = curContext.createShader(curContext.VERTEX_SHADER);
rob@100 10446 curContext.shaderSource(vertexShaderObject, vetexShaderSource);
rob@100 10447 curContext.compileShader(vertexShaderObject);
rob@100 10448 if (!curContext.getShaderParameter(vertexShaderObject, curContext.COMPILE_STATUS)) {
rob@100 10449 throw curContext.getShaderInfoLog(vertexShaderObject);
rob@100 10450 }
rob@100 10451
rob@100 10452 var fragmentShaderObject = curContext.createShader(curContext.FRAGMENT_SHADER);
rob@100 10453 curContext.shaderSource(fragmentShaderObject, fragmentShaderSource);
rob@100 10454 curContext.compileShader(fragmentShaderObject);
rob@100 10455 if (!curContext.getShaderParameter(fragmentShaderObject, curContext.COMPILE_STATUS)) {
rob@100 10456 throw curContext.getShaderInfoLog(fragmentShaderObject);
rob@100 10457 }
rob@100 10458
rob@100 10459 var programObject = curContext.createProgram();
rob@100 10460 curContext.attachShader(programObject, vertexShaderObject);
rob@100 10461 curContext.attachShader(programObject, fragmentShaderObject);
rob@100 10462 curContext.linkProgram(programObject);
rob@100 10463 if (!curContext.getProgramParameter(programObject, curContext.LINK_STATUS)) {
rob@100 10464 throw "Error linking shaders.";
rob@100 10465 }
rob@100 10466
rob@100 10467 return programObject;
rob@100 10468 };
rob@100 10469
rob@100 10470 ////////////////////////////////////////////////////////////////////////////
rob@100 10471 // 2D/3D drawing handling
rob@100 10472 ////////////////////////////////////////////////////////////////////////////
rob@100 10473 var imageModeCorner = function(x, y, w, h, whAreSizes) {
rob@100 10474 return {
rob@100 10475 x: x,
rob@100 10476 y: y,
rob@100 10477 w: w,
rob@100 10478 h: h
rob@100 10479 };
rob@100 10480 };
rob@100 10481 var imageModeConvert = imageModeCorner;
rob@100 10482
rob@100 10483 var imageModeCorners = function(x, y, w, h, whAreSizes) {
rob@100 10484 return {
rob@100 10485 x: x,
rob@100 10486 y: y,
rob@100 10487 w: whAreSizes ? w : w - x,
rob@100 10488 h: whAreSizes ? h : h - y
rob@100 10489 };
rob@100 10490 };
rob@100 10491
rob@100 10492 var imageModeCenter = function(x, y, w, h, whAreSizes) {
rob@100 10493 return {
rob@100 10494 x: x - w / 2,
rob@100 10495 y: y - h / 2,
rob@100 10496 w: w,
rob@100 10497 h: h
rob@100 10498 };
rob@100 10499 };
rob@100 10500
rob@100 10501 // Objects for shared, 2D and 3D contexts
rob@100 10502 var DrawingShared = function(){};
rob@100 10503 var Drawing2D = function(){};
rob@100 10504 var Drawing3D = function(){};
rob@100 10505 var DrawingPre = function(){};
rob@100 10506
rob@100 10507 // Setup the prototype chain
rob@100 10508 Drawing2D.prototype = new DrawingShared();
rob@100 10509 Drawing2D.prototype.constructor = Drawing2D;
rob@100 10510 Drawing3D.prototype = new DrawingShared();
rob@100 10511 Drawing3D.prototype.constructor = Drawing3D;
rob@100 10512 DrawingPre.prototype = new DrawingShared();
rob@100 10513 DrawingPre.prototype.constructor = DrawingPre;
rob@100 10514
rob@100 10515 // A no-op function for when the user calls 3D functions from a 2D sketch
rob@100 10516 // We can change this to a throw or console.error() later if we want
rob@100 10517 DrawingShared.prototype.a3DOnlyFunction = noop;
rob@100 10518
rob@100 10519 /**
rob@100 10520 * The shape() function displays shapes to the screen.
rob@100 10521 * Processing currently works with SVG shapes only.
rob@100 10522 * The <b>shape</b> parameter specifies the shape to display and the <b>x</b>
rob@100 10523 * and <b>y</b> parameters define the location of the shape from its
rob@100 10524 * upper-left corner.
rob@100 10525 * The shape is displayed at its original size unless the <b>width</b>
rob@100 10526 * and <b>height</b> parameters specify a different size.
rob@100 10527 * The <b>shapeMode()</b> function changes the way the parameters work.
rob@100 10528 * A call to <b>shapeMode(CORNERS)</b>, for example, will change the width
rob@100 10529 * and height parameters to define the x and y values of the opposite corner
rob@100 10530 * of the shape.
rob@100 10531 * <br><br>
rob@100 10532 * Note complex shapes may draw awkwardly with P2D, P3D, and OPENGL. Those
rob@100 10533 * renderers do not yet support shapes that have holes or complicated breaks.
rob@100 10534 *
rob@100 10535 * @param {PShape} shape the shape to display
rob@100 10536 * @param {int|float} x x-coordinate of the shape
rob@100 10537 * @param {int|float} y y-coordinate of the shape
rob@100 10538 * @param {int|float} width width to display the shape
rob@100 10539 * @param {int|float} height height to display the shape
rob@100 10540 *
rob@100 10541 * @see PShape
rob@100 10542 * @see loadShape()
rob@100 10543 * @see shapeMode()
rob@100 10544 */
rob@100 10545 p.shape = function(shape, x, y, width, height) {
rob@100 10546 if (arguments.length >= 1 && arguments[0] !== null) {
rob@100 10547 if (shape.isVisible()) {
rob@100 10548 p.pushMatrix();
rob@100 10549 if (curShapeMode === PConstants.CENTER) {
rob@100 10550 if (arguments.length === 5) {
rob@100 10551 p.translate(x - width/2, y - height/2);
rob@100 10552 p.scale(width / shape.getWidth(), height / shape.getHeight());
rob@100 10553 } else if (arguments.length === 3) {
rob@100 10554 p.translate(x - shape.getWidth()/2, - shape.getHeight()/2);
rob@100 10555 } else {
rob@100 10556 p.translate(-shape.getWidth()/2, -shape.getHeight()/2);
rob@100 10557 }
rob@100 10558 } else if (curShapeMode === PConstants.CORNER) {
rob@100 10559 if (arguments.length === 5) {
rob@100 10560 p.translate(x, y);
rob@100 10561 p.scale(width / shape.getWidth(), height / shape.getHeight());
rob@100 10562 } else if (arguments.length === 3) {
rob@100 10563 p.translate(x, y);
rob@100 10564 }
rob@100 10565 } else if (curShapeMode === PConstants.CORNERS) {
rob@100 10566 if (arguments.length === 5) {
rob@100 10567 width -= x;
rob@100 10568 height -= y;
rob@100 10569 p.translate(x, y);
rob@100 10570 p.scale(width / shape.getWidth(), height / shape.getHeight());
rob@100 10571 } else if (arguments.length === 3) {
rob@100 10572 p.translate(x, y);
rob@100 10573 }
rob@100 10574 }
rob@100 10575 shape.draw(p);
rob@100 10576 if ((arguments.length === 1 && curShapeMode === PConstants.CENTER ) || arguments.length > 1) {
rob@100 10577 p.popMatrix();
rob@100 10578 }
rob@100 10579 }
rob@100 10580 }
rob@100 10581 };
rob@100 10582
rob@100 10583 /**
rob@100 10584 * The shapeMode() function modifies the location from which shapes draw.
rob@100 10585 * The default mode is <b>shapeMode(CORNER)</b>, which specifies the
rob@100 10586 * location to be the upper left corner of the shape and uses the third
rob@100 10587 * and fourth parameters of <b>shape()</b> to specify the width and height.
rob@100 10588 * The syntax <b>shapeMode(CORNERS)</b> uses the first and second parameters
rob@100 10589 * of <b>shape()</b> to set the location of one corner and uses the third
rob@100 10590 * and fourth parameters to set the opposite corner.
rob@100 10591 * The syntax <b>shapeMode(CENTER)</b> draws the shape from its center point
rob@100 10592 * and uses the third and forth parameters of <b>shape()</b> to specify the
rob@100 10593 * width and height.
rob@100 10594 * The parameter must be written in "ALL CAPS" because Processing syntax
rob@100 10595 * is case sensitive.
rob@100 10596 *
rob@100 10597 * @param {int} mode One of CORNER, CORNERS, CENTER
rob@100 10598 *
rob@100 10599 * @see shape()
rob@100 10600 * @see rectMode()
rob@100 10601 */
rob@100 10602 p.shapeMode = function (mode) {
rob@100 10603 curShapeMode = mode;
rob@100 10604 };
rob@100 10605
rob@100 10606 /**
rob@100 10607 * The loadShape() function loads vector shapes into a variable of type PShape. Currently, only SVG files may be loaded.
rob@100 10608 * In most cases, <b>loadShape()</b> should be used inside <b>setup()</b> because loading shapes inside <b>draw()</b> will reduce the speed of a sketch.
rob@100 10609 *
rob@100 10610 * @param {String} filename an SVG file
rob@100 10611 *
rob@100 10612 * @return {PShape} a object of type PShape or null
rob@100 10613 * @see PShape
rob@100 10614 * @see PApplet#shape()
rob@100 10615 * @see PApplet#shapeMode()
rob@100 10616 */
rob@100 10617 p.loadShape = function (filename) {
rob@100 10618 if (arguments.length === 1) {
rob@100 10619 if (filename.indexOf(".svg") > -1) {
rob@100 10620 return new PShapeSVG(null, filename);
rob@100 10621 }
rob@100 10622 }
rob@100 10623 return null;
rob@100 10624 };
rob@100 10625
rob@100 10626 /**
rob@100 10627 * Processing 2.0 function for loading XML files.
rob@100 10628 *
rob@100 10629 * @param {String} uri The uri for the xml file to load.
rob@100 10630 *
rob@100 10631 * @return {XML} An XML object representing the xml data.
rob@100 10632 */
rob@100 10633 p.loadXML = function(uri) {
rob@100 10634 return new XML(p, uri);
rob@100 10635 };
rob@100 10636
rob@100 10637
rob@100 10638 ////////////////////////////////////////////////////////////////////////////
rob@100 10639 // 2D Matrix
rob@100 10640 ////////////////////////////////////////////////////////////////////////////
rob@100 10641
rob@100 10642 /**
rob@100 10643 * Helper function for printMatrix(). Finds the largest scalar
rob@100 10644 * in the matrix, then number of digits left of the decimal.
rob@100 10645 * Call from PMatrix2D and PMatrix3D's print() function.
rob@100 10646 */
rob@100 10647 var printMatrixHelper = function(elements) {
rob@100 10648 var big = 0;
rob@100 10649 for (var i = 0; i < elements.length; i++) {
rob@100 10650 if (i !== 0) {
rob@100 10651 big = Math.max(big, Math.abs(elements[i]));
rob@100 10652 } else {
rob@100 10653 big = Math.abs(elements[i]);
rob@100 10654 }
rob@100 10655 }
rob@100 10656
rob@100 10657 var digits = (big + "").indexOf(".");
rob@100 10658 if (digits === 0) {
rob@100 10659 digits = 1;
rob@100 10660 } else if (digits === -1) {
rob@100 10661 digits = (big + "").length;
rob@100 10662 }
rob@100 10663
rob@100 10664 return digits;
rob@100 10665 };
rob@100 10666 /**
rob@100 10667 * PMatrix2D is a 3x2 affine matrix implementation. The constructor accepts another PMatrix2D or a list of six float elements.
rob@100 10668 * If no parameters are provided the matrix is set to the identity matrix.
rob@100 10669 *
rob@100 10670 * @param {PMatrix2D} matrix the initial matrix to set to
rob@100 10671 * @param {float} m00 the first element of the matrix
rob@100 10672 * @param {float} m01 the second element of the matrix
rob@100 10673 * @param {float} m02 the third element of the matrix
rob@100 10674 * @param {float} m10 the fourth element of the matrix
rob@100 10675 * @param {float} m11 the fifth element of the matrix
rob@100 10676 * @param {float} m12 the sixth element of the matrix
rob@100 10677 */
rob@100 10678 var PMatrix2D = p.PMatrix2D = function() {
rob@100 10679 if (arguments.length === 0) {
rob@100 10680 this.reset();
rob@100 10681 } else if (arguments.length === 1 && arguments[0] instanceof PMatrix2D) {
rob@100 10682 this.set(arguments[0].array());
rob@100 10683 } else if (arguments.length === 6) {
rob@100 10684 this.set(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5]);
rob@100 10685 }
rob@100 10686 };
rob@100 10687 /**
rob@100 10688 * PMatrix2D methods
rob@100 10689 */
rob@100 10690 PMatrix2D.prototype = {
rob@100 10691 /**
rob@100 10692 * @member PMatrix2D
rob@100 10693 * The set() function sets the matrix elements. The function accepts either another PMatrix2D, an array of elements, or a list of six floats.
rob@100 10694 *
rob@100 10695 * @param {PMatrix2D} matrix the matrix to set this matrix to
rob@100 10696 * @param {float[]} elements an array of elements to set this matrix to
rob@100 10697 * @param {float} m00 the first element of the matrix
rob@100 10698 * @param {float} m01 the third element of the matrix
rob@100 10699 * @param {float} m10 the fourth element of the matrix
rob@100 10700 * @param {float} m11 the fith element of the matrix
rob@100 10701 * @param {float} m12 the sixth element of the matrix
rob@100 10702 */
rob@100 10703 set: function() {
rob@100 10704 if (arguments.length === 6) {
rob@100 10705 var a = arguments;
rob@100 10706 this.set([a[0], a[1], a[2],
rob@100 10707 a[3], a[4], a[5]]);
rob@100 10708 } else if (arguments.length === 1 && arguments[0] instanceof PMatrix2D) {
rob@100 10709 this.elements = arguments[0].array();
rob@100 10710 } else if (arguments.length === 1 && arguments[0] instanceof Array) {
rob@100 10711 this.elements = arguments[0].slice();
rob@100 10712 }
rob@100 10713 },
rob@100 10714 /**
rob@100 10715 * @member PMatrix2D
rob@100 10716 * The get() function returns a copy of this PMatrix2D.
rob@100 10717 *
rob@100 10718 * @return {PMatrix2D} a copy of this PMatrix2D
rob@100 10719 */
rob@100 10720 get: function() {
rob@100 10721 var outgoing = new PMatrix2D();
rob@100 10722 outgoing.set(this.elements);
rob@100 10723 return outgoing;
rob@100 10724 },
rob@100 10725 /**
rob@100 10726 * @member PMatrix2D
rob@100 10727 * The reset() function sets this PMatrix2D to the identity matrix.
rob@100 10728 */
rob@100 10729 reset: function() {
rob@100 10730 this.set([1, 0, 0, 0, 1, 0]);
rob@100 10731 },
rob@100 10732 /**
rob@100 10733 * @member PMatrix2D
rob@100 10734 * The array() function returns a copy of the element values.
rob@100 10735 * @addon
rob@100 10736 *
rob@100 10737 * @return {float[]} returns a copy of the element values
rob@100 10738 */
rob@100 10739 array: function array() {
rob@100 10740 return this.elements.slice();
rob@100 10741 },
rob@100 10742 /**
rob@100 10743 * @member PMatrix2D
rob@100 10744 * The translate() function translates this matrix by moving the current coordinates to the location specified by tx and ty.
rob@100 10745 *
rob@100 10746 * @param {float} tx the x-axis coordinate to move to
rob@100 10747 * @param {float} ty the y-axis coordinate to move to
rob@100 10748 */
rob@100 10749 translate: function(tx, ty) {
rob@100 10750 this.elements[2] = tx * this.elements[0] + ty * this.elements[1] + this.elements[2];
rob@100 10751 this.elements[5] = tx * this.elements[3] + ty * this.elements[4] + this.elements[5];
rob@100 10752 },
rob@100 10753 /**
rob@100 10754 * @member PMatrix2D
rob@100 10755 * The invTranslate() function translates this matrix by moving the current coordinates to the negative location specified by tx and ty.
rob@100 10756 *
rob@100 10757 * @param {float} tx the x-axis coordinate to move to
rob@100 10758 * @param {float} ty the y-axis coordinate to move to
rob@100 10759 */
rob@100 10760 invTranslate: function(tx, ty) {
rob@100 10761 this.translate(-tx, -ty);
rob@100 10762 },
rob@100 10763 /**
rob@100 10764 * @member PMatrix2D
rob@100 10765 * The transpose() function is not used in processingjs.
rob@100 10766 */
rob@100 10767 transpose: function() {
rob@100 10768 // Does nothing in Processing.
rob@100 10769 },
rob@100 10770 /**
rob@100 10771 * @member PMatrix2D
rob@100 10772 * The mult() function multiplied this matrix.
rob@100 10773 * If two array elements are passed in the function will multiply a two element vector against this matrix.
rob@100 10774 * If target is null or not length four, a new float array will be returned.
rob@100 10775 * The values for vec and target can be the same (though that's less efficient).
rob@100 10776 * If two PVectors are passed in the function multiply the x and y coordinates of a PVector against this matrix.
rob@100 10777 *
rob@100 10778 * @param {PVector} source, target the PVectors used to multiply this matrix
rob@100 10779 * @param {float[]} source, target the arrays used to multiply this matrix
rob@100 10780 *
rob@100 10781 * @return {PVector|float[]} returns a PVector or an array representing the new matrix
rob@100 10782 */
rob@100 10783 mult: function(source, target) {
rob@100 10784 var x, y;
rob@100 10785 if (source instanceof PVector) {
rob@100 10786 x = source.x;
rob@100 10787 y = source.y;
rob@100 10788 if (!target) {
rob@100 10789 target = new PVector();
rob@100 10790 }
rob@100 10791 } else if (source instanceof Array) {
rob@100 10792 x = source[0];
rob@100 10793 y = source[1];
rob@100 10794 if (!target) {
rob@100 10795 target = [];
rob@100 10796 }
rob@100 10797 }
rob@100 10798 if (target instanceof Array) {
rob@100 10799 target[0] = this.elements[0] * x + this.elements[1] * y + this.elements[2];
rob@100 10800 target[1] = this.elements[3] * x + this.elements[4] * y + this.elements[5];
rob@100 10801 } else if (target instanceof PVector) {
rob@100 10802 target.x = this.elements[0] * x + this.elements[1] * y + this.elements[2];
rob@100 10803 target.y = this.elements[3] * x + this.elements[4] * y + this.elements[5];
rob@100 10804 target.z = 0;
rob@100 10805 }
rob@100 10806 return target;
rob@100 10807 },
rob@100 10808 /**
rob@100 10809 * @member PMatrix2D
rob@100 10810 * The multX() function calculates the x component of a vector from a transformation.
rob@100 10811 *
rob@100 10812 * @param {float} x the x component of the vector being transformed
rob@100 10813 * @param {float} y the y component of the vector being transformed
rob@100 10814 *
rob@100 10815 * @return {float} returnes the result of the calculation
rob@100 10816 */
rob@100 10817 multX: function(x, y) {
rob@100 10818 return (x * this.elements[0] + y * this.elements[1] + this.elements[2]);
rob@100 10819 },
rob@100 10820 /**
rob@100 10821 * @member PMatrix2D
rob@100 10822 * The multY() function calculates the y component of a vector from a transformation.
rob@100 10823 *
rob@100 10824 * @param {float} x the x component of the vector being transformed
rob@100 10825 * @param {float} y the y component of the vector being transformed
rob@100 10826 *
rob@100 10827 * @return {float} returnes the result of the calculation
rob@100 10828 */
rob@100 10829 multY: function(x, y) {
rob@100 10830 return (x * this.elements[3] + y * this.elements[4] + this.elements[5]);
rob@100 10831 },
rob@100 10832 /**
rob@100 10833 * @member PMatrix2D
rob@100 10834 * The skewX() function skews the matrix along the x-axis the amount specified by the angle parameter.
rob@100 10835 * Angles should be specified in radians (values from 0 to PI*2) or converted to radians with the <b>radians()</b> function.
rob@100 10836 *
rob@100 10837 * @param {float} angle angle of skew specified in radians
rob@100 10838 */
rob@100 10839 skewX: function(angle) {
rob@100 10840 this.apply(1, 0, 1, angle, 0, 0);
rob@100 10841 },
rob@100 10842 /**
rob@100 10843 * @member PMatrix2D
rob@100 10844 * The skewY() function skews the matrix along the y-axis the amount specified by the angle parameter.
rob@100 10845 * Angles should be specified in radians (values from 0 to PI*2) or converted to radians with the <b>radians()</b> function.
rob@100 10846 *
rob@100 10847 * @param {float} angle angle of skew specified in radians
rob@100 10848 */
rob@100 10849 skewY: function(angle) {
rob@100 10850 this.apply(1, 0, 1, 0, angle, 0);
rob@100 10851 },
rob@100 10852 /**
rob@100 10853 * @member PMatrix2D
rob@100 10854 * The shearX() function shears the matrix along the x-axis the amount specified by the angle parameter.
rob@100 10855 * Angles should be specified in radians (values from 0 to PI*2) or converted to radians with the <b>radians()</b> function.
rob@100 10856 *
rob@100 10857 * @param {float} angle angle of skew specified in radians
rob@100 10858 */
rob@100 10859 shearX: function(angle) {
rob@100 10860 this.apply(1, 0, 1, Math.tan(angle) , 0, 0);
rob@100 10861 },
rob@100 10862 /**
rob@100 10863 * @member PMatrix2D
rob@100 10864 * The shearY() function shears the matrix along the y-axis the amount specified by the angle parameter.
rob@100 10865 * Angles should be specified in radians (values from 0 to PI*2) or converted to radians with the <b>radians()</b> function.
rob@100 10866 *
rob@100 10867 * @param {float} angle angle of skew specified in radians
rob@100 10868 */
rob@100 10869 shearY: function(angle) {
rob@100 10870 this.apply(1, 0, 1, 0, Math.tan(angle), 0);
rob@100 10871 },
rob@100 10872 /**
rob@100 10873 * @member PMatrix2D
rob@100 10874 * The determinant() function calvculates the determinant of this matrix.
rob@100 10875 *
rob@100 10876 * @return {float} the determinant of the matrix
rob@100 10877 */
rob@100 10878 determinant: function() {
rob@100 10879 return (this.elements[0] * this.elements[4] - this.elements[1] * this.elements[3]);
rob@100 10880 },
rob@100 10881 /**
rob@100 10882 * @member PMatrix2D
rob@100 10883 * The invert() function inverts this matrix
rob@100 10884 *
rob@100 10885 * @return {boolean} true if successful
rob@100 10886 */
rob@100 10887 invert: function() {
rob@100 10888 var d = this.determinant();
rob@100 10889 if (Math.abs( d ) > PConstants.MIN_INT) {
rob@100 10890 var old00 = this.elements[0];
rob@100 10891 var old01 = this.elements[1];
rob@100 10892 var old02 = this.elements[2];
rob@100 10893 var old10 = this.elements[3];
rob@100 10894 var old11 = this.elements[4];
rob@100 10895 var old12 = this.elements[5];
rob@100 10896 this.elements[0] = old11 / d;
rob@100 10897 this.elements[3] = -old10 / d;
rob@100 10898 this.elements[1] = -old01 / d;
rob@100 10899 this.elements[4] = old00 / d;
rob@100 10900 this.elements[2] = (old01 * old12 - old11 * old02) / d;
rob@100 10901 this.elements[5] = (old10 * old02 - old00 * old12) / d;
rob@100 10902 return true;
rob@100 10903 }
rob@100 10904 return false;
rob@100 10905 },
rob@100 10906 /**
rob@100 10907 * @member PMatrix2D
rob@100 10908 * The scale() function increases or decreases the size of a shape by expanding and contracting vertices. When only one parameter is specified scale will occur in all dimensions.
rob@100 10909 * This is equivalent to a two parameter call.
rob@100 10910 *
rob@100 10911 * @param {float} sx the amount to scale on the x-axis
rob@100 10912 * @param {float} sy the amount to scale on the y-axis
rob@100 10913 */
rob@100 10914 scale: function(sx, sy) {
rob@100 10915 if (sx && !sy) {
rob@100 10916 sy = sx;
rob@100 10917 }
rob@100 10918 if (sx && sy) {
rob@100 10919 this.elements[0] *= sx;
rob@100 10920 this.elements[1] *= sy;
rob@100 10921 this.elements[3] *= sx;
rob@100 10922 this.elements[4] *= sy;
rob@100 10923 }
rob@100 10924 },
rob@100 10925 /**
rob@100 10926 * @member PMatrix2D
rob@100 10927 * The invScale() function decreases or increases the size of a shape by contracting and expanding vertices. When only one parameter is specified scale will occur in all dimensions.
rob@100 10928 * This is equivalent to a two parameter call.
rob@100 10929 *
rob@100 10930 * @param {float} sx the amount to scale on the x-axis
rob@100 10931 * @param {float} sy the amount to scale on the y-axis
rob@100 10932 */
rob@100 10933 invScale: function(sx, sy) {
rob@100 10934 if (sx && !sy) {
rob@100 10935 sy = sx;
rob@100 10936 }
rob@100 10937 this.scale(1 / sx, 1 / sy);
rob@100 10938 },
rob@100 10939 /**
rob@100 10940 * @member PMatrix2D
rob@100 10941 * The apply() function multiplies the current matrix by the one specified through the parameters. Note that either a PMatrix2D or a list of floats can be passed in.
rob@100 10942 *
rob@100 10943 * @param {PMatrix2D} matrix the matrix to apply this matrix to
rob@100 10944 * @param {float} m00 the first element of the matrix
rob@100 10945 * @param {float} m01 the third element of the matrix
rob@100 10946 * @param {float} m10 the fourth element of the matrix
rob@100 10947 * @param {float} m11 the fith element of the matrix
rob@100 10948 * @param {float} m12 the sixth element of the matrix
rob@100 10949 */
rob@100 10950 apply: function() {
rob@100 10951 var source;
rob@100 10952 if (arguments.length === 1 && arguments[0] instanceof PMatrix2D) {
rob@100 10953 source = arguments[0].array();
rob@100 10954 } else if (arguments.length === 6) {
rob@100 10955 source = Array.prototype.slice.call(arguments);
rob@100 10956 } else if (arguments.length === 1 && arguments[0] instanceof Array) {
rob@100 10957 source = arguments[0];
rob@100 10958 }
rob@100 10959
rob@100 10960 var result = [0, 0, this.elements[2],
rob@100 10961 0, 0, this.elements[5]];
rob@100 10962 var e = 0;
rob@100 10963 for (var row = 0; row < 2; row++) {
rob@100 10964 for (var col = 0; col < 3; col++, e++) {
rob@100 10965 result[e] += this.elements[row * 3 + 0] * source[col + 0] +
rob@100 10966 this.elements[row * 3 + 1] * source[col + 3];
rob@100 10967 }
rob@100 10968 }
rob@100 10969 this.elements = result.slice();
rob@100 10970 },
rob@100 10971 /**
rob@100 10972 * @member PMatrix2D
rob@100 10973 * The preApply() function applies another matrix to the left of this one. Note that either a PMatrix2D or elements of a matrix can be passed in.
rob@100 10974 *
rob@100 10975 * @param {PMatrix2D} matrix the matrix to apply this matrix to
rob@100 10976 * @param {float} m00 the first element of the matrix
rob@100 10977 * @param {float} m01 the third element of the matrix
rob@100 10978 * @param {float} m10 the fourth element of the matrix
rob@100 10979 * @param {float} m11 the fith element of the matrix
rob@100 10980 * @param {float} m12 the sixth element of the matrix
rob@100 10981 */
rob@100 10982 preApply: function() {
rob@100 10983 var source;
rob@100 10984 if (arguments.length === 1 && arguments[0] instanceof PMatrix2D) {
rob@100 10985 source = arguments[0].array();
rob@100 10986 } else if (arguments.length === 6) {
rob@100 10987 source = Array.prototype.slice.call(arguments);
rob@100 10988 } else if (arguments.length === 1 && arguments[0] instanceof Array) {
rob@100 10989 source = arguments[0];
rob@100 10990 }
rob@100 10991 var result = [0, 0, source[2],
rob@100 10992 0, 0, source[5]];
rob@100 10993 result[2] = source[2] + this.elements[2] * source[0] + this.elements[5] * source[1];
rob@100 10994 result[5] = source[5] + this.elements[2] * source[3] + this.elements[5] * source[4];
rob@100 10995 result[0] = this.elements[0] * source[0] + this.elements[3] * source[1];
rob@100 10996 result[3] = this.elements[0] * source[3] + this.elements[3] * source[4];
rob@100 10997 result[1] = this.elements[1] * source[0] + this.elements[4] * source[1];
rob@100 10998 result[4] = this.elements[1] * source[3] + this.elements[4] * source[4];
rob@100 10999 this.elements = result.slice();
rob@100 11000 },
rob@100 11001 /**
rob@100 11002 * @member PMatrix2D
rob@100 11003 * The rotate() function rotates the matrix.
rob@100 11004 *
rob@100 11005 * @param {float} angle the angle of rotation in radiants
rob@100 11006 */
rob@100 11007 rotate: function(angle) {
rob@100 11008 var c = Math.cos(angle);
rob@100 11009 var s = Math.sin(angle);
rob@100 11010 var temp1 = this.elements[0];
rob@100 11011 var temp2 = this.elements[1];
rob@100 11012 this.elements[0] = c * temp1 + s * temp2;
rob@100 11013 this.elements[1] = -s * temp1 + c * temp2;
rob@100 11014 temp1 = this.elements[3];
rob@100 11015 temp2 = this.elements[4];
rob@100 11016 this.elements[3] = c * temp1 + s * temp2;
rob@100 11017 this.elements[4] = -s * temp1 + c * temp2;
rob@100 11018 },
rob@100 11019 /**
rob@100 11020 * @member PMatrix2D
rob@100 11021 * The rotateZ() function rotates the matrix.
rob@100 11022 *
rob@100 11023 * @param {float} angle the angle of rotation in radiants
rob@100 11024 */
rob@100 11025 rotateZ: function(angle) {
rob@100 11026 this.rotate(angle);
rob@100 11027 },
rob@100 11028 /**
rob@100 11029 * @member PMatrix2D
rob@100 11030 * The invRotateZ() function rotates the matrix in opposite direction.
rob@100 11031 *
rob@100 11032 * @param {float} angle the angle of rotation in radiants
rob@100 11033 */
rob@100 11034 invRotateZ: function(angle) {
rob@100 11035 this.rotateZ(angle - Math.PI);
rob@100 11036 },
rob@100 11037 /**
rob@100 11038 * @member PMatrix2D
rob@100 11039 * The print() function prints out the elements of this matrix
rob@100 11040 */
rob@100 11041 print: function() {
rob@100 11042 var digits = printMatrixHelper(this.elements);
rob@100 11043 var output = "" + p.nfs(this.elements[0], digits, 4) + " " +
rob@100 11044 p.nfs(this.elements[1], digits, 4) + " " +
rob@100 11045 p.nfs(this.elements[2], digits, 4) + "\n" +
rob@100 11046 p.nfs(this.elements[3], digits, 4) + " " +
rob@100 11047 p.nfs(this.elements[4], digits, 4) + " " +
rob@100 11048 p.nfs(this.elements[5], digits, 4) + "\n\n";
rob@100 11049 p.println(output);
rob@100 11050 }
rob@100 11051 };
rob@100 11052
rob@100 11053 /**
rob@100 11054 * PMatrix3D is a 4x4 matrix implementation. The constructor accepts another PMatrix3D or a list of six or sixteen float elements.
rob@100 11055 * If no parameters are provided the matrix is set to the identity matrix.
rob@100 11056 */
rob@100 11057 var PMatrix3D = p.PMatrix3D = function() {
rob@100 11058 // When a matrix is created, it is set to an identity matrix
rob@100 11059 this.reset();
rob@100 11060 };
rob@100 11061 /**
rob@100 11062 * PMatrix3D methods
rob@100 11063 */
rob@100 11064 PMatrix3D.prototype = {
rob@100 11065 /**
rob@100 11066 * @member PMatrix2D
rob@100 11067 * The set() function sets the matrix elements. The function accepts either another PMatrix3D, an array of elements, or a list of six or sixteen floats.
rob@100 11068 *
rob@100 11069 * @param {PMatrix3D} matrix the initial matrix to set to
rob@100 11070 * @param {float[]} elements an array of elements to set this matrix to
rob@100 11071 * @param {float} m00 the first element of the matrix
rob@100 11072 * @param {float} m01 the second element of the matrix
rob@100 11073 * @param {float} m02 the third element of the matrix
rob@100 11074 * @param {float} m03 the fourth element of the matrix
rob@100 11075 * @param {float} m10 the fifth element of the matrix
rob@100 11076 * @param {float} m11 the sixth element of the matrix
rob@100 11077 * @param {float} m12 the seventh element of the matrix
rob@100 11078 * @param {float} m13 the eight element of the matrix
rob@100 11079 * @param {float} m20 the nineth element of the matrix
rob@100 11080 * @param {float} m21 the tenth element of the matrix
rob@100 11081 * @param {float} m22 the eleventh element of the matrix
rob@100 11082 * @param {float} m23 the twelveth element of the matrix
rob@100 11083 * @param {float} m30 the thirteenth element of the matrix
rob@100 11084 * @param {float} m31 the fourtheenth element of the matrix
rob@100 11085 * @param {float} m32 the fivetheenth element of the matrix
rob@100 11086 * @param {float} m33 the sixteenth element of the matrix
rob@100 11087 */
rob@100 11088 set: function() {
rob@100 11089 if (arguments.length === 16) {
rob@100 11090 this.elements = Array.prototype.slice.call(arguments);
rob@100 11091 } else if (arguments.length === 1 && arguments[0] instanceof PMatrix3D) {
rob@100 11092 this.elements = arguments[0].array();
rob@100 11093 } else if (arguments.length === 1 && arguments[0] instanceof Array) {
rob@100 11094 this.elements = arguments[0].slice();
rob@100 11095 }
rob@100 11096 },
rob@100 11097 /**
rob@100 11098 * @member PMatrix3D
rob@100 11099 * The get() function returns a copy of this PMatrix3D.
rob@100 11100 *
rob@100 11101 * @return {PMatrix3D} a copy of this PMatrix3D
rob@100 11102 */
rob@100 11103 get: function() {
rob@100 11104 var outgoing = new PMatrix3D();
rob@100 11105 outgoing.set(this.elements);
rob@100 11106 return outgoing;
rob@100 11107 },
rob@100 11108 /**
rob@100 11109 * @member PMatrix3D
rob@100 11110 * The reset() function sets this PMatrix3D to the identity matrix.
rob@100 11111 */
rob@100 11112 reset: function() {
rob@100 11113 this.elements = [1,0,0,0,
rob@100 11114 0,1,0,0,
rob@100 11115 0,0,1,0,
rob@100 11116 0,0,0,1];
rob@100 11117 },
rob@100 11118 /**
rob@100 11119 * @member PMatrix3D
rob@100 11120 * The array() function returns a copy of the element values.
rob@100 11121 * @addon
rob@100 11122 *
rob@100 11123 * @return {float[]} returns a copy of the element values
rob@100 11124 */
rob@100 11125 array: function array() {
rob@100 11126 return this.elements.slice();
rob@100 11127 },
rob@100 11128 /**
rob@100 11129 * @member PMatrix3D
rob@100 11130 * The translate() function translates this matrix by moving the current coordinates to the location specified by tx, ty, and tz.
rob@100 11131 *
rob@100 11132 * @param {float} tx the x-axis coordinate to move to
rob@100 11133 * @param {float} ty the y-axis coordinate to move to
rob@100 11134 * @param {float} tz the z-axis coordinate to move to
rob@100 11135 */
rob@100 11136 translate: function(tx, ty, tz) {
rob@100 11137 if (tz === undef) {
rob@100 11138 tz = 0;
rob@100 11139 }
rob@100 11140
rob@100 11141 this.elements[3] += tx * this.elements[0] + ty * this.elements[1] + tz * this.elements[2];
rob@100 11142 this.elements[7] += tx * this.elements[4] + ty * this.elements[5] + tz * this.elements[6];
rob@100 11143 this.elements[11] += tx * this.elements[8] + ty * this.elements[9] + tz * this.elements[10];
rob@100 11144 this.elements[15] += tx * this.elements[12] + ty * this.elements[13] + tz * this.elements[14];
rob@100 11145 },
rob@100 11146 /**
rob@100 11147 * @member PMatrix3D
rob@100 11148 * The transpose() function transpose this matrix.
rob@100 11149 */
rob@100 11150 transpose: function() {
rob@100 11151 var temp = this.elements[4];
rob@100 11152 this.elements[4] = this.elements[1];
rob@100 11153 this.elements[1] = temp;
rob@100 11154
rob@100 11155 temp = this.elements[8];
rob@100 11156 this.elements[8] = this.elements[2];
rob@100 11157 this.elements[2] = temp;
rob@100 11158
rob@100 11159 temp = this.elements[6];
rob@100 11160 this.elements[6] = this.elements[9];
rob@100 11161 this.elements[9] = temp;
rob@100 11162
rob@100 11163 temp = this.elements[3];
rob@100 11164 this.elements[3] = this.elements[12];
rob@100 11165 this.elements[12] = temp;
rob@100 11166
rob@100 11167 temp = this.elements[7];
rob@100 11168 this.elements[7] = this.elements[13];
rob@100 11169 this.elements[13] = temp;
rob@100 11170
rob@100 11171 temp = this.elements[11];
rob@100 11172 this.elements[11] = this.elements[14];
rob@100 11173 this.elements[14] = temp;
rob@100 11174 },
rob@100 11175 /**
rob@100 11176 * @member PMatrix3D
rob@100 11177 * The mult() function multiplied this matrix.
rob@100 11178 * If two array elements are passed in the function will multiply a two element vector against this matrix.
rob@100 11179 * If target is null or not length four, a new float array will be returned.
rob@100 11180 * The values for vec and target can be the same (though that's less efficient).
rob@100 11181 * If two PVectors are passed in the function multiply the x and y coordinates of a PVector against this matrix.
rob@100 11182 *
rob@100 11183 * @param {PVector} source, target the PVectors used to multiply this matrix
rob@100 11184 * @param {float[]} source, target the arrays used to multiply this matrix
rob@100 11185 *
rob@100 11186 * @return {PVector|float[]} returns a PVector or an array representing the new matrix
rob@100 11187 */
rob@100 11188 mult: function(source, target) {
rob@100 11189 var x, y, z, w;
rob@100 11190 if (source instanceof PVector) {
rob@100 11191 x = source.x;
rob@100 11192 y = source.y;
rob@100 11193 z = source.z;
rob@100 11194 w = 1;
rob@100 11195 if (!target) {
rob@100 11196 target = new PVector();
rob@100 11197 }
rob@100 11198 } else if (source instanceof Array) {
rob@100 11199 x = source[0];
rob@100 11200 y = source[1];
rob@100 11201 z = source[2];
rob@100 11202 w = source[3] || 1;
rob@100 11203
rob@100 11204 if ( !target || (target.length !== 3 && target.length !== 4) ) {
rob@100 11205 target = [0, 0, 0];
rob@100 11206 }
rob@100 11207 }
rob@100 11208
rob@100 11209 if (target instanceof Array) {
rob@100 11210 if (target.length === 3) {
rob@100 11211 target[0] = this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3];
rob@100 11212 target[1] = this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7];
rob@100 11213 target[2] = this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11];
rob@100 11214 } else if (target.length === 4) {
rob@100 11215 target[0] = this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3] * w;
rob@100 11216 target[1] = this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7] * w;
rob@100 11217 target[2] = this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11] * w;
rob@100 11218 target[3] = this.elements[12] * x + this.elements[13] * y + this.elements[14] * z + this.elements[15] * w;
rob@100 11219 }
rob@100 11220 }
rob@100 11221 if (target instanceof PVector) {
rob@100 11222 target.x = this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3];
rob@100 11223 target.y = this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7];
rob@100 11224 target.z = this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11];
rob@100 11225 }
rob@100 11226 return target;
rob@100 11227 },
rob@100 11228 /**
rob@100 11229 * @member PMatrix3D
rob@100 11230 * The preApply() function applies another matrix to the left of this one. Note that either a PMatrix3D or elements of a matrix can be passed in.
rob@100 11231 *
rob@100 11232 * @param {PMatrix3D} matrix the matrix to apply this matrix to
rob@100 11233 * @param {float} m00 the first element of the matrix
rob@100 11234 * @param {float} m01 the second element of the matrix
rob@100 11235 * @param {float} m02 the third element of the matrix
rob@100 11236 * @param {float} m03 the fourth element of the matrix
rob@100 11237 * @param {float} m10 the fifth element of the matrix
rob@100 11238 * @param {float} m11 the sixth element of the matrix
rob@100 11239 * @param {float} m12 the seventh element of the matrix
rob@100 11240 * @param {float} m13 the eight element of the matrix
rob@100 11241 * @param {float} m20 the nineth element of the matrix
rob@100 11242 * @param {float} m21 the tenth element of the matrix
rob@100 11243 * @param {float} m22 the eleventh element of the matrix
rob@100 11244 * @param {float} m23 the twelveth element of the matrix
rob@100 11245 * @param {float} m30 the thirteenth element of the matrix
rob@100 11246 * @param {float} m31 the fourtheenth element of the matrix
rob@100 11247 * @param {float} m32 the fivetheenth element of the matrix
rob@100 11248 * @param {float} m33 the sixteenth element of the matrix
rob@100 11249 */
rob@100 11250 preApply: function() {
rob@100 11251 var source;
rob@100 11252 if (arguments.length === 1 && arguments[0] instanceof PMatrix3D) {
rob@100 11253 source = arguments[0].array();
rob@100 11254 } else if (arguments.length === 16) {
rob@100 11255 source = Array.prototype.slice.call(arguments);
rob@100 11256 } else if (arguments.length === 1 && arguments[0] instanceof Array) {
rob@100 11257 source = arguments[0];
rob@100 11258 }
rob@100 11259
rob@100 11260 var result = [0, 0, 0, 0,
rob@100 11261 0, 0, 0, 0,
rob@100 11262 0, 0, 0, 0,
rob@100 11263 0, 0, 0, 0];
rob@100 11264 var e = 0;
rob@100 11265 for (var row = 0; row < 4; row++) {
rob@100 11266 for (var col = 0; col < 4; col++, e++) {
rob@100 11267 result[e] += this.elements[col + 0] * source[row * 4 + 0] + this.elements[col + 4] *
rob@100 11268 source[row * 4 + 1] + this.elements[col + 8] * source[row * 4 + 2] +
rob@100 11269 this.elements[col + 12] * source[row * 4 + 3];
rob@100 11270 }
rob@100 11271 }
rob@100 11272 this.elements = result.slice();
rob@100 11273 },
rob@100 11274 /**
rob@100 11275 * @member PMatrix3D
rob@100 11276 * The apply() function multiplies the current matrix by the one specified through the parameters. Note that either a PMatrix3D or a list of floats can be passed in.
rob@100 11277 *
rob@100 11278 * @param {PMatrix3D} matrix the matrix to apply this matrix to
rob@100 11279 * @param {float} m00 the first element of the matrix
rob@100 11280 * @param {float} m01 the second element of the matrix
rob@100 11281 * @param {float} m02 the third element of the matrix
rob@100 11282 * @param {float} m03 the fourth element of the matrix
rob@100 11283 * @param {float} m10 the fifth element of the matrix
rob@100 11284 * @param {float} m11 the sixth element of the matrix
rob@100 11285 * @param {float} m12 the seventh element of the matrix
rob@100 11286 * @param {float} m13 the eight element of the matrix
rob@100 11287 * @param {float} m20 the nineth element of the matrix
rob@100 11288 * @param {float} m21 the tenth element of the matrix
rob@100 11289 * @param {float} m22 the eleventh element of the matrix
rob@100 11290 * @param {float} m23 the twelveth element of the matrix
rob@100 11291 * @param {float} m30 the thirteenth element of the matrix
rob@100 11292 * @param {float} m31 the fourtheenth element of the matrix
rob@100 11293 * @param {float} m32 the fivetheenth element of the matrix
rob@100 11294 * @param {float} m33 the sixteenth element of the matrix
rob@100 11295 */
rob@100 11296 apply: function() {
rob@100 11297 var source;
rob@100 11298 if (arguments.length === 1 && arguments[0] instanceof PMatrix3D) {
rob@100 11299 source = arguments[0].array();
rob@100 11300 } else if (arguments.length === 16) {
rob@100 11301 source = Array.prototype.slice.call(arguments);
rob@100 11302 } else if (arguments.length === 1 && arguments[0] instanceof Array) {
rob@100 11303 source = arguments[0];
rob@100 11304 }
rob@100 11305
rob@100 11306 var result = [0, 0, 0, 0,
rob@100 11307 0, 0, 0, 0,
rob@100 11308 0, 0, 0, 0,
rob@100 11309 0, 0, 0, 0];
rob@100 11310 var e = 0;
rob@100 11311 for (var row = 0; row < 4; row++) {
rob@100 11312 for (var col = 0; col < 4; col++, e++) {
rob@100 11313 result[e] += this.elements[row * 4 + 0] * source[col + 0] + this.elements[row * 4 + 1] *
rob@100 11314 source[col + 4] + this.elements[row * 4 + 2] * source[col + 8] +
rob@100 11315 this.elements[row * 4 + 3] * source[col + 12];
rob@100 11316 }
rob@100 11317 }
rob@100 11318 this.elements = result.slice();
rob@100 11319 },
rob@100 11320 /**
rob@100 11321 * @member PMatrix3D
rob@100 11322 * The rotate() function rotates the matrix.
rob@100 11323 *
rob@100 11324 * @param {float} angle the angle of rotation in radiants
rob@100 11325 */
rob@100 11326 rotate: function(angle, v0, v1, v2) {
rob@100 11327 if (arguments.length < 4) {
rob@100 11328 this.rotateZ(angle);
rob@100 11329 } else {
rob@100 11330 var v = new PVector(v0, v1, v2);
rob@100 11331 var m = v.mag();
rob@100 11332 if (m === 0) {
rob@100 11333 return;
rob@100 11334 } else if (m != 1) {
rob@100 11335 v.normalize();
rob@100 11336 v0 = v.x;
rob@100 11337 v1 = v.y;
rob@100 11338 v2 = v.z;
rob@100 11339 }
rob@100 11340 var c = p.cos(angle);
rob@100 11341 var s = p.sin(angle);
rob@100 11342 var t = 1.0 - c;
rob@100 11343
rob@100 11344 this.apply((t * v0 * v0) + c,
rob@100 11345 (t * v0 * v1) - (s * v2),
rob@100 11346 (t * v0 * v2) + (s * v1),
rob@100 11347 0,
rob@100 11348 (t * v0 * v1) + (s * v2),
rob@100 11349 (t * v1 * v1) + c,
rob@100 11350 (t * v1 * v2) - (s * v0),
rob@100 11351 0,
rob@100 11352 (t * v0 * v2) - (s * v1),
rob@100 11353 (t * v1 * v2) + (s * v0),
rob@100 11354 (t * v2 * v2) + c,
rob@100 11355 0,
rob@100 11356 0, 0, 0, 1);
rob@100 11357 }
rob@100 11358 },
rob@100 11359 /**
rob@100 11360 * @member PMatrix3D
rob@100 11361 * The invApply() function applies the inverted matrix to this matrix.
rob@100 11362 *
rob@100 11363 * @param {float} m00 the first element of the matrix
rob@100 11364 * @param {float} m01 the second element of the matrix
rob@100 11365 * @param {float} m02 the third element of the matrix
rob@100 11366 * @param {float} m03 the fourth element of the matrix
rob@100 11367 * @param {float} m10 the fifth element of the matrix
rob@100 11368 * @param {float} m11 the sixth element of the matrix
rob@100 11369 * @param {float} m12 the seventh element of the matrix
rob@100 11370 * @param {float} m13 the eight element of the matrix
rob@100 11371 * @param {float} m20 the nineth element of the matrix
rob@100 11372 * @param {float} m21 the tenth element of the matrix
rob@100 11373 * @param {float} m22 the eleventh element of the matrix
rob@100 11374 * @param {float} m23 the twelveth element of the matrix
rob@100 11375 * @param {float} m30 the thirteenth element of the matrix
rob@100 11376 * @param {float} m31 the fourtheenth element of the matrix
rob@100 11377 * @param {float} m32 the fivetheenth element of the matrix
rob@100 11378 * @param {float} m33 the sixteenth element of the matrix
rob@100 11379 *
rob@100 11380 * @return {boolean} returns true if the operation was successful.
rob@100 11381 */
rob@100 11382 invApply: function() {
rob@100 11383 if (inverseCopy === undef) {
rob@100 11384 inverseCopy = new PMatrix3D();
rob@100 11385 }
rob@100 11386 var a = arguments;
rob@100 11387 inverseCopy.set(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8],
rob@100 11388 a[9], a[10], a[11], a[12], a[13], a[14], a[15]);
rob@100 11389
rob@100 11390 if (!inverseCopy.invert()) {
rob@100 11391 return false;
rob@100 11392 }
rob@100 11393 this.preApply(inverseCopy);
rob@100 11394 return true;
rob@100 11395 },
rob@100 11396 /**
rob@100 11397 * @member PMatrix3D
rob@100 11398 * The rotateZ() function rotates the matrix.
rob@100 11399 *
rob@100 11400 * @param {float} angle the angle of rotation in radiants
rob@100 11401 */
rob@100 11402 rotateX: function(angle) {
rob@100 11403 var c = p.cos(angle);
rob@100 11404 var s = p.sin(angle);
rob@100 11405 this.apply([1, 0, 0, 0, 0, c, -s, 0, 0, s, c, 0, 0, 0, 0, 1]);
rob@100 11406 },
rob@100 11407 /**
rob@100 11408 * @member PMatrix3D
rob@100 11409 * The rotateY() function rotates the matrix.
rob@100 11410 *
rob@100 11411 * @param {float} angle the angle of rotation in radiants
rob@100 11412 */
rob@100 11413 rotateY: function(angle) {
rob@100 11414 var c = p.cos(angle);
rob@100 11415 var s = p.sin(angle);
rob@100 11416 this.apply([c, 0, s, 0, 0, 1, 0, 0, -s, 0, c, 0, 0, 0, 0, 1]);
rob@100 11417 },
rob@100 11418 /**
rob@100 11419 * @member PMatrix3D
rob@100 11420 * The rotateZ() function rotates the matrix.
rob@100 11421 *
rob@100 11422 * @param {float} angle the angle of rotation in radiants
rob@100 11423 */
rob@100 11424 rotateZ: function(angle) {
rob@100 11425 var c = Math.cos(angle);
rob@100 11426 var s = Math.sin(angle);
rob@100 11427 this.apply([c, -s, 0, 0, s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
rob@100 11428 },
rob@100 11429 /**
rob@100 11430 * @member PMatrix3D
rob@100 11431 * The scale() function increases or decreases the size of a matrix by expanding and contracting vertices. When only one parameter is specified scale will occur in all dimensions.
rob@100 11432 * This is equivalent to a three parameter call.
rob@100 11433 *
rob@100 11434 * @param {float} sx the amount to scale on the x-axis
rob@100 11435 * @param {float} sy the amount to scale on the y-axis
rob@100 11436 * @param {float} sz the amount to scale on the z-axis
rob@100 11437 */
rob@100 11438 scale: function(sx, sy, sz) {
rob@100 11439 if (sx && !sy && !sz) {
rob@100 11440 sy = sz = sx;
rob@100 11441 } else if (sx && sy && !sz) {
rob@100 11442 sz = 1;
rob@100 11443 }
rob@100 11444
rob@100 11445 if (sx && sy && sz) {
rob@100 11446 this.elements[0] *= sx;
rob@100 11447 this.elements[1] *= sy;
rob@100 11448 this.elements[2] *= sz;
rob@100 11449 this.elements[4] *= sx;
rob@100 11450 this.elements[5] *= sy;
rob@100 11451 this.elements[6] *= sz;
rob@100 11452 this.elements[8] *= sx;
rob@100 11453 this.elements[9] *= sy;
rob@100 11454 this.elements[10] *= sz;
rob@100 11455 this.elements[12] *= sx;
rob@100 11456 this.elements[13] *= sy;
rob@100 11457 this.elements[14] *= sz;
rob@100 11458 }
rob@100 11459 },
rob@100 11460 /**
rob@100 11461 * @member PMatrix3D
rob@100 11462 * The skewX() function skews the matrix along the x-axis the amount specified by the angle parameter.
rob@100 11463 * Angles should be specified in radians (values from 0 to PI*2) or converted to radians with the <b>radians()</b> function.
rob@100 11464 *
rob@100 11465 * @param {float} angle angle of skew specified in radians
rob@100 11466 */
rob@100 11467 skewX: function(angle) {
rob@100 11468 var t = Math.tan(angle);
rob@100 11469 this.apply(1, t, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
rob@100 11470 },
rob@100 11471 /**
rob@100 11472 * @member PMatrix3D
rob@100 11473 * The skewY() function skews the matrix along the y-axis the amount specified by the angle parameter.
rob@100 11474 * Angles should be specified in radians (values from 0 to PI*2) or converted to radians with the <b>radians()</b> function.
rob@100 11475 *
rob@100 11476 * @param {float} angle angle of skew specified in radians
rob@100 11477 */
rob@100 11478 skewY: function(angle) {
rob@100 11479 var t = Math.tan(angle);
rob@100 11480 this.apply(1, 0, 0, 0, t, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
rob@100 11481 },
rob@100 11482 /**
rob@100 11483 * @member PMatrix3D
rob@100 11484 * The shearX() function shears the matrix along the x-axis the amount specified by the angle parameter.
rob@100 11485 * Angles should be specified in radians (values from 0 to PI*2) or converted to radians with the <b>radians()</b> function.
rob@100 11486 *
rob@100 11487 * @param {float} angle angle of shear specified in radians
rob@100 11488 */
rob@100 11489 shearX: function(angle) {
rob@100 11490 var t = Math.tan(angle);
rob@100 11491 this.apply(1, t, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
rob@100 11492 },
rob@100 11493 /**
rob@100 11494 * @member PMatrix3D
rob@100 11495 * The shearY() function shears the matrix along the y-axis the amount specified by the angle parameter.
rob@100 11496 * Angles should be specified in radians (values from 0 to PI*2) or converted to radians with the <b>radians()</b> function.
rob@100 11497 *
rob@100 11498 * @param {float} angle angle of shear specified in radians
rob@100 11499 */
rob@100 11500 shearY: function(angle) {
rob@100 11501 var t = Math.tan(angle);
rob@100 11502 this.apply(1, 0, 0, 0, t, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
rob@100 11503 },
rob@100 11504 multX: function(x, y, z, w) {
rob@100 11505 if (!z) {
rob@100 11506 return this.elements[0] * x + this.elements[1] * y + this.elements[3];
rob@100 11507 }
rob@100 11508 if (!w) {
rob@100 11509 return this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3];
rob@100 11510 }
rob@100 11511 return this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3] * w;
rob@100 11512 },
rob@100 11513 multY: function(x, y, z, w) {
rob@100 11514 if (!z) {
rob@100 11515 return this.elements[4] * x + this.elements[5] * y + this.elements[7];
rob@100 11516 }
rob@100 11517 if (!w) {
rob@100 11518 return this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7];
rob@100 11519 }
rob@100 11520 return this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7] * w;
rob@100 11521 },
rob@100 11522 multZ: function(x, y, z, w) {
rob@100 11523 if (!w) {
rob@100 11524 return this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11];
rob@100 11525 }
rob@100 11526 return this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11] * w;
rob@100 11527 },
rob@100 11528 multW: function(x, y, z, w) {
rob@100 11529 if (!w) {
rob@100 11530 return this.elements[12] * x + this.elements[13] * y + this.elements[14] * z + this.elements[15];
rob@100 11531 }
rob@100 11532 return this.elements[12] * x + this.elements[13] * y + this.elements[14] * z + this.elements[15] * w;
rob@100 11533 },
rob@100 11534 /**
rob@100 11535 * @member PMatrix3D
rob@100 11536 * The invert() function inverts this matrix
rob@100 11537 *
rob@100 11538 * @return {boolean} true if successful
rob@100 11539 */
rob@100 11540 invert: function() {
rob@100 11541 var fA0 = this.elements[0] * this.elements[5] - this.elements[1] * this.elements[4];
rob@100 11542 var fA1 = this.elements[0] * this.elements[6] - this.elements[2] * this.elements[4];
rob@100 11543 var fA2 = this.elements[0] * this.elements[7] - this.elements[3] * this.elements[4];
rob@100 11544 var fA3 = this.elements[1] * this.elements[6] - this.elements[2] * this.elements[5];
rob@100 11545 var fA4 = this.elements[1] * this.elements[7] - this.elements[3] * this.elements[5];
rob@100 11546 var fA5 = this.elements[2] * this.elements[7] - this.elements[3] * this.elements[6];
rob@100 11547 var fB0 = this.elements[8] * this.elements[13] - this.elements[9] * this.elements[12];
rob@100 11548 var fB1 = this.elements[8] * this.elements[14] - this.elements[10] * this.elements[12];
rob@100 11549 var fB2 = this.elements[8] * this.elements[15] - this.elements[11] * this.elements[12];
rob@100 11550 var fB3 = this.elements[9] * this.elements[14] - this.elements[10] * this.elements[13];
rob@100 11551 var fB4 = this.elements[9] * this.elements[15] - this.elements[11] * this.elements[13];
rob@100 11552 var fB5 = this.elements[10] * this.elements[15] - this.elements[11] * this.elements[14];
rob@100 11553
rob@100 11554 // Determinant
rob@100 11555 var fDet = fA0 * fB5 - fA1 * fB4 + fA2 * fB3 + fA3 * fB2 - fA4 * fB1 + fA5 * fB0;
rob@100 11556
rob@100 11557 // Account for a very small value
rob@100 11558 // return false if not successful.
rob@100 11559 if (Math.abs(fDet) <= 1e-9) {
rob@100 11560 return false;
rob@100 11561 }
rob@100 11562
rob@100 11563 var kInv = [];
rob@100 11564 kInv[0] = +this.elements[5] * fB5 - this.elements[6] * fB4 + this.elements[7] * fB3;
rob@100 11565 kInv[4] = -this.elements[4] * fB5 + this.elements[6] * fB2 - this.elements[7] * fB1;
rob@100 11566 kInv[8] = +this.elements[4] * fB4 - this.elements[5] * fB2 + this.elements[7] * fB0;
rob@100 11567 kInv[12] = -this.elements[4] * fB3 + this.elements[5] * fB1 - this.elements[6] * fB0;
rob@100 11568 kInv[1] = -this.elements[1] * fB5 + this.elements[2] * fB4 - this.elements[3] * fB3;
rob@100 11569 kInv[5] = +this.elements[0] * fB5 - this.elements[2] * fB2 + this.elements[3] * fB1;
rob@100 11570 kInv[9] = -this.elements[0] * fB4 + this.elements[1] * fB2 - this.elements[3] * fB0;
rob@100 11571 kInv[13] = +this.elements[0] * fB3 - this.elements[1] * fB1 + this.elements[2] * fB0;
rob@100 11572 kInv[2] = +this.elements[13] * fA5 - this.elements[14] * fA4 + this.elements[15] * fA3;
rob@100 11573 kInv[6] = -this.elements[12] * fA5 + this.elements[14] * fA2 - this.elements[15] * fA1;
rob@100 11574 kInv[10] = +this.elements[12] * fA4 - this.elements[13] * fA2 + this.elements[15] * fA0;
rob@100 11575 kInv[14] = -this.elements[12] * fA3 + this.elements[13] * fA1 - this.elements[14] * fA0;
rob@100 11576 kInv[3] = -this.elements[9] * fA5 + this.elements[10] * fA4 - this.elements[11] * fA3;
rob@100 11577 kInv[7] = +this.elements[8] * fA5 - this.elements[10] * fA2 + this.elements[11] * fA1;
rob@100 11578 kInv[11] = -this.elements[8] * fA4 + this.elements[9] * fA2 - this.elements[11] * fA0;
rob@100 11579 kInv[15] = +this.elements[8] * fA3 - this.elements[9] * fA1 + this.elements[10] * fA0;
rob@100 11580
rob@100 11581 // Inverse using Determinant
rob@100 11582 var fInvDet = 1.0 / fDet;
rob@100 11583 kInv[0] *= fInvDet;
rob@100 11584 kInv[1] *= fInvDet;
rob@100 11585 kInv[2] *= fInvDet;
rob@100 11586 kInv[3] *= fInvDet;
rob@100 11587 kInv[4] *= fInvDet;
rob@100 11588 kInv[5] *= fInvDet;
rob@100 11589 kInv[6] *= fInvDet;
rob@100 11590 kInv[7] *= fInvDet;
rob@100 11591 kInv[8] *= fInvDet;
rob@100 11592 kInv[9] *= fInvDet;
rob@100 11593 kInv[10] *= fInvDet;
rob@100 11594 kInv[11] *= fInvDet;
rob@100 11595 kInv[12] *= fInvDet;
rob@100 11596 kInv[13] *= fInvDet;
rob@100 11597 kInv[14] *= fInvDet;
rob@100 11598 kInv[15] *= fInvDet;
rob@100 11599
rob@100 11600 this.elements = kInv.slice();
rob@100 11601 return true;
rob@100 11602 },
rob@100 11603 toString: function() {
rob@100 11604 var str = "";
rob@100 11605 for (var i = 0; i < 15; i++) {
rob@100 11606 str += this.elements[i] + ", ";
rob@100 11607 }
rob@100 11608 str += this.elements[15];
rob@100 11609 return str;
rob@100 11610 },
rob@100 11611 /**
rob@100 11612 * @member PMatrix3D
rob@100 11613 * The print() function prints out the elements of this matrix
rob@100 11614 */
rob@100 11615 print: function() {
rob@100 11616 var digits = printMatrixHelper(this.elements);
rob@100 11617
rob@100 11618 var output = "" + p.nfs(this.elements[0], digits, 4) + " " + p.nfs(this.elements[1], digits, 4) +
rob@100 11619 " " + p.nfs(this.elements[2], digits, 4) + " " + p.nfs(this.elements[3], digits, 4) +
rob@100 11620 "\n" + p.nfs(this.elements[4], digits, 4) + " " + p.nfs(this.elements[5], digits, 4) +
rob@100 11621 " " + p.nfs(this.elements[6], digits, 4) + " " + p.nfs(this.elements[7], digits, 4) +
rob@100 11622 "\n" + p.nfs(this.elements[8], digits, 4) + " " + p.nfs(this.elements[9], digits, 4) +
rob@100 11623 " " + p.nfs(this.elements[10], digits, 4) + " " + p.nfs(this.elements[11], digits, 4) +
rob@100 11624 "\n" + p.nfs(this.elements[12], digits, 4) + " " + p.nfs(this.elements[13], digits, 4) +
rob@100 11625 " " + p.nfs(this.elements[14], digits, 4) + " " + p.nfs(this.elements[15], digits, 4) + "\n\n";
rob@100 11626 p.println(output);
rob@100 11627 },
rob@100 11628 invTranslate: function(tx, ty, tz) {
rob@100 11629 this.preApply(1, 0, 0, -tx, 0, 1, 0, -ty, 0, 0, 1, -tz, 0, 0, 0, 1);
rob@100 11630 },
rob@100 11631 invRotateX: function(angle) {
rob@100 11632 var c = Math.cos(-angle);
rob@100 11633 var s = Math.sin(-angle);
rob@100 11634 this.preApply([1, 0, 0, 0, 0, c, -s, 0, 0, s, c, 0, 0, 0, 0, 1]);
rob@100 11635 },
rob@100 11636 invRotateY: function(angle) {
rob@100 11637 var c = Math.cos(-angle);
rob@100 11638 var s = Math.sin(-angle);
rob@100 11639 this.preApply([c, 0, s, 0, 0, 1, 0, 0, -s, 0, c, 0, 0, 0, 0, 1]);
rob@100 11640 },
rob@100 11641 invRotateZ: function(angle) {
rob@100 11642 var c = Math.cos(-angle);
rob@100 11643 var s = Math.sin(-angle);
rob@100 11644 this.preApply([c, -s, 0, 0, s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
rob@100 11645 },
rob@100 11646 invScale: function(x, y, z) {
rob@100 11647 this.preApply([1 / x, 0, 0, 0, 0, 1 / y, 0, 0, 0, 0, 1 / z, 0, 0, 0, 0, 1]);
rob@100 11648 }
rob@100 11649 };
rob@100 11650
rob@100 11651 /**
rob@100 11652 * @private
rob@100 11653 * The matrix stack stores the transformations and translations that occur within the space.
rob@100 11654 */
rob@100 11655 var PMatrixStack = p.PMatrixStack = function() {
rob@100 11656 this.matrixStack = [];
rob@100 11657 };
rob@100 11658
rob@100 11659 /**
rob@100 11660 * @member PMatrixStack
rob@100 11661 * load pushes the matrix given in the function into the stack
rob@100 11662 *
rob@100 11663 * @param {Object | Array} matrix the matrix to be pushed into the stack
rob@100 11664 */
rob@100 11665 PMatrixStack.prototype.load = function() {
rob@100 11666 var tmpMatrix = drawing.$newPMatrix();
rob@100 11667
rob@100 11668 if (arguments.length === 1) {
rob@100 11669 tmpMatrix.set(arguments[0]);
rob@100 11670 } else {
rob@100 11671 tmpMatrix.set(arguments);
rob@100 11672 }
rob@100 11673 this.matrixStack.push(tmpMatrix);
rob@100 11674 };
rob@100 11675
rob@100 11676 Drawing2D.prototype.$newPMatrix = function() {
rob@100 11677 return new PMatrix2D();
rob@100 11678 };
rob@100 11679
rob@100 11680 Drawing3D.prototype.$newPMatrix = function() {
rob@100 11681 return new PMatrix3D();
rob@100 11682 };
rob@100 11683
rob@100 11684 /**
rob@100 11685 * @member PMatrixStack
rob@100 11686 * push adds a duplicate of the top of the stack onto the stack - uses the peek function
rob@100 11687 */
rob@100 11688 PMatrixStack.prototype.push = function() {
rob@100 11689 this.matrixStack.push(this.peek());
rob@100 11690 };
rob@100 11691
rob@100 11692 /**
rob@100 11693 * @member PMatrixStack
rob@100 11694 * pop removes returns the matrix at the top of the stack
rob@100 11695 *
rob@100 11696 * @returns {Object} the matrix at the top of the stack
rob@100 11697 */
rob@100 11698 PMatrixStack.prototype.pop = function() {
rob@100 11699 return this.matrixStack.pop();
rob@100 11700 };
rob@100 11701
rob@100 11702 /**
rob@100 11703 * @member PMatrixStack
rob@100 11704 * peek returns but doesn't remove the matrix at the top of the stack
rob@100 11705 *
rob@100 11706 * @returns {Object} the matrix at the top of the stack
rob@100 11707 */
rob@100 11708 PMatrixStack.prototype.peek = function() {
rob@100 11709 var tmpMatrix = drawing.$newPMatrix();
rob@100 11710
rob@100 11711 tmpMatrix.set(this.matrixStack[this.matrixStack.length - 1]);
rob@100 11712 return tmpMatrix;
rob@100 11713 };
rob@100 11714
rob@100 11715 /**
rob@100 11716 * @member PMatrixStack
rob@100 11717 * this function multiplies the matrix at the top of the stack with the matrix given as a parameter
rob@100 11718 *
rob@100 11719 * @param {Object | Array} matrix the matrix to be multiplied into the stack
rob@100 11720 */
rob@100 11721 PMatrixStack.prototype.mult = function(matrix) {
rob@100 11722 this.matrixStack[this.matrixStack.length - 1].apply(matrix);
rob@100 11723 };
rob@100 11724
rob@100 11725 ////////////////////////////////////////////////////////////////////////////
rob@100 11726 // Array handling
rob@100 11727 ////////////////////////////////////////////////////////////////////////////
rob@100 11728
rob@100 11729 /**
rob@100 11730 * The split() function breaks a string into pieces using a character or string
rob@100 11731 * as the divider. The delim parameter specifies the character or characters that
rob@100 11732 * mark the boundaries between each piece. A String[] array is returned that contains
rob@100 11733 * each of the pieces.
rob@100 11734 * If the result is a set of numbers, you can convert the String[] array to to a float[]
rob@100 11735 * or int[] array using the datatype conversion functions int() and float() (see example above).
rob@100 11736 * The splitTokens() function works in a similar fashion, except that it splits using a range
rob@100 11737 * of characters instead of a specific character or sequence.
rob@100 11738 *
rob@100 11739 * @param {String} str the String to be split
rob@100 11740 * @param {String} delim the character or String used to separate the data
rob@100 11741 *
rob@100 11742 * @returns {string[]} The new string array
rob@100 11743 *
rob@100 11744 * @see splitTokens
rob@100 11745 * @see join
rob@100 11746 * @see trim
rob@100 11747 */
rob@100 11748 p.split = function(str, delim) {
rob@100 11749 return str.split(delim);
rob@100 11750 };
rob@100 11751
rob@100 11752 /**
rob@100 11753 * The splitTokens() function splits a String at one or many character "tokens." The tokens
rob@100 11754 * parameter specifies the character or characters to be used as a boundary.
rob@100 11755 * If no tokens character is specified, any whitespace character is used to split.
rob@100 11756 * Whitespace characters include tab (\t), line feed (\n), carriage return (\r), form
rob@100 11757 * feed (\f), and space. To convert a String to an array of integers or floats, use the
rob@100 11758 * datatype conversion functions int() and float() to convert the array of Strings.
rob@100 11759 *
rob@100 11760 * @param {String} str the String to be split
rob@100 11761 * @param {Char[]} tokens list of individual characters that will be used as separators
rob@100 11762 *
rob@100 11763 * @returns {string[]} The new string array
rob@100 11764 *
rob@100 11765 * @see split
rob@100 11766 * @see join
rob@100 11767 * @see trim
rob@100 11768 */
rob@100 11769 p.splitTokens = function(str, tokens) {
rob@100 11770 if (tokens === undef) {
rob@100 11771 return str.split(/\s+/g);
rob@100 11772 }
rob@100 11773
rob@100 11774 var chars = tokens.split(/()/g),
rob@100 11775 buffer = "",
rob@100 11776 len = str.length,
rob@100 11777 i, c,
rob@100 11778 tokenized = [];
rob@100 11779
rob@100 11780 for (i = 0; i < len; i++) {
rob@100 11781 c = str[i];
rob@100 11782 if (chars.indexOf(c) > -1) {
rob@100 11783 if (buffer !== "") {
rob@100 11784 tokenized.push(buffer);
rob@100 11785 }
rob@100 11786 buffer = "";
rob@100 11787 } else {
rob@100 11788 buffer += c;
rob@100 11789 }
rob@100 11790 }
rob@100 11791
rob@100 11792 if (buffer !== "") {
rob@100 11793 tokenized.push(buffer);
rob@100 11794 }
rob@100 11795
rob@100 11796 return tokenized;
rob@100 11797 };
rob@100 11798
rob@100 11799 /**
rob@100 11800 * Expands an array by one element and adds data to the new position. The datatype of
rob@100 11801 * the element parameter must be the same as the datatype of the array.
rob@100 11802 * When using an array of objects, the data returned from the function must be cast to
rob@100 11803 * the object array's data type. For example: SomeClass[] items = (SomeClass[])
rob@100 11804 * append(originalArray, element).
rob@100 11805 *
rob@100 11806 * @param {boolean[]|byte[]|char[]|int[]|float[]|String[]|array of objects} array boolean[],
rob@100 11807 * byte[], char[], int[], float[], or String[], or an array of objects
rob@100 11808 * @param {boolean[]|byte[]|char[]|int[]|float[]|String[]|array of objects} element new data for the array
rob@100 11809 *
rob@100 11810 * @returns Array (the same datatype as the input)
rob@100 11811 *
rob@100 11812 * @see shorten
rob@100 11813 * @see expand
rob@100 11814 */
rob@100 11815 p.append = function(array, element) {
rob@100 11816 array[array.length] = element;
rob@100 11817 return array;
rob@100 11818 };
rob@100 11819
rob@100 11820 /**
rob@100 11821 * Concatenates two arrays. For example, concatenating the array { 1, 2, 3 } and the
rob@100 11822 * array { 4, 5, 6 } yields { 1, 2, 3, 4, 5, 6 }. Both parameters must be arrays of the
rob@100 11823 * same datatype.
rob@100 11824 * When using an array of objects, the data returned from the function must be cast to the
rob@100 11825 * object array's data type. For example: SomeClass[] items = (SomeClass[]) concat(array1, array2).
rob@100 11826 *
rob@100 11827 * @param {boolean[]|byte[]|char[]|int[]|float[]|String[]|array of objects} array1 boolean[],
rob@100 11828 * byte[], char[], int[], float[], String[], or an array of objects
rob@100 11829 * @param {boolean[]|byte[]|char[]|int[]|float[]|String[]|array of objects} array2 boolean[],
rob@100 11830 * byte[], char[], int[], float[], String[], or an array of objects
rob@100 11831 *
rob@100 11832 * @returns Array (the same datatype as the input)
rob@100 11833 *
rob@100 11834 * @see splice
rob@100 11835 */
rob@100 11836 p.concat = function(array1, array2) {
rob@100 11837 return array1.concat(array2);
rob@100 11838 };
rob@100 11839
rob@100 11840 /**
rob@100 11841 * Sorts an array of numbers from smallest to largest and puts an array of
rob@100 11842 * words in alphabetical order. The original array is not modified, a
rob@100 11843 * re-ordered array is returned. The count parameter states the number of
rob@100 11844 * elements to sort. For example if there are 12 elements in an array and
rob@100 11845 * if count is the value 5, only the first five elements on the array will
rob@100 11846 * be sorted. Alphabetical ordering is case insensitive.
rob@100 11847 *
rob@100 11848 * @param {String[] | int[] | float[]} array Array of elements to sort
rob@100 11849 * @param {int} numElem Number of elements to sort
rob@100 11850 *
rob@100 11851 * @returns {String[] | int[] | float[]} Array (same datatype as the input)
rob@100 11852 *
rob@100 11853 * @see reverse
rob@100 11854 */
rob@100 11855 p.sort = function(array, numElem) {
rob@100 11856 var ret = [];
rob@100 11857
rob@100 11858 // depending on the type used (int, float) or string
rob@100 11859 // we'll need to use a different compare function
rob@100 11860 if (array.length > 0) {
rob@100 11861 // copy since we need to return another array
rob@100 11862 var elemsToCopy = numElem > 0 ? numElem : array.length;
rob@100 11863 for (var i = 0; i < elemsToCopy; i++) {
rob@100 11864 ret.push(array[i]);
rob@100 11865 }
rob@100 11866 if (typeof array[0] === "string") {
rob@100 11867 ret.sort();
rob@100 11868 }
rob@100 11869 // int or float
rob@100 11870 else {
rob@100 11871 ret.sort(function(a, b) {
rob@100 11872 return a - b;
rob@100 11873 });
rob@100 11874 }
rob@100 11875
rob@100 11876 // copy on the rest of the elements that were not sorted in case the user
rob@100 11877 // only wanted a subset of an array to be sorted.
rob@100 11878 if (numElem > 0) {
rob@100 11879 for (var j = ret.length; j < array.length; j++) {
rob@100 11880 ret.push(array[j]);
rob@100 11881 }
rob@100 11882 }
rob@100 11883 }
rob@100 11884 return ret;
rob@100 11885 };
rob@100 11886
rob@100 11887 /**
rob@100 11888 * Inserts a value or array of values into an existing array. The first two parameters must
rob@100 11889 * be of the same datatype. The array parameter defines the array which will be modified
rob@100 11890 * and the second parameter defines the data which will be inserted. When using an array
rob@100 11891 * of objects, the data returned from the function must be cast to the object array's data
rob@100 11892 * type. For example: SomeClass[] items = (SomeClass[]) splice(array1, array2, index).
rob@100 11893 *
rob@100 11894 * @param {boolean[]|byte[]|char[]|int[]|float[]|String[]|array of objects} array boolean[],
rob@100 11895 * byte[], char[], int[], float[], String[], or an array of objects
rob@100 11896 * @param {boolean|byte|char|int|float|String|boolean[]|byte[]|char[]|int[]|float[]|String[]|array of objects}
rob@100 11897 * value boolean, byte, char, int, float, String, boolean[], byte[], char[], int[],
rob@100 11898 * float[], String[], or other Object: value or an array of objects to be spliced in
rob@100 11899 * @param {int} index position in the array from which to insert data
rob@100 11900 *
rob@100 11901 * @returns Array (the same datatype as the input)
rob@100 11902 *
rob@100 11903 * @see contract
rob@100 11904 * @see subset
rob@100 11905 */
rob@100 11906 p.splice = function(array, value, index) {
rob@100 11907
rob@100 11908 // Trying to splice an empty array into "array" in P5 won't do
rob@100 11909 // anything, just return the original.
rob@100 11910 if(value.length === 0)
rob@100 11911 {
rob@100 11912 return array;
rob@100 11913 }
rob@100 11914
rob@100 11915 // If the second argument was an array, we'll need to iterate over all
rob@100 11916 // the "value" elements and add one by one because
rob@100 11917 // array.splice(index, 0, value);
rob@100 11918 // would create a multi-dimensional array which isn't what we want.
rob@100 11919 if(value instanceof Array) {
rob@100 11920 for(var i = 0, j = index; i < value.length; j++,i++) {
rob@100 11921 array.splice(j, 0, value[i]);
rob@100 11922 }
rob@100 11923 } else {
rob@100 11924 array.splice(index, 0, value);
rob@100 11925 }
rob@100 11926
rob@100 11927 return array;
rob@100 11928 };
rob@100 11929
rob@100 11930 /**
rob@100 11931 * Extracts an array of elements from an existing array. The array parameter defines the
rob@100 11932 * array from which the elements will be copied and the offset and length parameters determine
rob@100 11933 * which elements to extract. If no length is given, elements will be extracted from the offset
rob@100 11934 * to the end of the array. When specifying the offset remember the first array element is 0.
rob@100 11935 * This function does not change the source array.
rob@100 11936 * When using an array of objects, the data returned from the function must be cast to the
rob@100 11937 * object array's data type.
rob@100 11938 *
rob@100 11939 * @param {boolean[]|byte[]|char[]|int[]|float[]|String[]|array of objects} array boolean[],
rob@100 11940 * byte[], char[], int[], float[], String[], or an array of objects
rob@100 11941 * @param {int} offset position to begin
rob@100 11942 * @param {int} length number of values to extract
rob@100 11943 *
rob@100 11944 * @returns Array (the same datatype as the input)
rob@100 11945 *
rob@100 11946 * @see splice
rob@100 11947 */
rob@100 11948 p.subset = function(array, offset, length) {
rob@100 11949 var end = (length !== undef) ? offset + length : array.length;
rob@100 11950 return array.slice(offset, end);
rob@100 11951 };
rob@100 11952
rob@100 11953 /**
rob@100 11954 * Combines an array of Strings into one String, each separated by the character(s) used for
rob@100 11955 * the separator parameter. To join arrays of ints or floats, it's necessary to first convert
rob@100 11956 * them to strings using nf() or nfs().
rob@100 11957 *
rob@100 11958 * @param {Array} array array of Strings
rob@100 11959 * @param {char|String} separator char or String to be placed between each item
rob@100 11960 *
rob@100 11961 * @returns {String} The combined string
rob@100 11962 *
rob@100 11963 * @see split
rob@100 11964 * @see trim
rob@100 11965 * @see nf
rob@100 11966 * @see nfs
rob@100 11967 */
rob@100 11968 p.join = function(array, seperator) {
rob@100 11969 return array.join(seperator);
rob@100 11970 };
rob@100 11971
rob@100 11972 /**
rob@100 11973 * Decreases an array by one element and returns the shortened array. When using an
rob@100 11974 * array of objects, the data returned from the function must be cast to the object array's
rob@100 11975 * data type. For example: SomeClass[] items = (SomeClass[]) shorten(originalArray).
rob@100 11976 *
rob@100 11977 * @param {boolean[]|byte[]|char[]|int[]|float[]|String[]|array of objects} array
rob@100 11978 * boolean[], byte[], char[], int[], float[], or String[], or an array of objects
rob@100 11979 *
rob@100 11980 * @returns Array (the same datatype as the input)
rob@100 11981 *
rob@100 11982 * @see append
rob@100 11983 * @see expand
rob@100 11984 */
rob@100 11985 p.shorten = function(ary) {
rob@100 11986 var newary = [];
rob@100 11987
rob@100 11988 // copy array into new array
rob@100 11989 var len = ary.length;
rob@100 11990 for (var i = 0; i < len; i++) {
rob@100 11991 newary[i] = ary[i];
rob@100 11992 }
rob@100 11993 newary.pop();
rob@100 11994
rob@100 11995 return newary;
rob@100 11996 };
rob@100 11997
rob@100 11998 /**
rob@100 11999 * Increases the size of an array. By default, this function doubles the size of the array,
rob@100 12000 * but the optional newSize parameter provides precise control over the increase in size.
rob@100 12001 * When using an array of objects, the data returned from the function must be cast to the
rob@100 12002 * object array's data type. For example: SomeClass[] items = (SomeClass[]) expand(originalArray).
rob@100 12003 *
rob@100 12004 * @param {boolean[]|byte[]|char[]|int[]|float[]|String[]|array of objects} ary
rob@100 12005 * boolean[], byte[], char[], int[], float[], String[], or an array of objects
rob@100 12006 * @param {int} newSize positive int: new size for the array
rob@100 12007 *
rob@100 12008 * @returns Array (the same datatype as the input)
rob@100 12009 *
rob@100 12010 * @see contract
rob@100 12011 */
rob@100 12012 p.expand = function(ary, targetSize) {
rob@100 12013 var temp = ary.slice(0),
rob@100 12014 newSize = targetSize || ary.length * 2;
rob@100 12015 temp.length = newSize;
rob@100 12016 return temp;
rob@100 12017 };
rob@100 12018
rob@100 12019 /**
rob@100 12020 * Copies an array (or part of an array) to another array. The src array is copied to the
rob@100 12021 * dst array, beginning at the position specified by srcPos and into the position specified
rob@100 12022 * by dstPos. The number of elements to copy is determined by length. The simplified version
rob@100 12023 * with two arguments copies an entire array to another of the same size. It is equivalent
rob@100 12024 * to "arrayCopy(src, 0, dst, 0, src.length)". This function is far more efficient for copying
rob@100 12025 * array data than iterating through a for and copying each element.
rob@100 12026 *
rob@100 12027 * @param {Array} src an array of any data type: the source array
rob@100 12028 * @param {Array} dest an array of any data type (as long as it's the same as src): the destination array
rob@100 12029 * @param {int} srcPos starting position in the source array
rob@100 12030 * @param {int} destPos starting position in the destination array
rob@100 12031 * @param {int} length number of array elements to be copied
rob@100 12032 *
rob@100 12033 * @returns none
rob@100 12034 */
rob@100 12035 p.arrayCopy = function() { // src, srcPos, dest, destPos, length) {
rob@100 12036 var src, srcPos = 0, dest, destPos = 0, length;
rob@100 12037
rob@100 12038 if (arguments.length === 2) {
rob@100 12039 // recall itself and copy src to dest from start index 0 to 0 of src.length
rob@100 12040 src = arguments[0];
rob@100 12041 dest = arguments[1];
rob@100 12042 length = src.length;
rob@100 12043 } else if (arguments.length === 3) {
rob@100 12044 // recall itself and copy src to dest from start index 0 to 0 of length
rob@100 12045 src = arguments[0];
rob@100 12046 dest = arguments[1];
rob@100 12047 length = arguments[2];
rob@100 12048 } else if (arguments.length === 5) {
rob@100 12049 src = arguments[0];
rob@100 12050 srcPos = arguments[1];
rob@100 12051 dest = arguments[2];
rob@100 12052 destPos = arguments[3];
rob@100 12053 length = arguments[4];
rob@100 12054 }
rob@100 12055
rob@100 12056 // copy src to dest from index srcPos to index destPos of length recursivly on objects
rob@100 12057 for (var i = srcPos, j = destPos; i < length + srcPos; i++, j++) {
rob@100 12058 if (dest[j] !== undef) {
rob@100 12059 dest[j] = src[i];
rob@100 12060 } else {
rob@100 12061 throw "array index out of bounds exception";
rob@100 12062 }
rob@100 12063 }
rob@100 12064 };
rob@100 12065
rob@100 12066 /**
rob@100 12067 * Reverses the order of an array.
rob@100 12068 *
rob@100 12069 * @param {boolean[]|byte[]|char[]|int[]|float[]|String[]} array
rob@100 12070 * boolean[], byte[], char[], int[], float[], or String[]
rob@100 12071 *
rob@100 12072 * @returns Array (the same datatype as the input)
rob@100 12073 *
rob@100 12074 * @see sort
rob@100 12075 */
rob@100 12076 p.reverse = function(array) {
rob@100 12077 return array.reverse();
rob@100 12078 };
rob@100 12079
rob@100 12080
rob@100 12081 ////////////////////////////////////////////////////////////////////////////
rob@100 12082 // Color functions
rob@100 12083 ////////////////////////////////////////////////////////////////////////////
rob@100 12084
rob@100 12085 // helper functions for internal blending modes
rob@100 12086 p.mix = function(a, b, f) {
rob@100 12087 return a + (((b - a) * f) >> 8);
rob@100 12088 };
rob@100 12089
rob@100 12090 p.peg = function(n) {
rob@100 12091 return (n < 0) ? 0 : ((n > 255) ? 255 : n);
rob@100 12092 };
rob@100 12093
rob@100 12094 // blending modes
rob@100 12095 /**
rob@100 12096 * These are internal blending modes used for BlendColor()
rob@100 12097 *
rob@100 12098 * @param {Color} c1 First Color to blend
rob@100 12099 * @param {Color} c2 Second Color to blend
rob@100 12100 *
rob@100 12101 * @returns {Color} The blended Color
rob@100 12102 *
rob@100 12103 * @see BlendColor
rob@100 12104 * @see Blend
rob@100 12105 */
rob@100 12106 p.modes = (function() {
rob@100 12107 var ALPHA_MASK = PConstants.ALPHA_MASK,
rob@100 12108 RED_MASK = PConstants.RED_MASK,
rob@100 12109 GREEN_MASK = PConstants.GREEN_MASK,
rob@100 12110 BLUE_MASK = PConstants.BLUE_MASK,
rob@100 12111 min = Math.min,
rob@100 12112 max = Math.max;
rob@100 12113
rob@100 12114 function applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb) {
rob@100 12115 var a = min(((c1 & 0xff000000) >>> 24) + f, 0xff) << 24;
rob@100 12116
rob@100 12117 var r = (ar + (((cr - ar) * f) >> 8));
rob@100 12118 r = ((r < 0) ? 0 : ((r > 255) ? 255 : r)) << 16;
rob@100 12119
rob@100 12120 var g = (ag + (((cg - ag) * f) >> 8));
rob@100 12121 g = ((g < 0) ? 0 : ((g > 255) ? 255 : g)) << 8;
rob@100 12122
rob@100 12123 var b = ab + (((cb - ab) * f) >> 8);
rob@100 12124 b = (b < 0) ? 0 : ((b > 255) ? 255 : b);
rob@100 12125
rob@100 12126 return (a | r | g | b);
rob@100 12127 }
rob@100 12128
rob@100 12129 return {
rob@100 12130 replace: function(c1, c2) {
rob@100 12131 return c2;
rob@100 12132 },
rob@100 12133 blend: function(c1, c2) {
rob@100 12134 var f = (c2 & ALPHA_MASK) >>> 24,
rob@100 12135 ar = (c1 & RED_MASK),
rob@100 12136 ag = (c1 & GREEN_MASK),
rob@100 12137 ab = (c1 & BLUE_MASK),
rob@100 12138 br = (c2 & RED_MASK),
rob@100 12139 bg = (c2 & GREEN_MASK),
rob@100 12140 bb = (c2 & BLUE_MASK);
rob@100 12141
rob@100 12142 return (min(((c1 & ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
rob@100 12143 (ar + (((br - ar) * f) >> 8)) & RED_MASK |
rob@100 12144 (ag + (((bg - ag) * f) >> 8)) & GREEN_MASK |
rob@100 12145 (ab + (((bb - ab) * f) >> 8)) & BLUE_MASK);
rob@100 12146 },
rob@100 12147 add: function(c1, c2) {
rob@100 12148 var f = (c2 & ALPHA_MASK) >>> 24;
rob@100 12149 return (min(((c1 & ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
rob@100 12150 min(((c1 & RED_MASK) + ((c2 & RED_MASK) >> 8) * f), RED_MASK) & RED_MASK |
rob@100 12151 min(((c1 & GREEN_MASK) + ((c2 & GREEN_MASK) >> 8) * f), GREEN_MASK) & GREEN_MASK |
rob@100 12152 min((c1 & BLUE_MASK) + (((c2 & BLUE_MASK) * f) >> 8), BLUE_MASK));
rob@100 12153 },
rob@100 12154 subtract: function(c1, c2) {
rob@100 12155 var f = (c2 & ALPHA_MASK) >>> 24;
rob@100 12156 return (min(((c1 & ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
rob@100 12157 max(((c1 & RED_MASK) - ((c2 & RED_MASK) >> 8) * f), GREEN_MASK) & RED_MASK |
rob@100 12158 max(((c1 & GREEN_MASK) - ((c2 & GREEN_MASK) >> 8) * f), BLUE_MASK) & GREEN_MASK |
rob@100 12159 max((c1 & BLUE_MASK) - (((c2 & BLUE_MASK) * f) >> 8), 0));
rob@100 12160 },
rob@100 12161 lightest: function(c1, c2) {
rob@100 12162 var f = (c2 & ALPHA_MASK) >>> 24;
rob@100 12163 return (min(((c1 & ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
rob@100 12164 max(c1 & RED_MASK, ((c2 & RED_MASK) >> 8) * f) & RED_MASK |
rob@100 12165 max(c1 & GREEN_MASK, ((c2 & GREEN_MASK) >> 8) * f) & GREEN_MASK |
rob@100 12166 max(c1 & BLUE_MASK, ((c2 & BLUE_MASK) * f) >> 8));
rob@100 12167 },
rob@100 12168 darkest: function(c1, c2) {
rob@100 12169 var f = (c2 & ALPHA_MASK) >>> 24,
rob@100 12170 ar = (c1 & RED_MASK),
rob@100 12171 ag = (c1 & GREEN_MASK),
rob@100 12172 ab = (c1 & BLUE_MASK),
rob@100 12173 br = min(c1 & RED_MASK, ((c2 & RED_MASK) >> 8) * f),
rob@100 12174 bg = min(c1 & GREEN_MASK, ((c2 & GREEN_MASK) >> 8) * f),
rob@100 12175 bb = min(c1 & BLUE_MASK, ((c2 & BLUE_MASK) * f) >> 8);
rob@100 12176
rob@100 12177 return (min(((c1 & ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
rob@100 12178 (ar + (((br - ar) * f) >> 8)) & RED_MASK |
rob@100 12179 (ag + (((bg - ag) * f) >> 8)) & GREEN_MASK |
rob@100 12180 (ab + (((bb - ab) * f) >> 8)) & BLUE_MASK);
rob@100 12181 },
rob@100 12182 difference: function(c1, c2) {
rob@100 12183 var f = (c2 & ALPHA_MASK) >>> 24,
rob@100 12184 ar = (c1 & RED_MASK) >> 16,
rob@100 12185 ag = (c1 & GREEN_MASK) >> 8,
rob@100 12186 ab = (c1 & BLUE_MASK),
rob@100 12187 br = (c2 & RED_MASK) >> 16,
rob@100 12188 bg = (c2 & GREEN_MASK) >> 8,
rob@100 12189 bb = (c2 & BLUE_MASK),
rob@100 12190 cr = (ar > br) ? (ar - br) : (br - ar),
rob@100 12191 cg = (ag > bg) ? (ag - bg) : (bg - ag),
rob@100 12192 cb = (ab > bb) ? (ab - bb) : (bb - ab);
rob@100 12193
rob@100 12194 return applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb);
rob@100 12195 },
rob@100 12196 exclusion: function(c1, c2) {
rob@100 12197 var f = (c2 & ALPHA_MASK) >>> 24,
rob@100 12198 ar = (c1 & RED_MASK) >> 16,
rob@100 12199 ag = (c1 & GREEN_MASK) >> 8,
rob@100 12200 ab = (c1 & BLUE_MASK),
rob@100 12201 br = (c2 & RED_MASK) >> 16,
rob@100 12202 bg = (c2 & GREEN_MASK) >> 8,
rob@100 12203 bb = (c2 & BLUE_MASK),
rob@100 12204 cr = ar + br - ((ar * br) >> 7),
rob@100 12205 cg = ag + bg - ((ag * bg) >> 7),
rob@100 12206 cb = ab + bb - ((ab * bb) >> 7);
rob@100 12207
rob@100 12208 return applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb);
rob@100 12209 },
rob@100 12210 multiply: function(c1, c2) {
rob@100 12211 var f = (c2 & ALPHA_MASK) >>> 24,
rob@100 12212 ar = (c1 & RED_MASK) >> 16,
rob@100 12213 ag = (c1 & GREEN_MASK) >> 8,
rob@100 12214 ab = (c1 & BLUE_MASK),
rob@100 12215 br = (c2 & RED_MASK) >> 16,
rob@100 12216 bg = (c2 & GREEN_MASK) >> 8,
rob@100 12217 bb = (c2 & BLUE_MASK),
rob@100 12218 cr = (ar * br) >> 8,
rob@100 12219 cg = (ag * bg) >> 8,
rob@100 12220 cb = (ab * bb) >> 8;
rob@100 12221
rob@100 12222 return applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb);
rob@100 12223 },
rob@100 12224 screen: function(c1, c2) {
rob@100 12225 var f = (c2 & ALPHA_MASK) >>> 24,
rob@100 12226 ar = (c1 & RED_MASK) >> 16,
rob@100 12227 ag = (c1 & GREEN_MASK) >> 8,
rob@100 12228 ab = (c1 & BLUE_MASK),
rob@100 12229 br = (c2 & RED_MASK) >> 16,
rob@100 12230 bg = (c2 & GREEN_MASK) >> 8,
rob@100 12231 bb = (c2 & BLUE_MASK),
rob@100 12232 cr = 255 - (((255 - ar) * (255 - br)) >> 8),
rob@100 12233 cg = 255 - (((255 - ag) * (255 - bg)) >> 8),
rob@100 12234 cb = 255 - (((255 - ab) * (255 - bb)) >> 8);
rob@100 12235
rob@100 12236 return applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb);
rob@100 12237 },
rob@100 12238 hard_light: function(c1, c2) {
rob@100 12239 var f = (c2 & ALPHA_MASK) >>> 24,
rob@100 12240 ar = (c1 & RED_MASK) >> 16,
rob@100 12241 ag = (c1 & GREEN_MASK) >> 8,
rob@100 12242 ab = (c1 & BLUE_MASK),
rob@100 12243 br = (c2 & RED_MASK) >> 16,
rob@100 12244 bg = (c2 & GREEN_MASK) >> 8,
rob@100 12245 bb = (c2 & BLUE_MASK),
rob@100 12246 cr = (br < 128) ? ((ar * br) >> 7) : (255 - (((255 - ar) * (255 - br)) >> 7)),
rob@100 12247 cg = (bg < 128) ? ((ag * bg) >> 7) : (255 - (((255 - ag) * (255 - bg)) >> 7)),
rob@100 12248 cb = (bb < 128) ? ((ab * bb) >> 7) : (255 - (((255 - ab) * (255 - bb)) >> 7));
rob@100 12249
rob@100 12250 return applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb);
rob@100 12251 },
rob@100 12252 soft_light: function(c1, c2) {
rob@100 12253 var f = (c2 & ALPHA_MASK) >>> 24,
rob@100 12254 ar = (c1 & RED_MASK) >> 16,
rob@100 12255 ag = (c1 & GREEN_MASK) >> 8,
rob@100 12256 ab = (c1 & BLUE_MASK),
rob@100 12257 br = (c2 & RED_MASK) >> 16,
rob@100 12258 bg = (c2 & GREEN_MASK) >> 8,
rob@100 12259 bb = (c2 & BLUE_MASK),
rob@100 12260 cr = ((ar * br) >> 7) + ((ar * ar) >> 8) - ((ar * ar * br) >> 15),
rob@100 12261 cg = ((ag * bg) >> 7) + ((ag * ag) >> 8) - ((ag * ag * bg) >> 15),
rob@100 12262 cb = ((ab * bb) >> 7) + ((ab * ab) >> 8) - ((ab * ab * bb) >> 15);
rob@100 12263
rob@100 12264 return applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb);
rob@100 12265 },
rob@100 12266 overlay: function(c1, c2) {
rob@100 12267 var f = (c2 & ALPHA_MASK) >>> 24,
rob@100 12268 ar = (c1 & RED_MASK) >> 16,
rob@100 12269 ag = (c1 & GREEN_MASK) >> 8,
rob@100 12270 ab = (c1 & BLUE_MASK),
rob@100 12271 br = (c2 & RED_MASK) >> 16,
rob@100 12272 bg = (c2 & GREEN_MASK) >> 8,
rob@100 12273 bb = (c2 & BLUE_MASK),
rob@100 12274 cr = (ar < 128) ? ((ar * br) >> 7) : (255 - (((255 - ar) * (255 - br)) >> 7)),
rob@100 12275 cg = (ag < 128) ? ((ag * bg) >> 7) : (255 - (((255 - ag) * (255 - bg)) >> 7)),
rob@100 12276 cb = (ab < 128) ? ((ab * bb) >> 7) : (255 - (((255 - ab) * (255 - bb)) >> 7));
rob@100 12277
rob@100 12278 return applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb);
rob@100 12279 },
rob@100 12280 dodge: function(c1, c2) {
rob@100 12281 var f = (c2 & ALPHA_MASK) >>> 24,
rob@100 12282 ar = (c1 & RED_MASK) >> 16,
rob@100 12283 ag = (c1 & GREEN_MASK) >> 8,
rob@100 12284 ab = (c1 & BLUE_MASK),
rob@100 12285 br = (c2 & RED_MASK) >> 16,
rob@100 12286 bg = (c2 & GREEN_MASK) >> 8,
rob@100 12287 bb = (c2 & BLUE_MASK);
rob@100 12288
rob@100 12289 var cr = 255;
rob@100 12290 if (br !== 255) {
rob@100 12291 cr = (ar << 8) / (255 - br);
rob@100 12292 cr = (cr < 0) ? 0 : ((cr > 255) ? 255 : cr);
rob@100 12293 }
rob@100 12294
rob@100 12295 var cg = 255;
rob@100 12296 if (bg !== 255) {
rob@100 12297 cg = (ag << 8) / (255 - bg);
rob@100 12298 cg = (cg < 0) ? 0 : ((cg > 255) ? 255 : cg);
rob@100 12299 }
rob@100 12300
rob@100 12301 var cb = 255;
rob@100 12302 if (bb !== 255) {
rob@100 12303 cb = (ab << 8) / (255 - bb);
rob@100 12304 cb = (cb < 0) ? 0 : ((cb > 255) ? 255 : cb);
rob@100 12305 }
rob@100 12306
rob@100 12307 return applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb);
rob@100 12308 },
rob@100 12309 burn: function(c1, c2) {
rob@100 12310 var f = (c2 & ALPHA_MASK) >>> 24,
rob@100 12311 ar = (c1 & RED_MASK) >> 16,
rob@100 12312 ag = (c1 & GREEN_MASK) >> 8,
rob@100 12313 ab = (c1 & BLUE_MASK),
rob@100 12314 br = (c2 & RED_MASK) >> 16,
rob@100 12315 bg = (c2 & GREEN_MASK) >> 8,
rob@100 12316 bb = (c2 & BLUE_MASK);
rob@100 12317
rob@100 12318 var cr = 0;
rob@100 12319 if (br !== 0) {
rob@100 12320 cr = ((255 - ar) << 8) / br;
rob@100 12321 cr = 255 - ((cr < 0) ? 0 : ((cr > 255) ? 255 : cr));
rob@100 12322 }
rob@100 12323
rob@100 12324 var cg = 0;
rob@100 12325 if (bg !== 0) {
rob@100 12326 cg = ((255 - ag) << 8) / bg;
rob@100 12327 cg = 255 - ((cg < 0) ? 0 : ((cg > 255) ? 255 : cg));
rob@100 12328 }
rob@100 12329
rob@100 12330 var cb = 0;
rob@100 12331 if (bb !== 0) {
rob@100 12332 cb = ((255 - ab) << 8) / bb;
rob@100 12333 cb = 255 - ((cb < 0) ? 0 : ((cb > 255) ? 255 : cb));
rob@100 12334 }
rob@100 12335
rob@100 12336 return applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb);
rob@100 12337 }
rob@100 12338 };
rob@100 12339 }());
rob@100 12340
rob@100 12341 function color$4(aValue1, aValue2, aValue3, aValue4) {
rob@100 12342 var r, g, b, a;
rob@100 12343
rob@100 12344 if (curColorMode === PConstants.HSB) {
rob@100 12345 var rgb = p.color.toRGB(aValue1, aValue2, aValue3);
rob@100 12346 r = rgb[0];
rob@100 12347 g = rgb[1];
rob@100 12348 b = rgb[2];
rob@100 12349 } else {
rob@100 12350 r = Math.round(255 * (aValue1 / colorModeX));
rob@100 12351 g = Math.round(255 * (aValue2 / colorModeY));
rob@100 12352 b = Math.round(255 * (aValue3 / colorModeZ));
rob@100 12353 }
rob@100 12354
rob@100 12355 a = Math.round(255 * (aValue4 / colorModeA));
rob@100 12356
rob@100 12357 // Limit values less than 0 and greater than 255
rob@100 12358 r = (r < 0) ? 0 : r;
rob@100 12359 g = (g < 0) ? 0 : g;
rob@100 12360 b = (b < 0) ? 0 : b;
rob@100 12361 a = (a < 0) ? 0 : a;
rob@100 12362 r = (r > 255) ? 255 : r;
rob@100 12363 g = (g > 255) ? 255 : g;
rob@100 12364 b = (b > 255) ? 255 : b;
rob@100 12365 a = (a > 255) ? 255 : a;
rob@100 12366
rob@100 12367 // Create color int
rob@100 12368 return (a << 24) & PConstants.ALPHA_MASK | (r << 16) & PConstants.RED_MASK | (g << 8) & PConstants.GREEN_MASK | b & PConstants.BLUE_MASK;
rob@100 12369 }
rob@100 12370
rob@100 12371 function color$2(aValue1, aValue2) {
rob@100 12372 var a;
rob@100 12373
rob@100 12374 // Color int and alpha
rob@100 12375 if (aValue1 & PConstants.ALPHA_MASK) {
rob@100 12376 a = Math.round(255 * (aValue2 / colorModeA));
rob@100 12377 // Limit values less than 0 and greater than 255
rob@100 12378 a = (a > 255) ? 255 : a;
rob@100 12379 a = (a < 0) ? 0 : a;
rob@100 12380
rob@100 12381 return aValue1 - (aValue1 & PConstants.ALPHA_MASK) + ((a << 24) & PConstants.ALPHA_MASK);
rob@100 12382 }
rob@100 12383 // Grayscale and alpha
rob@100 12384 if (curColorMode === PConstants.RGB) {
rob@100 12385 return color$4(aValue1, aValue1, aValue1, aValue2);
rob@100 12386 }
rob@100 12387 if (curColorMode === PConstants.HSB) {
rob@100 12388 return color$4(0, 0, (aValue1 / colorModeX) * colorModeZ, aValue2);
rob@100 12389 }
rob@100 12390 }
rob@100 12391
rob@100 12392 function color$1(aValue1) {
rob@100 12393 // Grayscale
rob@100 12394 if (aValue1 <= colorModeX && aValue1 >= 0) {
rob@100 12395 if (curColorMode === PConstants.RGB) {
rob@100 12396 return color$4(aValue1, aValue1, aValue1, colorModeA);
rob@100 12397 }
rob@100 12398 if (curColorMode === PConstants.HSB) {
rob@100 12399 return color$4(0, 0, (aValue1 / colorModeX) * colorModeZ, colorModeA);
rob@100 12400 }
rob@100 12401 }
rob@100 12402 // Color int
rob@100 12403 if (aValue1) {
rob@100 12404 if (aValue1 > 2147483647) {
rob@100 12405 // Java Overflow
rob@100 12406 aValue1 -= 4294967296;
rob@100 12407 }
rob@100 12408 return aValue1;
rob@100 12409 }
rob@100 12410 }
rob@100 12411
rob@100 12412 /**
rob@100 12413 * Creates colors for storing in variables of the color datatype. The parameters are
rob@100 12414 * interpreted as RGB or HSB values depending on the current colorMode(). The default
rob@100 12415 * mode is RGB values from 0 to 255 and therefore, the function call color(255, 204, 0)
rob@100 12416 * will return a bright yellow color. More about how colors are stored can be found in
rob@100 12417 * the reference for the color datatype.
rob@100 12418 *
rob@100 12419 * @param {int|float} aValue1 red or hue or grey values relative to the current color range.
rob@100 12420 * Also can be color value in hexadecimal notation (i.e. #FFCC00 or 0xFFFFCC00)
rob@100 12421 * @param {int|float} aValue2 green or saturation values relative to the current color range
rob@100 12422 * @param {int|float} aValue3 blue or brightness values relative to the current color range
rob@100 12423 * @param {int|float} aValue4 relative to current color range. Represents alpha
rob@100 12424 *
rob@100 12425 * @returns {color} the color
rob@100 12426 *
rob@100 12427 * @see colorMode
rob@100 12428 */
rob@100 12429 p.color = function(aValue1, aValue2, aValue3, aValue4) {
rob@100 12430
rob@100 12431 // 4 arguments: (R, G, B, A) or (H, S, B, A)
rob@100 12432 if (aValue1 !== undef && aValue2 !== undef && aValue3 !== undef && aValue4 !== undef) {
rob@100 12433 return color$4(aValue1, aValue2, aValue3, aValue4);
rob@100 12434 }
rob@100 12435
rob@100 12436 // 3 arguments: (R, G, B) or (H, S, B)
rob@100 12437 if (aValue1 !== undef && aValue2 !== undef && aValue3 !== undef) {
rob@100 12438 return color$4(aValue1, aValue2, aValue3, colorModeA);
rob@100 12439 }
rob@100 12440
rob@100 12441 // 2 arguments: (Color, A) or (Grayscale, A)
rob@100 12442 if (aValue1 !== undef && aValue2 !== undef) {
rob@100 12443 return color$2(aValue1, aValue2);
rob@100 12444 }
rob@100 12445
rob@100 12446 // 1 argument: (Grayscale) or (Color)
rob@100 12447 if (typeof aValue1 === "number") {
rob@100 12448 return color$1(aValue1);
rob@100 12449 }
rob@100 12450
rob@100 12451 // Default
rob@100 12452 return color$4(colorModeX, colorModeY, colorModeZ, colorModeA);
rob@100 12453 };
rob@100 12454
rob@100 12455 // Ease of use function to extract the colour bits into a string
rob@100 12456 p.color.toString = function(colorInt) {
rob@100 12457 return "rgba(" + ((colorInt & PConstants.RED_MASK) >>> 16) + "," + ((colorInt & PConstants.GREEN_MASK) >>> 8) +
rob@100 12458 "," + ((colorInt & PConstants.BLUE_MASK)) + "," + ((colorInt & PConstants.ALPHA_MASK) >>> 24) / 255 + ")";
rob@100 12459 };
rob@100 12460
rob@100 12461 // Easy of use function to pack rgba values into a single bit-shifted color int.
rob@100 12462 p.color.toInt = function(r, g, b, a) {
rob@100 12463 return (a << 24) & PConstants.ALPHA_MASK | (r << 16) & PConstants.RED_MASK | (g << 8) & PConstants.GREEN_MASK | b & PConstants.BLUE_MASK;
rob@100 12464 };
rob@100 12465
rob@100 12466 // Creates a simple array in [R, G, B, A] format, [255, 255, 255, 255]
rob@100 12467 p.color.toArray = function(colorInt) {
rob@100 12468 return [(colorInt & PConstants.RED_MASK) >>> 16, (colorInt & PConstants.GREEN_MASK) >>> 8,
rob@100 12469 colorInt & PConstants.BLUE_MASK, (colorInt & PConstants.ALPHA_MASK) >>> 24];
rob@100 12470 };
rob@100 12471
rob@100 12472 // Creates a WebGL color array in [R, G, B, A] format. WebGL wants the color ranges between 0 and 1, [1, 1, 1, 1]
rob@100 12473 p.color.toGLArray = function(colorInt) {
rob@100 12474 return [((colorInt & PConstants.RED_MASK) >>> 16) / 255, ((colorInt & PConstants.GREEN_MASK) >>> 8) / 255,
rob@100 12475 (colorInt & PConstants.BLUE_MASK) / 255, ((colorInt & PConstants.ALPHA_MASK) >>> 24) / 255];
rob@100 12476 };
rob@100 12477
rob@100 12478 // HSB conversion function from Mootools, MIT Licensed
rob@100 12479 p.color.toRGB = function(h, s, b) {
rob@100 12480 // Limit values greater than range
rob@100 12481 h = (h > colorModeX) ? colorModeX : h;
rob@100 12482 s = (s > colorModeY) ? colorModeY : s;
rob@100 12483 b = (b > colorModeZ) ? colorModeZ : b;
rob@100 12484
rob@100 12485 h = (h / colorModeX) * 360;
rob@100 12486 s = (s / colorModeY) * 100;
rob@100 12487 b = (b / colorModeZ) * 100;
rob@100 12488
rob@100 12489 var br = Math.round(b / 100 * 255);
rob@100 12490
rob@100 12491 if (s === 0) { // Grayscale
rob@100 12492 return [br, br, br];
rob@100 12493 }
rob@100 12494 var hue = h % 360;
rob@100 12495 var f = hue % 60;
rob@100 12496 var p = Math.round((b * (100 - s)) / 10000 * 255);
rob@100 12497 var q = Math.round((b * (6000 - s * f)) / 600000 * 255);
rob@100 12498 var t = Math.round((b * (6000 - s * (60 - f))) / 600000 * 255);
rob@100 12499 switch (Math.floor(hue / 60)) {
rob@100 12500 case 0:
rob@100 12501 return [br, t, p];
rob@100 12502 case 1:
rob@100 12503 return [q, br, p];
rob@100 12504 case 2:
rob@100 12505 return [p, br, t];
rob@100 12506 case 3:
rob@100 12507 return [p, q, br];
rob@100 12508 case 4:
rob@100 12509 return [t, p, br];
rob@100 12510 case 5:
rob@100 12511 return [br, p, q];
rob@100 12512 }
rob@100 12513 };
rob@100 12514
rob@100 12515 function colorToHSB(colorInt) {
rob@100 12516 var red, green, blue;
rob@100 12517
rob@100 12518 red = ((colorInt & PConstants.RED_MASK) >>> 16) / 255;
rob@100 12519 green = ((colorInt & PConstants.GREEN_MASK) >>> 8) / 255;
rob@100 12520 blue = (colorInt & PConstants.BLUE_MASK) / 255;
rob@100 12521
rob@100 12522 var max = p.max(p.max(red,green), blue),
rob@100 12523 min = p.min(p.min(red,green), blue),
rob@100 12524 hue, saturation;
rob@100 12525
rob@100 12526 if (min === max) {
rob@100 12527 return [0, 0, max*colorModeZ];
rob@100 12528 }
rob@100 12529 saturation = (max - min) / max;
rob@100 12530
rob@100 12531 if (red === max) {
rob@100 12532 hue = (green - blue) / (max - min);
rob@100 12533 } else if (green === max) {
rob@100 12534 hue = 2 + ((blue - red) / (max - min));
rob@100 12535 } else {
rob@100 12536 hue = 4 + ((red - green) / (max - min));
rob@100 12537 }
rob@100 12538
rob@100 12539 hue /= 6;
rob@100 12540
rob@100 12541 if (hue < 0) {
rob@100 12542 hue += 1;
rob@100 12543 } else if (hue > 1) {
rob@100 12544 hue -= 1;
rob@100 12545 }
rob@100 12546 return [hue*colorModeX, saturation*colorModeY, max*colorModeZ];
rob@100 12547 }
rob@100 12548
rob@100 12549 /**
rob@100 12550 * Extracts the brightness value from a color.
rob@100 12551 *
rob@100 12552 * @param {color} colInt any value of the color datatype
rob@100 12553 *
rob@100 12554 * @returns {float} The brightness color value.
rob@100 12555 *
rob@100 12556 * @see red
rob@100 12557 * @see green
rob@100 12558 * @see blue
rob@100 12559 * @see hue
rob@100 12560 * @see saturation
rob@100 12561 */
rob@100 12562 p.brightness = function(colInt){
rob@100 12563 return colorToHSB(colInt)[2];
rob@100 12564 };
rob@100 12565
rob@100 12566 /**
rob@100 12567 * Extracts the saturation value from a color.
rob@100 12568 *
rob@100 12569 * @param {color} colInt any value of the color datatype
rob@100 12570 *
rob@100 12571 * @returns {float} The saturation color value.
rob@100 12572 *
rob@100 12573 * @see red
rob@100 12574 * @see green
rob@100 12575 * @see blue
rob@100 12576 * @see hue
rob@100 12577 * @see brightness
rob@100 12578 */
rob@100 12579 p.saturation = function(colInt){
rob@100 12580 return colorToHSB(colInt)[1];
rob@100 12581 };
rob@100 12582
rob@100 12583 /**
rob@100 12584 * Extracts the hue value from a color.
rob@100 12585 *
rob@100 12586 * @param {color} colInt any value of the color datatype
rob@100 12587 *
rob@100 12588 * @returns {float} The hue color value.
rob@100 12589 *
rob@100 12590 * @see red
rob@100 12591 * @see green
rob@100 12592 * @see blue
rob@100 12593 * @see saturation
rob@100 12594 * @see brightness
rob@100 12595 */
rob@100 12596 p.hue = function(colInt){
rob@100 12597 return colorToHSB(colInt)[0];
rob@100 12598 };
rob@100 12599
rob@100 12600 /**
rob@100 12601 * Extracts the red value from a color, scaled to match current colorMode().
rob@100 12602 * This value is always returned as a float so be careful not to assign it to an int value.
rob@100 12603 *
rob@100 12604 * @param {color} aColor any value of the color datatype
rob@100 12605 *
rob@100 12606 * @returns {float} The red color value.
rob@100 12607 *
rob@100 12608 * @see green
rob@100 12609 * @see blue
rob@100 12610 * @see alpha
rob@100 12611 * @see >> right shift
rob@100 12612 * @see hue
rob@100 12613 * @see saturation
rob@100 12614 * @see brightness
rob@100 12615 */
rob@100 12616 p.red = function(aColor) {
rob@100 12617 return ((aColor & PConstants.RED_MASK) >>> 16) / 255 * colorModeX;
rob@100 12618 };
rob@100 12619
rob@100 12620 /**
rob@100 12621 * Extracts the green value from a color, scaled to match current colorMode().
rob@100 12622 * This value is always returned as a float so be careful not to assign it to an int value.
rob@100 12623 *
rob@100 12624 * @param {color} aColor any value of the color datatype
rob@100 12625 *
rob@100 12626 * @returns {float} The green color value.
rob@100 12627 *
rob@100 12628 * @see red
rob@100 12629 * @see blue
rob@100 12630 * @see alpha
rob@100 12631 * @see >> right shift
rob@100 12632 * @see hue
rob@100 12633 * @see saturation
rob@100 12634 * @see brightness
rob@100 12635 */
rob@100 12636 p.green = function(aColor) {
rob@100 12637 return ((aColor & PConstants.GREEN_MASK) >>> 8) / 255 * colorModeY;
rob@100 12638 };
rob@100 12639
rob@100 12640 /**
rob@100 12641 * Extracts the blue value from a color, scaled to match current colorMode().
rob@100 12642 * This value is always returned as a float so be careful not to assign it to an int value.
rob@100 12643 *
rob@100 12644 * @param {color} aColor any value of the color datatype
rob@100 12645 *
rob@100 12646 * @returns {float} The blue color value.
rob@100 12647 *
rob@100 12648 * @see red
rob@100 12649 * @see green
rob@100 12650 * @see alpha
rob@100 12651 * @see >> right shift
rob@100 12652 * @see hue
rob@100 12653 * @see saturation
rob@100 12654 * @see brightness
rob@100 12655 */
rob@100 12656 p.blue = function(aColor) {
rob@100 12657 return (aColor & PConstants.BLUE_MASK) / 255 * colorModeZ;
rob@100 12658 };
rob@100 12659
rob@100 12660 /**
rob@100 12661 * Extracts the alpha value from a color, scaled to match current colorMode().
rob@100 12662 * This value is always returned as a float so be careful not to assign it to an int value.
rob@100 12663 *
rob@100 12664 * @param {color} aColor any value of the color datatype
rob@100 12665 *
rob@100 12666 * @returns {float} The alpha color value.
rob@100 12667 *
rob@100 12668 * @see red
rob@100 12669 * @see green
rob@100 12670 * @see blue
rob@100 12671 * @see >> right shift
rob@100 12672 * @see hue
rob@100 12673 * @see saturation
rob@100 12674 * @see brightness
rob@100 12675 */
rob@100 12676 p.alpha = function(aColor) {
rob@100 12677 return ((aColor & PConstants.ALPHA_MASK) >>> 24) / 255 * colorModeA;
rob@100 12678 };
rob@100 12679
rob@100 12680 /**
rob@100 12681 * Calculates a color or colors between two colors at a specific increment.
rob@100 12682 * The amt parameter is the amount to interpolate between the two values where 0.0
rob@100 12683 * equal to the first point, 0.1 is very near the first point, 0.5 is half-way in between, etc.
rob@100 12684 *
rob@100 12685 * @param {color} c1 interpolate from this color
rob@100 12686 * @param {color} c2 interpolate to this color
rob@100 12687 * @param {float} amt between 0.0 and 1.0
rob@100 12688 *
rob@100 12689 * @returns {float} The blended color.
rob@100 12690 *
rob@100 12691 * @see blendColor
rob@100 12692 * @see color
rob@100 12693 */
rob@100 12694 p.lerpColor = function(c1, c2, amt) {
rob@100 12695 var r, g, b, a, r1, g1, b1, a1, r2, g2, b2, a2;
rob@100 12696 var hsb1, hsb2, rgb, h, s;
rob@100 12697 var colorBits1 = p.color(c1);
rob@100 12698 var colorBits2 = p.color(c2);
rob@100 12699
rob@100 12700 if (curColorMode === PConstants.HSB) {
rob@100 12701 // Special processing for HSB mode.
rob@100 12702 // Get HSB and Alpha values for Color 1 and 2
rob@100 12703 hsb1 = colorToHSB(colorBits1);
rob@100 12704 a1 = ((colorBits1 & PConstants.ALPHA_MASK) >>> 24) / colorModeA;
rob@100 12705 hsb2 = colorToHSB(colorBits2);
rob@100 12706 a2 = ((colorBits2 & PConstants.ALPHA_MASK) >>> 24) / colorModeA;
rob@100 12707
rob@100 12708 // Return lerp value for each channel, for HSB components
rob@100 12709 h = p.lerp(hsb1[0], hsb2[0], amt);
rob@100 12710 s = p.lerp(hsb1[1], hsb2[1], amt);
rob@100 12711 b = p.lerp(hsb1[2], hsb2[2], amt);
rob@100 12712 rgb = p.color.toRGB(h, s, b);
rob@100 12713 // ... and for Alpha-range
rob@100 12714 a = p.lerp(a1, a2, amt) * colorModeA;
rob@100 12715
rob@100 12716 return (a << 24) & PConstants.ALPHA_MASK |
rob@100 12717 (rgb[0] << 16) & PConstants.RED_MASK |
rob@100 12718 (rgb[1] << 8) & PConstants.GREEN_MASK |
rob@100 12719 rgb[2] & PConstants.BLUE_MASK;
rob@100 12720 }
rob@100 12721
rob@100 12722 // Get RGBA values for Color 1 to floats
rob@100 12723 r1 = (colorBits1 & PConstants.RED_MASK) >>> 16;
rob@100 12724 g1 = (colorBits1 & PConstants.GREEN_MASK) >>> 8;
rob@100 12725 b1 = (colorBits1 & PConstants.BLUE_MASK);
rob@100 12726 a1 = ((colorBits1 & PConstants.ALPHA_MASK) >>> 24) / colorModeA;
rob@100 12727
rob@100 12728 // Get RGBA values for Color 2 to floats
rob@100 12729 r2 = (colorBits2 & PConstants.RED_MASK) >>> 16;
rob@100 12730 g2 = (colorBits2 & PConstants.GREEN_MASK) >>> 8;
rob@100 12731 b2 = (colorBits2 & PConstants.BLUE_MASK);
rob@100 12732 a2 = ((colorBits2 & PConstants.ALPHA_MASK) >>> 24) / colorModeA;
rob@100 12733
rob@100 12734 // Return lerp value for each channel, INT for color, Float for Alpha-range
rob@100 12735 r = p.lerp(r1, r2, amt) | 0;
rob@100 12736 g = p.lerp(g1, g2, amt) | 0;
rob@100 12737 b = p.lerp(b1, b2, amt) | 0;
rob@100 12738 a = p.lerp(a1, a2, amt) * colorModeA;
rob@100 12739
rob@100 12740 return (a << 24) & PConstants.ALPHA_MASK |
rob@100 12741 (r << 16) & PConstants.RED_MASK |
rob@100 12742 (g << 8) & PConstants.GREEN_MASK |
rob@100 12743 b & PConstants.BLUE_MASK;
rob@100 12744 };
rob@100 12745
rob@100 12746 /**
rob@100 12747 * Changes the way Processing interprets color data. By default, fill(), stroke(), and background()
rob@100 12748 * colors are set by values between 0 and 255 using the RGB color model. It is possible to change the
rob@100 12749 * numerical range used for specifying colors and to switch color systems. For example, calling colorMode(RGB, 1.0)
rob@100 12750 * will specify that values are specified between 0 and 1. The limits for defining colors are altered by setting the
rob@100 12751 * parameters range1, range2, range3, and range 4.
rob@100 12752 *
rob@100 12753 * @param {MODE} mode Either RGB or HSB, corresponding to Red/Green/Blue and Hue/Saturation/Brightness
rob@100 12754 * @param {int|float} range range for all color elements
rob@100 12755 * @param {int|float} range1 range for the red or hue depending on the current color mode
rob@100 12756 * @param {int|float} range2 range for the green or saturation depending on the current color mode
rob@100 12757 * @param {int|float} range3 range for the blue or brightness depending on the current color mode
rob@100 12758 * @param {int|float} range4 range for the alpha
rob@100 12759 *
rob@100 12760 * @returns none
rob@100 12761 *
rob@100 12762 * @see background
rob@100 12763 * @see fill
rob@100 12764 * @see stroke
rob@100 12765 */
rob@100 12766 p.colorMode = function() { // mode, range1, range2, range3, range4
rob@100 12767 curColorMode = arguments[0];
rob@100 12768 if (arguments.length > 1) {
rob@100 12769 colorModeX = arguments[1];
rob@100 12770 colorModeY = arguments[2] || arguments[1];
rob@100 12771 colorModeZ = arguments[3] || arguments[1];
rob@100 12772 colorModeA = arguments[4] || arguments[1];
rob@100 12773 }
rob@100 12774 };
rob@100 12775
rob@100 12776 /**
rob@100 12777 * Blends two color values together based on the blending mode given as the MODE parameter.
rob@100 12778 * The possible modes are described in the reference for the blend() function.
rob@100 12779 *
rob@100 12780 * @param {color} c1 color: the first color to blend
rob@100 12781 * @param {color} c2 color: the second color to blend
rob@100 12782 * @param {MODE} MODE Either BLEND, ADD, SUBTRACT, DARKEST, LIGHTEST, DIFFERENCE, EXCLUSION, MULTIPLY,
rob@100 12783 * SCREEN, OVERLAY, HARD_LIGHT, SOFT_LIGHT, DODGE, or BURN
rob@100 12784 *
rob@100 12785 * @returns {float} The blended color.
rob@100 12786 *
rob@100 12787 * @see blend
rob@100 12788 * @see color
rob@100 12789 */
rob@100 12790 p.blendColor = function(c1, c2, mode) {
rob@100 12791 if (mode === PConstants.REPLACE) {
rob@100 12792 return p.modes.replace(c1, c2);
rob@100 12793 } else if (mode === PConstants.BLEND) {
rob@100 12794 return p.modes.blend(c1, c2);
rob@100 12795 } else if (mode === PConstants.ADD) {
rob@100 12796 return p.modes.add(c1, c2);
rob@100 12797 } else if (mode === PConstants.SUBTRACT) {
rob@100 12798 return p.modes.subtract(c1, c2);
rob@100 12799 } else if (mode === PConstants.LIGHTEST) {
rob@100 12800 return p.modes.lightest(c1, c2);
rob@100 12801 } else if (mode === PConstants.DARKEST) {
rob@100 12802 return p.modes.darkest(c1, c2);
rob@100 12803 } else if (mode === PConstants.DIFFERENCE) {
rob@100 12804 return p.modes.difference(c1, c2);
rob@100 12805 } else if (mode === PConstants.EXCLUSION) {
rob@100 12806 return p.modes.exclusion(c1, c2);
rob@100 12807 } else if (mode === PConstants.MULTIPLY) {
rob@100 12808 return p.modes.multiply(c1, c2);
rob@100 12809 } else if (mode === PConstants.SCREEN) {
rob@100 12810 return p.modes.screen(c1, c2);
rob@100 12811 } else if (mode === PConstants.HARD_LIGHT) {
rob@100 12812 return p.modes.hard_light(c1, c2);
rob@100 12813 } else if (mode === PConstants.SOFT_LIGHT) {
rob@100 12814 return p.modes.soft_light(c1, c2);
rob@100 12815 } else if (mode === PConstants.OVERLAY) {
rob@100 12816 return p.modes.overlay(c1, c2);
rob@100 12817 } else if (mode === PConstants.DODGE) {
rob@100 12818 return p.modes.dodge(c1, c2);
rob@100 12819 } else if (mode === PConstants.BURN) {
rob@100 12820 return p.modes.burn(c1, c2);
rob@100 12821 }
rob@100 12822 };
rob@100 12823
rob@100 12824 ////////////////////////////////////////////////////////////////////////////
rob@100 12825 // Canvas-Matrix manipulation
rob@100 12826 ////////////////////////////////////////////////////////////////////////////
rob@100 12827
rob@100 12828 function saveContext() {
rob@100 12829 curContext.save();
rob@100 12830 }
rob@100 12831
rob@100 12832 function restoreContext() {
rob@100 12833 curContext.restore();
rob@100 12834 isStrokeDirty = true;
rob@100 12835 isFillDirty = true;
rob@100 12836 }
rob@100 12837
rob@100 12838 /**
rob@100 12839 * Prints the current matrix to the text window.
rob@100 12840 *
rob@100 12841 * @returns none
rob@100 12842 *
rob@100 12843 * @see pushMatrix
rob@100 12844 * @see popMatrix
rob@100 12845 * @see resetMatrix
rob@100 12846 * @see applyMatrix
rob@100 12847 */
rob@100 12848 p.printMatrix = function() {
rob@100 12849 modelView.print();
rob@100 12850 };
rob@100 12851
rob@100 12852 /**
rob@100 12853 * Specifies an amount to displace objects within the display window. The x parameter specifies left/right translation,
rob@100 12854 * the y parameter specifies up/down translation, and the z parameter specifies translations toward/away from the screen.
rob@100 12855 * Using this function with the z parameter requires using the P3D or OPENGL parameter in combination with size as shown
rob@100 12856 * in the above example. Transformations apply to everything that happens after and subsequent calls to the function
rob@100 12857 * accumulates the effect. For example, calling translate(50, 0) and then translate(20, 0) is the same as translate(70, 0).
rob@100 12858 * If translate() is called within draw(), the transformation is reset when the loop begins again.
rob@100 12859 * This function can be further controlled by the pushMatrix() and popMatrix().
rob@100 12860 *
rob@100 12861 * @param {int|float} x left/right translation
rob@100 12862 * @param {int|float} y up/down translation
rob@100 12863 * @param {int|float} z forward/back translation
rob@100 12864 *
rob@100 12865 * @returns none
rob@100 12866 *
rob@100 12867 * @see pushMatrix
rob@100 12868 * @see popMatrix
rob@100 12869 * @see scale
rob@100 12870 * @see rotate
rob@100 12871 * @see rotateX
rob@100 12872 * @see rotateY
rob@100 12873 * @see rotateZ
rob@100 12874 */
rob@100 12875 Drawing2D.prototype.translate = function(x, y) {
rob@100 12876 modelView.translate(x, y);
rob@100 12877 modelViewInv.invTranslate(x, y);
rob@100 12878 curContext.translate(x, y);
rob@100 12879 };
rob@100 12880
rob@100 12881 Drawing3D.prototype.translate = function(x, y, z) {
rob@100 12882 modelView.translate(x, y, z);
rob@100 12883 modelViewInv.invTranslate(x, y, z);
rob@100 12884 };
rob@100 12885
rob@100 12886 /**
rob@100 12887 * Increases or decreases the size of a shape by expanding and contracting vertices. Objects always scale from their
rob@100 12888 * relative origin to the coordinate system. Scale values are specified as decimal percentages. For example, the
rob@100 12889 * function call scale(2.0) increases the dimension of a shape by 200%. Transformations apply to everything that
rob@100 12890 * happens after and subsequent calls to the function multiply the effect. For example, calling scale(2.0) and
rob@100 12891 * then scale(1.5) is the same as scale(3.0). If scale() is called within draw(), the transformation is reset when
rob@100 12892 * the loop begins again. Using this fuction with the z parameter requires passing P3D or OPENGL into the size()
rob@100 12893 * parameter as shown in the example above. This function can be further controlled by pushMatrix() and popMatrix().
rob@100 12894 *
rob@100 12895 * @param {int|float} size percentage to scale the object
rob@100 12896 * @param {int|float} x percentage to scale the object in the x-axis
rob@100 12897 * @param {int|float} y percentage to scale the object in the y-axis
rob@100 12898 * @param {int|float} z percentage to scale the object in the z-axis
rob@100 12899 *
rob@100 12900 * @returns none
rob@100 12901 *
rob@100 12902 * @see pushMatrix
rob@100 12903 * @see popMatrix
rob@100 12904 * @see translate
rob@100 12905 * @see rotate
rob@100 12906 * @see rotateX
rob@100 12907 * @see rotateY
rob@100 12908 * @see rotateZ
rob@100 12909 */
rob@100 12910 Drawing2D.prototype.scale = function(x, y) {
rob@100 12911 modelView.scale(x, y);
rob@100 12912 modelViewInv.invScale(x, y);
rob@100 12913 curContext.scale(x, y || x);
rob@100 12914 };
rob@100 12915
rob@100 12916 Drawing3D.prototype.scale = function(x, y, z) {
rob@100 12917 modelView.scale(x, y, z);
rob@100 12918 modelViewInv.invScale(x, y, z);
rob@100 12919 };
rob@100 12920
rob@100 12921
rob@100 12922 /**
rob@100 12923 * helper function for applying a transfrom matrix to a 2D context.
rob@100 12924 */
rob@100 12925 Drawing2D.prototype.transform = function(pmatrix) {
rob@100 12926 var e = pmatrix.array();
rob@100 12927 curContext.transform(e[0],e[3],e[1],e[4],e[2],e[5]);
rob@100 12928 };
rob@100 12929
rob@100 12930 /**
rob@100 12931 * helper function for applying a transfrom matrix to a 3D context.
rob@100 12932 * not currently implemented.
rob@100 12933 */
rob@100 12934 Drawing3D.prototype.transformm = function(pmatrix3d) {
rob@100 12935 throw("p.transform is currently not supported in 3D mode");
rob@100 12936 };
rob@100 12937
rob@100 12938
rob@100 12939 /**
rob@100 12940 * Pushes the current transformation matrix onto the matrix stack. Understanding pushMatrix() and popMatrix()
rob@100 12941 * requires understanding the concept of a matrix stack. The pushMatrix() function saves the current coordinate
rob@100 12942 * system to the stack and popMatrix() restores the prior coordinate system. pushMatrix() and popMatrix() are
rob@100 12943 * used in conjuction with the other transformation methods and may be embedded to control the scope of
rob@100 12944 * the transformations.
rob@100 12945 *
rob@100 12946 * @returns none
rob@100 12947 *
rob@100 12948 * @see popMatrix
rob@100 12949 * @see translate
rob@100 12950 * @see rotate
rob@100 12951 * @see rotateX
rob@100 12952 * @see rotateY
rob@100 12953 * @see rotateZ
rob@100 12954 */
rob@100 12955 Drawing2D.prototype.pushMatrix = function() {
rob@100 12956 userMatrixStack.load(modelView);
rob@100 12957 userReverseMatrixStack.load(modelViewInv);
rob@100 12958 saveContext();
rob@100 12959 };
rob@100 12960
rob@100 12961 Drawing3D.prototype.pushMatrix = function() {
rob@100 12962 userMatrixStack.load(modelView);
rob@100 12963 userReverseMatrixStack.load(modelViewInv);
rob@100 12964 };
rob@100 12965
rob@100 12966 /**
rob@100 12967 * Pops the current transformation matrix off the matrix stack. Understanding pushing and popping requires
rob@100 12968 * understanding the concept of a matrix stack. The pushMatrix() function saves the current coordinate system to
rob@100 12969 * the stack and popMatrix() restores the prior coordinate system. pushMatrix() and popMatrix() are used in
rob@100 12970 * conjuction with the other transformation methods and may be embedded to control the scope of the transformations.
rob@100 12971 *
rob@100 12972 * @returns none
rob@100 12973 *
rob@100 12974 * @see popMatrix
rob@100 12975 * @see pushMatrix
rob@100 12976 */
rob@100 12977 Drawing2D.prototype.popMatrix = function() {
rob@100 12978 modelView.set(userMatrixStack.pop());
rob@100 12979 modelViewInv.set(userReverseMatrixStack.pop());
rob@100 12980 restoreContext();
rob@100 12981 };
rob@100 12982
rob@100 12983 Drawing3D.prototype.popMatrix = function() {
rob@100 12984 modelView.set(userMatrixStack.pop());
rob@100 12985 modelViewInv.set(userReverseMatrixStack.pop());
rob@100 12986 };
rob@100 12987
rob@100 12988 /**
rob@100 12989 * Replaces the current matrix with the identity matrix. The equivalent function in OpenGL is glLoadIdentity().
rob@100 12990 *
rob@100 12991 * @returns none
rob@100 12992 *
rob@100 12993 * @see popMatrix
rob@100 12994 * @see pushMatrix
rob@100 12995 * @see applyMatrix
rob@100 12996 * @see printMatrix
rob@100 12997 */
rob@100 12998 Drawing2D.prototype.resetMatrix = function() {
rob@100 12999 modelView.reset();
rob@100 13000 modelViewInv.reset();
rob@100 13001 curContext.setTransform(1,0,0,1,0,0);
rob@100 13002 };
rob@100 13003
rob@100 13004 Drawing3D.prototype.resetMatrix = function() {
rob@100 13005 modelView.reset();
rob@100 13006 modelViewInv.reset();
rob@100 13007 };
rob@100 13008
rob@100 13009 /**
rob@100 13010 * Multiplies the current matrix by the one specified through the parameters. This is very slow because it will
rob@100 13011 * try to calculate the inverse of the transform, so avoid it whenever possible. The equivalent function
rob@100 13012 * in OpenGL is glMultMatrix().
rob@100 13013 *
rob@100 13014 * @param {int|float} n00-n15 numbers which define the 4x4 matrix to be multiplied
rob@100 13015 *
rob@100 13016 * @returns none
rob@100 13017 *
rob@100 13018 * @see popMatrix
rob@100 13019 * @see pushMatrix
rob@100 13020 * @see resetMatrix
rob@100 13021 * @see printMatrix
rob@100 13022 */
rob@100 13023 DrawingShared.prototype.applyMatrix = function() {
rob@100 13024 var a = arguments;
rob@100 13025 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]);
rob@100 13026 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]);
rob@100 13027 };
rob@100 13028
rob@100 13029 Drawing2D.prototype.applyMatrix = function() {
rob@100 13030 var a = arguments;
rob@100 13031 for (var cnt = a.length; cnt < 16; cnt++) {
rob@100 13032 a[cnt] = 0;
rob@100 13033 }
rob@100 13034 a[10] = a[15] = 1;
rob@100 13035 DrawingShared.prototype.applyMatrix.apply(this, a);
rob@100 13036 };
rob@100 13037
rob@100 13038 /**
rob@100 13039 * Rotates a shape around the x-axis the amount specified by the angle parameter. Angles should be
rob@100 13040 * specified in radians (values from 0 to PI*2) or converted to radians with the radians() function.
rob@100 13041 * Objects are always rotated around their relative position to the origin and positive numbers
rob@100 13042 * rotate objects in a counterclockwise direction. Transformations apply to everything that happens
rob@100 13043 * after and subsequent calls to the function accumulates the effect. For example, calling rotateX(PI/2)
rob@100 13044 * and then rotateX(PI/2) is the same as rotateX(PI). If rotateX() is called within the draw(), the
rob@100 13045 * transformation is reset when the loop begins again. This function requires passing P3D or OPENGL
rob@100 13046 * into the size() parameter as shown in the example above.
rob@100 13047 *
rob@100 13048 * @param {int|float} angleInRadians angle of rotation specified in radians
rob@100 13049 *
rob@100 13050 * @returns none
rob@100 13051 *
rob@100 13052 * @see rotateY
rob@100 13053 * @see rotateZ
rob@100 13054 * @see rotate
rob@100 13055 * @see translate
rob@100 13056 * @see scale
rob@100 13057 * @see popMatrix
rob@100 13058 * @see pushMatrix
rob@100 13059 */
rob@100 13060 p.rotateX = function(angleInRadians) {
rob@100 13061 modelView.rotateX(angleInRadians);
rob@100 13062 modelViewInv.invRotateX(angleInRadians);
rob@100 13063 };
rob@100 13064
rob@100 13065 /**
rob@100 13066 * Rotates a shape around the z-axis the amount specified by the angle parameter. Angles should be
rob@100 13067 * specified in radians (values from 0 to PI*2) or converted to radians with the radians() function.
rob@100 13068 * Objects are always rotated around their relative position to the origin and positive numbers
rob@100 13069 * rotate objects in a counterclockwise direction. Transformations apply to everything that happens
rob@100 13070 * after and subsequent calls to the function accumulates the effect. For example, calling rotateZ(PI/2)
rob@100 13071 * and then rotateZ(PI/2) is the same as rotateZ(PI). If rotateZ() is called within the draw(), the
rob@100 13072 * transformation is reset when the loop begins again. This function requires passing P3D or OPENGL
rob@100 13073 * into the size() parameter as shown in the example above.
rob@100 13074 *
rob@100 13075 * @param {int|float} angleInRadians angle of rotation specified in radians
rob@100 13076 *
rob@100 13077 * @returns none
rob@100 13078 *
rob@100 13079 * @see rotateX
rob@100 13080 * @see rotateY
rob@100 13081 * @see rotate
rob@100 13082 * @see translate
rob@100 13083 * @see scale
rob@100 13084 * @see popMatrix
rob@100 13085 * @see pushMatrix
rob@100 13086 */
rob@100 13087 Drawing2D.prototype.rotateZ = function() {
rob@100 13088 throw "rotateZ() is not supported in 2D mode. Use rotate(float) instead.";
rob@100 13089 };
rob@100 13090
rob@100 13091 Drawing3D.prototype.rotateZ = function(angleInRadians) {
rob@100 13092 modelView.rotateZ(angleInRadians);
rob@100 13093 modelViewInv.invRotateZ(angleInRadians);
rob@100 13094 };
rob@100 13095
rob@100 13096 /**
rob@100 13097 * Rotates a shape around the y-axis the amount specified by the angle parameter. Angles should be
rob@100 13098 * specified in radians (values from 0 to PI*2) or converted to radians with the radians() function.
rob@100 13099 * Objects are always rotated around their relative position to the origin and positive numbers
rob@100 13100 * rotate objects in a counterclockwise direction. Transformations apply to everything that happens
rob@100 13101 * after and subsequent calls to the function accumulates the effect. For example, calling rotateY(PI/2)
rob@100 13102 * and then rotateY(PI/2) is the same as rotateY(PI). If rotateY() is called within the draw(), the
rob@100 13103 * transformation is reset when the loop begins again. This function requires passing P3D or OPENGL
rob@100 13104 * into the size() parameter as shown in the example above.
rob@100 13105 *
rob@100 13106 * @param {int|float} angleInRadians angle of rotation specified in radians
rob@100 13107 *
rob@100 13108 * @returns none
rob@100 13109 *
rob@100 13110 * @see rotateX
rob@100 13111 * @see rotateZ
rob@100 13112 * @see rotate
rob@100 13113 * @see translate
rob@100 13114 * @see scale
rob@100 13115 * @see popMatrix
rob@100 13116 * @see pushMatrix
rob@100 13117 */
rob@100 13118 p.rotateY = function(angleInRadians) {
rob@100 13119 modelView.rotateY(angleInRadians);
rob@100 13120 modelViewInv.invRotateY(angleInRadians);
rob@100 13121 };
rob@100 13122
rob@100 13123 /**
rob@100 13124 * Rotates a shape the amount specified by the angle parameter. Angles should be specified in radians
rob@100 13125 * (values from 0 to TWO_PI) or converted to radians with the radians() function. Objects are always
rob@100 13126 * rotated around their relative position to the origin and positive numbers rotate objects in a
rob@100 13127 * clockwise direction. Transformations apply to everything that happens after and subsequent calls
rob@100 13128 * to the function accumulates the effect. For example, calling rotate(HALF_PI) and then rotate(HALF_PI)
rob@100 13129 * is the same as rotate(PI). All tranformations are reset when draw() begins again. Technically,
rob@100 13130 * rotate() multiplies the current transformation matrix by a rotation matrix. This function can be
rob@100 13131 * further controlled by the pushMatrix() and popMatrix().
rob@100 13132 *
rob@100 13133 * @param {int|float} angleInRadians angle of rotation specified in radians
rob@100 13134 *
rob@100 13135 * @returns none
rob@100 13136 *
rob@100 13137 * @see rotateX
rob@100 13138 * @see rotateY
rob@100 13139 * @see rotateZ
rob@100 13140 * @see rotate
rob@100 13141 * @see translate
rob@100 13142 * @see scale
rob@100 13143 * @see popMatrix
rob@100 13144 * @see pushMatrix
rob@100 13145 */
rob@100 13146 Drawing2D.prototype.rotate = function(angleInRadians) {
rob@100 13147 modelView.rotateZ(angleInRadians);
rob@100 13148 modelViewInv.invRotateZ(angleInRadians);
rob@100 13149 curContext.rotate(angleInRadians);
rob@100 13150 };
rob@100 13151
rob@100 13152 Drawing3D.prototype.rotate = function(angleInRadians) {
rob@100 13153 if (arguments.length < 4) {
rob@100 13154 p.rotateZ(angleInRadians);
rob@100 13155 } else {
rob@100 13156 modelView.rotate(angleInRadians, arguments[1], arguments[2], arguments[3]);
rob@100 13157 modelViewInv.rotate((-angleInRadians), arguments[1], arguments[2], arguments[3]);
rob@100 13158 }
rob@100 13159 };
rob@100 13160
rob@100 13161 /**
rob@100 13162 * Shears a shape around the x-axis the amount specified by the angle parameter.
rob@100 13163 * Angles should be specified in radians (values from 0 to PI*2) or converted to radians
rob@100 13164 * with the radians() function. Objects are always sheared around their relative position
rob@100 13165 * to the origin and positive numbers shear objects in a clockwise direction. Transformations
rob@100 13166 * apply to everything that happens after and subsequent calls to the function accumulates the
rob@100 13167 * effect. For example, calling shearX(PI/2) and then shearX(PI/2) is the same as shearX(PI)
rob@100 13168 *
rob@100 13169 * @param {int|float} angleInRadians angle of rotation specified in radians
rob@100 13170 *
rob@100 13171 * @returns none
rob@100 13172 *
rob@100 13173 * @see rotateX
rob@100 13174 * @see rotateY
rob@100 13175 * @see rotateZ
rob@100 13176 * @see rotate
rob@100 13177 * @see translate
rob@100 13178 * @see scale
rob@100 13179 * @see popMatrix
rob@100 13180 * @see pushMatrix
rob@100 13181 */
rob@100 13182
rob@100 13183 Drawing2D.prototype.shearX = function(angleInRadians) {
rob@100 13184 modelView.shearX(angleInRadians);
rob@100 13185 curContext.transform(1,0,angleInRadians,1,0,0);
rob@100 13186 };
rob@100 13187
rob@100 13188 Drawing3D.prototype.shearX = function(angleInRadians) {
rob@100 13189 modelView.shearX(angleInRadians);
rob@100 13190 };
rob@100 13191
rob@100 13192 /**
rob@100 13193 * Shears a shape around the y-axis the amount specified by the angle parameter.
rob@100 13194 * Angles should be specified in radians (values from 0 to PI*2) or converted to
rob@100 13195 * radians with the radians() function. Objects are always sheared around their
rob@100 13196 * relative position to the origin and positive numbers shear objects in a
rob@100 13197 * clockwise direction. Transformations apply to everything that happens after
rob@100 13198 * and subsequent calls to the function accumulates the effect. For example,
rob@100 13199 * calling shearY(PI/2) and then shearY(PI/2) is the same as shearY(PI).
rob@100 13200 *
rob@100 13201 * @param {int|float} angleInRadians angle of rotation specified in radians
rob@100 13202 *
rob@100 13203 * @returns none
rob@100 13204 *
rob@100 13205 * @see rotateX
rob@100 13206 * @see rotateY
rob@100 13207 * @see rotateZ
rob@100 13208 * @see rotate
rob@100 13209 * @see translate
rob@100 13210 * @see scale
rob@100 13211 * @see popMatrix
rob@100 13212 * @see pushMatrix
rob@100 13213 * @see shearX
rob@100 13214 */
rob@100 13215
rob@100 13216 Drawing2D.prototype.shearY = function(angleInRadians) {
rob@100 13217 modelView.shearY(angleInRadians);
rob@100 13218 curContext.transform(1,angleInRadians,0,1,0,0);
rob@100 13219 };
rob@100 13220
rob@100 13221 Drawing3D.prototype.shearY = function(angleInRadians) {
rob@100 13222 modelView.shearY(angleInRadians);
rob@100 13223 };
rob@100 13224
rob@100 13225 /**
rob@100 13226 * The pushStyle() function saves the current style settings and popStyle() restores the prior settings.
rob@100 13227 * Note that these functions are always used together. They allow you to change the style settings and later
rob@100 13228 * return to what you had. When a new style is started with pushStyle(), it builds on the current style information.
rob@100 13229 * The pushStyle() and popStyle() functions can be embedded to provide more control (see the second example
rob@100 13230 * above for a demonstration.)
rob@100 13231 * The style information controlled by the following functions are included in the style: fill(), stroke(), tint(),
rob@100 13232 * strokeWeight(), strokeCap(), strokeJoin(), imageMode(), rectMode(), ellipseMode(), shapeMode(), colorMode(),
rob@100 13233 * textAlign(), textFont(), textMode(), textSize(), textLeading(), emissive(), specular(), shininess(), ambient()
rob@100 13234 *
rob@100 13235 * @returns none
rob@100 13236 *
rob@100 13237 * @see popStyle
rob@100 13238 */
rob@100 13239 p.pushStyle = function() {
rob@100 13240 // Save the canvas state.
rob@100 13241 saveContext();
rob@100 13242
rob@100 13243 p.pushMatrix();
rob@100 13244
rob@100 13245 var newState = {
rob@100 13246 'doFill': doFill,
rob@100 13247 'currentFillColor': currentFillColor,
rob@100 13248 'doStroke': doStroke,
rob@100 13249 'currentStrokeColor': currentStrokeColor,
rob@100 13250 'curTint': curTint,
rob@100 13251 'curRectMode': curRectMode,
rob@100 13252 'curColorMode': curColorMode,
rob@100 13253 'colorModeX': colorModeX,
rob@100 13254 'colorModeZ': colorModeZ,
rob@100 13255 'colorModeY': colorModeY,
rob@100 13256 'colorModeA': colorModeA,
rob@100 13257 'curTextFont': curTextFont,
rob@100 13258 'horizontalTextAlignment': horizontalTextAlignment,
rob@100 13259 'verticalTextAlignment': verticalTextAlignment,
rob@100 13260 'textMode': textMode,
rob@100 13261 'curFontName': curFontName,
rob@100 13262 'curTextSize': curTextSize,
rob@100 13263 'curTextAscent': curTextAscent,
rob@100 13264 'curTextDescent': curTextDescent,
rob@100 13265 'curTextLeading': curTextLeading
rob@100 13266 };
rob@100 13267
rob@100 13268 styleArray.push(newState);
rob@100 13269 };
rob@100 13270
rob@100 13271 /**
rob@100 13272 * The pushStyle() function saves the current style settings and popStyle() restores the prior settings; these
rob@100 13273 * functions are always used together. They allow you to change the style settings and later return to what you had.
rob@100 13274 * When a new style is started with pushStyle(), it builds on the current style information. The pushStyle() and
rob@100 13275 * popStyle() functions can be embedded to provide more control (see the second example above for a demonstration.)
rob@100 13276 *
rob@100 13277 * @returns none
rob@100 13278 *
rob@100 13279 * @see pushStyle
rob@100 13280 */
rob@100 13281 p.popStyle = function() {
rob@100 13282 var oldState = styleArray.pop();
rob@100 13283
rob@100 13284 if (oldState) {
rob@100 13285 restoreContext();
rob@100 13286
rob@100 13287 p.popMatrix();
rob@100 13288
rob@100 13289 doFill = oldState.doFill;
rob@100 13290 currentFillColor = oldState.currentFillColor;
rob@100 13291 doStroke = oldState.doStroke;
rob@100 13292 currentStrokeColor = oldState.currentStrokeColor;
rob@100 13293 curTint = oldState.curTint;
rob@100 13294 curRectMode = oldState.curRectMode;
rob@100 13295 curColorMode = oldState.curColorMode;
rob@100 13296 colorModeX = oldState.colorModeX;
rob@100 13297 colorModeZ = oldState.colorModeZ;
rob@100 13298 colorModeY = oldState.colorModeY;
rob@100 13299 colorModeA = oldState.colorModeA;
rob@100 13300 curTextFont = oldState.curTextFont;
rob@100 13301 curFontName = oldState.curFontName;
rob@100 13302 curTextSize = oldState.curTextSize;
rob@100 13303 horizontalTextAlignment = oldState.horizontalTextAlignment;
rob@100 13304 verticalTextAlignment = oldState.verticalTextAlignment;
rob@100 13305 textMode = oldState.textMode;
rob@100 13306 curTextAscent = oldState.curTextAscent;
rob@100 13307 curTextDescent = oldState.curTextDescent;
rob@100 13308 curTextLeading = oldState.curTextLeading;
rob@100 13309 } else {
rob@100 13310 throw "Too many popStyle() without enough pushStyle()";
rob@100 13311 }
rob@100 13312 };
rob@100 13313
rob@100 13314 ////////////////////////////////////////////////////////////////////////////
rob@100 13315 // Time based functions
rob@100 13316 ////////////////////////////////////////////////////////////////////////////
rob@100 13317
rob@100 13318 /**
rob@100 13319 * Processing communicates with the clock on your computer.
rob@100 13320 * The year() function returns the current year as an integer (2003, 2004, 2005, etc).
rob@100 13321 *
rob@100 13322 * @returns {float} The current year.
rob@100 13323 *
rob@100 13324 * @see millis
rob@100 13325 * @see second
rob@100 13326 * @see minute
rob@100 13327 * @see hour
rob@100 13328 * @see day
rob@100 13329 * @see month
rob@100 13330 */
rob@100 13331 p.year = function() {
rob@100 13332 return new Date().getFullYear();
rob@100 13333 };
rob@100 13334 /**
rob@100 13335 * Processing communicates with the clock on your computer.
rob@100 13336 * The month() function returns the current month as a value from 1 - 12.
rob@100 13337 *
rob@100 13338 * @returns {float} The current month.
rob@100 13339 *
rob@100 13340 * @see millis
rob@100 13341 * @see second
rob@100 13342 * @see minute
rob@100 13343 * @see hour
rob@100 13344 * @see day
rob@100 13345 * @see year
rob@100 13346 */
rob@100 13347 p.month = function() {
rob@100 13348 return new Date().getMonth() + 1;
rob@100 13349 };
rob@100 13350 /**
rob@100 13351 * Processing communicates with the clock on your computer.
rob@100 13352 * The day() function returns the current day as a value from 1 - 31.
rob@100 13353 *
rob@100 13354 * @returns {float} The current day.
rob@100 13355 *
rob@100 13356 * @see millis
rob@100 13357 * @see second
rob@100 13358 * @see minute
rob@100 13359 * @see hour
rob@100 13360 * @see month
rob@100 13361 * @see year
rob@100 13362 */
rob@100 13363 p.day = function() {
rob@100 13364 return new Date().getDate();
rob@100 13365 };
rob@100 13366 /**
rob@100 13367 * Processing communicates with the clock on your computer.
rob@100 13368 * The hour() function returns the current hour as a value from 0 - 23.
rob@100 13369 *
rob@100 13370 * @returns {float} The current hour.
rob@100 13371 *
rob@100 13372 * @see millis
rob@100 13373 * @see second
rob@100 13374 * @see minute
rob@100 13375 * @see month
rob@100 13376 * @see day
rob@100 13377 * @see year
rob@100 13378 */
rob@100 13379 p.hour = function() {
rob@100 13380 return new Date().getHours();
rob@100 13381 };
rob@100 13382 /**
rob@100 13383 * Processing communicates with the clock on your computer.
rob@100 13384 * The minute() function returns the current minute as a value from 0 - 59.
rob@100 13385 *
rob@100 13386 * @returns {float} The current minute.
rob@100 13387 *
rob@100 13388 * @see millis
rob@100 13389 * @see second
rob@100 13390 * @see month
rob@100 13391 * @see hour
rob@100 13392 * @see day
rob@100 13393 * @see year
rob@100 13394 */
rob@100 13395 p.minute = function() {
rob@100 13396 return new Date().getMinutes();
rob@100 13397 };
rob@100 13398 /**
rob@100 13399 * Processing communicates with the clock on your computer.
rob@100 13400 * The second() function returns the current second as a value from 0 - 59.
rob@100 13401 *
rob@100 13402 * @returns {float} The current minute.
rob@100 13403 *
rob@100 13404 * @see millis
rob@100 13405 * @see month
rob@100 13406 * @see minute
rob@100 13407 * @see hour
rob@100 13408 * @see day
rob@100 13409 * @see year
rob@100 13410 */
rob@100 13411 p.second = function() {
rob@100 13412 return new Date().getSeconds();
rob@100 13413 };
rob@100 13414 /**
rob@100 13415 * Returns the number of milliseconds (thousandths of a second) since starting a sketch.
rob@100 13416 * This information is often used for timing animation sequences.
rob@100 13417 *
rob@100 13418 * @returns {long} The number of milliseconds since starting the sketch.
rob@100 13419 *
rob@100 13420 * @see month
rob@100 13421 * @see second
rob@100 13422 * @see minute
rob@100 13423 * @see hour
rob@100 13424 * @see day
rob@100 13425 * @see year
rob@100 13426 */
rob@100 13427 p.millis = function() {
rob@100 13428 return Date.now() - start;
rob@100 13429 };
rob@100 13430
rob@100 13431 /**
rob@100 13432 * Executes the code within draw() one time. This functions allows the program to update
rob@100 13433 * the display window only when necessary, for example when an event registered by
rob@100 13434 * mousePressed() or keyPressed() occurs.
rob@100 13435 * In structuring a program, it only makes sense to call redraw() within events such as
rob@100 13436 * mousePressed(). This is because redraw() does not run draw() immediately (it only sets
rob@100 13437 * a flag that indicates an update is needed).
rob@100 13438 * Calling redraw() within draw() has no effect because draw() is continuously called anyway.
rob@100 13439 *
rob@100 13440 * @returns none
rob@100 13441 *
rob@100 13442 * @see noLoop
rob@100 13443 * @see loop
rob@100 13444 */
rob@100 13445 function redrawHelper() {
rob@100 13446 var sec = (Date.now() - timeSinceLastFPS) / 1000;
rob@100 13447 framesSinceLastFPS++;
rob@100 13448 var fps = framesSinceLastFPS / sec;
rob@100 13449
rob@100 13450 // recalculate FPS every half second for better accuracy.
rob@100 13451 if (sec > 0.5) {
rob@100 13452 timeSinceLastFPS = Date.now();
rob@100 13453 framesSinceLastFPS = 0;
rob@100 13454 p.__frameRate = fps;
rob@100 13455 }
rob@100 13456
rob@100 13457 p.frameCount++;
rob@100 13458 }
rob@100 13459
rob@100 13460 Drawing2D.prototype.redraw = function() {
rob@100 13461 redrawHelper();
rob@100 13462
rob@100 13463 curContext.lineWidth = lineWidth;
rob@100 13464 var pmouseXLastEvent = p.pmouseX,
rob@100 13465 pmouseYLastEvent = p.pmouseY;
rob@100 13466 p.pmouseX = pmouseXLastFrame;
rob@100 13467 p.pmouseY = pmouseYLastFrame;
rob@100 13468
rob@100 13469 saveContext();
rob@100 13470 p.draw();
rob@100 13471 restoreContext();
rob@100 13472
rob@100 13473 pmouseXLastFrame = p.mouseX;
rob@100 13474 pmouseYLastFrame = p.mouseY;
rob@100 13475 p.pmouseX = pmouseXLastEvent;
rob@100 13476 p.pmouseY = pmouseYLastEvent;
rob@100 13477 };
rob@100 13478
rob@100 13479 Drawing3D.prototype.redraw = function() {
rob@100 13480 redrawHelper();
rob@100 13481
rob@100 13482 var pmouseXLastEvent = p.pmouseX,
rob@100 13483 pmouseYLastEvent = p.pmouseY;
rob@100 13484 p.pmouseX = pmouseXLastFrame;
rob@100 13485 p.pmouseY = pmouseYLastFrame;
rob@100 13486 // even if the color buffer isn't cleared with background(),
rob@100 13487 // the depth buffer needs to be cleared regardless.
rob@100 13488 curContext.clear(curContext.DEPTH_BUFFER_BIT);
rob@100 13489 curContextCache = { attributes: {}, locations: {} };
rob@100 13490 // Delete all the lighting states and the materials the
rob@100 13491 // user set in the last draw() call.
rob@100 13492 p.noLights();
rob@100 13493 p.lightFalloff(1, 0, 0);
rob@100 13494 p.shininess(1);
rob@100 13495 p.ambient(255, 255, 255);
rob@100 13496 p.specular(0, 0, 0);
rob@100 13497 p.emissive(0, 0, 0);
rob@100 13498 p.camera();
rob@100 13499 p.draw();
rob@100 13500
rob@100 13501 pmouseXLastFrame = p.mouseX;
rob@100 13502 pmouseYLastFrame = p.mouseY;
rob@100 13503 p.pmouseX = pmouseXLastEvent;
rob@100 13504 p.pmouseY = pmouseYLastEvent;
rob@100 13505 };
rob@100 13506
rob@100 13507 /**
rob@100 13508 * Stops Processing from continuously executing the code within draw(). If loop() is
rob@100 13509 * called, the code in draw() begin to run continuously again. If using noLoop() in
rob@100 13510 * setup(), it should be the last line inside the block.
rob@100 13511 * When noLoop() is used, it's not possible to manipulate or access the screen inside event
rob@100 13512 * handling functions such as mousePressed() or keyPressed(). Instead, use those functions
rob@100 13513 * to call redraw() or loop(), which will run draw(), which can update the screen properly.
rob@100 13514 * This means that when noLoop() has been called, no drawing can happen, and functions like
rob@100 13515 * saveFrame() or loadPixels() may not be used.
rob@100 13516 * Note that if the sketch is resized, redraw() will be called to update the sketch, even
rob@100 13517 * after noLoop() has been specified. Otherwise, the sketch would enter an odd state until
rob@100 13518 * loop() was called.
rob@100 13519 *
rob@100 13520 * @returns none
rob@100 13521 *
rob@100 13522 * @see redraw
rob@100 13523 * @see draw
rob@100 13524 * @see loop
rob@100 13525 */
rob@100 13526 p.noLoop = function() {
rob@100 13527 doLoop = false;
rob@100 13528 loopStarted = false;
rob@100 13529 clearInterval(looping);
rob@100 13530 curSketch.onPause();
rob@100 13531 };
rob@100 13532
rob@100 13533 /**
rob@100 13534 * Causes Processing to continuously execute the code within draw(). If noLoop() is called,
rob@100 13535 * the code in draw() stops executing.
rob@100 13536 *
rob@100 13537 * @returns none
rob@100 13538 *
rob@100 13539 * @see noLoop
rob@100 13540 */
rob@100 13541 p.loop = function() {
rob@100 13542 if (loopStarted) {
rob@100 13543 return;
rob@100 13544 }
rob@100 13545
rob@100 13546 timeSinceLastFPS = Date.now();
rob@100 13547 framesSinceLastFPS = 0;
rob@100 13548
rob@100 13549 looping = window.setInterval(function() {
rob@100 13550 try {
rob@100 13551 curSketch.onFrameStart();
rob@100 13552 p.redraw();
rob@100 13553 curSketch.onFrameEnd();
rob@100 13554 } catch(e_loop) {
rob@100 13555 window.clearInterval(looping);
rob@100 13556 throw e_loop;
rob@100 13557 }
rob@100 13558 }, curMsPerFrame);
rob@100 13559 doLoop = true;
rob@100 13560 loopStarted = true;
rob@100 13561 curSketch.onLoop();
rob@100 13562 };
rob@100 13563
rob@100 13564 /**
rob@100 13565 * Specifies the number of frames to be displayed every second. If the processor is not
rob@100 13566 * fast enough to maintain the specified rate, it will not be achieved. For example, the
rob@100 13567 * function call frameRate(30) will attempt to refresh 30 times a second. It is recommended
rob@100 13568 * to set the frame rate within setup(). The default rate is 60 frames per second.
rob@100 13569 *
rob@100 13570 * @param {int} aRate number of frames per second.
rob@100 13571 *
rob@100 13572 * @returns none
rob@100 13573 *
rob@100 13574 * @see delay
rob@100 13575 */
rob@100 13576 p.frameRate = function(aRate) {
rob@100 13577 curFrameRate = aRate;
rob@100 13578 curMsPerFrame = 1000 / curFrameRate;
rob@100 13579
rob@100 13580 // clear and reset interval
rob@100 13581 if (doLoop) {
rob@100 13582 p.noLoop();
rob@100 13583 p.loop();
rob@100 13584 }
rob@100 13585 };
rob@100 13586
rob@100 13587 /**
rob@100 13588 * Quits/stops/exits the program.
rob@100 13589 * Rather than terminating immediately, exit() will cause the sketch to exit after draw()
rob@100 13590 * has completed (or after setup() completes if called during the setup() method).
rob@100 13591 *
rob@100 13592 * @returns none
rob@100 13593 */
rob@100 13594 p.exit = function() {
rob@100 13595 // cleanup
rob@100 13596 window.clearInterval(looping);
rob@100 13597 removeInstance(p.externals.canvas.id);
rob@100 13598 delete(curElement.onmousedown);
rob@100 13599
rob@100 13600 // Step through the libraries to detach them
rob@100 13601 for (var lib in Processing.lib) {
rob@100 13602 if (Processing.lib.hasOwnProperty(lib)) {
rob@100 13603 if (Processing.lib[lib].hasOwnProperty("detach")) {
rob@100 13604 Processing.lib[lib].detach(p);
rob@100 13605 }
rob@100 13606 }
rob@100 13607 }
rob@100 13608
rob@100 13609 // clean up all event handling
rob@100 13610 var i = eventHandlers.length;
rob@100 13611 while (i--) {
rob@100 13612 detachEventHandler(eventHandlers[i]);
rob@100 13613 }
rob@100 13614 curSketch.onExit();
rob@100 13615 };
rob@100 13616
rob@100 13617 ////////////////////////////////////////////////////////////////////////////
rob@100 13618 // MISC functions
rob@100 13619 ////////////////////////////////////////////////////////////////////////////
rob@100 13620
rob@100 13621 /**
rob@100 13622 * Sets the cursor to a predefined symbol, an image, or turns it on if already hidden.
rob@100 13623 * If you are trying to set an image as the cursor, it is recommended to make the size
rob@100 13624 * 16x16 or 32x32 pixels. It is not possible to load an image as the cursor if you are
rob@100 13625 * exporting your program for the Web. The values for parameters x and y must be less
rob@100 13626 * than the dimensions of the image.
rob@100 13627 *
rob@100 13628 * @param {MODE} MODE either ARROW, CROSS, HAND, MOVE, TEXT, WAIT
rob@100 13629 * @param {PImage} image any variable of type PImage
rob@100 13630 * @param {int} x the horizonal active spot of the cursor
rob@100 13631 * @param {int} y the vertical active spot of the cursor
rob@100 13632 *
rob@100 13633 * @returns none
rob@100 13634 *
rob@100 13635 * @see noCursor
rob@100 13636 */
rob@100 13637 p.cursor = function() {
rob@100 13638 if (arguments.length > 1 || (arguments.length === 1 && arguments[0] instanceof p.PImage)) {
rob@100 13639 var image = arguments[0],
rob@100 13640 x, y;
rob@100 13641 if (arguments.length >= 3) {
rob@100 13642 x = arguments[1];
rob@100 13643 y = arguments[2];
rob@100 13644 if (x < 0 || y < 0 || y >= image.height || x >= image.width) {
rob@100 13645 throw "x and y must be non-negative and less than the dimensions of the image";
rob@100 13646 }
rob@100 13647 } else {
rob@100 13648 x = image.width >>> 1;
rob@100 13649 y = image.height >>> 1;
rob@100 13650 }
rob@100 13651
rob@100 13652 // see https://developer.mozilla.org/en/Using_URL_values_for_the_cursor_property
rob@100 13653 var imageDataURL = image.toDataURL();
rob@100 13654 var style = "url(\"" + imageDataURL + "\") " + x + " " + y + ", default";
rob@100 13655 curCursor = curElement.style.cursor = style;
rob@100 13656 } else if (arguments.length === 1) {
rob@100 13657 var mode = arguments[0];
rob@100 13658 curCursor = curElement.style.cursor = mode;
rob@100 13659 } else {
rob@100 13660 curCursor = curElement.style.cursor = oldCursor;
rob@100 13661 }
rob@100 13662 };
rob@100 13663
rob@100 13664 /**
rob@100 13665 * Hides the cursor from view.
rob@100 13666 *
rob@100 13667 * @returns none
rob@100 13668 *
rob@100 13669 * @see cursor
rob@100 13670 */
rob@100 13671 p.noCursor = function() {
rob@100 13672 curCursor = curElement.style.cursor = PConstants.NOCURSOR;
rob@100 13673 };
rob@100 13674
rob@100 13675 /**
rob@100 13676 * Links to a webpage either in the same window or in a new window. The complete URL
rob@100 13677 * must be specified.
rob@100 13678 *
rob@100 13679 * @param {String} href complete url as a String in quotes
rob@100 13680 * @param {String} target name of the window to load the URL as a string in quotes
rob@100 13681 *
rob@100 13682 * @returns none
rob@100 13683 */
rob@100 13684 p.link = function(href, target) {
rob@100 13685 if (target !== undef) {
rob@100 13686 window.open(href, target);
rob@100 13687 } else {
rob@100 13688 window.location = href;
rob@100 13689 }
rob@100 13690 };
rob@100 13691
rob@100 13692 // PGraphics methods
rob@100 13693 // These functions exist only for compatibility with P5
rob@100 13694 p.beginDraw = noop;
rob@100 13695 p.endDraw = noop;
rob@100 13696
rob@100 13697 /**
rob@100 13698 * This function takes content from a canvas and turns it into an ImageData object to be used with a PImage
rob@100 13699 *
rob@100 13700 * @returns {ImageData} ImageData object to attach to a PImage (1D array of pixel data)
rob@100 13701 *
rob@100 13702 * @see PImage
rob@100 13703 */
rob@100 13704 Drawing2D.prototype.toImageData = function(x, y, w, h) {
rob@100 13705 x = x !== undef ? x : 0;
rob@100 13706 y = y !== undef ? y : 0;
rob@100 13707 w = w !== undef ? w : p.width;
rob@100 13708 h = h !== undef ? h : p.height;
rob@100 13709 return curContext.getImageData(x, y, w, h);
rob@100 13710 };
rob@100 13711
rob@100 13712 Drawing3D.prototype.toImageData = function(x, y, w, h) {
rob@100 13713 x = x !== undef ? x : 0;
rob@100 13714 y = y !== undef ? y : 0;
rob@100 13715 w = w !== undef ? w : p.width;
rob@100 13716 h = h !== undef ? h : p.height;
rob@100 13717 var c = document.createElement("canvas"),
rob@100 13718 ctx = c.getContext("2d"),
rob@100 13719 obj = ctx.createImageData(w, h),
rob@100 13720 uBuff = new Uint8Array(w * h * 4);
rob@100 13721 curContext.readPixels(x, y, w, h, curContext.RGBA, curContext.UNSIGNED_BYTE, uBuff);
rob@100 13722 for (var i=0, ul=uBuff.length, obj_data=obj.data; i < ul; i++) {
rob@100 13723 obj_data[i] = uBuff[(h - 1 - Math.floor(i / 4 / w)) * w * 4 + (i % (w * 4))];
rob@100 13724 }
rob@100 13725 return obj;
rob@100 13726 };
rob@100 13727
rob@100 13728 /**
rob@100 13729 * Displays message in the browser's status area. This is the text area in the lower
rob@100 13730 * left corner of the browser. The status() function will only work when the
rob@100 13731 * Processing program is running in a web browser.
rob@100 13732 *
rob@100 13733 * @param {String} text any valid String
rob@100 13734 *
rob@100 13735 * @returns none
rob@100 13736 */
rob@100 13737 p.status = function(text) {
rob@100 13738 window.status = text;
rob@100 13739 };
rob@100 13740
rob@100 13741 ////////////////////////////////////////////////////////////////////////////
rob@100 13742 // Binary Functions
rob@100 13743 ////////////////////////////////////////////////////////////////////////////
rob@100 13744
rob@100 13745 /**
rob@100 13746 * Converts a byte, char, int, or color to a String containing the equivalent binary
rob@100 13747 * notation. For example color(0, 102, 153, 255) will convert to the String
rob@100 13748 * "11111111000000000110011010011001". This function can help make your geeky debugging
rob@100 13749 * sessions much happier.
rob@100 13750 *
rob@100 13751 * @param {byte|char|int|color} num byte, char, int, color: value to convert
rob@100 13752 * @param {int} numBits number of digits to return
rob@100 13753 *
rob@100 13754 * @returns {String}
rob@100 13755 *
rob@100 13756 * @see unhex
rob@100 13757 * @see hex
rob@100 13758 * @see unbinary
rob@100 13759 */
rob@100 13760 p.binary = function(num, numBits) {
rob@100 13761 var bit;
rob@100 13762 if (numBits > 0) {
rob@100 13763 bit = numBits;
rob@100 13764 } else if(num instanceof Char) {
rob@100 13765 bit = 16;
rob@100 13766 num |= 0; // making it int
rob@100 13767 } else {
rob@100 13768 // autodetect, skipping zeros
rob@100 13769 bit = 32;
rob@100 13770 while (bit > 1 && !((num >>> (bit - 1)) & 1)) {
rob@100 13771 bit--;
rob@100 13772 }
rob@100 13773 }
rob@100 13774 var result = "";
rob@100 13775 while (bit > 0) {
rob@100 13776 result += ((num >>> (--bit)) & 1) ? "1" : "0";
rob@100 13777 }
rob@100 13778 return result;
rob@100 13779 };
rob@100 13780
rob@100 13781 /**
rob@100 13782 * Converts a String representation of a binary number to its equivalent integer value.
rob@100 13783 * For example, unbinary("00001000") will return 8.
rob@100 13784 *
rob@100 13785 * @param {String} binaryString String
rob@100 13786 *
rob@100 13787 * @returns {Int}
rob@100 13788 *
rob@100 13789 * @see hex
rob@100 13790 * @see binary
rob@100 13791 * @see unbinary
rob@100 13792 */
rob@100 13793 p.unbinary = function(binaryString) {
rob@100 13794 var i = binaryString.length - 1, mask = 1, result = 0;
rob@100 13795 while (i >= 0) {
rob@100 13796 var ch = binaryString[i--];
rob@100 13797 if (ch !== '0' && ch !== '1') {
rob@100 13798 throw "the value passed into unbinary was not an 8 bit binary number";
rob@100 13799 }
rob@100 13800 if (ch === '1') {
rob@100 13801 result += mask;
rob@100 13802 }
rob@100 13803 mask <<= 1;
rob@100 13804 }
rob@100 13805 return result;
rob@100 13806 };
rob@100 13807
rob@100 13808 var decimalToHex = function(d, padding) {
rob@100 13809 //if there is no padding value added, default padding to 8 else go into while statement.
rob@100 13810 padding = (padding === undef || padding === null) ? padding = 8 : padding;
rob@100 13811 if (d < 0) {
rob@100 13812 d = 0xFFFFFFFF + d + 1;
rob@100 13813 }
rob@100 13814 var hex = Number(d).toString(16).toUpperCase();
rob@100 13815 while (hex.length < padding) {
rob@100 13816 hex = "0" + hex;
rob@100 13817 }
rob@100 13818 if (hex.length >= padding) {
rob@100 13819 hex = hex.substring(hex.length - padding, hex.length);
rob@100 13820 }
rob@100 13821 return hex;
rob@100 13822 };
rob@100 13823
rob@100 13824 // note: since we cannot keep track of byte, int types by default the returned string is 8 chars long
rob@100 13825 // if no 2nd argument is passed. closest compromise we can use to match java implementation Feb 5 2010
rob@100 13826 // also the char parser has issues with chars that are not digits or letters IE: !@#$%^&*
rob@100 13827 /**
rob@100 13828 * Converts a byte, char, int, or color to a String containing the equivalent hexadecimal notation.
rob@100 13829 * For example color(0, 102, 153, 255) will convert to the String "FF006699". This function can help
rob@100 13830 * make your geeky debugging sessions much happier.
rob@100 13831 *
rob@100 13832 * @param {byte|char|int|Color} value the value to turn into a hex string
rob@100 13833 * @param {int} digits the number of digits to return
rob@100 13834 *
rob@100 13835 * @returns {String}
rob@100 13836 *
rob@100 13837 * @see unhex
rob@100 13838 * @see binary
rob@100 13839 * @see unbinary
rob@100 13840 */
rob@100 13841 p.hex = function(value, len) {
rob@100 13842 if (arguments.length === 1) {
rob@100 13843 if (value instanceof Char) {
rob@100 13844 len = 4;
rob@100 13845 } else { // int or byte, indistinguishable at the moment, default to 8
rob@100 13846 len = 8;
rob@100 13847 }
rob@100 13848 }
rob@100 13849 return decimalToHex(value, len);
rob@100 13850 };
rob@100 13851
rob@100 13852 function unhexScalar(hex) {
rob@100 13853 var value = parseInt("0x" + hex, 16);
rob@100 13854
rob@100 13855 // correct for int overflow java expectation
rob@100 13856 if (value > 2147483647) {
rob@100 13857 value -= 4294967296;
rob@100 13858 }
rob@100 13859 return value;
rob@100 13860 }
rob@100 13861
rob@100 13862 /**
rob@100 13863 * Converts a String representation of a hexadecimal number to its equivalent integer value.
rob@100 13864 *
rob@100 13865 * @param {String} hex the hex string to convert to an int
rob@100 13866 *
rob@100 13867 * @returns {int}
rob@100 13868 *
rob@100 13869 * @see hex
rob@100 13870 * @see binary
rob@100 13871 * @see unbinary
rob@100 13872 */
rob@100 13873 p.unhex = function(hex) {
rob@100 13874 if (hex instanceof Array) {
rob@100 13875 var arr = [];
rob@100 13876 for (var i = 0; i < hex.length; i++) {
rob@100 13877 arr.push(unhexScalar(hex[i]));
rob@100 13878 }
rob@100 13879 return arr;
rob@100 13880 }
rob@100 13881 return unhexScalar(hex);
rob@100 13882 };
rob@100 13883
rob@100 13884 // Load a file or URL into strings
rob@100 13885 /**
rob@100 13886 * Reads the contents of a file or url and creates a String array of its individual lines.
rob@100 13887 * The filename parameter can also be a URL to a file found online. If the file is not available or an error occurs,
rob@100 13888 * null will be returned and an error message will be printed to the console. The error message does not halt
rob@100 13889 * the program.
rob@100 13890 *
rob@100 13891 * @param {String} filename name of the file or url to load
rob@100 13892 *
rob@100 13893 * @returns {String[]}
rob@100 13894 *
rob@100 13895 * @see loadBytes
rob@100 13896 * @see saveStrings
rob@100 13897 * @see saveBytes
rob@100 13898 */
rob@100 13899 p.loadStrings = function(filename) {
rob@100 13900 if (localStorage[filename]) {
rob@100 13901 return localStorage[filename].split("\n");
rob@100 13902 }
rob@100 13903
rob@100 13904 var filecontent = ajax(filename);
rob@100 13905 if(typeof filecontent !== "string" || filecontent === "") {
rob@100 13906 return [];
rob@100 13907 }
rob@100 13908
rob@100 13909 // deal with the fact that Windows uses \r\n, Unix uses \n,
rob@100 13910 // Mac uses \r, and we actually expect \n
rob@100 13911 filecontent = filecontent.replace(/(\r\n?)/g,"\n").replace(/\n$/,"");
rob@100 13912
rob@100 13913 return filecontent.split("\n");
rob@100 13914 };
rob@100 13915
rob@100 13916 // Writes an array of strings to a file, one line per string
rob@100 13917 /**
rob@100 13918 * Writes an array of strings to a file, one line per string. This file is saved to the localStorage.
rob@100 13919 *
rob@100 13920 * @param {String} filename name of the file to save to localStorage
rob@100 13921 * @param {String[]} strings string array to be written
rob@100 13922 *
rob@100 13923 * @see loadBytes
rob@100 13924 * @see loadStrings
rob@100 13925 * @see saveBytes
rob@100 13926 */
rob@100 13927 p.saveStrings = function(filename, strings) {
rob@100 13928 localStorage[filename] = strings.join('\n');
rob@100 13929 };
rob@100 13930
rob@100 13931 /**
rob@100 13932 * Reads the contents of a file or url and places it in a byte array. If a file is specified, it must be located in the localStorage.
rob@100 13933 * The filename parameter can also be a URL to a file found online.
rob@100 13934 *
rob@100 13935 * @param {String} filename name of a file in the localStorage or a URL.
rob@100 13936 *
rob@100 13937 * @returns {byte[]}
rob@100 13938 *
rob@100 13939 * @see loadStrings
rob@100 13940 * @see saveStrings
rob@100 13941 * @see saveBytes
rob@100 13942 */
rob@100 13943 p.loadBytes = function(url) {
rob@100 13944 var string = ajax(url);
rob@100 13945 var ret = [];
rob@100 13946
rob@100 13947 for (var i = 0; i < string.length; i++) {
rob@100 13948 ret.push(string.charCodeAt(i));
rob@100 13949 }
rob@100 13950
rob@100 13951 return ret;
rob@100 13952 };
rob@100 13953
rob@100 13954 ////////////////////////////////////////////////////////////////////////////
rob@100 13955 // String Functions
rob@100 13956 ////////////////////////////////////////////////////////////////////////////
rob@100 13957 /**
rob@100 13958 * The matchAll() function is identical to match(), except that it returns an array of all matches in
rob@100 13959 * the specified String, rather than just the first.
rob@100 13960 *
rob@100 13961 * @param {String} aString the String to search inside
rob@100 13962 * @param {String} aRegExp the regexp to be used for matching
rob@100 13963 *
rob@100 13964 * @return {String[]} returns an array of matches
rob@100 13965 *
rob@100 13966 * @see #match
rob@100 13967 */
rob@100 13968 p.matchAll = function(aString, aRegExp) {
rob@100 13969 var results = [],
rob@100 13970 latest;
rob@100 13971 var regexp = new RegExp(aRegExp, "g");
rob@100 13972 while ((latest = regexp.exec(aString)) !== null) {
rob@100 13973 results.push(latest);
rob@100 13974 if (latest[0].length === 0) {
rob@100 13975 ++regexp.lastIndex;
rob@100 13976 }
rob@100 13977 }
rob@100 13978 return results.length > 0 ? results : null;
rob@100 13979 };
rob@100 13980 /**
rob@100 13981 * The match() function matches a string with a regular expression, and returns the match as an
rob@100 13982 * array. The first index is the matching expression, and array elements
rob@100 13983 * [1] and higher represent each of the groups (sequences found in parens).
rob@100 13984 *
rob@100 13985 * @param {String} str the String to be searched
rob@100 13986 * @param {String} regexp the regexp to be used for matching
rob@100 13987 *
rob@100 13988 * @return {String[]} an array of matching strings
rob@100 13989 */
rob@100 13990 p.match = function(str, regexp) {
rob@100 13991 return str.match(regexp);
rob@100 13992 };
rob@100 13993
rob@100 13994 ////////////////////////////////////////////////////////////////////////////
rob@100 13995 // Other java specific functions
rob@100 13996 ////////////////////////////////////////////////////////////////////////////
rob@100 13997
rob@100 13998
rob@100 13999 var logBuffer = [];
rob@100 14000
rob@100 14001 /**
rob@100 14002 * The println() function writes to the console area of the Processing environment.
rob@100 14003 * Each call to this function creates a new line of output. Individual elements can be separated with quotes ("") and joined with the string concatenation operator (+).
rob@100 14004 *
rob@100 14005 * @param {String} message the string to write to the console
rob@100 14006 *
rob@100 14007 * @see #join
rob@100 14008 * @see #print
rob@100 14009 */
rob@100 14010 p.println = function(message) {
rob@100 14011 Processing.logger.println(message);
rob@100 14012 };
rob@100 14013 /**
rob@100 14014 * The print() function writes to the console area of the Processing environment.
rob@100 14015 *
rob@100 14016 * @param {String} message the string to write to the console
rob@100 14017 *
rob@100 14018 * @see #join
rob@100 14019 */
rob@100 14020 p.print = function(message) {
rob@100 14021 Processing.logger.print(message);
rob@100 14022 };
rob@100 14023
rob@100 14024 // Alphanumeric chars arguments automatically converted to numbers when
rob@100 14025 // passed in, and will come out as numbers.
rob@100 14026 p.str = function(val) {
rob@100 14027 if (val instanceof Array) {
rob@100 14028 var arr = [];
rob@100 14029 for (var i = 0; i < val.length; i++) {
rob@100 14030 arr.push(val[i].toString() + "");
rob@100 14031 }
rob@100 14032 return arr;
rob@100 14033 }
rob@100 14034 return (val.toString() + "");
rob@100 14035 };
rob@100 14036
rob@100 14037
rob@100 14038 // Conversion
rob@100 14039 function booleanScalar(val) {
rob@100 14040 if (typeof val === 'number') {
rob@100 14041 return val !== 0;
rob@100 14042 }
rob@100 14043 if (typeof val === 'boolean') {
rob@100 14044 return val;
rob@100 14045 }
rob@100 14046 if (typeof val === 'string') {
rob@100 14047 return val.toLowerCase() === 'true';
rob@100 14048 }
rob@100 14049 if (val instanceof Char) {
rob@100 14050 // 1, T or t
rob@100 14051 return val.code === 49 || val.code === 84 || val.code === 116;
rob@100 14052 }
rob@100 14053 }
rob@100 14054
rob@100 14055 /**
rob@100 14056 * Converts the passed parameter to the function to its boolean value.
rob@100 14057 * It will return an array of booleans if an array is passed in.
rob@100 14058 *
rob@100 14059 * @param {int, byte, string} val the parameter to be converted to boolean
rob@100 14060 * @param {int[], byte[], string[]} val the array to be converted to boolean[]
rob@100 14061 *
rob@100 14062 * @return {boolean|boolean[]} returns a boolean or an array of booleans
rob@100 14063 */
rob@100 14064 p.parseBoolean = function (val) {
rob@100 14065 if (val instanceof Array) {
rob@100 14066 var ret = [];
rob@100 14067 for (var i = 0; i < val.length; i++) {
rob@100 14068 ret.push(booleanScalar(val[i]));
rob@100 14069 }
rob@100 14070 return ret;
rob@100 14071 }
rob@100 14072 return booleanScalar(val);
rob@100 14073 };
rob@100 14074
rob@100 14075 /**
rob@100 14076 * Converts the passed parameter to the function to its byte value.
rob@100 14077 * A byte is a number between -128 and 127.
rob@100 14078 * It will return an array of bytes if an array is passed in.
rob@100 14079 *
rob@100 14080 * @param {int, char} what the parameter to be conveted to byte
rob@100 14081 * @param {int[], char[]} what the array to be converted to byte[]
rob@100 14082 *
rob@100 14083 * @return {byte|byte[]} returns a byte or an array of bytes
rob@100 14084 */
rob@100 14085 p.parseByte = function(what) {
rob@100 14086 if (what instanceof Array) {
rob@100 14087 var bytes = [];
rob@100 14088 for (var i = 0; i < what.length; i++) {
rob@100 14089 bytes.push((0 - (what[i] & 0x80)) | (what[i] & 0x7F));
rob@100 14090 }
rob@100 14091 return bytes;
rob@100 14092 }
rob@100 14093 return (0 - (what & 0x80)) | (what & 0x7F);
rob@100 14094 };
rob@100 14095
rob@100 14096 /**
rob@100 14097 * Converts the passed parameter to the function to its char value.
rob@100 14098 * It will return an array of chars if an array is passed in.
rob@100 14099 *
rob@100 14100 * @param {int, byte} key the parameter to be conveted to char
rob@100 14101 * @param {int[], byte[]} key the array to be converted to char[]
rob@100 14102 *
rob@100 14103 * @return {char|char[]} returns a char or an array of chars
rob@100 14104 */
rob@100 14105 p.parseChar = function(key) {
rob@100 14106 if (typeof key === "number") {
rob@100 14107 return new Char(String.fromCharCode(key & 0xFFFF));
rob@100 14108 }
rob@100 14109 if (key instanceof Array) {
rob@100 14110 var ret = [];
rob@100 14111 for (var i = 0; i < key.length; i++) {
rob@100 14112 ret.push(new Char(String.fromCharCode(key[i] & 0xFFFF)));
rob@100 14113 }
rob@100 14114 return ret;
rob@100 14115 }
rob@100 14116 throw "char() may receive only one argument of type int, byte, int[], or byte[].";
rob@100 14117 };
rob@100 14118
rob@100 14119 // Processing doc claims good argument types are: int, char, byte, boolean,
rob@100 14120 // String, int[], char[], byte[], boolean[], String[].
rob@100 14121 // floats should not work. However, floats with only zeroes right of the
rob@100 14122 // decimal will work because JS converts those to int.
rob@100 14123 function floatScalar(val) {
rob@100 14124 if (typeof val === 'number') {
rob@100 14125 return val;
rob@100 14126 }
rob@100 14127 if (typeof val === 'boolean') {
rob@100 14128 return val ? 1 : 0;
rob@100 14129 }
rob@100 14130 if (typeof val === 'string') {
rob@100 14131 return parseFloat(val);
rob@100 14132 }
rob@100 14133 if (val instanceof Char) {
rob@100 14134 return val.code;
rob@100 14135 }
rob@100 14136 }
rob@100 14137
rob@100 14138 /**
rob@100 14139 * Converts the passed parameter to the function to its float value.
rob@100 14140 * It will return an array of floats if an array is passed in.
rob@100 14141 *
rob@100 14142 * @param {int, char, boolean, string} val the parameter to be conveted to float
rob@100 14143 * @param {int[], char[], boolean[], string[]} val the array to be converted to float[]
rob@100 14144 *
rob@100 14145 * @return {float|float[]} returns a float or an array of floats
rob@100 14146 */
rob@100 14147 p.parseFloat = function(val) {
rob@100 14148 if (val instanceof Array) {
rob@100 14149 var ret = [];
rob@100 14150 for (var i = 0; i < val.length; i++) {
rob@100 14151 ret.push(floatScalar(val[i]));
rob@100 14152 }
rob@100 14153 return ret;
rob@100 14154 }
rob@100 14155 return floatScalar(val);
rob@100 14156 };
rob@100 14157
rob@100 14158 function intScalar(val, radix) {
rob@100 14159 if (typeof val === 'number') {
rob@100 14160 return val & 0xFFFFFFFF;
rob@100 14161 }
rob@100 14162 if (typeof val === 'boolean') {
rob@100 14163 return val ? 1 : 0;
rob@100 14164 }
rob@100 14165 if (typeof val === 'string') {
rob@100 14166 var number = parseInt(val, radix || 10); // Default to decimal radix.
rob@100 14167 return number & 0xFFFFFFFF;
rob@100 14168 }
rob@100 14169 if (val instanceof Char) {
rob@100 14170 return val.code;
rob@100 14171 }
rob@100 14172 }
rob@100 14173
rob@100 14174 /**
rob@100 14175 * Converts the passed parameter to the function to its int value.
rob@100 14176 * It will return an array of ints if an array is passed in.
rob@100 14177 *
rob@100 14178 * @param {string, char, boolean, float} val the parameter to be conveted to int
rob@100 14179 * @param {string[], char[], boolean[], float[]} val the array to be converted to int[]
rob@100 14180 * @param {int} radix optional the radix of the number (for js compatibility)
rob@100 14181 *
rob@100 14182 * @return {int|int[]} returns a int or an array of ints
rob@100 14183 */
rob@100 14184 p.parseInt = function(val, radix) {
rob@100 14185 if (val instanceof Array) {
rob@100 14186 var ret = [];
rob@100 14187 for (var i = 0; i < val.length; i++) {
rob@100 14188 if (typeof val[i] === 'string' && !/^\s*[+\-]?\d+\s*$/.test(val[i])) {
rob@100 14189 ret.push(0);
rob@100 14190 } else {
rob@100 14191 ret.push(intScalar(val[i], radix));
rob@100 14192 }
rob@100 14193 }
rob@100 14194 return ret;
rob@100 14195 }
rob@100 14196 return intScalar(val, radix);
rob@100 14197 };
rob@100 14198
rob@100 14199 p.__int_cast = function(val) {
rob@100 14200 return 0|val;
rob@100 14201 };
rob@100 14202
rob@100 14203 p.__instanceof = function(obj, type) {
rob@100 14204 if (typeof type !== "function") {
rob@100 14205 throw "Function is expected as type argument for instanceof operator";
rob@100 14206 }
rob@100 14207
rob@100 14208 if (typeof obj === "string") {
rob@100 14209 // special case for strings
rob@100 14210 return type === Object || type === String;
rob@100 14211 }
rob@100 14212
rob@100 14213 if (obj instanceof type) {
rob@100 14214 // fast check if obj is already of type instance
rob@100 14215 return true;
rob@100 14216 }
rob@100 14217
rob@100 14218 if (typeof obj !== "object" || obj === null) {
rob@100 14219 return false; // not an object or null
rob@100 14220 }
rob@100 14221
rob@100 14222 var objType = obj.constructor;
rob@100 14223 if (type.$isInterface) {
rob@100 14224 // expecting the interface
rob@100 14225 // queueing interfaces from type and its base classes
rob@100 14226 var interfaces = [];
rob@100 14227 while (objType) {
rob@100 14228 if (objType.$interfaces) {
rob@100 14229 interfaces = interfaces.concat(objType.$interfaces);
rob@100 14230 }
rob@100 14231 objType = objType.$base;
rob@100 14232 }
rob@100 14233 while (interfaces.length > 0) {
rob@100 14234 var i = interfaces.shift();
rob@100 14235 if (i === type) {
rob@100 14236 return true;
rob@100 14237 }
rob@100 14238 // wide search in base interfaces
rob@100 14239 if (i.$interfaces) {
rob@100 14240 interfaces = interfaces.concat(i.$interfaces);
rob@100 14241 }
rob@100 14242 }
rob@100 14243 return false;
rob@100 14244 }
rob@100 14245
rob@100 14246 while (objType.hasOwnProperty("$base")) {
rob@100 14247 objType = objType.$base;
rob@100 14248 if (objType === type) {
rob@100 14249 return true; // object was found
rob@100 14250 }
rob@100 14251 }
rob@100 14252
rob@100 14253 return false;
rob@100 14254 };
rob@100 14255
rob@100 14256 /**
rob@100 14257 * Defines the dimension of the display window in units of pixels. The size() function must
rob@100 14258 * be the first line in setup(). If size() is not called, the default size of the window is
rob@100 14259 * 100x100 pixels. The system variables width and height are set by the parameters passed to
rob@100 14260 * the size() function.
rob@100 14261 *
rob@100 14262 * @param {int} aWidth width of the display window in units of pixels
rob@100 14263 * @param {int} aHeight height of the display window in units of pixels
rob@100 14264 * @param {MODE} aMode Either P2D, P3D, JAVA2D, or OPENGL
rob@100 14265 *
rob@100 14266 * @see createGraphics
rob@100 14267 * @see screen
rob@100 14268 */
rob@100 14269 DrawingShared.prototype.size = function(aWidth, aHeight, aMode) {
rob@100 14270 if (doStroke) {
rob@100 14271 p.stroke(0);
rob@100 14272 }
rob@100 14273
rob@100 14274 if (doFill) {
rob@100 14275 p.fill(255);
rob@100 14276 }
rob@100 14277
rob@100 14278 // The default 2d context has already been created in the p.init() stage if
rob@100 14279 // a 3d context was not specified. This is so that a 2d context will be
rob@100 14280 // available if size() was not called.
rob@100 14281 var savedProperties = {
rob@100 14282 fillStyle: curContext.fillStyle,
rob@100 14283 strokeStyle: curContext.strokeStyle,
rob@100 14284 lineCap: curContext.lineCap,
rob@100 14285 lineJoin: curContext.lineJoin
rob@100 14286 };
rob@100 14287 // remove the style width and height properties to ensure that the canvas gets set to
rob@100 14288 // aWidth and aHeight coming in
rob@100 14289 if (curElement.style.length > 0 ) {
rob@100 14290 curElement.style.removeProperty("width");
rob@100 14291 curElement.style.removeProperty("height");
rob@100 14292 }
rob@100 14293
rob@100 14294 curElement.width = p.width = aWidth || 100;
rob@100 14295 curElement.height = p.height = aHeight || 100;
rob@100 14296
rob@100 14297 for (var prop in savedProperties) {
rob@100 14298 if (savedProperties.hasOwnProperty(prop)) {
rob@100 14299 curContext[prop] = savedProperties[prop];
rob@100 14300 }
rob@100 14301 }
rob@100 14302
rob@100 14303 // make sure to set the default font the first time round.
rob@100 14304 p.textFont(curTextFont);
rob@100 14305
rob@100 14306 // Set the background to whatever it was called last as if background() was called before size()
rob@100 14307 // If background() hasn't been called before, set background() to a light gray
rob@100 14308 p.background();
rob@100 14309
rob@100 14310 // set 5% for pixels to cache (or 1000)
rob@100 14311 maxPixelsCached = Math.max(1000, aWidth * aHeight * 0.05);
rob@100 14312
rob@100 14313 // Externalize the context
rob@100 14314 p.externals.context = curContext;
rob@100 14315
rob@100 14316 for (var i = 0; i < PConstants.SINCOS_LENGTH; i++) {
rob@100 14317 sinLUT[i] = p.sin(i * (PConstants.PI / 180) * 0.5);
rob@100 14318 cosLUT[i] = p.cos(i * (PConstants.PI / 180) * 0.5);
rob@100 14319 }
rob@100 14320 };
rob@100 14321
rob@100 14322 Drawing2D.prototype.size = function(aWidth, aHeight, aMode) {
rob@100 14323 if (curContext === undef) {
rob@100 14324 // size() was called without p.init() default context, i.e. p.createGraphics()
rob@100 14325 curContext = curElement.getContext("2d");
rob@100 14326 userMatrixStack = new PMatrixStack();
rob@100 14327 userReverseMatrixStack = new PMatrixStack();
rob@100 14328 modelView = new PMatrix2D();
rob@100 14329 modelViewInv = new PMatrix2D();
rob@100 14330 }
rob@100 14331
rob@100 14332 DrawingShared.prototype.size.apply(this, arguments);
rob@100 14333 };
rob@100 14334
rob@100 14335 Drawing3D.prototype.size = (function() {
rob@100 14336 var size3DCalled = false;
rob@100 14337
rob@100 14338 return function size(aWidth, aHeight, aMode) {
rob@100 14339 if (size3DCalled) {
rob@100 14340 throw "Multiple calls to size() for 3D renders are not allowed.";
rob@100 14341 }
rob@100 14342 size3DCalled = true;
rob@100 14343
rob@100 14344 function getGLContext(canvas) {
rob@100 14345 var ctxNames = ['experimental-webgl', 'webgl', 'webkit-3d'],
rob@100 14346 gl;
rob@100 14347
rob@100 14348 for (var i=0, l=ctxNames.length; i<l; i++) {
rob@100 14349 gl = canvas.getContext(ctxNames[i], {antialias: false, preserveDrawingBuffer: true});
rob@100 14350 if (gl) {
rob@100 14351 break;
rob@100 14352 }
rob@100 14353 }
rob@100 14354
rob@100 14355 return gl;
rob@100 14356 }
rob@100 14357
rob@100 14358 // Get the 3D rendering context.
rob@100 14359 try {
rob@100 14360 // If the HTML <canvas> dimensions differ from the
rob@100 14361 // dimensions specified in the size() call in the sketch, for
rob@100 14362 // 3D sketches, browsers will either not render or render the
rob@100 14363 // scene incorrectly. To fix this, we need to adjust the
rob@100 14364 // width and height attributes of the canvas.
rob@100 14365 curElement.width = p.width = aWidth || 100;
rob@100 14366 curElement.height = p.height = aHeight || 100;
rob@100 14367 curContext = getGLContext(curElement);
rob@100 14368 canTex = curContext.createTexture();
rob@100 14369 textTex = curContext.createTexture();
rob@100 14370 } catch(e_size) {
rob@100 14371 Processing.debug(e_size);
rob@100 14372 }
rob@100 14373
rob@100 14374 if (!curContext) {
rob@100 14375 throw "WebGL context is not supported on this browser.";
rob@100 14376 }
rob@100 14377
rob@100 14378 // Set defaults
rob@100 14379 curContext.viewport(0, 0, curElement.width, curElement.height);
rob@100 14380 curContext.enable(curContext.DEPTH_TEST);
rob@100 14381 curContext.enable(curContext.BLEND);
rob@100 14382 curContext.blendFunc(curContext.SRC_ALPHA, curContext.ONE_MINUS_SRC_ALPHA);
rob@100 14383
rob@100 14384 // Create the program objects to render 2D (points, lines) and
rob@100 14385 // 3D (spheres, boxes) shapes. Because 2D shapes are not lit,
rob@100 14386 // lighting calculations are ommitted from this program object.
rob@100 14387 programObject2D = createProgramObject(curContext, vertexShaderSrc2D, fragmentShaderSrc2D);
rob@100 14388
rob@100 14389 programObjectUnlitShape = createProgramObject(curContext, vertexShaderSrcUnlitShape, fragmentShaderSrcUnlitShape);
rob@100 14390
rob@100 14391 // Set the default point and line width for the 2D and unlit shapes.
rob@100 14392 p.strokeWeight(1);
rob@100 14393
rob@100 14394 // Now that the programs have been compiled, we can set the default
rob@100 14395 // states for the lights.
rob@100 14396 programObject3D = createProgramObject(curContext, vertexShaderSrc3D, fragmentShaderSrc3D);
rob@100 14397 curContext.useProgram(programObject3D);
rob@100 14398
rob@100 14399 // Assume we aren't using textures by default.
rob@100 14400 uniformi("usingTexture3d", programObject3D, "usingTexture", usingTexture);
rob@100 14401
rob@100 14402 // Set some defaults.
rob@100 14403 p.lightFalloff(1, 0, 0);
rob@100 14404 p.shininess(1);
rob@100 14405 p.ambient(255, 255, 255);
rob@100 14406 p.specular(0, 0, 0);
rob@100 14407 p.emissive(0, 0, 0);
rob@100 14408
rob@100 14409 // Create buffers for 3D primitives
rob@100 14410 boxBuffer = curContext.createBuffer();
rob@100 14411 curContext.bindBuffer(curContext.ARRAY_BUFFER, boxBuffer);
rob@100 14412 curContext.bufferData(curContext.ARRAY_BUFFER, boxVerts, curContext.STATIC_DRAW);
rob@100 14413
rob@100 14414 boxNormBuffer = curContext.createBuffer();
rob@100 14415 curContext.bindBuffer(curContext.ARRAY_BUFFER, boxNormBuffer);
rob@100 14416 curContext.bufferData(curContext.ARRAY_BUFFER, boxNorms, curContext.STATIC_DRAW);
rob@100 14417
rob@100 14418 boxOutlineBuffer = curContext.createBuffer();
rob@100 14419 curContext.bindBuffer(curContext.ARRAY_BUFFER, boxOutlineBuffer);
rob@100 14420 curContext.bufferData(curContext.ARRAY_BUFFER, boxOutlineVerts, curContext.STATIC_DRAW);
rob@100 14421
rob@100 14422 // used to draw the rectangle and the outline
rob@100 14423 rectBuffer = curContext.createBuffer();
rob@100 14424 curContext.bindBuffer(curContext.ARRAY_BUFFER, rectBuffer);
rob@100 14425 curContext.bufferData(curContext.ARRAY_BUFFER, rectVerts, curContext.STATIC_DRAW);
rob@100 14426
rob@100 14427 rectNormBuffer = curContext.createBuffer();
rob@100 14428 curContext.bindBuffer(curContext.ARRAY_BUFFER, rectNormBuffer);
rob@100 14429 curContext.bufferData(curContext.ARRAY_BUFFER, rectNorms, curContext.STATIC_DRAW);
rob@100 14430
rob@100 14431 // The sphere vertices are specified dynamically since the user
rob@100 14432 // can change the level of detail. Everytime the user does that
rob@100 14433 // using sphereDetail(), the new vertices are calculated.
rob@100 14434 sphereBuffer = curContext.createBuffer();
rob@100 14435
rob@100 14436 lineBuffer = curContext.createBuffer();
rob@100 14437
rob@100 14438 // Shape buffers
rob@100 14439 fillBuffer = curContext.createBuffer();
rob@100 14440 fillColorBuffer = curContext.createBuffer();
rob@100 14441 strokeColorBuffer = curContext.createBuffer();
rob@100 14442 shapeTexVBO = curContext.createBuffer();
rob@100 14443
rob@100 14444 pointBuffer = curContext.createBuffer();
rob@100 14445 curContext.bindBuffer(curContext.ARRAY_BUFFER, pointBuffer);
rob@100 14446 curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array([0, 0, 0]), curContext.STATIC_DRAW);
rob@100 14447
rob@100 14448 textBuffer = curContext.createBuffer();
rob@100 14449 curContext.bindBuffer(curContext.ARRAY_BUFFER, textBuffer );
rob@100 14450 curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array([1,1,0,-1,1,0,-1,-1,0,1,-1,0]), curContext.STATIC_DRAW);
rob@100 14451
rob@100 14452 textureBuffer = curContext.createBuffer();
rob@100 14453 curContext.bindBuffer(curContext.ARRAY_BUFFER, textureBuffer);
rob@100 14454 curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array([0,0,1,0,1,1,0,1]), curContext.STATIC_DRAW);
rob@100 14455
rob@100 14456 indexBuffer = curContext.createBuffer();
rob@100 14457 curContext.bindBuffer(curContext.ELEMENT_ARRAY_BUFFER, indexBuffer);
rob@100 14458 curContext.bufferData(curContext.ELEMENT_ARRAY_BUFFER, new Uint16Array([0,1,2,2,3,0]), curContext.STATIC_DRAW);
rob@100 14459
rob@100 14460 cam = new PMatrix3D();
rob@100 14461 cameraInv = new PMatrix3D();
rob@100 14462 modelView = new PMatrix3D();
rob@100 14463 modelViewInv = new PMatrix3D();
rob@100 14464 projection = new PMatrix3D();
rob@100 14465 p.camera();
rob@100 14466 p.perspective();
rob@100 14467
rob@100 14468 userMatrixStack = new PMatrixStack();
rob@100 14469 userReverseMatrixStack = new PMatrixStack();
rob@100 14470 // used by both curve and bezier, so just init here
rob@100 14471 curveBasisMatrix = new PMatrix3D();
rob@100 14472 curveToBezierMatrix = new PMatrix3D();
rob@100 14473 curveDrawMatrix = new PMatrix3D();
rob@100 14474 bezierDrawMatrix = new PMatrix3D();
rob@100 14475 bezierBasisInverse = new PMatrix3D();
rob@100 14476 bezierBasisMatrix = new PMatrix3D();
rob@100 14477 bezierBasisMatrix.set(-1, 3, -3, 1, 3, -6, 3, 0, -3, 3, 0, 0, 1, 0, 0, 0);
rob@100 14478
rob@100 14479 DrawingShared.prototype.size.apply(this, arguments);
rob@100 14480 };
rob@100 14481 }());
rob@100 14482
rob@100 14483 ////////////////////////////////////////////////////////////////////////////
rob@100 14484 // Lights
rob@100 14485 ////////////////////////////////////////////////////////////////////////////
rob@100 14486
rob@100 14487 /**
rob@100 14488 * Adds an ambient light. Ambient light doesn't come from a specific direction,
rob@100 14489 * the rays have light have bounced around so much that objects are evenly lit
rob@100 14490 * from all sides. Ambient lights are almost always used in combination with
rob@100 14491 * other types of lights. Lights need to be included in the <b>draw()</b> to
rob@100 14492 * remain persistent in a looping program. Placing them in the <b>setup()</b>
rob@100 14493 * of a looping program will cause them to only have an effect the first time
rob@100 14494 * through the loop. The effect of the parameters is determined by the current
rob@100 14495 * color mode.
rob@100 14496 *
rob@100 14497 * @param {int | float} r red or hue value
rob@100 14498 * @param {int | float} g green or hue value
rob@100 14499 * @param {int | float} b blue or hue value
rob@100 14500 *
rob@100 14501 * @param {int | float} x x position of light (used for falloff)
rob@100 14502 * @param {int | float} y y position of light (used for falloff)
rob@100 14503 * @param {int | float} z z position of light (used for falloff)
rob@100 14504 *
rob@100 14505 * @returns none
rob@100 14506 *
rob@100 14507 * @see lights
rob@100 14508 * @see directionalLight
rob@100 14509 * @see pointLight
rob@100 14510 * @see spotLight
rob@100 14511 */
rob@100 14512 Drawing2D.prototype.ambientLight = DrawingShared.prototype.a3DOnlyFunction;
rob@100 14513
rob@100 14514 Drawing3D.prototype.ambientLight = function(r, g, b, x, y, z) {
rob@100 14515 if (lightCount === PConstants.MAX_LIGHTS) {
rob@100 14516 throw "can only create " + PConstants.MAX_LIGHTS + " lights";
rob@100 14517 }
rob@100 14518
rob@100 14519 var pos = new PVector(x, y, z);
rob@100 14520 var view = new PMatrix3D();
rob@100 14521 view.scale(1, -1, 1);
rob@100 14522 view.apply(modelView.array());
rob@100 14523 view.mult(pos, pos);
rob@100 14524
rob@100 14525 // Instead of calling p.color, we do the calculations ourselves to
rob@100 14526 // reduce property lookups.
rob@100 14527 var col = color$4(r, g, b, 0);
rob@100 14528 var normalizedCol = [ ((col & PConstants.RED_MASK) >>> 16) / 255,
rob@100 14529 ((col & PConstants.GREEN_MASK) >>> 8) / 255,
rob@100 14530 (col & PConstants.BLUE_MASK) / 255 ];
rob@100 14531
rob@100 14532 curContext.useProgram(programObject3D);
rob@100 14533 uniformf("uLights.color.3d." + lightCount, programObject3D, "uLights" + lightCount + ".color", normalizedCol);
rob@100 14534 uniformf("uLights.position.3d." + lightCount, programObject3D, "uLights" + lightCount + ".position", pos.array());
rob@100 14535 uniformi("uLights.type.3d." + lightCount, programObject3D, "uLights" + lightCount + ".type", 0);
rob@100 14536 uniformi("uLightCount3d", programObject3D, "uLightCount", ++lightCount);
rob@100 14537 };
rob@100 14538
rob@100 14539 /**
rob@100 14540 * Adds a directional light. Directional light comes from one direction and
rob@100 14541 * is stronger when hitting a surface squarely and weaker if it hits at a
rob@100 14542 * gentle angle. After hitting a surface, a directional lights scatters in
rob@100 14543 * all directions. Lights need to be included in the <b>draw()</b> to remain
rob@100 14544 * persistent in a looping program. Placing them in the <b>setup()</b> of a
rob@100 14545 * looping program will cause them to only have an effect the first time
rob@100 14546 * through the loop. The affect of the <br>r</b>, <br>g</b>, and <br>b</b>
rob@100 14547 * parameters is determined by the current color mode. The <b>nx</b>,
rob@100 14548 * <b>ny</b>, and <b>nz</b> parameters specify the direction the light is
rob@100 14549 * facing. For example, setting <b>ny</b> to -1 will cause the geometry to be
rob@100 14550 * lit from below (the light is facing directly upward).
rob@100 14551 *
rob@100 14552 * @param {int | float} r red or hue value
rob@100 14553 * @param {int | float} g green or hue value
rob@100 14554 * @param {int | float} b blue or hue value
rob@100 14555 *
rob@100 14556 * @param {int | float} nx direction along the x axis
rob@100 14557 * @param {int | float} ny direction along the y axis
rob@100 14558 * @param {int | float} nz direction along the z axis
rob@100 14559 *
rob@100 14560 * @returns none
rob@100 14561 *
rob@100 14562 * @see lights
rob@100 14563 * @see ambientLight
rob@100 14564 * @see pointLight
rob@100 14565 * @see spotLight
rob@100 14566 */
rob@100 14567 Drawing2D.prototype.directionalLight = DrawingShared.prototype.a3DOnlyFunction;
rob@100 14568
rob@100 14569 Drawing3D.prototype.directionalLight = function(r, g, b, nx, ny, nz) {
rob@100 14570 if (lightCount === PConstants.MAX_LIGHTS) {
rob@100 14571 throw "can only create " + PConstants.MAX_LIGHTS + " lights";
rob@100 14572 }
rob@100 14573
rob@100 14574 curContext.useProgram(programObject3D);
rob@100 14575
rob@100 14576 var mvm = new PMatrix3D();
rob@100 14577 mvm.scale(1, -1, 1);
rob@100 14578 mvm.apply(modelView.array());
rob@100 14579 mvm = mvm.array();
rob@100 14580
rob@100 14581 // We need to multiply the direction by the model view matrix, but
rob@100 14582 // the mult function checks the w component of the vector, if it isn't
rob@100 14583 // present, it uses 1, so we manually multiply.
rob@100 14584 var dir = [
rob@100 14585 mvm[0] * nx + mvm[4] * ny + mvm[8] * nz,
rob@100 14586 mvm[1] * nx + mvm[5] * ny + mvm[9] * nz,
rob@100 14587 mvm[2] * nx + mvm[6] * ny + mvm[10] * nz
rob@100 14588 ];
rob@100 14589
rob@100 14590 // Instead of calling p.color, we do the calculations ourselves to
rob@100 14591 // reduce property lookups.
rob@100 14592 var col = color$4(r, g, b, 0);
rob@100 14593 var normalizedCol = [ ((col & PConstants.RED_MASK) >>> 16) / 255,
rob@100 14594 ((col & PConstants.GREEN_MASK) >>> 8) / 255,
rob@100 14595 (col & PConstants.BLUE_MASK) / 255 ];
rob@100 14596
rob@100 14597 uniformf("uLights.color.3d." + lightCount, programObject3D, "uLights" + lightCount + ".color", normalizedCol);
rob@100 14598 uniformf("uLights.position.3d." + lightCount, programObject3D, "uLights" + lightCount + ".position", dir);
rob@100 14599 uniformi("uLights.type.3d." + lightCount, programObject3D, "uLights" + lightCount + ".type", 1);
rob@100 14600 uniformi("uLightCount3d", programObject3D, "uLightCount", ++lightCount);
rob@100 14601 };
rob@100 14602
rob@100 14603 /**
rob@100 14604 * Sets the falloff rates for point lights, spot lights, and ambient lights.
rob@100 14605 * The parameters are used to determine the falloff with the following equation:
rob@100 14606 *
rob@100 14607 * d = distance from light position to vertex position
rob@100 14608 * falloff = 1 / (CONSTANT + d * LINEAR + (d*d) * QUADRATIC)
rob@100 14609 *
rob@100 14610 * Like <b>fill()</b>, it affects only the elements which are created after it in the
rob@100 14611 * code. The default value if <b>LightFalloff(1.0, 0.0, 0.0)</b>. Thinking about an
rob@100 14612 * ambient light with a falloff can be tricky. It is used, for example, if you
rob@100 14613 * wanted a region of your scene to be lit ambiently one color and another region
rob@100 14614 * to be lit ambiently by another color, you would use an ambient light with location
rob@100 14615 * and falloff. You can think of it as a point light that doesn't care which direction
rob@100 14616 * a surface is facing.
rob@100 14617 *
rob@100 14618 * @param {int | float} constant constant value for determining falloff
rob@100 14619 * @param {int | float} linear linear value for determining falloff
rob@100 14620 * @param {int | float} quadratic quadratic value for determining falloff
rob@100 14621 *
rob@100 14622 * @returns none
rob@100 14623 *
rob@100 14624 * @see lights
rob@100 14625 * @see ambientLight
rob@100 14626 * @see pointLight
rob@100 14627 * @see spotLight
rob@100 14628 * @see lightSpecular
rob@100 14629 */
rob@100 14630 Drawing2D.prototype.lightFalloff = DrawingShared.prototype.a3DOnlyFunction;
rob@100 14631
rob@100 14632 Drawing3D.prototype.lightFalloff = function(constant, linear, quadratic) {
rob@100 14633 curContext.useProgram(programObject3D);
rob@100 14634 uniformf("uFalloff3d", programObject3D, "uFalloff", [constant, linear, quadratic]);
rob@100 14635 };
rob@100 14636
rob@100 14637 /**
rob@100 14638 * Sets the specular color for lights. Like <b>fill()</b>, it affects only the
rob@100 14639 * elements which are created after it in the code. Specular refers to light
rob@100 14640 * which bounces off a surface in a perferred direction (rather than bouncing
rob@100 14641 * in all directions like a diffuse light) and is used for creating highlights.
rob@100 14642 * The specular quality of a light interacts with the specular material qualities
rob@100 14643 * set through the <b>specular()</b> and <b>shininess()</b> functions.
rob@100 14644 *
rob@100 14645 * @param {int | float} r red or hue value
rob@100 14646 * @param {int | float} g green or hue value
rob@100 14647 * @param {int | float} b blue or hue value
rob@100 14648 *
rob@100 14649 * @returns none
rob@100 14650 *
rob@100 14651 * @see lights
rob@100 14652 * @see ambientLight
rob@100 14653 * @see pointLight
rob@100 14654 * @see spotLight
rob@100 14655 */
rob@100 14656 Drawing2D.prototype.lightSpecular = DrawingShared.prototype.a3DOnlyFunction;
rob@100 14657
rob@100 14658 Drawing3D.prototype.lightSpecular = function(r, g, b) {
rob@100 14659
rob@100 14660 // Instead of calling p.color, we do the calculations ourselves to
rob@100 14661 // reduce property lookups.
rob@100 14662 var col = color$4(r, g, b, 0);
rob@100 14663 var normalizedCol = [ ((col & PConstants.RED_MASK) >>> 16) / 255,
rob@100 14664 ((col & PConstants.GREEN_MASK) >>> 8) / 255,
rob@100 14665 (col & PConstants.BLUE_MASK) / 255 ];
rob@100 14666
rob@100 14667 curContext.useProgram(programObject3D);
rob@100 14668 uniformf("uSpecular3d", programObject3D, "uSpecular", normalizedCol);
rob@100 14669 };
rob@100 14670
rob@100 14671 /**
rob@100 14672 * Sets the default ambient light, directional light, falloff, and specular
rob@100 14673 * values. The defaults are ambientLight(128, 128, 128) and
rob@100 14674 * directionalLight(128, 128, 128, 0, 0, -1), lightFalloff(1, 0, 0), and
rob@100 14675 * lightSpecular(0, 0, 0). Lights need to be included in the draw() to remain
rob@100 14676 * persistent in a looping program. Placing them in the setup() of a looping
rob@100 14677 * program will cause them to only have an effect the first time through the
rob@100 14678 * loop.
rob@100 14679 *
rob@100 14680 * @returns none
rob@100 14681 *
rob@100 14682 * @see ambientLight
rob@100 14683 * @see directionalLight
rob@100 14684 * @see pointLight
rob@100 14685 * @see spotLight
rob@100 14686 * @see noLights
rob@100 14687 *
rob@100 14688 */
rob@100 14689 p.lights = function() {
rob@100 14690 p.ambientLight(128, 128, 128);
rob@100 14691 p.directionalLight(128, 128, 128, 0, 0, -1);
rob@100 14692 p.lightFalloff(1, 0, 0);
rob@100 14693 p.lightSpecular(0, 0, 0);
rob@100 14694 };
rob@100 14695
rob@100 14696 /**
rob@100 14697 * Adds a point light. Lights need to be included in the <b>draw()</b> to remain
rob@100 14698 * persistent in a looping program. Placing them in the <b>setup()</b> of a
rob@100 14699 * looping program will cause them to only have an effect the first time through
rob@100 14700 * the loop. The affect of the <b>r</b>, <b>g</b>, and <b>b</b> parameters
rob@100 14701 * is determined by the current color mode. The <b>x</b>, <b>y</b>, and <b>z</b>
rob@100 14702 * parameters set the position of the light.
rob@100 14703 *
rob@100 14704 * @param {int | float} r red or hue value
rob@100 14705 * @param {int | float} g green or hue value
rob@100 14706 * @param {int | float} b blue or hue value
rob@100 14707 * @param {int | float} x x coordinate of the light
rob@100 14708 * @param {int | float} y y coordinate of the light
rob@100 14709 * @param {int | float} z z coordinate of the light
rob@100 14710 *
rob@100 14711 * @returns none
rob@100 14712 *
rob@100 14713 * @see lights
rob@100 14714 * @see directionalLight
rob@100 14715 * @see ambientLight
rob@100 14716 * @see spotLight
rob@100 14717 */
rob@100 14718 Drawing2D.prototype.pointLight = DrawingShared.prototype.a3DOnlyFunction;
rob@100 14719
rob@100 14720 Drawing3D.prototype.pointLight = function(r, g, b, x, y, z) {
rob@100 14721 if (lightCount === PConstants.MAX_LIGHTS) {
rob@100 14722 throw "can only create " + PConstants.MAX_LIGHTS + " lights";
rob@100 14723 }
rob@100 14724
rob@100 14725 // Place the point in view space once instead of once per vertex
rob@100 14726 // in the shader.
rob@100 14727 var pos = new PVector(x, y, z);
rob@100 14728 var view = new PMatrix3D();
rob@100 14729 view.scale(1, -1, 1);
rob@100 14730 view.apply(modelView.array());
rob@100 14731 view.mult(pos, pos);
rob@100 14732
rob@100 14733 // Instead of calling p.color, we do the calculations ourselves to
rob@100 14734 // reduce property lookups.
rob@100 14735 var col = color$4(r, g, b, 0);
rob@100 14736 var normalizedCol = [ ((col & PConstants.RED_MASK) >>> 16) / 255,
rob@100 14737 ((col & PConstants.GREEN_MASK) >>> 8) / 255,
rob@100 14738 (col & PConstants.BLUE_MASK) / 255 ];
rob@100 14739
rob@100 14740 curContext.useProgram(programObject3D);
rob@100 14741 uniformf("uLights.color.3d." + lightCount, programObject3D, "uLights" + lightCount + ".color", normalizedCol);
rob@100 14742 uniformf("uLights.position.3d." + lightCount, programObject3D, "uLights" + lightCount + ".position", pos.array());
rob@100 14743 uniformi("uLights.type.3d." + lightCount, programObject3D, "uLights" + lightCount + ".type", 2);
rob@100 14744 uniformi("uLightCount3d", programObject3D, "uLightCount", ++lightCount);
rob@100 14745 };
rob@100 14746
rob@100 14747 /**
rob@100 14748 * Disable all lighting. Lighting is turned off by default and enabled with
rob@100 14749 * the lights() method. This function can be used to disable lighting so
rob@100 14750 * that 2D geometry (which does not require lighting) can be drawn after a
rob@100 14751 * set of lighted 3D geometry.
rob@100 14752 *
rob@100 14753 * @returns none
rob@100 14754 *
rob@100 14755 * @see lights
rob@100 14756 */
rob@100 14757 Drawing2D.prototype.noLights = DrawingShared.prototype.a3DOnlyFunction;
rob@100 14758
rob@100 14759 Drawing3D.prototype.noLights = function() {
rob@100 14760 lightCount = 0;
rob@100 14761 curContext.useProgram(programObject3D);
rob@100 14762 uniformi("uLightCount3d", programObject3D, "uLightCount", lightCount);
rob@100 14763 };
rob@100 14764
rob@100 14765 /**
rob@100 14766 * Adds a spot light. Lights need to be included in the <b>draw()</b> to
rob@100 14767 * remain persistent in a looping program. Placing them in the <b>setup()</b>
rob@100 14768 * of a looping program will cause them to only have an effect the first time
rob@100 14769 * through the loop. The affect of the <b>r</b>, <b>g</b>, and <b>b</b> parameters
rob@100 14770 * is determined by the current color mode. The <b>x</b>, <b>y</b>, and <b>z</b>
rob@100 14771 * parameters specify the position of the light and <b>nx</b>, <b>ny</b>, <b>nz</b>
rob@100 14772 * specify the direction or light. The angle parameter affects <b>angle</b> of the
rob@100 14773 * spotlight cone.
rob@100 14774 *
rob@100 14775 * @param {int | float} r red or hue value
rob@100 14776 * @param {int | float} g green or hue value
rob@100 14777 * @param {int | float} b blue or hue value
rob@100 14778 * @param {int | float} x coordinate of the light
rob@100 14779 * @param {int | float} y coordinate of the light
rob@100 14780 * @param {int | float} z coordinate of the light
rob@100 14781 * @param {int | float} nx direction along the x axis
rob@100 14782 * @param {int | float} ny direction along the y axis
rob@100 14783 * @param {int | float} nz direction along the z axis
rob@100 14784 * @param {float} angle angle of the spotlight cone
rob@100 14785 * @param {float} concentration exponent determining the center bias of the cone
rob@100 14786 *
rob@100 14787 * @returns none
rob@100 14788 *
rob@100 14789 * @see lights
rob@100 14790 * @see directionalLight
rob@100 14791 * @see ambientLight
rob@100 14792 * @see pointLight
rob@100 14793 */
rob@100 14794 Drawing2D.prototype.spotLight = DrawingShared.prototype.a3DOnlyFunction;
rob@100 14795
rob@100 14796 Drawing3D.prototype.spotLight = function(r, g, b, x, y, z, nx, ny, nz, angle, concentration) {
rob@100 14797 if (lightCount === PConstants.MAX_LIGHTS) {
rob@100 14798 throw "can only create " + PConstants.MAX_LIGHTS + " lights";
rob@100 14799 }
rob@100 14800
rob@100 14801 curContext.useProgram(programObject3D);
rob@100 14802
rob@100 14803 // multiply the position and direction by the model view matrix
rob@100 14804 // once per object rather than once per vertex.
rob@100 14805 var pos = new PVector(x, y, z);
rob@100 14806 var mvm = new PMatrix3D();
rob@100 14807 mvm.scale(1, -1, 1);
rob@100 14808 mvm.apply(modelView.array());
rob@100 14809 mvm.mult(pos, pos);
rob@100 14810
rob@100 14811 // Convert to array since we need to directly access the elements.
rob@100 14812 mvm = mvm.array();
rob@100 14813
rob@100 14814 // We need to multiply the direction by the model view matrix, but
rob@100 14815 // the mult function checks the w component of the vector, if it isn't
rob@100 14816 // present, it uses 1, so we use a very small value as a work around.
rob@100 14817 var dir = [
rob@100 14818 mvm[0] * nx + mvm[4] * ny + mvm[8] * nz,
rob@100 14819 mvm[1] * nx + mvm[5] * ny + mvm[9] * nz,
rob@100 14820 mvm[2] * nx + mvm[6] * ny + mvm[10] * nz
rob@100 14821 ];
rob@100 14822
rob@100 14823 // Instead of calling p.color, we do the calculations ourselves to
rob@100 14824 // reduce property lookups.
rob@100 14825 var col = color$4(r, g, b, 0);
rob@100 14826 var normalizedCol = [ ((col & PConstants.RED_MASK) >>> 16) / 255,
rob@100 14827 ((col & PConstants.GREEN_MASK) >>> 8) / 255,
rob@100 14828 (col & PConstants.BLUE_MASK) / 255 ];
rob@100 14829
rob@100 14830 uniformf("uLights.color.3d." + lightCount, programObject3D, "uLights" + lightCount + ".color", normalizedCol);
rob@100 14831 uniformf("uLights.position.3d." + lightCount, programObject3D, "uLights" + lightCount + ".position", pos.array());
rob@100 14832 uniformf("uLights.direction.3d." + lightCount, programObject3D, "uLights" + lightCount + ".direction", dir);
rob@100 14833 uniformf("uLights.concentration.3d." + lightCount, programObject3D, "uLights" + lightCount + ".concentration", concentration);
rob@100 14834 uniformf("uLights.angle.3d." + lightCount, programObject3D, "uLights" + lightCount + ".angle", angle);
rob@100 14835 uniformi("uLights.type.3d." + lightCount, programObject3D, "uLights" + lightCount + ".type", 3);
rob@100 14836 uniformi("uLightCount3d", programObject3D, "uLightCount", ++lightCount);
rob@100 14837 };
rob@100 14838
rob@100 14839 ////////////////////////////////////////////////////////////////////////////
rob@100 14840 // Camera functions
rob@100 14841 ////////////////////////////////////////////////////////////////////////////
rob@100 14842
rob@100 14843 /**
rob@100 14844 * The <b>beginCamera()</b> and <b>endCamera()</b> functions enable advanced customization of the camera space.
rob@100 14845 * The functions are useful if you want to more control over camera movement, however for most users, the <b>camera()</b>
rob@100 14846 * function will be sufficient.<br /><br />The camera functions will replace any transformations (such as <b>rotate()</b>
rob@100 14847 * or <b>translate()</b>) that occur before them in <b>draw()</b>, but they will not automatically replace the camera
rob@100 14848 * transform itself. For this reason, camera functions should be placed at the beginning of <b>draw()</b> (so that
rob@100 14849 * transformations happen afterwards), and the <b>camera()</b> function can be used after <b>beginCamera()</b> if
rob@100 14850 * you want to reset the camera before applying transformations.<br /><br />This function sets the matrix mode to the
rob@100 14851 * camera matrix so calls such as <b>translate()</b>, <b>rotate()</b>, applyMatrix() and resetMatrix() affect the camera.
rob@100 14852 * <b>beginCamera()</b> should always be used with a following <b>endCamera()</b> and pairs of <b>beginCamera()</b> and
rob@100 14853 * <b>endCamera()</b> cannot be nested.
rob@100 14854 *
rob@100 14855 * @see camera
rob@100 14856 * @see endCamera
rob@100 14857 * @see applyMatrix
rob@100 14858 * @see resetMatrix
rob@100 14859 * @see translate
rob@100 14860 * @see rotate
rob@100 14861 * @see scale
rob@100 14862 */
rob@100 14863 Drawing2D.prototype.beginCamera = function() {
rob@100 14864 throw ("beginCamera() is not available in 2D mode");
rob@100 14865 };
rob@100 14866
rob@100 14867 Drawing3D.prototype.beginCamera = function() {
rob@100 14868 if (manipulatingCamera) {
rob@100 14869 throw ("You cannot call beginCamera() again before calling endCamera()");
rob@100 14870 }
rob@100 14871 manipulatingCamera = true;
rob@100 14872 modelView = cameraInv;
rob@100 14873 modelViewInv = cam;
rob@100 14874 };
rob@100 14875
rob@100 14876 /**
rob@100 14877 * The <b>beginCamera()</b> and <b>endCamera()</b> functions enable advanced customization of the camera space.
rob@100 14878 * Please see the reference for <b>beginCamera()</b> for a description of how the functions are used.
rob@100 14879 *
rob@100 14880 * @see beginCamera
rob@100 14881 */
rob@100 14882 Drawing2D.prototype.endCamera = function() {
rob@100 14883 throw ("endCamera() is not available in 2D mode");
rob@100 14884 };
rob@100 14885
rob@100 14886 Drawing3D.prototype.endCamera = function() {
rob@100 14887 if (!manipulatingCamera) {
rob@100 14888 throw ("You cannot call endCamera() before calling beginCamera()");
rob@100 14889 }
rob@100 14890 modelView.set(cam);
rob@100 14891 modelViewInv.set(cameraInv);
rob@100 14892 manipulatingCamera = false;
rob@100 14893 };
rob@100 14894
rob@100 14895 /**
rob@100 14896 * Sets the position of the camera through setting the eye position, the center of the scene, and which axis is facing
rob@100 14897 * upward. Moving the eye position and the direction it is pointing (the center of the scene) allows the images to be
rob@100 14898 * seen from different angles. The version without any parameters sets the camera to the default position, pointing to
rob@100 14899 * the center of the display window with the Y axis as up. The default values are camera(width/2.0, height/2.0,
rob@100 14900 * (height/2.0) / tan(PI*60.0 / 360.0), width/2.0, height/2.0, 0, 0, 1, 0). This function is similar to gluLookAt()
rob@100 14901 * in OpenGL, but it first clears the current camera settings.
rob@100 14902 *
rob@100 14903 * @param {float} eyeX x-coordinate for the eye
rob@100 14904 * @param {float} eyeY y-coordinate for the eye
rob@100 14905 * @param {float} eyeZ z-coordinate for the eye
rob@100 14906 * @param {float} centerX x-coordinate for the center of the scene
rob@100 14907 * @param {float} centerY y-coordinate for the center of the scene
rob@100 14908 * @param {float} centerZ z-coordinate for the center of the scene
rob@100 14909 * @param {float} upX usually 0.0, 1.0, -1.0
rob@100 14910 * @param {float} upY usually 0.0, 1.0, -1.0
rob@100 14911 * @param {float} upZ usually 0.0, 1.0, -1.0
rob@100 14912 *
rob@100 14913 * @see beginCamera
rob@100 14914 * @see endCamera
rob@100 14915 * @see frustum
rob@100 14916 */
rob@100 14917 p.camera = function(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ) {
rob@100 14918 if (eyeX === undef) {
rob@100 14919 // Workaround if createGraphics is used.
rob@100 14920 cameraX = p.width / 2;
rob@100 14921 cameraY = p.height / 2;
rob@100 14922 cameraZ = cameraY / Math.tan(cameraFOV / 2);
rob@100 14923 eyeX = cameraX;
rob@100 14924 eyeY = cameraY;
rob@100 14925 eyeZ = cameraZ;
rob@100 14926 centerX = cameraX;
rob@100 14927 centerY = cameraY;
rob@100 14928 centerZ = 0;
rob@100 14929 upX = 0;
rob@100 14930 upY = 1;
rob@100 14931 upZ = 0;
rob@100 14932 }
rob@100 14933
rob@100 14934 var z = new PVector(eyeX - centerX, eyeY - centerY, eyeZ - centerZ);
rob@100 14935 var y = new PVector(upX, upY, upZ);
rob@100 14936 z.normalize();
rob@100 14937 var x = PVector.cross(y, z);
rob@100 14938 y = PVector.cross(z, x);
rob@100 14939 x.normalize();
rob@100 14940 y.normalize();
rob@100 14941
rob@100 14942 var xX = x.x,
rob@100 14943 xY = x.y,
rob@100 14944 xZ = x.z;
rob@100 14945
rob@100 14946 var yX = y.x,
rob@100 14947 yY = y.y,
rob@100 14948 yZ = y.z;
rob@100 14949
rob@100 14950 var zX = z.x,
rob@100 14951 zY = z.y,
rob@100 14952 zZ = z.z;
rob@100 14953
rob@100 14954 cam.set(xX, xY, xZ, 0, yX, yY, yZ, 0, zX, zY, zZ, 0, 0, 0, 0, 1);
rob@100 14955
rob@100 14956 cam.translate(-eyeX, -eyeY, -eyeZ);
rob@100 14957
rob@100 14958 cameraInv.reset();
rob@100 14959 cameraInv.invApply(xX, xY, xZ, 0, yX, yY, yZ, 0, zX, zY, zZ, 0, 0, 0, 0, 1);
rob@100 14960
rob@100 14961 cameraInv.translate(eyeX, eyeY, eyeZ);
rob@100 14962
rob@100 14963 modelView.set(cam);
rob@100 14964 modelViewInv.set(cameraInv);
rob@100 14965 };
rob@100 14966
rob@100 14967 /**
rob@100 14968 * Sets a perspective projection applying foreshortening, making distant objects appear smaller than closer ones. The
rob@100 14969 * parameters define a viewing volume with the shape of truncated pyramid. Objects near to the front of the volume appear
rob@100 14970 * their actual size, while farther objects appear smaller. This projection simulates the perspective of the world more
rob@100 14971 * accurately than orthographic projection. The version of perspective without parameters sets the default perspective and
rob@100 14972 * the version with four parameters allows the programmer to set the area precisely. The default values are:
rob@100 14973 * perspective(PI/3.0, width/height, cameraZ/10.0, cameraZ*10.0) where cameraZ is ((height/2.0) / tan(PI*60.0/360.0));
rob@100 14974 *
rob@100 14975 * @param {float} fov field-of-view angle (in radians) for vertical direction
rob@100 14976 * @param {float} aspect ratio of width to height
rob@100 14977 * @param {float} zNear z-position of nearest clipping plane
rob@100 14978 * @param {float} zFar z-positions of farthest clipping plane
rob@100 14979 */
rob@100 14980 p.perspective = function(fov, aspect, near, far) {
rob@100 14981 if (arguments.length === 0) {
rob@100 14982 //in case canvas is resized
rob@100 14983 cameraY = curElement.height / 2;
rob@100 14984 cameraZ = cameraY / Math.tan(cameraFOV / 2);
rob@100 14985 cameraNear = cameraZ / 10;
rob@100 14986 cameraFar = cameraZ * 10;
rob@100 14987 cameraAspect = p.width / p.height;
rob@100 14988 fov = cameraFOV;
rob@100 14989 aspect = cameraAspect;
rob@100 14990 near = cameraNear;
rob@100 14991 far = cameraFar;
rob@100 14992 }
rob@100 14993
rob@100 14994 var yMax, yMin, xMax, xMin;
rob@100 14995 yMax = near * Math.tan(fov / 2);
rob@100 14996 yMin = -yMax;
rob@100 14997 xMax = yMax * aspect;
rob@100 14998 xMin = yMin * aspect;
rob@100 14999 p.frustum(xMin, xMax, yMin, yMax, near, far);
rob@100 15000 };
rob@100 15001
rob@100 15002 /**
rob@100 15003 * Sets a perspective matrix defined through the parameters. Works like glFrustum, except it wipes out the current
rob@100 15004 * perspective matrix rather than muliplying itself with it.
rob@100 15005 *
rob@100 15006 * @param {float} left left coordinate of the clipping plane
rob@100 15007 * @param {float} right right coordinate of the clipping plane
rob@100 15008 * @param {float} bottom bottom coordinate of the clipping plane
rob@100 15009 * @param {float} top top coordinate of the clipping plane
rob@100 15010 * @param {float} near near coordinate of the clipping plane
rob@100 15011 * @param {float} far far coordinate of the clipping plane
rob@100 15012 *
rob@100 15013 * @see beginCamera
rob@100 15014 * @see camera
rob@100 15015 * @see endCamera
rob@100 15016 * @see perspective
rob@100 15017 */
rob@100 15018 Drawing2D.prototype.frustum = function() {
rob@100 15019 throw("Processing.js: frustum() is not supported in 2D mode");
rob@100 15020 };
rob@100 15021
rob@100 15022 Drawing3D.prototype.frustum = function(left, right, bottom, top, near, far) {
rob@100 15023 frustumMode = true;
rob@100 15024 projection = new PMatrix3D();
rob@100 15025 projection.set((2 * near) / (right - left), 0, (right + left) / (right - left),
rob@100 15026 0, 0, (2 * near) / (top - bottom), (top + bottom) / (top - bottom),
rob@100 15027 0, 0, 0, -(far + near) / (far - near), -(2 * far * near) / (far - near),
rob@100 15028 0, 0, -1, 0);
rob@100 15029 var proj = new PMatrix3D();
rob@100 15030 proj.set(projection);
rob@100 15031 proj.transpose();
rob@100 15032 curContext.useProgram(programObject2D);
rob@100 15033 uniformMatrix("projection2d", programObject2D, "uProjection", false, proj.array());
rob@100 15034 curContext.useProgram(programObject3D);
rob@100 15035 uniformMatrix("projection3d", programObject3D, "uProjection", false, proj.array());
rob@100 15036 curContext.useProgram(programObjectUnlitShape);
rob@100 15037 uniformMatrix("uProjectionUS", programObjectUnlitShape, "uProjection", false, proj.array());
rob@100 15038 };
rob@100 15039
rob@100 15040 /**
rob@100 15041 * Sets an orthographic projection and defines a parallel clipping volume. All objects with the same dimension appear
rob@100 15042 * the same size, regardless of whether they are near or far from the camera. The parameters to this function specify
rob@100 15043 * the clipping volume where left and right are the minimum and maximum x values, top and bottom are the minimum and
rob@100 15044 * maximum y values, and near and far are the minimum and maximum z values. If no parameters are given, the default
rob@100 15045 * is used: ortho(0, width, 0, height, -10, 10).
rob@100 15046 *
rob@100 15047 * @param {float} left left plane of the clipping volume
rob@100 15048 * @param {float} right right plane of the clipping volume
rob@100 15049 * @param {float} bottom bottom plane of the clipping volume
rob@100 15050 * @param {float} top top plane of the clipping volume
rob@100 15051 * @param {float} near maximum distance from the origin to the viewer
rob@100 15052 * @param {float} far maximum distance from the origin away from the viewer
rob@100 15053 */
rob@100 15054 p.ortho = function(left, right, bottom, top, near, far) {
rob@100 15055 if (arguments.length === 0) {
rob@100 15056 left = 0;
rob@100 15057 right = p.width;
rob@100 15058 bottom = 0;
rob@100 15059 top = p.height;
rob@100 15060 near = -10;
rob@100 15061 far = 10;
rob@100 15062 }
rob@100 15063
rob@100 15064 var x = 2 / (right - left);
rob@100 15065 var y = 2 / (top - bottom);
rob@100 15066 var z = -2 / (far - near);
rob@100 15067
rob@100 15068 var tx = -(right + left) / (right - left);
rob@100 15069 var ty = -(top + bottom) / (top - bottom);
rob@100 15070 var tz = -(far + near) / (far - near);
rob@100 15071
rob@100 15072 projection = new PMatrix3D();
rob@100 15073 projection.set(x, 0, 0, tx, 0, y, 0, ty, 0, 0, z, tz, 0, 0, 0, 1);
rob@100 15074
rob@100 15075 var proj = new PMatrix3D();
rob@100 15076 proj.set(projection);
rob@100 15077 proj.transpose();
rob@100 15078 curContext.useProgram(programObject2D);
rob@100 15079 uniformMatrix("projection2d", programObject2D, "uProjection", false, proj.array());
rob@100 15080 curContext.useProgram(programObject3D);
rob@100 15081 uniformMatrix("projection3d", programObject3D, "uProjection", false, proj.array());
rob@100 15082 curContext.useProgram(programObjectUnlitShape);
rob@100 15083 uniformMatrix("uProjectionUS", programObjectUnlitShape, "uProjection", false, proj.array());
rob@100 15084 frustumMode = false;
rob@100 15085 };
rob@100 15086 /**
rob@100 15087 * The printProjection() prints the current projection matrix to the text window.
rob@100 15088 */
rob@100 15089 p.printProjection = function() {
rob@100 15090 projection.print();
rob@100 15091 };
rob@100 15092 /**
rob@100 15093 * The printCamera() function prints the current camera matrix.
rob@100 15094 */
rob@100 15095 p.printCamera = function() {
rob@100 15096 cam.print();
rob@100 15097 };
rob@100 15098
rob@100 15099 ////////////////////////////////////////////////////////////////////////////
rob@100 15100 // Shapes
rob@100 15101 ////////////////////////////////////////////////////////////////////////////
rob@100 15102 /**
rob@100 15103 * The box() function renders a box. A box is an extruded rectangle. A box with equal dimension on all sides is a cube.
rob@100 15104 * Calling this function with only one parameter will create a cube.
rob@100 15105 *
rob@100 15106 * @param {int|float} w dimension of the box in the x-dimension
rob@100 15107 * @param {int|float} h dimension of the box in the y-dimension
rob@100 15108 * @param {int|float} d dimension of the box in the z-dimension
rob@100 15109 */
rob@100 15110 Drawing2D.prototype.box = DrawingShared.prototype.a3DOnlyFunction;
rob@100 15111
rob@100 15112 Drawing3D.prototype.box = function(w, h, d) {
rob@100 15113 // user can uniformly scale the box by
rob@100 15114 // passing in only one argument.
rob@100 15115 if (!h || !d) {
rob@100 15116 h = d = w;
rob@100 15117 }
rob@100 15118
rob@100 15119 // Modeling transformation
rob@100 15120 var model = new PMatrix3D();
rob@100 15121 model.scale(w, h, d);
rob@100 15122
rob@100 15123 // Viewing transformation needs to have Y flipped
rob@100 15124 // becuase that's what Processing does.
rob@100 15125 var view = new PMatrix3D();
rob@100 15126 view.scale(1, -1, 1);
rob@100 15127 view.apply(modelView.array());
rob@100 15128 view.transpose();
rob@100 15129
rob@100 15130 if (doFill) {
rob@100 15131 curContext.useProgram(programObject3D);
rob@100 15132 uniformMatrix("model3d", programObject3D, "uModel", false, model.array());
rob@100 15133 uniformMatrix("view3d", programObject3D, "uView", false, view.array());
rob@100 15134 // Fix stitching problems. (lines get occluded by triangles
rob@100 15135 // since they share the same depth values). This is not entirely
rob@100 15136 // working, but it's a start for drawing the outline. So
rob@100 15137 // developers can start playing around with styles.
rob@100 15138 curContext.enable(curContext.POLYGON_OFFSET_FILL);
rob@100 15139 curContext.polygonOffset(1, 1);
rob@100 15140 uniformf("color3d", programObject3D, "uColor", fillStyle);
rob@100 15141
rob@100 15142 // Calculating the normal matrix can be expensive, so only
rob@100 15143 // do it if it's necessary.
rob@100 15144 if(lightCount > 0){
rob@100 15145 // Create the normal transformation matrix.
rob@100 15146 var v = new PMatrix3D();
rob@100 15147 v.set(view);
rob@100 15148
rob@100 15149 var m = new PMatrix3D();
rob@100 15150 m.set(model);
rob@100 15151
rob@100 15152 v.mult(m);
rob@100 15153
rob@100 15154 var normalMatrix = new PMatrix3D();
rob@100 15155 normalMatrix.set(v);
rob@100 15156 normalMatrix.invert();
rob@100 15157 normalMatrix.transpose();
rob@100 15158
rob@100 15159 uniformMatrix("uNormalTransform3d", programObject3D, "uNormalTransform", false, normalMatrix.array());
rob@100 15160 vertexAttribPointer("aNormal3d", programObject3D, "aNormal", 3, boxNormBuffer);
rob@100 15161 }
rob@100 15162 else{
rob@100 15163 disableVertexAttribPointer("aNormal3d", programObject3D, "aNormal");
rob@100 15164 }
rob@100 15165
rob@100 15166 vertexAttribPointer("aVertex3d", programObject3D, "aVertex", 3, boxBuffer);
rob@100 15167
rob@100 15168 // Turn off per vertex colors.
rob@100 15169 disableVertexAttribPointer("aColor3d", programObject3D, "aColor");
rob@100 15170 disableVertexAttribPointer("aTexture3d", programObject3D, "aTexture");
rob@100 15171
rob@100 15172 curContext.drawArrays(curContext.TRIANGLES, 0, boxVerts.length / 3);
rob@100 15173 curContext.disable(curContext.POLYGON_OFFSET_FILL);
rob@100 15174 }
rob@100 15175
rob@100 15176 // Draw the box outline.
rob@100 15177 if (lineWidth > 0 && doStroke) {
rob@100 15178 curContext.useProgram(programObject2D);
rob@100 15179 uniformMatrix("uModel2d", programObject2D, "uModel", false, model.array());
rob@100 15180 uniformMatrix("uView2d", programObject2D, "uView", false, view.array());
rob@100 15181 uniformf("uColor2d", programObject2D, "uColor", strokeStyle);
rob@100 15182 uniformi("uIsDrawingText2d", programObject2D, "uIsDrawingText", false);
rob@100 15183 vertexAttribPointer("vertex2d", programObject2D, "aVertex", 3, boxOutlineBuffer);
rob@100 15184 disableVertexAttribPointer("aTextureCoord2d", programObject2D, "aTextureCoord");
rob@100 15185 curContext.drawArrays(curContext.LINES, 0, boxOutlineVerts.length / 3);
rob@100 15186 }
rob@100 15187 };
rob@100 15188
rob@100 15189 /**
rob@100 15190 * The initSphere() function is a helper function used by <b>sphereDetail()</b>
rob@100 15191 * This function creates and stores sphere vertices every time the user changes sphere detail.
rob@100 15192 *
rob@100 15193 * @see #sphereDetail
rob@100 15194 */
rob@100 15195 var initSphere = function() {
rob@100 15196 var i;
rob@100 15197 sphereVerts = [];
rob@100 15198
rob@100 15199 for (i = 0; i < sphereDetailU; i++) {
rob@100 15200 sphereVerts.push(0);
rob@100 15201 sphereVerts.push(-1);
rob@100 15202 sphereVerts.push(0);
rob@100 15203 sphereVerts.push(sphereX[i]);
rob@100 15204 sphereVerts.push(sphereY[i]);
rob@100 15205 sphereVerts.push(sphereZ[i]);
rob@100 15206 }
rob@100 15207 sphereVerts.push(0);
rob@100 15208 sphereVerts.push(-1);
rob@100 15209 sphereVerts.push(0);
rob@100 15210 sphereVerts.push(sphereX[0]);
rob@100 15211 sphereVerts.push(sphereY[0]);
rob@100 15212 sphereVerts.push(sphereZ[0]);
rob@100 15213
rob@100 15214 var v1, v11, v2;
rob@100 15215
rob@100 15216 // middle rings
rob@100 15217 var voff = 0;
rob@100 15218 for (i = 2; i < sphereDetailV; i++) {
rob@100 15219 v1 = v11 = voff;
rob@100 15220 voff += sphereDetailU;
rob@100 15221 v2 = voff;
rob@100 15222 for (var j = 0; j < sphereDetailU; j++) {
rob@100 15223 sphereVerts.push(sphereX[v1]);
rob@100 15224 sphereVerts.push(sphereY[v1]);
rob@100 15225 sphereVerts.push(sphereZ[v1++]);
rob@100 15226 sphereVerts.push(sphereX[v2]);
rob@100 15227 sphereVerts.push(sphereY[v2]);
rob@100 15228 sphereVerts.push(sphereZ[v2++]);
rob@100 15229 }
rob@100 15230
rob@100 15231 // close each ring
rob@100 15232 v1 = v11;
rob@100 15233 v2 = voff;
rob@100 15234
rob@100 15235 sphereVerts.push(sphereX[v1]);
rob@100 15236 sphereVerts.push(sphereY[v1]);
rob@100 15237 sphereVerts.push(sphereZ[v1]);
rob@100 15238 sphereVerts.push(sphereX[v2]);
rob@100 15239 sphereVerts.push(sphereY[v2]);
rob@100 15240 sphereVerts.push(sphereZ[v2]);
rob@100 15241 }
rob@100 15242
rob@100 15243 // add the northern cap
rob@100 15244 for (i = 0; i < sphereDetailU; i++) {
rob@100 15245 v2 = voff + i;
rob@100 15246
rob@100 15247 sphereVerts.push(sphereX[v2]);
rob@100 15248 sphereVerts.push(sphereY[v2]);
rob@100 15249 sphereVerts.push(sphereZ[v2]);
rob@100 15250 sphereVerts.push(0);
rob@100 15251 sphereVerts.push(1);
rob@100 15252 sphereVerts.push(0);
rob@100 15253 }
rob@100 15254
rob@100 15255 sphereVerts.push(sphereX[voff]);
rob@100 15256 sphereVerts.push(sphereY[voff]);
rob@100 15257 sphereVerts.push(sphereZ[voff]);
rob@100 15258 sphereVerts.push(0);
rob@100 15259 sphereVerts.push(1);
rob@100 15260 sphereVerts.push(0);
rob@100 15261
rob@100 15262 //set the buffer data
rob@100 15263 curContext.bindBuffer(curContext.ARRAY_BUFFER, sphereBuffer);
rob@100 15264 curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array(sphereVerts), curContext.STATIC_DRAW);
rob@100 15265 };
rob@100 15266
rob@100 15267 /**
rob@100 15268 * The sphereDetail() function controls the detail used to render a sphere by adjusting the number of
rob@100 15269 * vertices of the sphere mesh. The default resolution is 30, which creates
rob@100 15270 * a fairly detailed sphere definition with vertices every 360/30 = 12
rob@100 15271 * degrees. If you're going to render a great number of spheres per frame,
rob@100 15272 * it is advised to reduce the level of detail using this function.
rob@100 15273 * The setting stays active until <b>sphereDetail()</b> is called again with
rob@100 15274 * a new parameter and so should <i>not</i> be called prior to every
rob@100 15275 * <b>sphere()</b> statement, unless you wish to render spheres with
rob@100 15276 * different settings, e.g. using less detail for smaller spheres or ones
rob@100 15277 * further away from the camera. To control the detail of the horizontal
rob@100 15278 * and vertical resolution independently, use the version of the functions
rob@100 15279 * with two parameters. Calling this function with one parameter sets the number of segments
rob@100 15280 *(minimum of 3) used per full circle revolution. This is equivalent to calling the function with
rob@100 15281 * two identical values.
rob@100 15282 *
rob@100 15283 * @param {int} ures number of segments used horizontally (longitudinally) per full circle revolution
rob@100 15284 * @param {int} vres number of segments used vertically (latitudinally) from top to bottom
rob@100 15285 *
rob@100 15286 * @see #sphere()
rob@100 15287 */
rob@100 15288 p.sphereDetail = function(ures, vres) {
rob@100 15289 var i;
rob@100 15290
rob@100 15291 if (arguments.length === 1) {
rob@100 15292 ures = vres = arguments[0];
rob@100 15293 }
rob@100 15294
rob@100 15295 if (ures < 3) {
rob@100 15296 ures = 3;
rob@100 15297 } // force a minimum res
rob@100 15298 if (vres < 2) {
rob@100 15299 vres = 2;
rob@100 15300 } // force a minimum res
rob@100 15301 // if it hasn't changed do nothing
rob@100 15302 if ((ures === sphereDetailU) && (vres === sphereDetailV)) {
rob@100 15303 return;
rob@100 15304 }
rob@100 15305
rob@100 15306 var delta = PConstants.SINCOS_LENGTH / ures;
rob@100 15307 var cx = new Float32Array(ures);
rob@100 15308 var cz = new Float32Array(ures);
rob@100 15309 // calc unit circle in XZ plane
rob@100 15310 for (i = 0; i < ures; i++) {
rob@100 15311 cx[i] = cosLUT[((i * delta) % PConstants.SINCOS_LENGTH) | 0];
rob@100 15312 cz[i] = sinLUT[((i * delta) % PConstants.SINCOS_LENGTH) | 0];
rob@100 15313 }
rob@100 15314
rob@100 15315 // computing vertexlist
rob@100 15316 // vertexlist starts at south pole
rob@100 15317 var vertCount = ures * (vres - 1) + 2;
rob@100 15318 var currVert = 0;
rob@100 15319
rob@100 15320 // re-init arrays to store vertices
rob@100 15321 sphereX = new Float32Array(vertCount);
rob@100 15322 sphereY = new Float32Array(vertCount);
rob@100 15323 sphereZ = new Float32Array(vertCount);
rob@100 15324
rob@100 15325 var angle_step = (PConstants.SINCOS_LENGTH * 0.5) / vres;
rob@100 15326 var angle = angle_step;
rob@100 15327
rob@100 15328 // step along Y axis
rob@100 15329 for (i = 1; i < vres; i++) {
rob@100 15330 var curradius = sinLUT[(angle % PConstants.SINCOS_LENGTH) | 0];
rob@100 15331 var currY = -cosLUT[(angle % PConstants.SINCOS_LENGTH) | 0];
rob@100 15332 for (var j = 0; j < ures; j++) {
rob@100 15333 sphereX[currVert] = cx[j] * curradius;
rob@100 15334 sphereY[currVert] = currY;
rob@100 15335 sphereZ[currVert++] = cz[j] * curradius;
rob@100 15336 }
rob@100 15337 angle += angle_step;
rob@100 15338 }
rob@100 15339 sphereDetailU = ures;
rob@100 15340 sphereDetailV = vres;
rob@100 15341
rob@100 15342 // make the sphere verts and norms
rob@100 15343 initSphere();
rob@100 15344 };
rob@100 15345
rob@100 15346 /**
rob@100 15347 * The sphere() function draws a sphere with radius r centered at coordinate 0, 0, 0.
rob@100 15348 * A sphere is a hollow ball made from tessellated triangles.
rob@100 15349 *
rob@100 15350 * @param {int|float} r the radius of the sphere
rob@100 15351 */
rob@100 15352 Drawing2D.prototype.sphere = DrawingShared.prototype.a3DOnlyFunction;
rob@100 15353
rob@100 15354 Drawing3D.prototype.sphere = function() {
rob@100 15355 var sRad = arguments[0];
rob@100 15356
rob@100 15357 if ((sphereDetailU < 3) || (sphereDetailV < 2)) {
rob@100 15358 p.sphereDetail(30);
rob@100 15359 }
rob@100 15360
rob@100 15361 // Modeling transformation.
rob@100 15362 var model = new PMatrix3D();
rob@100 15363 model.scale(sRad, sRad, sRad);
rob@100 15364
rob@100 15365 // viewing transformation needs to have Y flipped
rob@100 15366 // becuase that's what Processing does.
rob@100 15367 var view = new PMatrix3D();
rob@100 15368 view.scale(1, -1, 1);
rob@100 15369 view.apply(modelView.array());
rob@100 15370 view.transpose();
rob@100 15371
rob@100 15372 if (doFill) {
rob@100 15373 // Calculating the normal matrix can be expensive, so only
rob@100 15374 // do it if it's necessary.
rob@100 15375 if(lightCount > 0){
rob@100 15376 // Create a normal transformation matrix.
rob@100 15377 var v = new PMatrix3D();
rob@100 15378 v.set(view);
rob@100 15379
rob@100 15380 var m = new PMatrix3D();
rob@100 15381 m.set(model);
rob@100 15382
rob@100 15383 v.mult(m);
rob@100 15384
rob@100 15385 var normalMatrix = new PMatrix3D();
rob@100 15386 normalMatrix.set(v);
rob@100 15387 normalMatrix.invert();
rob@100 15388 normalMatrix.transpose();
rob@100 15389
rob@100 15390 uniformMatrix("uNormalTransform3d", programObject3D, "uNormalTransform", false, normalMatrix.array());
rob@100 15391 vertexAttribPointer("aNormal3d", programObject3D, "aNormal", 3, sphereBuffer);
rob@100 15392 }
rob@100 15393 else{
rob@100 15394 disableVertexAttribPointer("aNormal3d", programObject3D, "aNormal");
rob@100 15395 }
rob@100 15396
rob@100 15397 curContext.useProgram(programObject3D);
rob@100 15398 disableVertexAttribPointer("aTexture3d", programObject3D, "aTexture");
rob@100 15399
rob@100 15400 uniformMatrix("uModel3d", programObject3D, "uModel", false, model.array());
rob@100 15401 uniformMatrix("uView3d", programObject3D, "uView", false, view.array());
rob@100 15402 vertexAttribPointer("aVertex3d", programObject3D, "aVertex", 3, sphereBuffer);
rob@100 15403
rob@100 15404 // Turn off per vertex colors.
rob@100 15405 disableVertexAttribPointer("aColor3d", programObject3D, "aColor");
rob@100 15406
rob@100 15407 // fix stitching problems. (lines get occluded by triangles
rob@100 15408 // since they share the same depth values). This is not entirely
rob@100 15409 // working, but it's a start for drawing the outline. So
rob@100 15410 // developers can start playing around with styles.
rob@100 15411 curContext.enable(curContext.POLYGON_OFFSET_FILL);
rob@100 15412 curContext.polygonOffset(1, 1);
rob@100 15413 uniformf("uColor3d", programObject3D, "uColor", fillStyle);
rob@100 15414 curContext.drawArrays(curContext.TRIANGLE_STRIP, 0, sphereVerts.length / 3);
rob@100 15415 curContext.disable(curContext.POLYGON_OFFSET_FILL);
rob@100 15416 }
rob@100 15417
rob@100 15418 // Draw the sphere outline.
rob@100 15419 if (lineWidth > 0 && doStroke) {
rob@100 15420 curContext.useProgram(programObject2D);
rob@100 15421 uniformMatrix("uModel2d", programObject2D, "uModel", false, model.array());
rob@100 15422 uniformMatrix("uView2d", programObject2D, "uView", false, view.array());
rob@100 15423 vertexAttribPointer("aVertex2d", programObject2D, "aVertex", 3, sphereBuffer);
rob@100 15424 disableVertexAttribPointer("aTextureCoord2d", programObject2D, "aTextureCoord");
rob@100 15425 uniformf("uColor2d", programObject2D, "uColor", strokeStyle);
rob@100 15426 uniformi("uIsDrawingText", programObject2D, "uIsDrawingText", false);
rob@100 15427 curContext.drawArrays(curContext.LINE_STRIP, 0, sphereVerts.length / 3);
rob@100 15428 }
rob@100 15429 };
rob@100 15430
rob@100 15431 ////////////////////////////////////////////////////////////////////////////
rob@100 15432 // Coordinates
rob@100 15433 ////////////////////////////////////////////////////////////////////////////
rob@100 15434
rob@100 15435 /**
rob@100 15436 * Returns the three-dimensional X, Y, Z position in model space. This returns
rob@100 15437 * the X value for a given coordinate based on the current set of transformations
rob@100 15438 * (scale, rotate, translate, etc.) The X value can be used to place an object
rob@100 15439 * in space relative to the location of the original point once the transformations
rob@100 15440 * are no longer in use.<br />
rob@100 15441 * <br />
rob@100 15442 *
rob@100 15443 * @param {int | float} x 3D x coordinate to be mapped
rob@100 15444 * @param {int | float} y 3D y coordinate to be mapped
rob@100 15445 * @param {int | float} z 3D z coordinate to be mapped
rob@100 15446 *
rob@100 15447 * @returns {float}
rob@100 15448 *
rob@100 15449 * @see modelY
rob@100 15450 * @see modelZ
rob@100 15451 */
rob@100 15452 p.modelX = function(x, y, z) {
rob@100 15453 var mv = modelView.array();
rob@100 15454 var ci = cameraInv.array();
rob@100 15455
rob@100 15456 var ax = mv[0] * x + mv[1] * y + mv[2] * z + mv[3];
rob@100 15457 var ay = mv[4] * x + mv[5] * y + mv[6] * z + mv[7];
rob@100 15458 var az = mv[8] * x + mv[9] * y + mv[10] * z + mv[11];
rob@100 15459 var aw = mv[12] * x + mv[13] * y + mv[14] * z + mv[15];
rob@100 15460
rob@100 15461 var ox = ci[0] * ax + ci[1] * ay + ci[2] * az + ci[3] * aw;
rob@100 15462 var ow = ci[12] * ax + ci[13] * ay + ci[14] * az + ci[15] * aw;
rob@100 15463
rob@100 15464 return (ow !== 0) ? ox / ow : ox;
rob@100 15465 };
rob@100 15466
rob@100 15467 /**
rob@100 15468 * Returns the three-dimensional X, Y, Z position in model space. This returns
rob@100 15469 * the Y value for a given coordinate based on the current set of transformations
rob@100 15470 * (scale, rotate, translate, etc.) The Y value can be used to place an object in
rob@100 15471 * space relative to the location of the original point once the transformations
rob@100 15472 * are no longer in use.<br />
rob@100 15473 * <br />
rob@100 15474 *
rob@100 15475 * @param {int | float} x 3D x coordinate to be mapped
rob@100 15476 * @param {int | float} y 3D y coordinate to be mapped
rob@100 15477 * @param {int | float} z 3D z coordinate to be mapped
rob@100 15478 *
rob@100 15479 * @returns {float}
rob@100 15480 *
rob@100 15481 * @see modelX
rob@100 15482 * @see modelZ
rob@100 15483 */
rob@100 15484 p.modelY = function(x, y, z) {
rob@100 15485 var mv = modelView.array();
rob@100 15486 var ci = cameraInv.array();
rob@100 15487
rob@100 15488 var ax = mv[0] * x + mv[1] * y + mv[2] * z + mv[3];
rob@100 15489 var ay = mv[4] * x + mv[5] * y + mv[6] * z + mv[7];
rob@100 15490 var az = mv[8] * x + mv[9] * y + mv[10] * z + mv[11];
rob@100 15491 var aw = mv[12] * x + mv[13] * y + mv[14] * z + mv[15];
rob@100 15492
rob@100 15493 var oy = ci[4] * ax + ci[5] * ay + ci[6] * az + ci[7] * aw;
rob@100 15494 var ow = ci[12] * ax + ci[13] * ay + ci[14] * az + ci[15] * aw;
rob@100 15495
rob@100 15496 return (ow !== 0) ? oy / ow : oy;
rob@100 15497 };
rob@100 15498
rob@100 15499 /**
rob@100 15500 * Returns the three-dimensional X, Y, Z position in model space. This returns
rob@100 15501 * the Z value for a given coordinate based on the current set of transformations
rob@100 15502 * (scale, rotate, translate, etc.) The Z value can be used to place an object in
rob@100 15503 * space relative to the location of the original point once the transformations
rob@100 15504 * are no longer in use.
rob@100 15505 *
rob@100 15506 * @param {int | float} x 3D x coordinate to be mapped
rob@100 15507 * @param {int | float} y 3D y coordinate to be mapped
rob@100 15508 * @param {int | float} z 3D z coordinate to be mapped
rob@100 15509 *
rob@100 15510 * @returns {float}
rob@100 15511 *
rob@100 15512 * @see modelX
rob@100 15513 * @see modelY
rob@100 15514 */
rob@100 15515 p.modelZ = function(x, y, z) {
rob@100 15516 var mv = modelView.array();
rob@100 15517 var ci = cameraInv.array();
rob@100 15518
rob@100 15519 var ax = mv[0] * x + mv[1] * y + mv[2] * z + mv[3];
rob@100 15520 var ay = mv[4] * x + mv[5] * y + mv[6] * z + mv[7];
rob@100 15521 var az = mv[8] * x + mv[9] * y + mv[10] * z + mv[11];
rob@100 15522 var aw = mv[12] * x + mv[13] * y + mv[14] * z + mv[15];
rob@100 15523
rob@100 15524 var oz = ci[8] * ax + ci[9] * ay + ci[10] * az + ci[11] * aw;
rob@100 15525 var ow = ci[12] * ax + ci[13] * ay + ci[14] * az + ci[15] * aw;
rob@100 15526
rob@100 15527 return (ow !== 0) ? oz / ow : oz;
rob@100 15528 };
rob@100 15529
rob@100 15530 ////////////////////////////////////////////////////////////////////////////
rob@100 15531 // Material Properties
rob@100 15532 ////////////////////////////////////////////////////////////////////////////
rob@100 15533
rob@100 15534 /**
rob@100 15535 * Sets the ambient reflectance for shapes drawn to the screen. This is
rob@100 15536 * combined with the ambient light component of environment. The color
rob@100 15537 * components set through the parameters define the reflectance. For example in
rob@100 15538 * the default color mode, setting v1=255, v2=126, v3=0, would cause all the
rob@100 15539 * red light to reflect and half of the green light to reflect. Used in combination
rob@100 15540 * with <b>emissive()</b>, <b>specular()</b>, and <b>shininess()</b> in setting
rob@100 15541 * the materal properties of shapes.
rob@100 15542 *
rob@100 15543 * @param {int | float} gray
rob@100 15544 *
rob@100 15545 * @returns none
rob@100 15546 *
rob@100 15547 * @see emissive
rob@100 15548 * @see specular
rob@100 15549 * @see shininess
rob@100 15550 */
rob@100 15551 Drawing2D.prototype.ambient = DrawingShared.prototype.a3DOnlyFunction;
rob@100 15552
rob@100 15553 Drawing3D.prototype.ambient = function(v1, v2, v3) {
rob@100 15554 curContext.useProgram(programObject3D);
rob@100 15555 uniformi("uUsingMat3d", programObject3D, "uUsingMat", true);
rob@100 15556 var col = p.color(v1, v2, v3);
rob@100 15557 uniformf("uMaterialAmbient3d", programObject3D, "uMaterialAmbient", p.color.toGLArray(col).slice(0, 3));
rob@100 15558 };
rob@100 15559
rob@100 15560 /**
rob@100 15561 * Sets the emissive color of the material used for drawing shapes
rob@100 15562 * drawn to the screen. Used in combination with ambient(), specular(),
rob@100 15563 * and shininess() in setting the material properties of shapes.
rob@100 15564 *
rob@100 15565 * Can be called in the following ways:
rob@100 15566 *
rob@100 15567 * emissive(gray)
rob@100 15568 * @param {int | float} gray number specifying value between white and black
rob@100 15569 *
rob@100 15570 * emissive(color)
rob@100 15571 * @param {color} color any value of the color datatype
rob@100 15572 *
rob@100 15573 * emissive(v1, v2, v3)
rob@100 15574 * @param {int | float} v1 red or hue value
rob@100 15575 * @param {int | float} v2 green or saturation value
rob@100 15576 * @param {int | float} v3 blue or brightness value
rob@100 15577 *
rob@100 15578 * @returns none
rob@100 15579 *
rob@100 15580 * @see ambient
rob@100 15581 * @see specular
rob@100 15582 * @see shininess
rob@100 15583 */
rob@100 15584 Drawing2D.prototype.emissive = DrawingShared.prototype.a3DOnlyFunction;
rob@100 15585
rob@100 15586 Drawing3D.prototype.emissive = function(v1, v2, v3) {
rob@100 15587 curContext.useProgram(programObject3D);
rob@100 15588 uniformi("uUsingMat3d", programObject3D, "uUsingMat", true);
rob@100 15589 var col = p.color(v1, v2, v3);
rob@100 15590 uniformf("uMaterialEmissive3d", programObject3D, "uMaterialEmissive", p.color.toGLArray(col).slice(0, 3));
rob@100 15591 };
rob@100 15592
rob@100 15593 /**
rob@100 15594 * Sets the amount of gloss in the surface of shapes. Used in combination with
rob@100 15595 * <b>ambient()</b>, <b>specular()</b>, and <b>emissive()</b> in setting the
rob@100 15596 * material properties of shapes.
rob@100 15597 *
rob@100 15598 * @param {float} shine degree of shininess
rob@100 15599 *
rob@100 15600 * @returns none
rob@100 15601 */
rob@100 15602 Drawing2D.prototype.shininess = DrawingShared.prototype.a3DOnlyFunction;
rob@100 15603
rob@100 15604 Drawing3D.prototype.shininess = function(shine) {
rob@100 15605 curContext.useProgram(programObject3D);
rob@100 15606 uniformi("uUsingMat3d", programObject3D, "uUsingMat", true);
rob@100 15607 uniformf("uShininess3d", programObject3D, "uShininess", shine);
rob@100 15608 };
rob@100 15609
rob@100 15610 /**
rob@100 15611 * Sets the specular color of the materials used for shapes drawn to the screen,
rob@100 15612 * which sets the color of hightlights. Specular refers to light which bounces
rob@100 15613 * off a surface in a perferred direction (rather than bouncing in all directions
rob@100 15614 * like a diffuse light). Used in combination with emissive(), ambient(), and
rob@100 15615 * shininess() in setting the material properties of shapes.
rob@100 15616 *
rob@100 15617 * Can be called in the following ways:
rob@100 15618 *
rob@100 15619 * specular(gray)
rob@100 15620 * @param {int | float} gray number specifying value between white and black
rob@100 15621 *
rob@100 15622 * specular(gray, alpha)
rob@100 15623 * @param {int | float} gray number specifying value between white and black
rob@100 15624 * @param {int | float} alpha opacity
rob@100 15625 *
rob@100 15626 * specular(color)
rob@100 15627 * @param {color} color any value of the color datatype
rob@100 15628 *
rob@100 15629 * specular(v1, v2, v3)
rob@100 15630 * @param {int | float} v1 red or hue value
rob@100 15631 * @param {int | float} v2 green or saturation value
rob@100 15632 * @param {int | float} v3 blue or brightness value
rob@100 15633 *
rob@100 15634 * specular(v1, v2, v3, alpha)
rob@100 15635 * @param {int | float} v1 red or hue value
rob@100 15636 * @param {int | float} v2 green or saturation value
rob@100 15637 * @param {int | float} v3 blue or brightness value
rob@100 15638 * @param {int | float} alpha opacity
rob@100 15639 *
rob@100 15640 * @returns none
rob@100 15641 *
rob@100 15642 * @see ambient
rob@100 15643 * @see emissive
rob@100 15644 * @see shininess
rob@100 15645 */
rob@100 15646 Drawing2D.prototype.specular = DrawingShared.prototype.a3DOnlyFunction;
rob@100 15647
rob@100 15648 Drawing3D.prototype.specular = function(v1, v2, v3) {
rob@100 15649 curContext.useProgram(programObject3D);
rob@100 15650 uniformi("uUsingMat3d", programObject3D, "uUsingMat", true);
rob@100 15651 var col = p.color(v1, v2, v3);
rob@100 15652 uniformf("uMaterialSpecular3d", programObject3D, "uMaterialSpecular", p.color.toGLArray(col).slice(0, 3));
rob@100 15653 };
rob@100 15654
rob@100 15655 ////////////////////////////////////////////////////////////////////////////
rob@100 15656 // Coordinates
rob@100 15657 ////////////////////////////////////////////////////////////////////////////
rob@100 15658
rob@100 15659 /**
rob@100 15660 * Takes a three-dimensional X, Y, Z position and returns the X value for
rob@100 15661 * where it will appear on a (two-dimensional) screen.
rob@100 15662 *
rob@100 15663 * @param {int | float} x 3D x coordinate to be mapped
rob@100 15664 * @param {int | float} y 3D y coordinate to be mapped
rob@100 15665 * @param {int | float} z 3D z optional coordinate to be mapped
rob@100 15666 *
rob@100 15667 * @returns {float}
rob@100 15668 *
rob@100 15669 * @see screenY
rob@100 15670 * @see screenZ
rob@100 15671 */
rob@100 15672 p.screenX = function( x, y, z ) {
rob@100 15673 var mv = modelView.array();
rob@100 15674 if( mv.length === 16 )
rob@100 15675 {
rob@100 15676 var ax = mv[ 0]*x + mv[ 1]*y + mv[ 2]*z + mv[ 3];
rob@100 15677 var ay = mv[ 4]*x + mv[ 5]*y + mv[ 6]*z + mv[ 7];
rob@100 15678 var az = mv[ 8]*x + mv[ 9]*y + mv[10]*z + mv[11];
rob@100 15679 var aw = mv[12]*x + mv[13]*y + mv[14]*z + mv[15];
rob@100 15680
rob@100 15681 var pj = projection.array();
rob@100 15682
rob@100 15683 var ox = pj[ 0]*ax + pj[ 1]*ay + pj[ 2]*az + pj[ 3]*aw;
rob@100 15684 var ow = pj[12]*ax + pj[13]*ay + pj[14]*az + pj[15]*aw;
rob@100 15685
rob@100 15686 if ( ow !== 0 ){
rob@100 15687 ox /= ow;
rob@100 15688 }
rob@100 15689 return p.width * ( 1 + ox ) / 2.0;
rob@100 15690 }
rob@100 15691 // We assume that we're in 2D
rob@100 15692 return modelView.multX(x, y);
rob@100 15693 };
rob@100 15694
rob@100 15695 /**
rob@100 15696 * Takes a three-dimensional X, Y, Z position and returns the Y value for
rob@100 15697 * where it will appear on a (two-dimensional) screen.
rob@100 15698 *
rob@100 15699 * @param {int | float} x 3D x coordinate to be mapped
rob@100 15700 * @param {int | float} y 3D y coordinate to be mapped
rob@100 15701 * @param {int | float} z 3D z optional coordinate to be mapped
rob@100 15702 *
rob@100 15703 * @returns {float}
rob@100 15704 *
rob@100 15705 * @see screenX
rob@100 15706 * @see screenZ
rob@100 15707 */
rob@100 15708 p.screenY = function screenY( x, y, z ) {
rob@100 15709 var mv = modelView.array();
rob@100 15710 if( mv.length === 16 ) {
rob@100 15711 var ax = mv[ 0]*x + mv[ 1]*y + mv[ 2]*z + mv[ 3];
rob@100 15712 var ay = mv[ 4]*x + mv[ 5]*y + mv[ 6]*z + mv[ 7];
rob@100 15713 var az = mv[ 8]*x + mv[ 9]*y + mv[10]*z + mv[11];
rob@100 15714 var aw = mv[12]*x + mv[13]*y + mv[14]*z + mv[15];
rob@100 15715
rob@100 15716 var pj = projection.array();
rob@100 15717
rob@100 15718 var oy = pj[ 4]*ax + pj[ 5]*ay + pj[ 6]*az + pj[ 7]*aw;
rob@100 15719 var ow = pj[12]*ax + pj[13]*ay + pj[14]*az + pj[15]*aw;
rob@100 15720
rob@100 15721 if ( ow !== 0 ){
rob@100 15722 oy /= ow;
rob@100 15723 }
rob@100 15724 return p.height * ( 1 + oy ) / 2.0;
rob@100 15725 }
rob@100 15726 // We assume that we're in 2D
rob@100 15727 return modelView.multY(x, y);
rob@100 15728 };
rob@100 15729
rob@100 15730 /**
rob@100 15731 * Takes a three-dimensional X, Y, Z position and returns the Z value for
rob@100 15732 * where it will appear on a (two-dimensional) screen.
rob@100 15733 *
rob@100 15734 * @param {int | float} x 3D x coordinate to be mapped
rob@100 15735 * @param {int | float} y 3D y coordinate to be mapped
rob@100 15736 * @param {int | float} z 3D z coordinate to be mapped
rob@100 15737 *
rob@100 15738 * @returns {float}
rob@100 15739 *
rob@100 15740 * @see screenX
rob@100 15741 * @see screenY
rob@100 15742 */
rob@100 15743 p.screenZ = function screenZ( x, y, z ) {
rob@100 15744 var mv = modelView.array();
rob@100 15745 if( mv.length !== 16 ) {
rob@100 15746 return 0;
rob@100 15747 }
rob@100 15748
rob@100 15749 var pj = projection.array();
rob@100 15750
rob@100 15751 var ax = mv[ 0]*x + mv[ 1]*y + mv[ 2]*z + mv[ 3];
rob@100 15752 var ay = mv[ 4]*x + mv[ 5]*y + mv[ 6]*z + mv[ 7];
rob@100 15753 var az = mv[ 8]*x + mv[ 9]*y + mv[10]*z + mv[11];
rob@100 15754 var aw = mv[12]*x + mv[13]*y + mv[14]*z + mv[15];
rob@100 15755
rob@100 15756 var oz = pj[ 8]*ax + pj[ 9]*ay + pj[10]*az + pj[11]*aw;
rob@100 15757 var ow = pj[12]*ax + pj[13]*ay + pj[14]*az + pj[15]*aw;
rob@100 15758
rob@100 15759 if ( ow !== 0 ) {
rob@100 15760 oz /= ow;
rob@100 15761 }
rob@100 15762 return ( oz + 1 ) / 2.0;
rob@100 15763 };
rob@100 15764
rob@100 15765 ////////////////////////////////////////////////////////////////////////////
rob@100 15766 // Style functions
rob@100 15767 ////////////////////////////////////////////////////////////////////////////
rob@100 15768 /**
rob@100 15769 * The fill() function sets the color used to fill shapes. For example, if you run <b>fill(204, 102, 0)</b>, all subsequent shapes will be filled with orange.
rob@100 15770 * This color is either specified in terms of the RGB or HSB color depending on the current <b>colorMode()</b>
rob@100 15771 *(the default color space is RGB, with each value in the range from 0 to 255).
rob@100 15772 * <br><br>When using hexadecimal notation to specify a color, use "#" or "0x" before the values (e.g. #CCFFAA, 0xFFCCFFAA).
rob@100 15773 * The # syntax uses six digits to specify a color (the way colors are specified in HTML and CSS). When using the hexadecimal notation starting with "0x",
rob@100 15774 * the hexadecimal value must be specified with eight characters; the first two characters define the alpha component and the remainder the red, green, and blue components.
rob@100 15775 * <br><br>The value for the parameter "gray" must be less than or equal to the current maximum value as specified by <b>colorMode()</b>. The default maximum value is 255.
rob@100 15776 * <br><br>To change the color of an image (or a texture), use tint().
rob@100 15777 *
rob@100 15778 * @param {int|float} gray number specifying value between white and black
rob@100 15779 * @param {int|float} value1 red or hue value
rob@100 15780 * @param {int|float} value2 green or saturation value
rob@100 15781 * @param {int|float} value3 blue or brightness value
rob@100 15782 * @param {int|float} alpha opacity of the fill
rob@100 15783 * @param {Color} color any value of the color datatype
rob@100 15784 * @param {int} hex color value in hexadecimal notation (i.e. #FFCC00 or 0xFFFFCC00)
rob@100 15785 *
rob@100 15786 * @see #noFill()
rob@100 15787 * @see #stroke()
rob@100 15788 * @see #tint()
rob@100 15789 * @see #background()
rob@100 15790 * @see #colorMode()
rob@100 15791 */
rob@100 15792 DrawingShared.prototype.fill = function() {
rob@100 15793 var color = p.color.apply(this, arguments);
rob@100 15794 if(color === currentFillColor && doFill) {
rob@100 15795 return;
rob@100 15796 }
rob@100 15797 doFill = true;
rob@100 15798 currentFillColor = color;
rob@100 15799 };
rob@100 15800
rob@100 15801 Drawing2D.prototype.fill = function() {
rob@100 15802 DrawingShared.prototype.fill.apply(this, arguments);
rob@100 15803 isFillDirty = true;
rob@100 15804 };
rob@100 15805
rob@100 15806 Drawing3D.prototype.fill = function() {
rob@100 15807 DrawingShared.prototype.fill.apply(this, arguments);
rob@100 15808 fillStyle = p.color.toGLArray(currentFillColor);
rob@100 15809 };
rob@100 15810
rob@100 15811 function executeContextFill() {
rob@100 15812 if(doFill) {
rob@100 15813 if(isFillDirty) {
rob@100 15814 curContext.fillStyle = p.color.toString(currentFillColor);
rob@100 15815 isFillDirty = false;
rob@100 15816 }
rob@100 15817 curContext.fill();
rob@100 15818 }
rob@100 15819 }
rob@100 15820
rob@100 15821 /**
rob@100 15822 * The noFill() function disables filling geometry. If both <b>noStroke()</b> and <b>noFill()</b>
rob@100 15823 * are called, no shapes will be drawn to the screen.
rob@100 15824 *
rob@100 15825 * @see #fill()
rob@100 15826 *
rob@100 15827 */
rob@100 15828 p.noFill = function() {
rob@100 15829 doFill = false;
rob@100 15830 };
rob@100 15831
rob@100 15832 /**
rob@100 15833 * The stroke() function sets the color used to draw lines and borders around shapes. This color
rob@100 15834 * is either specified in terms of the RGB or HSB color depending on the
rob@100 15835 * current <b>colorMode()</b> (the default color space is RGB, with each
rob@100 15836 * value in the range from 0 to 255).
rob@100 15837 * <br><br>When using hexadecimal notation to specify a color, use "#" or
rob@100 15838 * "0x" before the values (e.g. #CCFFAA, 0xFFCCFFAA). The # syntax uses six
rob@100 15839 * digits to specify a color (the way colors are specified in HTML and CSS).
rob@100 15840 * When using the hexadecimal notation starting with "0x", the hexadecimal
rob@100 15841 * value must be specified with eight characters; the first two characters
rob@100 15842 * define the alpha component and the remainder the red, green, and blue
rob@100 15843 * components.
rob@100 15844 * <br><br>The value for the parameter "gray" must be less than or equal
rob@100 15845 * to the current maximum value as specified by <b>colorMode()</b>.
rob@100 15846 * The default maximum value is 255.
rob@100 15847 *
rob@100 15848 * @param {int|float} gray number specifying value between white and black
rob@100 15849 * @param {int|float} value1 red or hue value
rob@100 15850 * @param {int|float} value2 green or saturation value
rob@100 15851 * @param {int|float} value3 blue or brightness value
rob@100 15852 * @param {int|float} alpha opacity of the stroke
rob@100 15853 * @param {Color} color any value of the color datatype
rob@100 15854 * @param {int} hex color value in hexadecimal notation (i.e. #FFCC00 or 0xFFFFCC00)
rob@100 15855 *
rob@100 15856 * @see #fill()
rob@100 15857 * @see #noStroke()
rob@100 15858 * @see #tint()
rob@100 15859 * @see #background()
rob@100 15860 * @see #colorMode()
rob@100 15861 */
rob@100 15862 DrawingShared.prototype.stroke = function() {
rob@100 15863 var color = p.color.apply(this, arguments);
rob@100 15864 if(color === currentStrokeColor && doStroke) {
rob@100 15865 return;
rob@100 15866 }
rob@100 15867 doStroke = true;
rob@100 15868 currentStrokeColor = color;
rob@100 15869 };
rob@100 15870
rob@100 15871 Drawing2D.prototype.stroke = function() {
rob@100 15872 DrawingShared.prototype.stroke.apply(this, arguments);
rob@100 15873 isStrokeDirty = true;
rob@100 15874 };
rob@100 15875
rob@100 15876 Drawing3D.prototype.stroke = function() {
rob@100 15877 DrawingShared.prototype.stroke.apply(this, arguments);
rob@100 15878 strokeStyle = p.color.toGLArray(currentStrokeColor);
rob@100 15879 };
rob@100 15880
rob@100 15881 function executeContextStroke() {
rob@100 15882 if(doStroke) {
rob@100 15883 if(isStrokeDirty) {
rob@100 15884 curContext.strokeStyle = p.color.toString(currentStrokeColor);
rob@100 15885 isStrokeDirty = false;
rob@100 15886 }
rob@100 15887 curContext.stroke();
rob@100 15888 }
rob@100 15889 }
rob@100 15890
rob@100 15891 /**
rob@100 15892 * The noStroke() function disables drawing the stroke (outline). If both <b>noStroke()</b> and
rob@100 15893 * <b>noFill()</b> are called, no shapes will be drawn to the screen.
rob@100 15894 *
rob@100 15895 * @see #stroke()
rob@100 15896 */
rob@100 15897 p.noStroke = function() {
rob@100 15898 doStroke = false;
rob@100 15899 };
rob@100 15900
rob@100 15901 /**
rob@100 15902 * The strokeWeight() function sets the width of the stroke used for lines, points, and the border around shapes.
rob@100 15903 * All widths are set in units of pixels.
rob@100 15904 *
rob@100 15905 * @param {int|float} w the weight (in pixels) of the stroke
rob@100 15906 */
rob@100 15907 DrawingShared.prototype.strokeWeight = function(w) {
rob@100 15908 lineWidth = w;
rob@100 15909 };
rob@100 15910
rob@100 15911 Drawing2D.prototype.strokeWeight = function(w) {
rob@100 15912 DrawingShared.prototype.strokeWeight.apply(this, arguments);
rob@100 15913 curContext.lineWidth = w;
rob@100 15914 };
rob@100 15915
rob@100 15916 Drawing3D.prototype.strokeWeight = function(w) {
rob@100 15917 DrawingShared.prototype.strokeWeight.apply(this, arguments);
rob@100 15918
rob@100 15919 // Processing groups the weight of points and lines under this one function,
rob@100 15920 // but for WebGL, we need to set a uniform for points and call a function for line.
rob@100 15921
rob@100 15922 curContext.useProgram(programObject2D);
rob@100 15923 uniformf("pointSize2d", programObject2D, "uPointSize", w);
rob@100 15924
rob@100 15925 curContext.useProgram(programObjectUnlitShape);
rob@100 15926 uniformf("pointSizeUnlitShape", programObjectUnlitShape, "uPointSize", w);
rob@100 15927
rob@100 15928 curContext.lineWidth(w);
rob@100 15929 };
rob@100 15930
rob@100 15931 /**
rob@100 15932 * The strokeCap() function sets the style for rendering line endings. These ends are either squared, extended, or rounded and
rob@100 15933 * specified with the corresponding parameters SQUARE, PROJECT, and ROUND. The default cap is ROUND.
rob@100 15934 * This function is not available with the P2D, P3D, or OPENGL renderers
rob@100 15935 *
rob@100 15936 * @param {int} value Either SQUARE, PROJECT, or ROUND
rob@100 15937 */
rob@100 15938 p.strokeCap = function(value) {
rob@100 15939 drawing.$ensureContext().lineCap = value;
rob@100 15940 };
rob@100 15941
rob@100 15942 /**
rob@100 15943 * The strokeJoin() function sets the style of the joints which connect line segments.
rob@100 15944 * These joints are either mitered, beveled, or rounded and specified with the corresponding parameters MITER, BEVEL, and ROUND. The default joint is MITER.
rob@100 15945 * This function is not available with the P2D, P3D, or OPENGL renderers
rob@100 15946 *
rob@100 15947 * @param {int} value Either SQUARE, PROJECT, or ROUND
rob@100 15948 */
rob@100 15949 p.strokeJoin = function(value) {
rob@100 15950 drawing.$ensureContext().lineJoin = value;
rob@100 15951 };
rob@100 15952
rob@100 15953 /**
rob@100 15954 * The smooth() function draws all geometry with smooth (anti-aliased) edges. This will slow down the frame rate of the application,
rob@100 15955 * but will enhance the visual refinement. <br/><br/>
rob@100 15956 * Note that smooth() will also improve image quality of resized images, and noSmooth() will disable image (and font) smoothing altogether.
rob@100 15957 * When working with a 3D sketch, smooth will draw points as circles rather than squares.
rob@100 15958 *
rob@100 15959 * @see #noSmooth()
rob@100 15960 * @see #hint()
rob@100 15961 * @see #size()
rob@100 15962 */
rob@100 15963
rob@100 15964 Drawing2D.prototype.smooth = function() {
rob@100 15965 renderSmooth = true;
rob@100 15966 var style = curElement.style;
rob@100 15967 style.setProperty("image-rendering", "optimizeQuality", "important");
rob@100 15968 style.setProperty("-ms-interpolation-mode", "bicubic", "important");
rob@100 15969 if (curContext.hasOwnProperty("mozImageSmoothingEnabled")) {
rob@100 15970 curContext.mozImageSmoothingEnabled = true;
rob@100 15971 }
rob@100 15972 };
rob@100 15973
rob@100 15974 Drawing3D.prototype.smooth = function(){
rob@100 15975 renderSmooth = true;
rob@100 15976 };
rob@100 15977
rob@100 15978 /**
rob@100 15979 * The noSmooth() function draws all geometry with jagged (aliased) edges.
rob@100 15980 *
rob@100 15981 * @see #smooth()
rob@100 15982 */
rob@100 15983
rob@100 15984 Drawing2D.prototype.noSmooth = function() {
rob@100 15985 renderSmooth = false;
rob@100 15986 var style = curElement.style;
rob@100 15987 style.setProperty("image-rendering", "optimizeSpeed", "important");
rob@100 15988 style.setProperty("image-rendering", "-moz-crisp-edges", "important");
rob@100 15989 style.setProperty("image-rendering", "-webkit-optimize-contrast", "important");
rob@100 15990 style.setProperty("image-rendering", "optimize-contrast", "important");
rob@100 15991 style.setProperty("-ms-interpolation-mode", "nearest-neighbor", "important");
rob@100 15992 if (curContext.hasOwnProperty("mozImageSmoothingEnabled")) {
rob@100 15993 curContext.mozImageSmoothingEnabled = false;
rob@100 15994 }
rob@100 15995 };
rob@100 15996
rob@100 15997 Drawing3D.prototype.noSmooth = function(){
rob@100 15998 renderSmooth = false;
rob@100 15999 };
rob@100 16000
rob@100 16001 ////////////////////////////////////////////////////////////////////////////
rob@100 16002 // Vector drawing functions
rob@100 16003 ////////////////////////////////////////////////////////////////////////////
rob@100 16004 /**
rob@100 16005 * The point() function draws a point, a coordinate in space at the dimension of one pixel.
rob@100 16006 * The first parameter is the horizontal value for the point, the second
rob@100 16007 * value is the vertical value for the point, and the optional third value
rob@100 16008 * is the depth value. Drawing this shape in 3D using the <b>z</b>
rob@100 16009 * parameter requires the P3D or OPENGL parameter in combination with
rob@100 16010 * size as shown in the above example.
rob@100 16011 *
rob@100 16012 * @param {int|float} x x-coordinate of the point
rob@100 16013 * @param {int|float} y y-coordinate of the point
rob@100 16014 * @param {int|float} z z-coordinate of the point
rob@100 16015 *
rob@100 16016 * @see #beginShape()
rob@100 16017 */
rob@100 16018 Drawing2D.prototype.point = function(x, y) {
rob@100 16019 if (!doStroke) {
rob@100 16020 return;
rob@100 16021 }
rob@100 16022
rob@100 16023 x = Math.round(x);
rob@100 16024 y = Math.round(y);
rob@100 16025 curContext.fillStyle = p.color.toString(currentStrokeColor);
rob@100 16026 isFillDirty = true;
rob@100 16027 // Draw a circle for any point larger than 1px
rob@100 16028 if (lineWidth > 1) {
rob@100 16029 curContext.beginPath();
rob@100 16030 curContext.arc(x, y, lineWidth / 2, 0, PConstants.TWO_PI, false);
rob@100 16031 curContext.fill();
rob@100 16032 } else {
rob@100 16033 curContext.fillRect(x, y, 1, 1);
rob@100 16034 }
rob@100 16035 };
rob@100 16036
rob@100 16037 Drawing3D.prototype.point = function(x, y, z) {
rob@100 16038 var model = new PMatrix3D();
rob@100 16039
rob@100 16040 // move point to position
rob@100 16041 model.translate(x, y, z || 0);
rob@100 16042 model.transpose();
rob@100 16043
rob@100 16044 var view = new PMatrix3D();
rob@100 16045 view.scale(1, -1, 1);
rob@100 16046 view.apply(modelView.array());
rob@100 16047 view.transpose();
rob@100 16048
rob@100 16049 curContext.useProgram(programObject2D);
rob@100 16050 uniformMatrix("uModel2d", programObject2D, "uModel", false, model.array());
rob@100 16051 uniformMatrix("uView2d", programObject2D, "uView", false, view.array());
rob@100 16052
rob@100 16053 if (lineWidth > 0 && doStroke) {
rob@100 16054 // this will be replaced with the new bit shifting color code
rob@100 16055 uniformf("uColor2d", programObject2D, "uColor", strokeStyle);
rob@100 16056 uniformi("uIsDrawingText2d", programObject2D, "uIsDrawingText", false);
rob@100 16057 uniformi("uSmooth2d", programObject2D, "uSmooth", renderSmooth);
rob@100 16058 vertexAttribPointer("aVertex2d", programObject2D, "aVertex", 3, pointBuffer);
rob@100 16059 disableVertexAttribPointer("aTextureCoord2d", programObject2D, "aTextureCoord");
rob@100 16060 curContext.drawArrays(curContext.POINTS, 0, 1);
rob@100 16061 }
rob@100 16062 };
rob@100 16063
rob@100 16064 /**
rob@100 16065 * Using the <b>beginShape()</b> and <b>endShape()</b> functions allow creating more complex forms.
rob@100 16066 * <b>beginShape()</b> begins recording vertices for a shape and <b>endShape()</b> stops recording.
rob@100 16067 * The value of the <b>MODE</b> parameter tells it which types of shapes to create from the provided vertices.
rob@100 16068 * With no mode specified, the shape can be any irregular polygon. After calling the <b>beginShape()</b> function,
rob@100 16069 * a series of <b>vertex()</b> commands must follow. To stop drawing the shape, call <b>endShape()</b>.
rob@100 16070 * The <b>vertex()</b> function with two parameters specifies a position in 2D and the <b>vertex()</b>
rob@100 16071 * function with three parameters specifies a position in 3D. Each shape will be outlined with the current
rob@100 16072 * stroke color and filled with the fill color.
rob@100 16073 *
rob@100 16074 * @param {int} MODE either POINTS, LINES, TRIANGLES, TRIANGLE_FAN, TRIANGLE_STRIP, QUADS, and QUAD_STRIP.
rob@100 16075 *
rob@100 16076 * @see endShape
rob@100 16077 * @see vertex
rob@100 16078 * @see curveVertex
rob@100 16079 * @see bezierVertex
rob@100 16080 */
rob@100 16081 p.beginShape = function(type) {
rob@100 16082 curShape = type;
rob@100 16083 curvePoints = [];
rob@100 16084 vertArray = [];
rob@100 16085 };
rob@100 16086
rob@100 16087 /**
rob@100 16088 * All shapes are constructed by connecting a series of vertices. <b>vertex()</b> is used to specify the vertex
rob@100 16089 * coordinates for points, lines, triangles, quads, and polygons and is used exclusively within the <b>beginShape()</b>
rob@100 16090 * and <b>endShape()</b> function. <br /><br />Drawing a vertex in 3D using the <b>z</b> parameter requires the P3D or
rob@100 16091 * OPENGL parameter in combination with size as shown in the above example.<br /><br />This function is also used to map a
rob@100 16092 * texture onto the geometry. The <b>texture()</b> function declares the texture to apply to the geometry and the <b>u</b>
rob@100 16093 * and <b>v</b> coordinates set define the mapping of this texture to the form. By default, the coordinates used for
rob@100 16094 * <b>u</b> and <b>v</b> are specified in relation to the image's size in pixels, but this relation can be changed with
rob@100 16095 * <b>textureMode()</b>.
rob@100 16096 *
rob@100 16097 * @param {int | float} x x-coordinate of the vertex
rob@100 16098 * @param {int | float} y y-coordinate of the vertex
rob@100 16099 * @param {boolean} moveto flag to indicate whether this is a new subpath
rob@100 16100 *
rob@100 16101 * @see beginShape
rob@100 16102 * @see endShape
rob@100 16103 * @see bezierVertex
rob@100 16104 * @see curveVertex
rob@100 16105 * @see texture
rob@100 16106 */
rob@100 16107
rob@100 16108 Drawing2D.prototype.vertex = function(x, y, moveTo) {
rob@100 16109 var vert = [];
rob@100 16110
rob@100 16111 if (firstVert) { firstVert = false; }
rob@100 16112 vert.isVert = true;
rob@100 16113
rob@100 16114 vert[0] = x;
rob@100 16115 vert[1] = y;
rob@100 16116 vert[2] = 0;
rob@100 16117 vert[3] = 0;
rob@100 16118 vert[4] = 0;
rob@100 16119
rob@100 16120 // fill and stroke color
rob@100 16121 vert[5] = currentFillColor;
rob@100 16122 vert[6] = currentStrokeColor;
rob@100 16123
rob@100 16124 vertArray.push(vert);
rob@100 16125 if (moveTo) {
rob@100 16126 vertArray[vertArray.length-1].moveTo = moveTo;
rob@100 16127 }
rob@100 16128 };
rob@100 16129
rob@100 16130 Drawing3D.prototype.vertex = function(x, y, z, u, v) {
rob@100 16131 var vert = [];
rob@100 16132
rob@100 16133 if (firstVert) { firstVert = false; }
rob@100 16134 vert.isVert = true;
rob@100 16135
rob@100 16136 if (v === undef && usingTexture) {
rob@100 16137 v = u;
rob@100 16138 u = z;
rob@100 16139 z = 0;
rob@100 16140 }
rob@100 16141
rob@100 16142 // Convert u and v to normalized coordinates
rob@100 16143 if (u !== undef && v !== undef) {
rob@100 16144 if (curTextureMode === PConstants.IMAGE) {
rob@100 16145 u /= curTexture.width;
rob@100 16146 v /= curTexture.height;
rob@100 16147 }
rob@100 16148 u = u > 1 ? 1 : u;
rob@100 16149 u = u < 0 ? 0 : u;
rob@100 16150 v = v > 1 ? 1 : v;
rob@100 16151 v = v < 0 ? 0 : v;
rob@100 16152 }
rob@100 16153
rob@100 16154 vert[0] = x;
rob@100 16155 vert[1] = y;
rob@100 16156 vert[2] = z || 0;
rob@100 16157 vert[3] = u || 0;
rob@100 16158 vert[4] = v || 0;
rob@100 16159
rob@100 16160 // fill rgba
rob@100 16161 vert[5] = fillStyle[0];
rob@100 16162 vert[6] = fillStyle[1];
rob@100 16163 vert[7] = fillStyle[2];
rob@100 16164 vert[8] = fillStyle[3];
rob@100 16165 // stroke rgba
rob@100 16166 vert[9] = strokeStyle[0];
rob@100 16167 vert[10] = strokeStyle[1];
rob@100 16168 vert[11] = strokeStyle[2];
rob@100 16169 vert[12] = strokeStyle[3];
rob@100 16170 //normals
rob@100 16171 vert[13] = normalX;
rob@100 16172 vert[14] = normalY;
rob@100 16173 vert[15] = normalZ;
rob@100 16174
rob@100 16175 vertArray.push(vert);
rob@100 16176 };
rob@100 16177
rob@100 16178 /**
rob@100 16179 * @private
rob@100 16180 * Renders 3D points created from calls to vertex and beginShape/endShape
rob@100 16181 *
rob@100 16182 * @param {Array} vArray an array of vertex coordinate
rob@100 16183 * @param {Array} cArray an array of colours used for the vertices
rob@100 16184 *
rob@100 16185 * @see beginShape
rob@100 16186 * @see endShape
rob@100 16187 * @see vertex
rob@100 16188 */
rob@100 16189 var point3D = function(vArray, cArray){
rob@100 16190 var view = new PMatrix3D();
rob@100 16191 view.scale(1, -1, 1);
rob@100 16192 view.apply(modelView.array());
rob@100 16193 view.transpose();
rob@100 16194
rob@100 16195 curContext.useProgram(programObjectUnlitShape);
rob@100 16196
rob@100 16197 uniformMatrix("uViewUS", programObjectUnlitShape, "uView", false, view.array());
rob@100 16198 uniformi("uSmoothUS", programObjectUnlitShape, "uSmooth", renderSmooth);
rob@100 16199
rob@100 16200 vertexAttribPointer("aVertexUS", programObjectUnlitShape, "aVertex", 3, pointBuffer);
rob@100 16201 curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array(vArray), curContext.STREAM_DRAW);
rob@100 16202
rob@100 16203 vertexAttribPointer("aColorUS", programObjectUnlitShape, "aColor", 4, fillColorBuffer);
rob@100 16204 curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array(cArray), curContext.STREAM_DRAW);
rob@100 16205
rob@100 16206 curContext.drawArrays(curContext.POINTS, 0, vArray.length/3);
rob@100 16207 };
rob@100 16208
rob@100 16209 /**
rob@100 16210 * @private
rob@100 16211 * Renders 3D lines created from calls to beginShape/vertex/endShape - based on the mode specified LINES, LINE_LOOP, etc.
rob@100 16212 *
rob@100 16213 * @param {Array} vArray an array of vertex coordinate
rob@100 16214 * @param {String} mode either LINES, LINE_LOOP, or LINE_STRIP
rob@100 16215 * @param {Array} cArray an array of colours used for the vertices
rob@100 16216 *
rob@100 16217 * @see beginShape
rob@100 16218 * @see endShape
rob@100 16219 * @see vertex
rob@100 16220 */
rob@100 16221 var line3D = function(vArray, mode, cArray){
rob@100 16222 var ctxMode;
rob@100 16223 if (mode === "LINES"){
rob@100 16224 ctxMode = curContext.LINES;
rob@100 16225 }
rob@100 16226 else if(mode === "LINE_LOOP"){
rob@100 16227 ctxMode = curContext.LINE_LOOP;
rob@100 16228 }
rob@100 16229 else{
rob@100 16230 ctxMode = curContext.LINE_STRIP;
rob@100 16231 }
rob@100 16232
rob@100 16233 var view = new PMatrix3D();
rob@100 16234 view.scale(1, -1, 1);
rob@100 16235 view.apply(modelView.array());
rob@100 16236 view.transpose();
rob@100 16237
rob@100 16238 curContext.useProgram(programObjectUnlitShape);
rob@100 16239 uniformMatrix("uViewUS", programObjectUnlitShape, "uView", false, view.array());
rob@100 16240 vertexAttribPointer("aVertexUS", programObjectUnlitShape, "aVertex", 3, lineBuffer);
rob@100 16241 curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array(vArray), curContext.STREAM_DRAW);
rob@100 16242 vertexAttribPointer("aColorUS", programObjectUnlitShape, "aColor", 4, strokeColorBuffer);
rob@100 16243 curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array(cArray), curContext.STREAM_DRAW);
rob@100 16244 curContext.drawArrays(ctxMode, 0, vArray.length/3);
rob@100 16245 };
rob@100 16246
rob@100 16247 /**
rob@100 16248 * @private
rob@100 16249 * Render filled shapes created from calls to beginShape/vertex/endShape - based on the mode specified TRIANGLES, etc.
rob@100 16250 *
rob@100 16251 * @param {Array} vArray an array of vertex coordinate
rob@100 16252 * @param {String} mode either LINES, LINE_LOOP, or LINE_STRIP
rob@100 16253 * @param {Array} cArray an array of colours used for the vertices
rob@100 16254 * @param {Array} tArray an array of u,v coordinates for textures
rob@100 16255 *
rob@100 16256 * @see beginShape
rob@100 16257 * @see endShape
rob@100 16258 * @see vertex
rob@100 16259 */
rob@100 16260 var fill3D = function(vArray, mode, cArray, tArray){
rob@100 16261 var ctxMode;
rob@100 16262 if (mode === "TRIANGLES") {
rob@100 16263 ctxMode = curContext.TRIANGLES;
rob@100 16264 } else if(mode === "TRIANGLE_FAN") {
rob@100 16265 ctxMode = curContext.TRIANGLE_FAN;
rob@100 16266 } else {
rob@100 16267 ctxMode = curContext.TRIANGLE_STRIP;
rob@100 16268 }
rob@100 16269
rob@100 16270 var view = new PMatrix3D();
rob@100 16271 view.scale( 1, -1, 1 );
rob@100 16272 view.apply( modelView.array() );
rob@100 16273 view.transpose();
rob@100 16274
rob@100 16275 curContext.useProgram( programObject3D );
rob@100 16276 uniformMatrix( "model3d", programObject3D, "uModel", false, [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1] );
rob@100 16277 uniformMatrix( "view3d", programObject3D, "uView", false, view.array() );
rob@100 16278 curContext.enable( curContext.POLYGON_OFFSET_FILL );
rob@100 16279 curContext.polygonOffset( 1, 1 );
rob@100 16280 uniformf( "color3d", programObject3D, "uColor", [-1,0,0,0] );
rob@100 16281 vertexAttribPointer( "vertex3d", programObject3D, "aVertex", 3, fillBuffer );
rob@100 16282 curContext.bufferData( curContext.ARRAY_BUFFER, new Float32Array(vArray), curContext.STREAM_DRAW );
rob@100 16283
rob@100 16284 // if we are using a texture and a tint, then overwrite the
rob@100 16285 // contents of the color buffer with the current tint
rob@100 16286 if ( usingTexture && curTint !== null ){
rob@100 16287 curTint3d( cArray );
rob@100 16288 }
rob@100 16289
rob@100 16290 vertexAttribPointer( "aColor3d", programObject3D, "aColor", 4, fillColorBuffer );
rob@100 16291 curContext.bufferData( curContext.ARRAY_BUFFER, new Float32Array(cArray), curContext.STREAM_DRAW );
rob@100 16292
rob@100 16293 // No support for lights....yet
rob@100 16294 disableVertexAttribPointer( "aNormal3d", programObject3D, "aNormal" );
rob@100 16295
rob@100 16296 if ( usingTexture ) {
rob@100 16297 uniformi( "uUsingTexture3d", programObject3D, "uUsingTexture", usingTexture );
rob@100 16298 vertexAttribPointer( "aTexture3d", programObject3D, "aTexture", 2, shapeTexVBO );
rob@100 16299 curContext.bufferData( curContext.ARRAY_BUFFER, new Float32Array(tArray), curContext.STREAM_DRAW );
rob@100 16300 }
rob@100 16301
rob@100 16302 curContext.drawArrays( ctxMode, 0, vArray.length/3 );
rob@100 16303 curContext.disable( curContext.POLYGON_OFFSET_FILL );
rob@100 16304 };
rob@100 16305
rob@100 16306 /**
rob@100 16307 * this series of three operations is used a lot in Drawing2D.prototype.endShape
rob@100 16308 * and has been split off as its own function, to tighten the code and allow for
rob@100 16309 * fewer bugs.
rob@100 16310 */
rob@100 16311 function fillStrokeClose() {
rob@100 16312 executeContextFill();
rob@100 16313 executeContextStroke();
rob@100 16314 curContext.closePath();
rob@100 16315 }
rob@100 16316
rob@100 16317 /**
rob@100 16318 * The endShape() function is the companion to beginShape() and may only be called after beginShape().
rob@100 16319 * When endshape() is called, all of image data defined since the previous call to beginShape() is written
rob@100 16320 * into the image buffer.
rob@100 16321 *
rob@100 16322 * @param {int} MODE Use CLOSE to close the shape
rob@100 16323 *
rob@100 16324 * @see beginShape
rob@100 16325 */
rob@100 16326 Drawing2D.prototype.endShape = function(mode) {
rob@100 16327 // Duplicated in Drawing3D; too many variables used
rob@100 16328 if (vertArray.length === 0) { return; }
rob@100 16329
rob@100 16330 var closeShape = mode === PConstants.CLOSE;
rob@100 16331
rob@100 16332 // if the shape is closed, the first element is also the last element
rob@100 16333 if (closeShape) {
rob@100 16334 vertArray.push(vertArray[0]);
rob@100 16335 }
rob@100 16336
rob@100 16337 var lineVertArray = [];
rob@100 16338 var fillVertArray = [];
rob@100 16339 var colorVertArray = [];
rob@100 16340 var strokeVertArray = [];
rob@100 16341 var texVertArray = [];
rob@100 16342 var cachedVertArray;
rob@100 16343
rob@100 16344 firstVert = true;
rob@100 16345 var i, j, k;
rob@100 16346 var vertArrayLength = vertArray.length;
rob@100 16347
rob@100 16348 for (i = 0; i < vertArrayLength; i++) {
rob@100 16349 cachedVertArray = vertArray[i];
rob@100 16350 for (j = 0; j < 3; j++) {
rob@100 16351 fillVertArray.push(cachedVertArray[j]);
rob@100 16352 }
rob@100 16353 }
rob@100 16354
rob@100 16355 // 5,6,7,8
rob@100 16356 // R,G,B,A - fill colour
rob@100 16357 for (i = 0; i < vertArrayLength; i++) {
rob@100 16358 cachedVertArray = vertArray[i];
rob@100 16359 for (j = 5; j < 9; j++) {
rob@100 16360 colorVertArray.push(cachedVertArray[j]);
rob@100 16361 }
rob@100 16362 }
rob@100 16363
rob@100 16364 // 9,10,11,12
rob@100 16365 // R, G, B, A - stroke colour
rob@100 16366 for (i = 0; i < vertArrayLength; i++) {
rob@100 16367 cachedVertArray = vertArray[i];
rob@100 16368 for (j = 9; j < 13; j++) {
rob@100 16369 strokeVertArray.push(cachedVertArray[j]);
rob@100 16370 }
rob@100 16371 }
rob@100 16372
rob@100 16373 // texture u,v
rob@100 16374 for (i = 0; i < vertArrayLength; i++) {
rob@100 16375 cachedVertArray = vertArray[i];
rob@100 16376 texVertArray.push(cachedVertArray[3]);
rob@100 16377 texVertArray.push(cachedVertArray[4]);
rob@100 16378 }
rob@100 16379
rob@100 16380 // curveVertex
rob@100 16381 if ( isCurve && (curShape === PConstants.POLYGON || curShape === undef) ) {
rob@100 16382 if (vertArrayLength > 3) {
rob@100 16383 var b = [],
rob@100 16384 s = 1 - curTightness;
rob@100 16385 curContext.beginPath();
rob@100 16386 curContext.moveTo(vertArray[1][0], vertArray[1][1]);
rob@100 16387 /*
rob@100 16388 * Matrix to convert from Catmull-Rom to cubic Bezier
rob@100 16389 * where t = curTightness
rob@100 16390 * |0 1 0 0 |
rob@100 16391 * |(t-1)/6 1 (1-t)/6 0 |
rob@100 16392 * |0 (1-t)/6 1 (t-1)/6 |
rob@100 16393 * |0 0 0 0 |
rob@100 16394 */
rob@100 16395 for (i = 1; (i+2) < vertArrayLength; i++) {
rob@100 16396 cachedVertArray = vertArray[i];
rob@100 16397 b[0] = [cachedVertArray[0], cachedVertArray[1]];
rob@100 16398 b[1] = [cachedVertArray[0] + (s * vertArray[i+1][0] - s * vertArray[i-1][0]) / 6,
rob@100 16399 cachedVertArray[1] + (s * vertArray[i+1][1] - s * vertArray[i-1][1]) / 6];
rob@100 16400 b[2] = [vertArray[i+1][0] + (s * vertArray[i][0] - s * vertArray[i+2][0]) / 6,
rob@100 16401 vertArray[i+1][1] + (s * vertArray[i][1] - s * vertArray[i+2][1]) / 6];
rob@100 16402 b[3] = [vertArray[i+1][0], vertArray[i+1][1]];
rob@100 16403 curContext.bezierCurveTo(b[1][0], b[1][1], b[2][0], b[2][1], b[3][0], b[3][1]);
rob@100 16404 }
rob@100 16405 fillStrokeClose();
rob@100 16406 }
rob@100 16407 }
rob@100 16408
rob@100 16409 // bezierVertex
rob@100 16410 else if ( isBezier && (curShape === PConstants.POLYGON || curShape === undef) ) {
rob@100 16411 curContext.beginPath();
rob@100 16412 for (i = 0; i < vertArrayLength; i++) {
rob@100 16413 cachedVertArray = vertArray[i];
rob@100 16414 if (vertArray[i].isVert) { //if it is a vertex move to the position
rob@100 16415 if (vertArray[i].moveTo) {
rob@100 16416 curContext.moveTo(cachedVertArray[0], cachedVertArray[1]);
rob@100 16417 } else {
rob@100 16418 curContext.lineTo(cachedVertArray[0], cachedVertArray[1]);
rob@100 16419 }
rob@100 16420 } else { //otherwise continue drawing bezier
rob@100 16421 curContext.bezierCurveTo(vertArray[i][0], vertArray[i][1], vertArray[i][2], vertArray[i][3], vertArray[i][4], vertArray[i][5]);
rob@100 16422 }
rob@100 16423 }
rob@100 16424 fillStrokeClose();
rob@100 16425 }
rob@100 16426
rob@100 16427 // render the vertices provided
rob@100 16428 else {
rob@100 16429 if (curShape === PConstants.POINTS) {
rob@100 16430 for (i = 0; i < vertArrayLength; i++) {
rob@100 16431 cachedVertArray = vertArray[i];
rob@100 16432 if (doStroke) {
rob@100 16433 p.stroke(cachedVertArray[6]);
rob@100 16434 }
rob@100 16435 p.point(cachedVertArray[0], cachedVertArray[1]);
rob@100 16436 }
rob@100 16437 } else if (curShape === PConstants.LINES) {
rob@100 16438 for (i = 0; (i + 1) < vertArrayLength; i+=2) {
rob@100 16439 cachedVertArray = vertArray[i];
rob@100 16440 if (doStroke) {
rob@100 16441 p.stroke(vertArray[i+1][6]);
rob@100 16442 }
rob@100 16443 p.line(cachedVertArray[0], cachedVertArray[1], vertArray[i+1][0], vertArray[i+1][1]);
rob@100 16444 }
rob@100 16445 } else if (curShape === PConstants.TRIANGLES) {
rob@100 16446 for (i = 0; (i + 2) < vertArrayLength; i+=3) {
rob@100 16447 cachedVertArray = vertArray[i];
rob@100 16448 curContext.beginPath();
rob@100 16449 curContext.moveTo(cachedVertArray[0], cachedVertArray[1]);
rob@100 16450 curContext.lineTo(vertArray[i+1][0], vertArray[i+1][1]);
rob@100 16451 curContext.lineTo(vertArray[i+2][0], vertArray[i+2][1]);
rob@100 16452 curContext.lineTo(cachedVertArray[0], cachedVertArray[1]);
rob@100 16453
rob@100 16454 if (doFill) {
rob@100 16455 p.fill(vertArray[i+2][5]);
rob@100 16456 executeContextFill();
rob@100 16457 }
rob@100 16458 if (doStroke) {
rob@100 16459 p.stroke(vertArray[i+2][6]);
rob@100 16460 executeContextStroke();
rob@100 16461 }
rob@100 16462
rob@100 16463 curContext.closePath();
rob@100 16464 }
rob@100 16465 } else if (curShape === PConstants.TRIANGLE_STRIP) {
rob@100 16466 for (i = 0; (i+1) < vertArrayLength; i++) {
rob@100 16467 cachedVertArray = vertArray[i];
rob@100 16468 curContext.beginPath();
rob@100 16469 curContext.moveTo(vertArray[i+1][0], vertArray[i+1][1]);
rob@100 16470 curContext.lineTo(cachedVertArray[0], cachedVertArray[1]);
rob@100 16471
rob@100 16472 if (doStroke) {
rob@100 16473 p.stroke(vertArray[i+1][6]);
rob@100 16474 }
rob@100 16475 if (doFill) {
rob@100 16476 p.fill(vertArray[i+1][5]);
rob@100 16477 }
rob@100 16478
rob@100 16479 if (i + 2 < vertArrayLength) {
rob@100 16480 curContext.lineTo(vertArray[i+2][0], vertArray[i+2][1]);
rob@100 16481 if (doStroke) {
rob@100 16482 p.stroke(vertArray[i+2][6]);
rob@100 16483 }
rob@100 16484 if (doFill) {
rob@100 16485 p.fill(vertArray[i+2][5]);
rob@100 16486 }
rob@100 16487 }
rob@100 16488 fillStrokeClose();
rob@100 16489 }
rob@100 16490 } else if (curShape === PConstants.TRIANGLE_FAN) {
rob@100 16491 if (vertArrayLength > 2) {
rob@100 16492 curContext.beginPath();
rob@100 16493 curContext.moveTo(vertArray[0][0], vertArray[0][1]);
rob@100 16494 curContext.lineTo(vertArray[1][0], vertArray[1][1]);
rob@100 16495 curContext.lineTo(vertArray[2][0], vertArray[2][1]);
rob@100 16496
rob@100 16497 if (doFill) {
rob@100 16498 p.fill(vertArray[2][5]);
rob@100 16499 executeContextFill();
rob@100 16500 }
rob@100 16501 if (doStroke) {
rob@100 16502 p.stroke(vertArray[2][6]);
rob@100 16503 executeContextStroke();
rob@100 16504 }
rob@100 16505
rob@100 16506 curContext.closePath();
rob@100 16507 for (i = 3; i < vertArrayLength; i++) {
rob@100 16508 cachedVertArray = vertArray[i];
rob@100 16509 curContext.beginPath();
rob@100 16510 curContext.moveTo(vertArray[0][0], vertArray[0][1]);
rob@100 16511 curContext.lineTo(vertArray[i-1][0], vertArray[i-1][1]);
rob@100 16512 curContext.lineTo(cachedVertArray[0], cachedVertArray[1]);
rob@100 16513
rob@100 16514 if (doFill) {
rob@100 16515 p.fill(cachedVertArray[5]);
rob@100 16516 executeContextFill();
rob@100 16517 }
rob@100 16518 if (doStroke) {
rob@100 16519 p.stroke(cachedVertArray[6]);
rob@100 16520 executeContextStroke();
rob@100 16521 }
rob@100 16522
rob@100 16523 curContext.closePath();
rob@100 16524 }
rob@100 16525 }
rob@100 16526 } else if (curShape === PConstants.QUADS) {
rob@100 16527 for (i = 0; (i + 3) < vertArrayLength; i+=4) {
rob@100 16528 cachedVertArray = vertArray[i];
rob@100 16529 curContext.beginPath();
rob@100 16530 curContext.moveTo(cachedVertArray[0], cachedVertArray[1]);
rob@100 16531 for (j = 1; j < 4; j++) {
rob@100 16532 curContext.lineTo(vertArray[i+j][0], vertArray[i+j][1]);
rob@100 16533 }
rob@100 16534 curContext.lineTo(cachedVertArray[0], cachedVertArray[1]);
rob@100 16535
rob@100 16536 if (doFill) {
rob@100 16537 p.fill(vertArray[i+3][5]);
rob@100 16538 executeContextFill();
rob@100 16539 }
rob@100 16540 if (doStroke) {
rob@100 16541 p.stroke(vertArray[i+3][6]);
rob@100 16542 executeContextStroke();
rob@100 16543 }
rob@100 16544
rob@100 16545 curContext.closePath();
rob@100 16546 }
rob@100 16547 } else if (curShape === PConstants.QUAD_STRIP) {
rob@100 16548 if (vertArrayLength > 3) {
rob@100 16549 for (i = 0; (i+1) < vertArrayLength; i+=2) {
rob@100 16550 cachedVertArray = vertArray[i];
rob@100 16551 curContext.beginPath();
rob@100 16552 if (i+3 < vertArrayLength) {
rob@100 16553 curContext.moveTo(vertArray[i+2][0], vertArray[i+2][1]);
rob@100 16554 curContext.lineTo(cachedVertArray[0], cachedVertArray[1]);
rob@100 16555 curContext.lineTo(vertArray[i+1][0], vertArray[i+1][1]);
rob@100 16556 curContext.lineTo(vertArray[i+3][0], vertArray[i+3][1]);
rob@100 16557
rob@100 16558 if (doFill) {
rob@100 16559 p.fill(vertArray[i+3][5]);
rob@100 16560 }
rob@100 16561 if (doStroke) {
rob@100 16562 p.stroke(vertArray[i+3][6]);
rob@100 16563 }
rob@100 16564 } else {
rob@100 16565 curContext.moveTo(cachedVertArray[0], cachedVertArray[1]);
rob@100 16566 curContext.lineTo(vertArray[i+1][0], vertArray[i+1][1]);
rob@100 16567 }
rob@100 16568 fillStrokeClose();
rob@100 16569 }
rob@100 16570 }
rob@100 16571 } else {
rob@100 16572 curContext.beginPath();
rob@100 16573 curContext.moveTo(vertArray[0][0], vertArray[0][1]);
rob@100 16574 for (i = 1; i < vertArrayLength; i++) {
rob@100 16575 cachedVertArray = vertArray[i];
rob@100 16576 if (cachedVertArray.isVert) { //if it is a vertex move to the position
rob@100 16577 if (cachedVertArray.moveTo) {
rob@100 16578 curContext.moveTo(cachedVertArray[0], cachedVertArray[1]);
rob@100 16579 } else {
rob@100 16580 curContext.lineTo(cachedVertArray[0], cachedVertArray[1]);
rob@100 16581 }
rob@100 16582 }
rob@100 16583 }
rob@100 16584 fillStrokeClose();
rob@100 16585 }
rob@100 16586 }
rob@100 16587
rob@100 16588 // Reset some settings
rob@100 16589 isCurve = false;
rob@100 16590 isBezier = false;
rob@100 16591 curveVertArray = [];
rob@100 16592 curveVertCount = 0;
rob@100 16593
rob@100 16594 // If the shape is closed, the first element was added as last element.
rob@100 16595 // We must remove it again to prevent the list of vertices from growing
rob@100 16596 // over successive calls to endShape(CLOSE)
rob@100 16597 if (closeShape) {
rob@100 16598 vertArray.pop();
rob@100 16599 }
rob@100 16600 };
rob@100 16601
rob@100 16602 Drawing3D.prototype.endShape = function(mode) {
rob@100 16603 // Duplicated in Drawing3D; too many variables used
rob@100 16604 if (vertArray.length === 0) { return; }
rob@100 16605
rob@100 16606 var closeShape = mode === PConstants.CLOSE;
rob@100 16607 var lineVertArray = [];
rob@100 16608 var fillVertArray = [];
rob@100 16609 var colorVertArray = [];
rob@100 16610 var strokeVertArray = [];
rob@100 16611 var texVertArray = [];
rob@100 16612 var cachedVertArray;
rob@100 16613
rob@100 16614 firstVert = true;
rob@100 16615 var i, j, k;
rob@100 16616 var vertArrayLength = vertArray.length;
rob@100 16617
rob@100 16618 for (i = 0; i < vertArrayLength; i++) {
rob@100 16619 cachedVertArray = vertArray[i];
rob@100 16620 for (j = 0; j < 3; j++) {
rob@100 16621 fillVertArray.push(cachedVertArray[j]);
rob@100 16622 }
rob@100 16623 }
rob@100 16624
rob@100 16625 // 5,6,7,8
rob@100 16626 // R,G,B,A - fill colour
rob@100 16627 for (i = 0; i < vertArrayLength; i++) {
rob@100 16628 cachedVertArray = vertArray[i];
rob@100 16629 for (j = 5; j < 9; j++) {
rob@100 16630 colorVertArray.push(cachedVertArray[j]);
rob@100 16631 }
rob@100 16632 }
rob@100 16633
rob@100 16634 // 9,10,11,12
rob@100 16635 // R, G, B, A - stroke colour
rob@100 16636 for (i = 0; i < vertArrayLength; i++) {
rob@100 16637 cachedVertArray = vertArray[i];
rob@100 16638 for (j = 9; j < 13; j++) {
rob@100 16639 strokeVertArray.push(cachedVertArray[j]);
rob@100 16640 }
rob@100 16641 }
rob@100 16642
rob@100 16643 // texture u,v
rob@100 16644 for (i = 0; i < vertArrayLength; i++) {
rob@100 16645 cachedVertArray = vertArray[i];
rob@100 16646 texVertArray.push(cachedVertArray[3]);
rob@100 16647 texVertArray.push(cachedVertArray[4]);
rob@100 16648 }
rob@100 16649
rob@100 16650 // if shape is closed, push the first point into the last point (including colours)
rob@100 16651 if (closeShape) {
rob@100 16652 fillVertArray.push(vertArray[0][0]);
rob@100 16653 fillVertArray.push(vertArray[0][1]);
rob@100 16654 fillVertArray.push(vertArray[0][2]);
rob@100 16655
rob@100 16656 for (i = 5; i < 9; i++) {
rob@100 16657 colorVertArray.push(vertArray[0][i]);
rob@100 16658 }
rob@100 16659
rob@100 16660 for (i = 9; i < 13; i++) {
rob@100 16661 strokeVertArray.push(vertArray[0][i]);
rob@100 16662 }
rob@100 16663
rob@100 16664 texVertArray.push(vertArray[0][3]);
rob@100 16665 texVertArray.push(vertArray[0][4]);
rob@100 16666 }
rob@100 16667 // End duplication
rob@100 16668
rob@100 16669 // curveVertex
rob@100 16670 if ( isCurve && (curShape === PConstants.POLYGON || curShape === undef) ) {
rob@100 16671 lineVertArray = fillVertArray;
rob@100 16672 if (doStroke) {
rob@100 16673 line3D(lineVertArray, null, strokeVertArray);
rob@100 16674 }
rob@100 16675 if (doFill) {
rob@100 16676 fill3D(fillVertArray, null, colorVertArray);
rob@100 16677 }
rob@100 16678 }
rob@100 16679 // bezierVertex
rob@100 16680 else if ( isBezier && (curShape === PConstants.POLYGON || curShape === undef) ) {
rob@100 16681 lineVertArray = fillVertArray;
rob@100 16682 lineVertArray.splice(lineVertArray.length - 3);
rob@100 16683 strokeVertArray.splice(strokeVertArray.length - 4);
rob@100 16684 if (doStroke) {
rob@100 16685 line3D(lineVertArray, null, strokeVertArray);
rob@100 16686 }
rob@100 16687 if (doFill) {
rob@100 16688 fill3D(fillVertArray, "TRIANGLES", colorVertArray);
rob@100 16689 }
rob@100 16690 }
rob@100 16691
rob@100 16692 // render the vertices provided
rob@100 16693 else {
rob@100 16694 if (curShape === PConstants.POINTS) { // if POINTS was the specified parameter in beginShape
rob@100 16695 for (i = 0; i < vertArrayLength; i++) { // loop through and push the point location information to the array
rob@100 16696 cachedVertArray = vertArray[i];
rob@100 16697 for (j = 0; j < 3; j++) {
rob@100 16698 lineVertArray.push(cachedVertArray[j]);
rob@100 16699 }
rob@100 16700 }
rob@100 16701 point3D(lineVertArray, strokeVertArray); // render function for points
rob@100 16702 } else if (curShape === PConstants.LINES) { // if LINES was the specified parameter in beginShape
rob@100 16703 for (i = 0; i < vertArrayLength; i++) { // loop through and push the point location information to the array
rob@100 16704 cachedVertArray = vertArray[i];
rob@100 16705 for (j = 0; j < 3; j++) {
rob@100 16706 lineVertArray.push(cachedVertArray[j]);
rob@100 16707 }
rob@100 16708 }
rob@100 16709 for (i = 0; i < vertArrayLength; i++) { // loop through and push the color information to the array
rob@100 16710 cachedVertArray = vertArray[i];
rob@100 16711 for (j = 5; j < 9; j++) {
rob@100 16712 colorVertArray.push(cachedVertArray[j]);
rob@100 16713 }
rob@100 16714 }
rob@100 16715 line3D(lineVertArray, "LINES", strokeVertArray); // render function for lines
rob@100 16716 } else if (curShape === PConstants.TRIANGLES) { // if TRIANGLES was the specified parameter in beginShape
rob@100 16717 if (vertArrayLength > 2) {
rob@100 16718 for (i = 0; (i+2) < vertArrayLength; i+=3) { // loop through the array per triangle
rob@100 16719 fillVertArray = [];
rob@100 16720 texVertArray = [];
rob@100 16721 lineVertArray = [];
rob@100 16722 colorVertArray = [];
rob@100 16723 strokeVertArray = [];
rob@100 16724 for (j = 0; j < 3; j++) {
rob@100 16725 for (k = 0; k < 3; k++) { // loop through and push
rob@100 16726 lineVertArray.push(vertArray[i+j][k]); // the line point location information
rob@100 16727 fillVertArray.push(vertArray[i+j][k]); // and fill point location information
rob@100 16728 }
rob@100 16729 }
rob@100 16730 for (j = 0; j < 3; j++) { // loop through and push the texture information
rob@100 16731 for (k = 3; k < 5; k++) {
rob@100 16732 texVertArray.push(vertArray[i+j][k]);
rob@100 16733 }
rob@100 16734 }
rob@100 16735 for (j = 0; j < 3; j++) {
rob@100 16736 for (k = 5; k < 9; k++) { // loop through and push
rob@100 16737 colorVertArray.push(vertArray[i+j][k]); // the colour information
rob@100 16738 strokeVertArray.push(vertArray[i+j][k+4]);// and the stroke information
rob@100 16739 }
rob@100 16740 }
rob@100 16741 if (doStroke) {
rob@100 16742 line3D(lineVertArray, "LINE_LOOP", strokeVertArray ); // line render function
rob@100 16743 }
rob@100 16744 if (doFill || usingTexture) {
rob@100 16745 fill3D(fillVertArray, "TRIANGLES", colorVertArray, texVertArray); // fill shape render function
rob@100 16746 }
rob@100 16747 }
rob@100 16748 }
rob@100 16749 } else if (curShape === PConstants.TRIANGLE_STRIP) { // if TRIANGLE_STRIP was the specified parameter in beginShape
rob@100 16750 if (vertArrayLength > 2) {
rob@100 16751 for (i = 0; (i+2) < vertArrayLength; i++) {
rob@100 16752 lineVertArray = [];
rob@100 16753 fillVertArray = [];
rob@100 16754 strokeVertArray = [];
rob@100 16755 colorVertArray = [];
rob@100 16756 texVertArray = [];
rob@100 16757 for (j = 0; j < 3; j++) {
rob@100 16758 for (k = 0; k < 3; k++) {
rob@100 16759 lineVertArray.push(vertArray[i+j][k]);
rob@100 16760 fillVertArray.push(vertArray[i+j][k]);
rob@100 16761 }
rob@100 16762 }
rob@100 16763 for (j = 0; j < 3; j++) {
rob@100 16764 for (k = 3; k < 5; k++) {
rob@100 16765 texVertArray.push(vertArray[i+j][k]);
rob@100 16766 }
rob@100 16767 }
rob@100 16768 for (j = 0; j < 3; j++) {
rob@100 16769 for (k = 5; k < 9; k++) {
rob@100 16770 strokeVertArray.push(vertArray[i+j][k+4]);
rob@100 16771 colorVertArray.push(vertArray[i+j][k]);
rob@100 16772 }
rob@100 16773 }
rob@100 16774
rob@100 16775 if (doFill || usingTexture) {
rob@100 16776 fill3D(fillVertArray, "TRIANGLE_STRIP", colorVertArray, texVertArray);
rob@100 16777 }
rob@100 16778 if (doStroke) {
rob@100 16779 line3D(lineVertArray, "LINE_LOOP", strokeVertArray);
rob@100 16780 }
rob@100 16781 }
rob@100 16782 }
rob@100 16783 } else if (curShape === PConstants.TRIANGLE_FAN) {
rob@100 16784 if (vertArrayLength > 2) {
rob@100 16785 for (i = 0; i < 3; i++) {
rob@100 16786 cachedVertArray = vertArray[i];
rob@100 16787 for (j = 0; j < 3; j++) {
rob@100 16788 lineVertArray.push(cachedVertArray[j]);
rob@100 16789 }
rob@100 16790 }
rob@100 16791 for (i = 0; i < 3; i++) {
rob@100 16792 cachedVertArray = vertArray[i];
rob@100 16793 for (j = 9; j < 13; j++) {
rob@100 16794 strokeVertArray.push(cachedVertArray[j]);
rob@100 16795 }
rob@100 16796 }
rob@100 16797 if (doStroke) {
rob@100 16798 line3D(lineVertArray, "LINE_LOOP", strokeVertArray);
rob@100 16799 }
rob@100 16800
rob@100 16801 for (i = 2; (i+1) < vertArrayLength; i++) {
rob@100 16802 lineVertArray = [];
rob@100 16803 strokeVertArray = [];
rob@100 16804 lineVertArray.push(vertArray[0][0]);
rob@100 16805 lineVertArray.push(vertArray[0][1]);
rob@100 16806 lineVertArray.push(vertArray[0][2]);
rob@100 16807
rob@100 16808 strokeVertArray.push(vertArray[0][9]);
rob@100 16809 strokeVertArray.push(vertArray[0][10]);
rob@100 16810 strokeVertArray.push(vertArray[0][11]);
rob@100 16811 strokeVertArray.push(vertArray[0][12]);
rob@100 16812
rob@100 16813 for (j = 0; j < 2; j++) {
rob@100 16814 for (k = 0; k < 3; k++) {
rob@100 16815 lineVertArray.push(vertArray[i+j][k]);
rob@100 16816 }
rob@100 16817 }
rob@100 16818 for (j = 0; j < 2; j++) {
rob@100 16819 for (k = 9; k < 13; k++) {
rob@100 16820 strokeVertArray.push(vertArray[i+j][k]);
rob@100 16821 }
rob@100 16822 }
rob@100 16823 if (doStroke) {
rob@100 16824 line3D(lineVertArray, "LINE_STRIP",strokeVertArray);
rob@100 16825 }
rob@100 16826 }
rob@100 16827 if (doFill || usingTexture) {
rob@100 16828 fill3D(fillVertArray, "TRIANGLE_FAN", colorVertArray, texVertArray);
rob@100 16829 }
rob@100 16830 }
rob@100 16831 } else if (curShape === PConstants.QUADS) {
rob@100 16832 for (i = 0; (i + 3) < vertArrayLength; i+=4) {
rob@100 16833 lineVertArray = [];
rob@100 16834 for (j = 0; j < 4; j++) {
rob@100 16835 cachedVertArray = vertArray[i+j];
rob@100 16836 for (k = 0; k < 3; k++) {
rob@100 16837 lineVertArray.push(cachedVertArray[k]);
rob@100 16838 }
rob@100 16839 }
rob@100 16840 if (doStroke) {
rob@100 16841 line3D(lineVertArray, "LINE_LOOP",strokeVertArray);
rob@100 16842 }
rob@100 16843
rob@100 16844 if (doFill) {
rob@100 16845 fillVertArray = [];
rob@100 16846 colorVertArray = [];
rob@100 16847 texVertArray = [];
rob@100 16848 for (j = 0; j < 3; j++) {
rob@100 16849 fillVertArray.push(vertArray[i][j]);
rob@100 16850 }
rob@100 16851 for (j = 5; j < 9; j++) {
rob@100 16852 colorVertArray.push(vertArray[i][j]);
rob@100 16853 }
rob@100 16854
rob@100 16855 for (j = 0; j < 3; j++) {
rob@100 16856 fillVertArray.push(vertArray[i+1][j]);
rob@100 16857 }
rob@100 16858 for (j = 5; j < 9; j++) {
rob@100 16859 colorVertArray.push(vertArray[i+1][j]);
rob@100 16860 }
rob@100 16861
rob@100 16862 for (j = 0; j < 3; j++) {
rob@100 16863 fillVertArray.push(vertArray[i+3][j]);
rob@100 16864 }
rob@100 16865 for (j = 5; j < 9; j++) {
rob@100 16866 colorVertArray.push(vertArray[i+3][j]);
rob@100 16867 }
rob@100 16868
rob@100 16869 for (j = 0; j < 3; j++) {
rob@100 16870 fillVertArray.push(vertArray[i+2][j]);
rob@100 16871 }
rob@100 16872 for (j = 5; j < 9; j++) {
rob@100 16873 colorVertArray.push(vertArray[i+2][j]);
rob@100 16874 }
rob@100 16875
rob@100 16876 if (usingTexture) {
rob@100 16877 texVertArray.push(vertArray[i+0][3]);
rob@100 16878 texVertArray.push(vertArray[i+0][4]);
rob@100 16879 texVertArray.push(vertArray[i+1][3]);
rob@100 16880 texVertArray.push(vertArray[i+1][4]);
rob@100 16881 texVertArray.push(vertArray[i+3][3]);
rob@100 16882 texVertArray.push(vertArray[i+3][4]);
rob@100 16883 texVertArray.push(vertArray[i+2][3]);
rob@100 16884 texVertArray.push(vertArray[i+2][4]);
rob@100 16885 }
rob@100 16886
rob@100 16887 fill3D(fillVertArray, "TRIANGLE_STRIP", colorVertArray, texVertArray);
rob@100 16888 }
rob@100 16889 }
rob@100 16890 } else if (curShape === PConstants.QUAD_STRIP) {
rob@100 16891 var tempArray = [];
rob@100 16892 if (vertArrayLength > 3) {
rob@100 16893 for (i = 0; i < 2; i++) {
rob@100 16894 cachedVertArray = vertArray[i];
rob@100 16895 for (j = 0; j < 3; j++) {
rob@100 16896 lineVertArray.push(cachedVertArray[j]);
rob@100 16897 }
rob@100 16898 }
rob@100 16899
rob@100 16900 for (i = 0; i < 2; i++) {
rob@100 16901 cachedVertArray = vertArray[i];
rob@100 16902 for (j = 9; j < 13; j++) {
rob@100 16903 strokeVertArray.push(cachedVertArray[j]);
rob@100 16904 }
rob@100 16905 }
rob@100 16906
rob@100 16907 line3D(lineVertArray, "LINE_STRIP", strokeVertArray);
rob@100 16908 if (vertArrayLength > 4 && vertArrayLength % 2 > 0) {
rob@100 16909 tempArray = fillVertArray.splice(fillVertArray.length - 3);
rob@100 16910 vertArray.pop();
rob@100 16911 }
rob@100 16912 for (i = 0; (i+3) < vertArrayLength; i+=2) {
rob@100 16913 lineVertArray = [];
rob@100 16914 strokeVertArray = [];
rob@100 16915 for (j = 0; j < 3; j++) {
rob@100 16916 lineVertArray.push(vertArray[i+1][j]);
rob@100 16917 }
rob@100 16918 for (j = 0; j < 3; j++) {
rob@100 16919 lineVertArray.push(vertArray[i+3][j]);
rob@100 16920 }
rob@100 16921 for (j = 0; j < 3; j++) {
rob@100 16922 lineVertArray.push(vertArray[i+2][j]);
rob@100 16923 }
rob@100 16924 for (j = 0; j < 3; j++) {
rob@100 16925 lineVertArray.push(vertArray[i+0][j]);
rob@100 16926 }
rob@100 16927 for (j = 9; j < 13; j++) {
rob@100 16928 strokeVertArray.push(vertArray[i+1][j]);
rob@100 16929 }
rob@100 16930 for (j = 9; j < 13; j++) {
rob@100 16931 strokeVertArray.push(vertArray[i+3][j]);
rob@100 16932 }
rob@100 16933 for (j = 9; j < 13; j++) {
rob@100 16934 strokeVertArray.push(vertArray[i+2][j]);
rob@100 16935 }
rob@100 16936 for (j = 9; j < 13; j++) {
rob@100 16937 strokeVertArray.push(vertArray[i+0][j]);
rob@100 16938 }
rob@100 16939 if (doStroke) {
rob@100 16940 line3D(lineVertArray, "LINE_STRIP", strokeVertArray);
rob@100 16941 }
rob@100 16942 }
rob@100 16943
rob@100 16944 if (doFill || usingTexture) {
rob@100 16945 fill3D(fillVertArray, "TRIANGLE_LIST", colorVertArray, texVertArray);
rob@100 16946 }
rob@100 16947 }
rob@100 16948 }
rob@100 16949 // If the user didn't specify a type (LINES, TRIANGLES, etc)
rob@100 16950 else {
rob@100 16951 // If only one vertex was specified, it must be a point
rob@100 16952 if (vertArrayLength === 1) {
rob@100 16953 for (j = 0; j < 3; j++) {
rob@100 16954 lineVertArray.push(vertArray[0][j]);
rob@100 16955 }
rob@100 16956 for (j = 9; j < 13; j++) {
rob@100 16957 strokeVertArray.push(vertArray[0][j]);
rob@100 16958 }
rob@100 16959 point3D(lineVertArray,strokeVertArray);
rob@100 16960 } else {
rob@100 16961 for (i = 0; i < vertArrayLength; i++) {
rob@100 16962 cachedVertArray = vertArray[i];
rob@100 16963 for (j = 0; j < 3; j++) {
rob@100 16964 lineVertArray.push(cachedVertArray[j]);
rob@100 16965 }
rob@100 16966 for (j = 5; j < 9; j++) {
rob@100 16967 strokeVertArray.push(cachedVertArray[j]);
rob@100 16968 }
rob@100 16969 }
rob@100 16970 if (doStroke && closeShape) {
rob@100 16971 line3D(lineVertArray, "LINE_LOOP", strokeVertArray);
rob@100 16972 } else if (doStroke && !closeShape) {
rob@100 16973 line3D(lineVertArray, "LINE_STRIP", strokeVertArray);
rob@100 16974 }
rob@100 16975
rob@100 16976 // fill is ignored if textures are used
rob@100 16977 if (doFill || usingTexture) {
rob@100 16978 fill3D(fillVertArray, "TRIANGLE_FAN", colorVertArray, texVertArray);
rob@100 16979 }
rob@100 16980 }
rob@100 16981 }
rob@100 16982 // everytime beginShape is followed by a call to
rob@100 16983 // texture(), texturing it turned back on. We do this to
rob@100 16984 // figure out if the shape should be textured or filled
rob@100 16985 // with a color.
rob@100 16986 usingTexture = false;
rob@100 16987 curContext.useProgram(programObject3D);
rob@100 16988 uniformi("usingTexture3d", programObject3D, "uUsingTexture", usingTexture);
rob@100 16989 }
rob@100 16990
rob@100 16991 // Reset some settings
rob@100 16992 isCurve = false;
rob@100 16993 isBezier = false;
rob@100 16994 curveVertArray = [];
rob@100 16995 curveVertCount = 0;
rob@100 16996 };
rob@100 16997
rob@100 16998 /**
rob@100 16999 * The function splineForward() setup forward-differencing matrix to be used for speedy
rob@100 17000 * curve rendering. It's based on using a specific number
rob@100 17001 * of curve segments and just doing incremental adds for each
rob@100 17002 * vertex of the segment, rather than running the mathematically
rob@100 17003 * expensive cubic equation. This function is used by both curveDetail and bezierDetail.
rob@100 17004 *
rob@100 17005 * @param {int} segments number of curve segments to use when drawing
rob@100 17006 * @param {PMatrix3D} matrix target object for the new matrix
rob@100 17007 */
rob@100 17008 var splineForward = function(segments, matrix) {
rob@100 17009 var f = 1.0 / segments;
rob@100 17010 var ff = f * f;
rob@100 17011 var fff = ff * f;
rob@100 17012
rob@100 17013 matrix.set(0, 0, 0, 1, fff, ff, f, 0, 6 * fff, 2 * ff, 0, 0, 6 * fff, 0, 0, 0);
rob@100 17014 };
rob@100 17015
rob@100 17016 /**
rob@100 17017 * The curveInit() function set the number of segments to use when drawing a Catmull-Rom
rob@100 17018 * curve, and setting the s parameter, which defines how tightly
rob@100 17019 * the curve fits to each vertex. Catmull-Rom curves are actually
rob@100 17020 * a subset of this curve type where the s is set to zero.
rob@100 17021 * This in an internal function used by curveDetail() and curveTightness().
rob@100 17022 */
rob@100 17023 var curveInit = function() {
rob@100 17024 // allocate only if/when used to save startup time
rob@100 17025 if (!curveDrawMatrix) {
rob@100 17026 curveBasisMatrix = new PMatrix3D();
rob@100 17027 curveDrawMatrix = new PMatrix3D();
rob@100 17028 curveInited = true;
rob@100 17029 }
rob@100 17030
rob@100 17031 var s = curTightness;
rob@100 17032 curveBasisMatrix.set((s - 1) / 2, (s + 3) / 2, (-3 - s) / 2, (1 - s) / 2,
rob@100 17033 (1 - s), (-5 - s) / 2, (s + 2), (s - 1) / 2,
rob@100 17034 (s - 1) / 2, 0, (1 - s) / 2, 0, 0, 1, 0, 0);
rob@100 17035
rob@100 17036 splineForward(curveDet, curveDrawMatrix);
rob@100 17037
rob@100 17038 if (!bezierBasisInverse) {
rob@100 17039 //bezierBasisInverse = bezierBasisMatrix.get();
rob@100 17040 //bezierBasisInverse.invert();
rob@100 17041 curveToBezierMatrix = new PMatrix3D();
rob@100 17042 }
rob@100 17043
rob@100 17044 // TODO only needed for PGraphicsJava2D? if so, move it there
rob@100 17045 // actually, it's generally useful for other renderers, so keep it
rob@100 17046 // or hide the implementation elsewhere.
rob@100 17047 curveToBezierMatrix.set(curveBasisMatrix);
rob@100 17048 curveToBezierMatrix.preApply(bezierBasisInverse);
rob@100 17049
rob@100 17050 // multiply the basis and forward diff matrices together
rob@100 17051 // saves much time since this needn't be done for each curve
rob@100 17052 curveDrawMatrix.apply(curveBasisMatrix);
rob@100 17053 };
rob@100 17054
rob@100 17055 /**
rob@100 17056 * Specifies vertex coordinates for Bezier curves. Each call to <b>bezierVertex()</b> defines the position of two control
rob@100 17057 * points and one anchor point of a Bezier curve, adding a new segment to a line or shape. The first time
rob@100 17058 * <b>bezierVertex()</b> is used within a <b>beginShape()</b> call, it must be prefaced with a call to <b>vertex()</b>
rob@100 17059 * to set the first anchor point. This function must be used between <b>beginShape()</b> and <b>endShape()</b> and only
rob@100 17060 * when there is no MODE parameter specified to <b>beginShape()</b>. Using the 3D version of requires rendering with P3D
rob@100 17061 * or OPENGL (see the Environment reference for more information). <br /> <br /> <b>NOTE: </b> Fill does not work properly yet.
rob@100 17062 *
rob@100 17063 * @param {float | int} cx1 The x-coordinate of 1st control point
rob@100 17064 * @param {float | int} cy1 The y-coordinate of 1st control point
rob@100 17065 * @param {float | int} cz1 The z-coordinate of 1st control point
rob@100 17066 * @param {float | int} cx2 The x-coordinate of 2nd control point
rob@100 17067 * @param {float | int} cy2 The y-coordinate of 2nd control point
rob@100 17068 * @param {float | int} cz2 The z-coordinate of 2nd control point
rob@100 17069 * @param {float | int} x The x-coordinate of the anchor point
rob@100 17070 * @param {float | int} y The y-coordinate of the anchor point
rob@100 17071 * @param {float | int} z The z-coordinate of the anchor point
rob@100 17072 *
rob@100 17073 * @see curveVertex
rob@100 17074 * @see vertex
rob@100 17075 * @see bezier
rob@100 17076 */
rob@100 17077 Drawing2D.prototype.bezierVertex = function() {
rob@100 17078 isBezier = true;
rob@100 17079 var vert = [];
rob@100 17080 if (firstVert) {
rob@100 17081 throw ("vertex() must be used at least once before calling bezierVertex()");
rob@100 17082 }
rob@100 17083
rob@100 17084 for (var i = 0; i < arguments.length; i++) {
rob@100 17085 vert[i] = arguments[i];
rob@100 17086 }
rob@100 17087 vertArray.push(vert);
rob@100 17088 vertArray[vertArray.length -1].isVert = false;
rob@100 17089 };
rob@100 17090
rob@100 17091 Drawing3D.prototype.bezierVertex = function() {
rob@100 17092 isBezier = true;
rob@100 17093 var vert = [];
rob@100 17094 if (firstVert) {
rob@100 17095 throw ("vertex() must be used at least once before calling bezierVertex()");
rob@100 17096 }
rob@100 17097
rob@100 17098 if (arguments.length === 9) {
rob@100 17099 if (bezierDrawMatrix === undef) {
rob@100 17100 bezierDrawMatrix = new PMatrix3D();
rob@100 17101 }
rob@100 17102 // setup matrix for forward differencing to speed up drawing
rob@100 17103 var lastPoint = vertArray.length - 1;
rob@100 17104 splineForward( bezDetail, bezierDrawMatrix );
rob@100 17105 bezierDrawMatrix.apply( bezierBasisMatrix );
rob@100 17106 var draw = bezierDrawMatrix.array();
rob@100 17107 var x1 = vertArray[lastPoint][0],
rob@100 17108 y1 = vertArray[lastPoint][1],
rob@100 17109 z1 = vertArray[lastPoint][2];
rob@100 17110 var xplot1 = draw[4] * x1 + draw[5] * arguments[0] + draw[6] * arguments[3] + draw[7] * arguments[6];
rob@100 17111 var xplot2 = draw[8] * x1 + draw[9] * arguments[0] + draw[10]* arguments[3] + draw[11]* arguments[6];
rob@100 17112 var xplot3 = draw[12]* x1 + draw[13]* arguments[0] + draw[14]* arguments[3] + draw[15]* arguments[6];
rob@100 17113
rob@100 17114 var yplot1 = draw[4] * y1 + draw[5] * arguments[1] + draw[6] * arguments[4] + draw[7] * arguments[7];
rob@100 17115 var yplot2 = draw[8] * y1 + draw[9] * arguments[1] + draw[10]* arguments[4] + draw[11]* arguments[7];
rob@100 17116 var yplot3 = draw[12]* y1 + draw[13]* arguments[1] + draw[14]* arguments[4] + draw[15]* arguments[7];
rob@100 17117
rob@100 17118 var zplot1 = draw[4] * z1 + draw[5] * arguments[2] + draw[6] * arguments[5] + draw[7] * arguments[8];
rob@100 17119 var zplot2 = draw[8] * z1 + draw[9] * arguments[2] + draw[10]* arguments[5] + draw[11]* arguments[8];
rob@100 17120 var zplot3 = draw[12]* z1 + draw[13]* arguments[2] + draw[14]* arguments[5] + draw[15]* arguments[8];
rob@100 17121 for (var j = 0; j < bezDetail; j++) {
rob@100 17122 x1 += xplot1; xplot1 += xplot2; xplot2 += xplot3;
rob@100 17123 y1 += yplot1; yplot1 += yplot2; yplot2 += yplot3;
rob@100 17124 z1 += zplot1; zplot1 += zplot2; zplot2 += zplot3;
rob@100 17125 p.vertex(x1, y1, z1);
rob@100 17126 }
rob@100 17127 p.vertex(arguments[6], arguments[7], arguments[8]);
rob@100 17128 }
rob@100 17129 };
rob@100 17130
rob@100 17131 /**
rob@100 17132 * Sets a texture to be applied to vertex points. The <b>texture()</b> function
rob@100 17133 * must be called between <b>beginShape()</b> and <b>endShape()</b> and before
rob@100 17134 * any calls to vertex().
rob@100 17135 *
rob@100 17136 * When textures are in use, the fill color is ignored. Instead, use tint() to
rob@100 17137 * specify the color of the texture as it is applied to the shape.
rob@100 17138 *
rob@100 17139 * @param {PImage} pimage the texture to apply
rob@100 17140 *
rob@100 17141 * @returns none
rob@100 17142 *
rob@100 17143 * @see textureMode
rob@100 17144 * @see beginShape
rob@100 17145 * @see endShape
rob@100 17146 * @see vertex
rob@100 17147 */
rob@100 17148 p.texture = function(pimage) {
rob@100 17149 var curContext = drawing.$ensureContext();
rob@100 17150
rob@100 17151 if (pimage.__texture) {
rob@100 17152 curContext.bindTexture(curContext.TEXTURE_2D, pimage.__texture);
rob@100 17153 } else if (pimage.localName === "canvas") {
rob@100 17154 curContext.bindTexture(curContext.TEXTURE_2D, canTex);
rob@100 17155 curContext.texImage2D(curContext.TEXTURE_2D, 0, curContext.RGBA, curContext.RGBA, curContext.UNSIGNED_BYTE, pimage);
rob@100 17156 curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_MAG_FILTER, curContext.LINEAR);
rob@100 17157 curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_MIN_FILTER, curContext.LINEAR);
rob@100 17158 curContext.generateMipmap(curContext.TEXTURE_2D);
rob@100 17159 curTexture.width = pimage.width;
rob@100 17160 curTexture.height = pimage.height;
rob@100 17161 } else {
rob@100 17162 var texture = curContext.createTexture(),
rob@100 17163 cvs = document.createElement('canvas'),
rob@100 17164 cvsTextureCtx = cvs.getContext('2d'),
rob@100 17165 pot;
rob@100 17166
rob@100 17167 // WebGL requires power of two textures
rob@100 17168 if (pimage.width & (pimage.width-1) === 0) {
rob@100 17169 cvs.width = pimage.width;
rob@100 17170 } else {
rob@100 17171 pot = 1;
rob@100 17172 while (pot < pimage.width) {
rob@100 17173 pot *= 2;
rob@100 17174 }
rob@100 17175 cvs.width = pot;
rob@100 17176 }
rob@100 17177
rob@100 17178 if (pimage.height & (pimage.height-1) === 0) {
rob@100 17179 cvs.height = pimage.height;
rob@100 17180 } else {
rob@100 17181 pot = 1;
rob@100 17182 while (pot < pimage.height) {
rob@100 17183 pot *= 2;
rob@100 17184 }
rob@100 17185 cvs.height = pot;
rob@100 17186 }
rob@100 17187
rob@100 17188 cvsTextureCtx.drawImage(pimage.sourceImg, 0, 0, pimage.width, pimage.height, 0, 0, cvs.width, cvs.height);
rob@100 17189
rob@100 17190 curContext.bindTexture(curContext.TEXTURE_2D, texture);
rob@100 17191 curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_MIN_FILTER, curContext.LINEAR_MIPMAP_LINEAR);
rob@100 17192 curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_MAG_FILTER, curContext.LINEAR);
rob@100 17193 curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_WRAP_T, curContext.CLAMP_TO_EDGE);
rob@100 17194 curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_WRAP_S, curContext.CLAMP_TO_EDGE);
rob@100 17195 curContext.texImage2D(curContext.TEXTURE_2D, 0, curContext.RGBA, curContext.RGBA, curContext.UNSIGNED_BYTE, cvs);
rob@100 17196 curContext.generateMipmap(curContext.TEXTURE_2D);
rob@100 17197
rob@100 17198 pimage.__texture = texture;
rob@100 17199 curTexture.width = pimage.width;
rob@100 17200 curTexture.height = pimage.height;
rob@100 17201 }
rob@100 17202
rob@100 17203 usingTexture = true;
rob@100 17204 curContext.useProgram(programObject3D);
rob@100 17205 uniformi("usingTexture3d", programObject3D, "uUsingTexture", usingTexture);
rob@100 17206 };
rob@100 17207
rob@100 17208 /**
rob@100 17209 * Sets the coordinate space for texture mapping. There are two options, IMAGE,
rob@100 17210 * which refers to the actual coordinates of the image, and NORMALIZED, which
rob@100 17211 * refers to a normalized space of values ranging from 0 to 1. The default mode
rob@100 17212 * is IMAGE. In IMAGE, if an image is 100 x 200 pixels, mapping the image onto
rob@100 17213 * the entire size of a quad would require the points (0,0) (0,100) (100,200) (0,200).
rob@100 17214 * The same mapping in NORMAL_SPACE is (0,0) (0,1) (1,1) (0,1).
rob@100 17215 *
rob@100 17216 * @param MODE either IMAGE or NORMALIZED
rob@100 17217 *
rob@100 17218 * @returns none
rob@100 17219 *
rob@100 17220 * @see texture
rob@100 17221 */
rob@100 17222 p.textureMode = function(mode){
rob@100 17223 curTextureMode = mode;
rob@100 17224 };
rob@100 17225 /**
rob@100 17226 * The curveVertexSegment() function handle emitting a specific segment of Catmull-Rom curve. Internal helper function used by <b>curveVertex()</b>.
rob@100 17227 */
rob@100 17228 var curveVertexSegment = function(x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4) {
rob@100 17229 var x0 = x2;
rob@100 17230 var y0 = y2;
rob@100 17231 var z0 = z2;
rob@100 17232
rob@100 17233 var draw = curveDrawMatrix.array();
rob@100 17234
rob@100 17235 var xplot1 = draw[4] * x1 + draw[5] * x2 + draw[6] * x3 + draw[7] * x4;
rob@100 17236 var xplot2 = draw[8] * x1 + draw[9] * x2 + draw[10] * x3 + draw[11] * x4;
rob@100 17237 var xplot3 = draw[12] * x1 + draw[13] * x2 + draw[14] * x3 + draw[15] * x4;
rob@100 17238
rob@100 17239 var yplot1 = draw[4] * y1 + draw[5] * y2 + draw[6] * y3 + draw[7] * y4;
rob@100 17240 var yplot2 = draw[8] * y1 + draw[9] * y2 + draw[10] * y3 + draw[11] * y4;
rob@100 17241 var yplot3 = draw[12] * y1 + draw[13] * y2 + draw[14] * y3 + draw[15] * y4;
rob@100 17242
rob@100 17243 var zplot1 = draw[4] * z1 + draw[5] * z2 + draw[6] * z3 + draw[7] * z4;
rob@100 17244 var zplot2 = draw[8] * z1 + draw[9] * z2 + draw[10] * z3 + draw[11] * z4;
rob@100 17245 var zplot3 = draw[12] * z1 + draw[13] * z2 + draw[14] * z3 + draw[15] * z4;
rob@100 17246
rob@100 17247 p.vertex(x0, y0, z0);
rob@100 17248 for (var j = 0; j < curveDet; j++) {
rob@100 17249 x0 += xplot1; xplot1 += xplot2; xplot2 += xplot3;
rob@100 17250 y0 += yplot1; yplot1 += yplot2; yplot2 += yplot3;
rob@100 17251 z0 += zplot1; zplot1 += zplot2; zplot2 += zplot3;
rob@100 17252 p.vertex(x0, y0, z0);
rob@100 17253 }
rob@100 17254 };
rob@100 17255
rob@100 17256 /**
rob@100 17257 * Specifies vertex coordinates for curves. This function may only be used between <b>beginShape()</b> and
rob@100 17258 * <b>endShape()</b> and only when there is no MODE parameter specified to <b>beginShape()</b>. The first and last points
rob@100 17259 * in a series of <b>curveVertex()</b> lines will be used to guide the beginning and end of a the curve. A minimum of four
rob@100 17260 * points is required to draw a tiny curve between the second and third points. Adding a fifth point with
rob@100 17261 * <b>curveVertex()</b> will draw the curve between the second, third, and fourth points. The <b>curveVertex()</b> function
rob@100 17262 * is an implementation of Catmull-Rom splines. Using the 3D version of requires rendering with P3D or OPENGL (see the
rob@100 17263 * Environment reference for more information). <br /> <br /><b>NOTE: </b> Fill does not work properly yet.
rob@100 17264 *
rob@100 17265 * @param {float | int} x The x-coordinate of the vertex
rob@100 17266 * @param {float | int} y The y-coordinate of the vertex
rob@100 17267 * @param {float | int} z The z-coordinate of the vertex
rob@100 17268 *
rob@100 17269 * @see curve
rob@100 17270 * @see beginShape
rob@100 17271 * @see endShape
rob@100 17272 * @see vertex
rob@100 17273 * @see bezierVertex
rob@100 17274 */
rob@100 17275 Drawing2D.prototype.curveVertex = function(x, y) {
rob@100 17276 isCurve = true;
rob@100 17277
rob@100 17278 p.vertex(x, y);
rob@100 17279 };
rob@100 17280
rob@100 17281 Drawing3D.prototype.curveVertex = function(x, y, z) {
rob@100 17282 isCurve = true;
rob@100 17283
rob@100 17284 if (!curveInited) {
rob@100 17285 curveInit();
rob@100 17286 }
rob@100 17287 var vert = [];
rob@100 17288 vert[0] = x;
rob@100 17289 vert[1] = y;
rob@100 17290 vert[2] = z;
rob@100 17291 curveVertArray.push(vert);
rob@100 17292 curveVertCount++;
rob@100 17293
rob@100 17294 if (curveVertCount > 3) {
rob@100 17295 curveVertexSegment( curveVertArray[curveVertCount-4][0],
rob@100 17296 curveVertArray[curveVertCount-4][1],
rob@100 17297 curveVertArray[curveVertCount-4][2],
rob@100 17298 curveVertArray[curveVertCount-3][0],
rob@100 17299 curveVertArray[curveVertCount-3][1],
rob@100 17300 curveVertArray[curveVertCount-3][2],
rob@100 17301 curveVertArray[curveVertCount-2][0],
rob@100 17302 curveVertArray[curveVertCount-2][1],
rob@100 17303 curveVertArray[curveVertCount-2][2],
rob@100 17304 curveVertArray[curveVertCount-1][0],
rob@100 17305 curveVertArray[curveVertCount-1][1],
rob@100 17306 curveVertArray[curveVertCount-1][2] );
rob@100 17307 }
rob@100 17308 };
rob@100 17309
rob@100 17310 /**
rob@100 17311 * The curve() function draws a curved line on the screen. The first and second parameters
rob@100 17312 * specify the beginning control point and the last two parameters specify
rob@100 17313 * the ending control point. The middle parameters specify the start and
rob@100 17314 * stop of the curve. Longer curves can be created by putting a series of
rob@100 17315 * <b>curve()</b> functions together or using <b>curveVertex()</b>.
rob@100 17316 * An additional function called <b>curveTightness()</b> provides control
rob@100 17317 * for the visual quality of the curve. The <b>curve()</b> function is an
rob@100 17318 * implementation of Catmull-Rom splines. Using the 3D version of requires
rob@100 17319 * rendering with P3D or OPENGL (see the Environment reference for more
rob@100 17320 * information).
rob@100 17321 *
rob@100 17322 * @param {int|float} x1 coordinates for the beginning control point
rob@100 17323 * @param {int|float} y1 coordinates for the beginning control point
rob@100 17324 * @param {int|float} z1 coordinates for the beginning control point
rob@100 17325 * @param {int|float} x2 coordinates for the first point
rob@100 17326 * @param {int|float} y2 coordinates for the first point
rob@100 17327 * @param {int|float} z2 coordinates for the first point
rob@100 17328 * @param {int|float} x3 coordinates for the second point
rob@100 17329 * @param {int|float} y3 coordinates for the second point
rob@100 17330 * @param {int|float} z3 coordinates for the second point
rob@100 17331 * @param {int|float} x4 coordinates for the ending control point
rob@100 17332 * @param {int|float} y4 coordinates for the ending control point
rob@100 17333 * @param {int|float} z4 coordinates for the ending control point
rob@100 17334 *
rob@100 17335 * @see #curveVertex()
rob@100 17336 * @see #curveTightness()
rob@100 17337 * @see #bezier()
rob@100 17338 */
rob@100 17339 Drawing2D.prototype.curve = function(x1, y1, x2, y2, x3, y3, x4, y4) {
rob@100 17340 p.beginShape();
rob@100 17341 p.curveVertex(x1, y1);
rob@100 17342 p.curveVertex(x2, y2);
rob@100 17343 p.curveVertex(x3, y3);
rob@100 17344 p.curveVertex(x4, y4);
rob@100 17345 p.endShape();
rob@100 17346 };
rob@100 17347
rob@100 17348 Drawing3D.prototype.curve = function(x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4) {
rob@100 17349 if (z4 !== undef) {
rob@100 17350 p.beginShape();
rob@100 17351 p.curveVertex(x1, y1, z1);
rob@100 17352 p.curveVertex(x2, y2, z2);
rob@100 17353 p.curveVertex(x3, y3, z3);
rob@100 17354 p.curveVertex(x4, y4, z4);
rob@100 17355 p.endShape();
rob@100 17356 return;
rob@100 17357 }
rob@100 17358 p.beginShape();
rob@100 17359 p.curveVertex(x1, y1);
rob@100 17360 p.curveVertex(z1, x2);
rob@100 17361 p.curveVertex(y2, z2);
rob@100 17362 p.curveVertex(x3, y3);
rob@100 17363 p.endShape();
rob@100 17364 };
rob@100 17365
rob@100 17366 /**
rob@100 17367 * The curveTightness() function modifies the quality of forms created with <b>curve()</b> and
rob@100 17368 * <b>curveVertex()</b>. The parameter <b>squishy</b> determines how the
rob@100 17369 * curve fits to the vertex points. The value 0.0 is the default value for
rob@100 17370 * <b>squishy</b> (this value defines the curves to be Catmull-Rom splines)
rob@100 17371 * and the value 1.0 connects all the points with straight lines.
rob@100 17372 * Values within the range -5.0 and 5.0 will deform the curves but
rob@100 17373 * will leave them recognizable and as values increase in magnitude,
rob@100 17374 * they will continue to deform.
rob@100 17375 *
rob@100 17376 * @param {float} tightness amount of deformation from the original vertices
rob@100 17377 *
rob@100 17378 * @see #curve()
rob@100 17379 * @see #curveVertex()
rob@100 17380 *
rob@100 17381 */
rob@100 17382 p.curveTightness = function(tightness) {
rob@100 17383 curTightness = tightness;
rob@100 17384 };
rob@100 17385
rob@100 17386 /**
rob@100 17387 * The curveDetail() function sets the resolution at which curves display. The default value is 20.
rob@100 17388 * This function is only useful when using the P3D or OPENGL renderer.
rob@100 17389 *
rob@100 17390 * @param {int} detail resolution of the curves
rob@100 17391 *
rob@100 17392 * @see curve()
rob@100 17393 * @see curveVertex()
rob@100 17394 * @see curveTightness()
rob@100 17395 */
rob@100 17396 p.curveDetail = function(detail) {
rob@100 17397 curveDet = detail;
rob@100 17398 curveInit();
rob@100 17399 };
rob@100 17400
rob@100 17401 /**
rob@100 17402 * Modifies the location from which rectangles draw. The default mode is rectMode(CORNER), which
rob@100 17403 * specifies the location to be the upper left corner of the shape and uses the third and fourth
rob@100 17404 * parameters of rect() to specify the width and height. The syntax rectMode(CORNERS) uses the
rob@100 17405 * first and second parameters of rect() to set the location of one corner and uses the third and
rob@100 17406 * fourth parameters to set the opposite corner. The syntax rectMode(CENTER) draws the image from
rob@100 17407 * its center point and uses the third and forth parameters of rect() to specify the image's width
rob@100 17408 * and height. The syntax rectMode(RADIUS) draws the image from its center point and uses the third
rob@100 17409 * and forth parameters of rect() to specify half of the image's width and height. The parameter must
rob@100 17410 * be written in ALL CAPS because Processing is a case sensitive language. Note: In version 125, the
rob@100 17411 * mode named CENTER_RADIUS was shortened to RADIUS.
rob@100 17412 *
rob@100 17413 * @param {MODE} MODE Either CORNER, CORNERS, CENTER, or RADIUS
rob@100 17414 *
rob@100 17415 * @see rect
rob@100 17416 */
rob@100 17417 p.rectMode = function(aRectMode) {
rob@100 17418 curRectMode = aRectMode;
rob@100 17419 };
rob@100 17420
rob@100 17421 /**
rob@100 17422 * Modifies the location from which images draw. The default mode is imageMode(CORNER), which specifies
rob@100 17423 * the location to be the upper left corner and uses the fourth and fifth parameters of image() to set
rob@100 17424 * the image's width and height. The syntax imageMode(CORNERS) uses the second and third parameters of
rob@100 17425 * image() to set the location of one corner of the image and uses the fourth and fifth parameters to
rob@100 17426 * set the opposite corner. Use imageMode(CENTER) to draw images centered at the given x and y position.
rob@100 17427 * The parameter to imageMode() must be written in ALL CAPS because Processing is a case sensitive language.
rob@100 17428 *
rob@100 17429 * @param {MODE} MODE Either CORNER, CORNERS, or CENTER
rob@100 17430 *
rob@100 17431 * @see loadImage
rob@100 17432 * @see PImage
rob@100 17433 * @see image
rob@100 17434 * @see background
rob@100 17435 */
rob@100 17436 p.imageMode = function(mode) {
rob@100 17437 switch (mode) {
rob@100 17438 case PConstants.CORNER:
rob@100 17439 imageModeConvert = imageModeCorner;
rob@100 17440 break;
rob@100 17441 case PConstants.CORNERS:
rob@100 17442 imageModeConvert = imageModeCorners;
rob@100 17443 break;
rob@100 17444 case PConstants.CENTER:
rob@100 17445 imageModeConvert = imageModeCenter;
rob@100 17446 break;
rob@100 17447 default:
rob@100 17448 throw "Invalid imageMode";
rob@100 17449 }
rob@100 17450 };
rob@100 17451
rob@100 17452 /**
rob@100 17453 * The origin of the ellipse is modified by the ellipseMode() function. The default configuration is
rob@100 17454 * ellipseMode(CENTER), which specifies the location of the ellipse as the center of the shape. The RADIUS
rob@100 17455 * mode is the same, but the width and height parameters to ellipse() specify the radius of the ellipse,
rob@100 17456 * rather than the diameter. The CORNER mode draws the shape from the upper-left corner of its bounding box.
rob@100 17457 * The CORNERS mode uses the four parameters to ellipse() to set two opposing corners of the ellipse's bounding
rob@100 17458 * box. The parameter must be written in "ALL CAPS" because Processing is a case sensitive language.
rob@100 17459 *
rob@100 17460 * @param {MODE} MODE Either CENTER, RADIUS, CORNER, or CORNERS.
rob@100 17461 *
rob@100 17462 * @see ellipse
rob@100 17463 */
rob@100 17464 p.ellipseMode = function(aEllipseMode) {
rob@100 17465 curEllipseMode = aEllipseMode;
rob@100 17466 };
rob@100 17467
rob@100 17468 /**
rob@100 17469 * The arc() function draws an arc in the display window.
rob@100 17470 * Arcs are drawn along the outer edge of an ellipse defined by the
rob@100 17471 * <b>x</b>, <b>y</b>, <b>width</b> and <b>height</b> parameters.
rob@100 17472 * The origin or the arc's ellipse may be changed with the
rob@100 17473 * <b>ellipseMode()</b> function.
rob@100 17474 * The <b>start</b> and <b>stop</b> parameters specify the angles
rob@100 17475 * at which to draw the arc.
rob@100 17476 *
rob@100 17477 * @param {float} a x-coordinate of the arc's ellipse
rob@100 17478 * @param {float} b y-coordinate of the arc's ellipse
rob@100 17479 * @param {float} c width of the arc's ellipse
rob@100 17480 * @param {float} d height of the arc's ellipse
rob@100 17481 * @param {float} start angle to start the arc, specified in radians
rob@100 17482 * @param {float} stop angle to stop the arc, specified in radians
rob@100 17483 *
rob@100 17484 * @see #ellipseMode()
rob@100 17485 * @see #ellipse()
rob@100 17486 */
rob@100 17487 p.arc = function(x, y, width, height, start, stop) {
rob@100 17488 if (width <= 0 || stop < start) { return; }
rob@100 17489
rob@100 17490 if (curEllipseMode === PConstants.CORNERS) {
rob@100 17491 width = width - x;
rob@100 17492 height = height - y;
rob@100 17493 } else if (curEllipseMode === PConstants.RADIUS) {
rob@100 17494 x = x - width;
rob@100 17495 y = y - height;
rob@100 17496 width = width * 2;
rob@100 17497 height = height * 2;
rob@100 17498 } else if (curEllipseMode === PConstants.CENTER) {
rob@100 17499 x = x - width/2;
rob@100 17500 y = y - height/2;
rob@100 17501 }
rob@100 17502 // make sure that we're starting at a useful point
rob@100 17503 while (start < 0) {
rob@100 17504 start += PConstants.TWO_PI;
rob@100 17505 stop += PConstants.TWO_PI;
rob@100 17506 }
rob@100 17507 if (stop - start > PConstants.TWO_PI) {
rob@100 17508 start = 0;
rob@100 17509 stop = PConstants.TWO_PI;
rob@100 17510 }
rob@100 17511 var hr = width / 2,
rob@100 17512 vr = height / 2,
rob@100 17513 centerX = x + hr,
rob@100 17514 centerY = y + vr,
rob@100 17515 startLUT = 0 | (0.5 + start * p.RAD_TO_DEG * 2),
rob@100 17516 stopLUT = 0 | (0.5 + stop * p.RAD_TO_DEG * 2),
rob@100 17517 i, j;
rob@100 17518 if (doFill) {
rob@100 17519 // shut off stroke for a minute
rob@100 17520 var savedStroke = doStroke;
rob@100 17521 doStroke = false;
rob@100 17522 p.beginShape();
rob@100 17523 p.vertex(centerX, centerY);
rob@100 17524 for (i = startLUT; i <= stopLUT; i++) {
rob@100 17525 j = i % PConstants.SINCOS_LENGTH;
rob@100 17526 p.vertex(centerX + cosLUT[j] * hr, centerY + sinLUT[j] * vr);
rob@100 17527 }
rob@100 17528 p.endShape(PConstants.CLOSE);
rob@100 17529 doStroke = savedStroke;
rob@100 17530 }
rob@100 17531
rob@100 17532 if (doStroke) {
rob@100 17533 // and doesn't include the first (center) vertex.
rob@100 17534 var savedFill = doFill;
rob@100 17535 doFill = false;
rob@100 17536 p.beginShape();
rob@100 17537 for (i = startLUT; i <= stopLUT; i++) {
rob@100 17538 j = i % PConstants.SINCOS_LENGTH;
rob@100 17539 p.vertex(centerX + cosLUT[j] * hr, centerY + sinLUT[j] * vr);
rob@100 17540 }
rob@100 17541 p.endShape();
rob@100 17542 doFill = savedFill;
rob@100 17543 }
rob@100 17544 };
rob@100 17545
rob@100 17546 /**
rob@100 17547 * Draws a line (a direct path between two points) to the screen. The version of line() with four parameters
rob@100 17548 * draws the line in 2D. To color a line, use the stroke() function. A line cannot be filled, therefore the
rob@100 17549 * fill() method will not affect the color of a line. 2D lines are drawn with a width of one pixel by default,
rob@100 17550 * but this can be changed with the strokeWeight() function. The version with six parameters allows the line
rob@100 17551 * to be placed anywhere within XYZ space. Drawing this shape in 3D using the z parameter requires the P3D or
rob@100 17552 * OPENGL parameter in combination with size.
rob@100 17553 *
rob@100 17554 * @param {int|float} x1 x-coordinate of the first point
rob@100 17555 * @param {int|float} y1 y-coordinate of the first point
rob@100 17556 * @param {int|float} z1 z-coordinate of the first point
rob@100 17557 * @param {int|float} x2 x-coordinate of the second point
rob@100 17558 * @param {int|float} y2 y-coordinate of the second point
rob@100 17559 * @param {int|float} z2 z-coordinate of the second point
rob@100 17560 *
rob@100 17561 * @see strokeWeight
rob@100 17562 * @see strokeJoin
rob@100 17563 * @see strokeCap
rob@100 17564 * @see beginShape
rob@100 17565 */
rob@100 17566 Drawing2D.prototype.line = function(x1, y1, x2, y2) {
rob@100 17567 if (!doStroke) {
rob@100 17568 return;
rob@100 17569 }
rob@100 17570 x1 = Math.round(x1);
rob@100 17571 x2 = Math.round(x2);
rob@100 17572 y1 = Math.round(y1);
rob@100 17573 y2 = Math.round(y2);
rob@100 17574 // A line is only defined if it has different start and end coordinates.
rob@100 17575 // If they are the same, we call point instead.
rob@100 17576 if (x1 === x2 && y1 === y2) {
rob@100 17577 p.point(x1, y1);
rob@100 17578 return;
rob@100 17579 }
rob@100 17580
rob@100 17581 var swap = undef,
rob@100 17582 lineCap = undef,
rob@100 17583 drawCrisp = true,
rob@100 17584 currentModelView = modelView.array(),
rob@100 17585 identityMatrix = [1, 0, 0, 0, 1, 0];
rob@100 17586 // Test if any transformations have been applied to the sketch
rob@100 17587 for (var i = 0; i < 6 && drawCrisp; i++) {
rob@100 17588 drawCrisp = currentModelView[i] === identityMatrix[i];
rob@100 17589 }
rob@100 17590 /* Draw crisp lines if the line is vertical or horizontal with the following method
rob@100 17591 * If any transformations have been applied to the sketch, don't make the line crisp
rob@100 17592 * If the line is directed up or to the left, reverse it by swapping x1/x2 or y1/y2
rob@100 17593 * Make the line 1 pixel longer to work around cross-platform canvas implementations
rob@100 17594 * If the lineWidth is odd, translate the line by 0.5 in the perpendicular direction
rob@100 17595 * Even lineWidths do not need to be translated because the canvas will draw them on pixel boundaries
rob@100 17596 * Change the cap to butt-end to work around cross-platform canvas implementations
rob@100 17597 * Reverse the translate and lineCap canvas state changes after drawing the line
rob@100 17598 */
rob@100 17599 if (drawCrisp) {
rob@100 17600 if (x1 === x2) {
rob@100 17601 if (y1 > y2) {
rob@100 17602 swap = y1;
rob@100 17603 y1 = y2;
rob@100 17604 y2 = swap;
rob@100 17605 }
rob@100 17606 y2++;
rob@100 17607 if (lineWidth % 2 === 1) {
rob@100 17608 curContext.translate(0.5, 0.0);
rob@100 17609 }
rob@100 17610 } else if (y1 === y2) {
rob@100 17611 if (x1 > x2) {
rob@100 17612 swap = x1;
rob@100 17613 x1 = x2;
rob@100 17614 x2 = swap;
rob@100 17615 }
rob@100 17616 x2++;
rob@100 17617 if (lineWidth % 2 === 1) {
rob@100 17618 curContext.translate(0.0, 0.5);
rob@100 17619 }
rob@100 17620 }
rob@100 17621 if (lineWidth === 1) {
rob@100 17622 lineCap = curContext.lineCap;
rob@100 17623 curContext.lineCap = 'butt';
rob@100 17624 }
rob@100 17625 }
rob@100 17626 curContext.beginPath();
rob@100 17627 curContext.moveTo(x1 || 0, y1 || 0);
rob@100 17628 curContext.lineTo(x2 || 0, y2 || 0);
rob@100 17629 executeContextStroke();
rob@100 17630 if (drawCrisp) {
rob@100 17631 if (x1 === x2 && lineWidth % 2 === 1) {
rob@100 17632 curContext.translate(-0.5, 0.0);
rob@100 17633 } else if (y1 === y2 && lineWidth % 2 === 1) {
rob@100 17634 curContext.translate(0.0, -0.5);
rob@100 17635 }
rob@100 17636 if (lineWidth === 1) {
rob@100 17637 curContext.lineCap = lineCap;
rob@100 17638 }
rob@100 17639 }
rob@100 17640 };
rob@100 17641
rob@100 17642 Drawing3D.prototype.line = function(x1, y1, z1, x2, y2, z2) {
rob@100 17643 if (y2 === undef || z2 === undef) { // 2D line called in 3D context
rob@100 17644 z2 = 0;
rob@100 17645 y2 = x2;
rob@100 17646 x2 = z1;
rob@100 17647 z1 = 0;
rob@100 17648 }
rob@100 17649
rob@100 17650 // a line is only defined if it has different start and end coordinates.
rob@100 17651 // If they are the same, we call point instead.
rob@100 17652 if (x1===x2 && y1===y2 && z1===z2) {
rob@100 17653 p.point(x1,y1,z1);
rob@100 17654 return;
rob@100 17655 }
rob@100 17656
rob@100 17657 var lineVerts = [x1, y1, z1, x2, y2, z2];
rob@100 17658
rob@100 17659 var view = new PMatrix3D();
rob@100 17660 view.scale(1, -1, 1);
rob@100 17661 view.apply(modelView.array());
rob@100 17662 view.transpose();
rob@100 17663
rob@100 17664 if (lineWidth > 0 && doStroke) {
rob@100 17665 curContext.useProgram(programObject2D);
rob@100 17666
rob@100 17667 uniformMatrix("uModel2d", programObject2D, "uModel", false, [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1]);
rob@100 17668 uniformMatrix("uView2d", programObject2D, "uView", false, view.array());
rob@100 17669
rob@100 17670 uniformf("uColor2d", programObject2D, "uColor", strokeStyle);
rob@100 17671 uniformi("uIsDrawingText", programObject2D, "uIsDrawingText", false);
rob@100 17672
rob@100 17673 vertexAttribPointer("aVertex2d", programObject2D, "aVertex", 3, lineBuffer);
rob@100 17674 disableVertexAttribPointer("aTextureCoord2d", programObject2D, "aTextureCoord");
rob@100 17675
rob@100 17676 curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array(lineVerts), curContext.STREAM_DRAW);
rob@100 17677 curContext.drawArrays(curContext.LINES, 0, 2);
rob@100 17678 }
rob@100 17679 };
rob@100 17680
rob@100 17681 /**
rob@100 17682 * Draws a Bezier curve on the screen. These curves are defined by a series of anchor and control points. The first
rob@100 17683 * two parameters specify the first anchor point and the last two parameters specify the other anchor point. The
rob@100 17684 * middle parameters specify the control points which define the shape of the curve. Bezier curves were developed
rob@100 17685 * by French engineer Pierre Bezier. Using the 3D version of requires rendering with P3D or OPENGL (see the
rob@100 17686 * Environment reference for more information).
rob@100 17687 *
rob@100 17688 * @param {int | float} x1,y1,z1 coordinates for the first anchor point
rob@100 17689 * @param {int | float} cx1,cy1,cz1 coordinates for the first control point
rob@100 17690 * @param {int | float} cx2,cy2,cz2 coordinates for the second control point
rob@100 17691 * @param {int | float} x2,y2,z2 coordinates for the second anchor point
rob@100 17692 *
rob@100 17693 * @see bezierVertex
rob@100 17694 * @see curve
rob@100 17695 */
rob@100 17696 Drawing2D.prototype.bezier = function() {
rob@100 17697 if (arguments.length !== 8) {
rob@100 17698 throw("You must use 8 parameters for bezier() in 2D mode");
rob@100 17699 }
rob@100 17700
rob@100 17701 p.beginShape();
rob@100 17702 p.vertex( arguments[0], arguments[1] );
rob@100 17703 p.bezierVertex( arguments[2], arguments[3],
rob@100 17704 arguments[4], arguments[5],
rob@100 17705 arguments[6], arguments[7] );
rob@100 17706 p.endShape();
rob@100 17707 };
rob@100 17708
rob@100 17709 Drawing3D.prototype.bezier = function() {
rob@100 17710 if (arguments.length !== 12) {
rob@100 17711 throw("You must use 12 parameters for bezier() in 3D mode");
rob@100 17712 }
rob@100 17713
rob@100 17714 p.beginShape();
rob@100 17715 p.vertex( arguments[0], arguments[1], arguments[2] );
rob@100 17716 p.bezierVertex( arguments[3], arguments[4], arguments[5],
rob@100 17717 arguments[6], arguments[7], arguments[8],
rob@100 17718 arguments[9], arguments[10], arguments[11] );
rob@100 17719 p.endShape();
rob@100 17720 };
rob@100 17721
rob@100 17722 /**
rob@100 17723 * Sets the resolution at which Beziers display. The default value is 20. This function is only useful when using the P3D
rob@100 17724 * or OPENGL renderer as the default (JAVA2D) renderer does not use this information.
rob@100 17725 *
rob@100 17726 * @param {int} detail resolution of the curves
rob@100 17727 *
rob@100 17728 * @see curve
rob@100 17729 * @see curveVertex
rob@100 17730 * @see curveTightness
rob@100 17731 */
rob@100 17732 p.bezierDetail = function( detail ){
rob@100 17733 bezDetail = detail;
rob@100 17734 };
rob@100 17735
rob@100 17736 /**
rob@100 17737 * The bezierPoint() function evalutes quadratic bezier at point t for points a, b, c, d.
rob@100 17738 * The parameter t varies between 0 and 1. The a and d parameters are the
rob@100 17739 * on-curve points, b and c are the control points. To make a two-dimensional
rob@100 17740 * curve, call this function once with the x coordinates and a second time
rob@100 17741 * with the y coordinates to get the location of a bezier curve at t.
rob@100 17742 *
rob@100 17743 * @param {float} a coordinate of first point on the curve
rob@100 17744 * @param {float} b coordinate of first control point
rob@100 17745 * @param {float} c coordinate of second control point
rob@100 17746 * @param {float} d coordinate of second point on the curve
rob@100 17747 * @param {float} t value between 0 and 1
rob@100 17748 *
rob@100 17749 * @see #bezier()
rob@100 17750 * @see #bezierVertex()
rob@100 17751 * @see #curvePoint()
rob@100 17752 */
rob@100 17753 p.bezierPoint = function(a, b, c, d, t) {
rob@100 17754 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;
rob@100 17755 };
rob@100 17756
rob@100 17757 /**
rob@100 17758 * The bezierTangent() function calculates the tangent of a point on a Bezier curve. There is a good
rob@100 17759 * definition of "tangent" at Wikipedia: <a href="http://en.wikipedia.org/wiki/Tangent" target="new">http://en.wikipedia.org/wiki/Tangent</a>
rob@100 17760 *
rob@100 17761 * @param {float} a coordinate of first point on the curve
rob@100 17762 * @param {float} b coordinate of first control point
rob@100 17763 * @param {float} c coordinate of second control point
rob@100 17764 * @param {float} d coordinate of second point on the curve
rob@100 17765 * @param {float} t value between 0 and 1
rob@100 17766 *
rob@100 17767 * @see #bezier()
rob@100 17768 * @see #bezierVertex()
rob@100 17769 * @see #curvePoint()
rob@100 17770 */
rob@100 17771 p.bezierTangent = function(a, b, c, d, t) {
rob@100 17772 return (3 * t * t * (-a + 3 * b - 3 * c + d) + 6 * t * (a - 2 * b + c) + 3 * (-a + b));
rob@100 17773 };
rob@100 17774
rob@100 17775 /**
rob@100 17776 * The curvePoint() function evalutes the Catmull-Rom curve at point t for points a, b, c, d. The
rob@100 17777 * parameter t varies between 0 and 1, a and d are points on the curve,
rob@100 17778 * and b and c are the control points. This can be done once with the x
rob@100 17779 * coordinates and a second time with the y coordinates to get the
rob@100 17780 * location of a curve at t.
rob@100 17781 *
rob@100 17782 * @param {int|float} a coordinate of first point on the curve
rob@100 17783 * @param {int|float} b coordinate of second point on the curve
rob@100 17784 * @param {int|float} c coordinate of third point on the curve
rob@100 17785 * @param {int|float} d coordinate of fourth point on the curve
rob@100 17786 * @param {float} t value between 0 and 1
rob@100 17787 *
rob@100 17788 * @see #curve()
rob@100 17789 * @see #curveVertex()
rob@100 17790 * @see #bezierPoint()
rob@100 17791 */
rob@100 17792 p.curvePoint = function(a, b, c, d, t) {
rob@100 17793 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);
rob@100 17794 };
rob@100 17795
rob@100 17796 /**
rob@100 17797 * The curveTangent() function calculates the tangent of a point on a Catmull-Rom curve.
rob@100 17798 * There is a good definition of "tangent" at Wikipedia: <a href="http://en.wikipedia.org/wiki/Tangent" target="new">http://en.wikipedia.org/wiki/Tangent</a>.
rob@100 17799 *
rob@100 17800 * @param {int|float} a coordinate of first point on the curve
rob@100 17801 * @param {int|float} b coordinate of first control point
rob@100 17802 * @param {int|float} c coordinate of second control point
rob@100 17803 * @param {int|float} d coordinate of second point on the curve
rob@100 17804 * @param {float} t value between 0 and 1
rob@100 17805 *
rob@100 17806 * @see #curve()
rob@100 17807 * @see #curveVertex()
rob@100 17808 * @see #curvePoint()
rob@100 17809 * @see #bezierTangent()
rob@100 17810 */
rob@100 17811 p.curveTangent = function(a, b, c, d, t) {
rob@100 17812 return 0.5 * ((-a + c) + 2 * (2 * a - 5 * b + 4 * c - d) * t + 3 * (-a + 3 * b - 3 * c + d) * t * t);
rob@100 17813 };
rob@100 17814
rob@100 17815 /**
rob@100 17816 * A triangle is a plane created by connecting three points. The first two arguments specify the first point,
rob@100 17817 * the middle two arguments specify the second point, and the last two arguments specify the third point.
rob@100 17818 *
rob@100 17819 * @param {int | float} x1 x-coordinate of the first point
rob@100 17820 * @param {int | float} y1 y-coordinate of the first point
rob@100 17821 * @param {int | float} x2 x-coordinate of the second point
rob@100 17822 * @param {int | float} y2 y-coordinate of the second point
rob@100 17823 * @param {int | float} x3 x-coordinate of the third point
rob@100 17824 * @param {int | float} y3 y-coordinate of the third point
rob@100 17825 */
rob@100 17826 p.triangle = function(x1, y1, x2, y2, x3, y3) {
rob@100 17827 p.beginShape(PConstants.TRIANGLES);
rob@100 17828 p.vertex(x1, y1, 0);
rob@100 17829 p.vertex(x2, y2, 0);
rob@100 17830 p.vertex(x3, y3, 0);
rob@100 17831 p.endShape();
rob@100 17832 };
rob@100 17833
rob@100 17834 /**
rob@100 17835 * A quad is a quadrilateral, a four sided polygon. It is similar to a rectangle, but the angles between its
rob@100 17836 * edges are not constrained to ninety degrees. The first pair of parameters (x1,y1) sets the first vertex
rob@100 17837 * and the subsequent pairs should proceed clockwise or counter-clockwise around the defined shape.
rob@100 17838 *
rob@100 17839 * @param {float | int} x1 x-coordinate of the first corner
rob@100 17840 * @param {float | int} y1 y-coordinate of the first corner
rob@100 17841 * @param {float | int} x2 x-coordinate of the second corner
rob@100 17842 * @param {float | int} y2 y-coordinate of the second corner
rob@100 17843 * @param {float | int} x3 x-coordinate of the third corner
rob@100 17844 * @param {float | int} y3 y-coordinate of the third corner
rob@100 17845 * @param {float | int} x4 x-coordinate of the fourth corner
rob@100 17846 * @param {float | int} y4 y-coordinate of the fourth corner
rob@100 17847 */
rob@100 17848 p.quad = function(x1, y1, x2, y2, x3, y3, x4, y4) {
rob@100 17849 p.beginShape(PConstants.QUADS);
rob@100 17850 p.vertex(x1, y1, 0);
rob@100 17851 p.vertex(x2, y2, 0);
rob@100 17852 p.vertex(x3, y3, 0);
rob@100 17853 p.vertex(x4, y4, 0);
rob@100 17854 p.endShape();
rob@100 17855 };
rob@100 17856
rob@100 17857 var roundedRect$2d = function(x, y, width, height, tl, tr, br, bl) {
rob@100 17858 if (bl === undef) {
rob@100 17859 tr = tl;
rob@100 17860 br = tl;
rob@100 17861 bl = tl;
rob@100 17862 }
rob@100 17863 var halfWidth = width / 2,
rob@100 17864 halfHeight = height / 2;
rob@100 17865 if (tl > halfWidth || tl > halfHeight) {
rob@100 17866 tl = Math.min(halfWidth, halfHeight);
rob@100 17867 }
rob@100 17868 if (tr > halfWidth || tr > halfHeight) {
rob@100 17869 tr = Math.min(halfWidth, halfHeight);
rob@100 17870 }
rob@100 17871 if (br > halfWidth || br > halfHeight) {
rob@100 17872 br = Math.min(halfWidth, halfHeight);
rob@100 17873 }
rob@100 17874 if (bl > halfWidth || bl > halfHeight) {
rob@100 17875 bl = Math.min(halfWidth, halfHeight);
rob@100 17876 }
rob@100 17877 // Translate the stroke by (0.5, 0.5) to draw a crisp border
rob@100 17878 if (!doFill || doStroke) {
rob@100 17879 curContext.translate(0.5, 0.5);
rob@100 17880 }
rob@100 17881 curContext.beginPath();
rob@100 17882 curContext.moveTo(x + tl, y);
rob@100 17883 curContext.lineTo(x + width - tr, y);
rob@100 17884 curContext.quadraticCurveTo(x + width, y, x + width, y + tr);
rob@100 17885 curContext.lineTo(x + width, y + height - br);
rob@100 17886 curContext.quadraticCurveTo(x + width, y + height, x + width - br, y + height);
rob@100 17887 curContext.lineTo(x + bl, y + height);
rob@100 17888 curContext.quadraticCurveTo(x, y + height, x, y + height - bl);
rob@100 17889 curContext.lineTo(x, y + tl);
rob@100 17890 curContext.quadraticCurveTo(x, y, x + tl, y);
rob@100 17891 if (!doFill || doStroke) {
rob@100 17892 curContext.translate(-0.5, -0.5);
rob@100 17893 }
rob@100 17894 executeContextFill();
rob@100 17895 executeContextStroke();
rob@100 17896 };
rob@100 17897
rob@100 17898 /**
rob@100 17899 * Draws a rectangle to the screen. A rectangle is a four-sided shape with every angle at ninety
rob@100 17900 * degrees. The first two parameters set the location, the third sets the width, and the fourth
rob@100 17901 * sets the height. The origin is changed with the rectMode() function.
rob@100 17902 *
rob@100 17903 * @param {int|float} x x-coordinate of the rectangle
rob@100 17904 * @param {int|float} y y-coordinate of the rectangle
rob@100 17905 * @param {int|float} width width of the rectangle
rob@100 17906 * @param {int|float} height height of the rectangle
rob@100 17907 *
rob@100 17908 * @see rectMode
rob@100 17909 * @see quad
rob@100 17910 */
rob@100 17911 Drawing2D.prototype.rect = function(x, y, width, height, tl, tr, br, bl) {
rob@100 17912 if (!width && !height) {
rob@100 17913 return;
rob@100 17914 }
rob@100 17915
rob@100 17916 if (curRectMode === PConstants.CORNERS) {
rob@100 17917 width -= x;
rob@100 17918 height -= y;
rob@100 17919 } else if (curRectMode === PConstants.RADIUS) {
rob@100 17920 width *= 2;
rob@100 17921 height *= 2;
rob@100 17922 x -= width / 2;
rob@100 17923 y -= height / 2;
rob@100 17924 } else if (curRectMode === PConstants.CENTER) {
rob@100 17925 x -= width / 2;
rob@100 17926 y -= height / 2;
rob@100 17927 }
rob@100 17928
rob@100 17929 x = Math.round(x);
rob@100 17930 y = Math.round(y);
rob@100 17931 width = Math.round(width);
rob@100 17932 height = Math.round(height);
rob@100 17933 if (tl !== undef) {
rob@100 17934 roundedRect$2d(x, y, width, height, tl, tr, br, bl);
rob@100 17935 return;
rob@100 17936 }
rob@100 17937
rob@100 17938 // Translate the line by (0.5, 0.5) to draw a crisp rectangle border
rob@100 17939 if (doStroke && lineWidth % 2 === 1) {
rob@100 17940 curContext.translate(0.5, 0.5);
rob@100 17941 }
rob@100 17942 curContext.beginPath();
rob@100 17943 curContext.rect(x, y, width, height);
rob@100 17944 executeContextFill();
rob@100 17945 executeContextStroke();
rob@100 17946 if (doStroke && lineWidth % 2 === 1) {
rob@100 17947 curContext.translate(-0.5, -0.5);
rob@100 17948 }
rob@100 17949 };
rob@100 17950
rob@100 17951 Drawing3D.prototype.rect = function(x, y, width, height, tl, tr, br, bl) {
rob@100 17952 if (tl !== undef) {
rob@100 17953 throw "rect() with rounded corners is not supported in 3D mode";
rob@100 17954 }
rob@100 17955
rob@100 17956 if (curRectMode === PConstants.CORNERS) {
rob@100 17957 width -= x;
rob@100 17958 height -= y;
rob@100 17959 } else if (curRectMode === PConstants.RADIUS) {
rob@100 17960 width *= 2;
rob@100 17961 height *= 2;
rob@100 17962 x -= width / 2;
rob@100 17963 y -= height / 2;
rob@100 17964 } else if (curRectMode === PConstants.CENTER) {
rob@100 17965 x -= width / 2;
rob@100 17966 y -= height / 2;
rob@100 17967 }
rob@100 17968
rob@100 17969 // Modeling transformation
rob@100 17970 var model = new PMatrix3D();
rob@100 17971 model.translate(x, y, 0);
rob@100 17972 model.scale(width, height, 1);
rob@100 17973 model.transpose();
rob@100 17974
rob@100 17975 // viewing transformation needs to have Y flipped
rob@100 17976 // becuase that's what Processing does.
rob@100 17977 var view = new PMatrix3D();
rob@100 17978 view.scale(1, -1, 1);
rob@100 17979 view.apply(modelView.array());
rob@100 17980 view.transpose();
rob@100 17981
rob@100 17982 if (lineWidth > 0 && doStroke) {
rob@100 17983 curContext.useProgram(programObject2D);
rob@100 17984 uniformMatrix("uModel2d", programObject2D, "uModel", false, model.array());
rob@100 17985 uniformMatrix("uView2d", programObject2D, "uView", false, view.array());
rob@100 17986 uniformf("uColor2d", programObject2D, "uColor", strokeStyle);
rob@100 17987 uniformi("uIsDrawingText2d", programObject2D, "uIsDrawingText", false);
rob@100 17988 vertexAttribPointer("aVertex2d", programObject2D, "aVertex", 3, rectBuffer);
rob@100 17989 disableVertexAttribPointer("aTextureCoord2d", programObject2D, "aTextureCoord");
rob@100 17990 curContext.drawArrays(curContext.LINE_LOOP, 0, rectVerts.length / 3);
rob@100 17991 }
rob@100 17992
rob@100 17993 if (doFill) {
rob@100 17994 curContext.useProgram(programObject3D);
rob@100 17995 uniformMatrix("uModel3d", programObject3D, "uModel", false, model.array());
rob@100 17996 uniformMatrix("uView3d", programObject3D, "uView", false, view.array());
rob@100 17997
rob@100 17998 // fix stitching problems. (lines get occluded by triangles
rob@100 17999 // since they share the same depth values). This is not entirely
rob@100 18000 // working, but it's a start for drawing the outline. So
rob@100 18001 // developers can start playing around with styles.
rob@100 18002 curContext.enable(curContext.POLYGON_OFFSET_FILL);
rob@100 18003 curContext.polygonOffset(1, 1);
rob@100 18004
rob@100 18005 uniformf("color3d", programObject3D, "uColor", fillStyle);
rob@100 18006
rob@100 18007 if(lightCount > 0){
rob@100 18008 var v = new PMatrix3D();
rob@100 18009 v.set(view);
rob@100 18010
rob@100 18011 var m = new PMatrix3D();
rob@100 18012 m.set(model);
rob@100 18013
rob@100 18014 v.mult(m);
rob@100 18015
rob@100 18016 var normalMatrix = new PMatrix3D();
rob@100 18017 normalMatrix.set(v);
rob@100 18018 normalMatrix.invert();
rob@100 18019 normalMatrix.transpose();
rob@100 18020
rob@100 18021 uniformMatrix("uNormalTransform3d", programObject3D, "uNormalTransform", false, normalMatrix.array());
rob@100 18022 vertexAttribPointer("aNormal3d", programObject3D, "aNormal", 3, rectNormBuffer);
rob@100 18023 }
rob@100 18024 else{
rob@100 18025 disableVertexAttribPointer("normal3d", programObject3D, "aNormal");
rob@100 18026 }
rob@100 18027
rob@100 18028 vertexAttribPointer("vertex3d", programObject3D, "aVertex", 3, rectBuffer);
rob@100 18029
rob@100 18030 curContext.drawArrays(curContext.TRIANGLE_FAN, 0, rectVerts.length / 3);
rob@100 18031 curContext.disable(curContext.POLYGON_OFFSET_FILL);
rob@100 18032 }
rob@100 18033 };
rob@100 18034
rob@100 18035 /**
rob@100 18036 * Draws an ellipse (oval) in the display window. An ellipse with an equal <b>width</b> and <b>height</b> is a circle.
rob@100 18037 * The first two parameters set the location, the third sets the width, and the fourth sets the height. The origin may be
rob@100 18038 * changed with the <b>ellipseMode()</b> function.
rob@100 18039 *
rob@100 18040 * @param {float|int} x x-coordinate of the ellipse
rob@100 18041 * @param {float|int} y y-coordinate of the ellipse
rob@100 18042 * @param {float|int} width width of the ellipse
rob@100 18043 * @param {float|int} height height of the ellipse
rob@100 18044 *
rob@100 18045 * @see ellipseMode
rob@100 18046 */
rob@100 18047 Drawing2D.prototype.ellipse = function(x, y, width, height) {
rob@100 18048 x = x || 0;
rob@100 18049 y = y || 0;
rob@100 18050
rob@100 18051 if (width <= 0 && height <= 0) {
rob@100 18052 return;
rob@100 18053 }
rob@100 18054
rob@100 18055 if (curEllipseMode === PConstants.RADIUS) {
rob@100 18056 width *= 2;
rob@100 18057 height *= 2;
rob@100 18058 } else if (curEllipseMode === PConstants.CORNERS) {
rob@100 18059 width = width - x;
rob@100 18060 height = height - y;
rob@100 18061 x += width / 2;
rob@100 18062 y += height / 2;
rob@100 18063 } else if (curEllipseMode === PConstants.CORNER) {
rob@100 18064 x += width / 2;
rob@100 18065 y += height / 2;
rob@100 18066 }
rob@100 18067
rob@100 18068 // Shortcut for drawing a 2D circle
rob@100 18069 if (width === height) {
rob@100 18070 curContext.beginPath();
rob@100 18071 curContext.arc(x, y, width / 2, 0, PConstants.TWO_PI, false);
rob@100 18072 executeContextFill();
rob@100 18073 executeContextStroke();
rob@100 18074 } else {
rob@100 18075 var w = width / 2,
rob@100 18076 h = height / 2,
rob@100 18077 C = 0.5522847498307933,
rob@100 18078 c_x = C * w,
rob@100 18079 c_y = C * h;
rob@100 18080
rob@100 18081 p.beginShape();
rob@100 18082 p.vertex(x + w, y);
rob@100 18083 p.bezierVertex(x + w, y - c_y, x + c_x, y - h, x, y - h);
rob@100 18084 p.bezierVertex(x - c_x, y - h, x - w, y - c_y, x - w, y);
rob@100 18085 p.bezierVertex(x - w, y + c_y, x - c_x, y + h, x, y + h);
rob@100 18086 p.bezierVertex(x + c_x, y + h, x + w, y + c_y, x + w, y);
rob@100 18087 p.endShape();
rob@100 18088 }
rob@100 18089 };
rob@100 18090
rob@100 18091 Drawing3D.prototype.ellipse = function(x, y, width, height) {
rob@100 18092 x = x || 0;
rob@100 18093 y = y || 0;
rob@100 18094
rob@100 18095 if (width <= 0 && height <= 0) {
rob@100 18096 return;
rob@100 18097 }
rob@100 18098
rob@100 18099 if (curEllipseMode === PConstants.RADIUS) {
rob@100 18100 width *= 2;
rob@100 18101 height *= 2;
rob@100 18102 } else if (curEllipseMode === PConstants.CORNERS) {
rob@100 18103 width = width - x;
rob@100 18104 height = height - y;
rob@100 18105 x += width / 2;
rob@100 18106 y += height / 2;
rob@100 18107 } else if (curEllipseMode === PConstants.CORNER) {
rob@100 18108 x += width / 2;
rob@100 18109 y += height / 2;
rob@100 18110 }
rob@100 18111
rob@100 18112 var w = width / 2,
rob@100 18113 h = height / 2,
rob@100 18114 C = 0.5522847498307933,
rob@100 18115 c_x = C * w,
rob@100 18116 c_y = C * h;
rob@100 18117
rob@100 18118 p.beginShape();
rob@100 18119 p.vertex(x + w, y);
rob@100 18120 p.bezierVertex(x + w, y - c_y, 0, x + c_x, y - h, 0, x, y - h, 0);
rob@100 18121 p.bezierVertex(x - c_x, y - h, 0, x - w, y - c_y, 0, x - w, y, 0);
rob@100 18122 p.bezierVertex(x - w, y + c_y, 0, x - c_x, y + h, 0, x, y + h, 0);
rob@100 18123 p.bezierVertex(x + c_x, y + h, 0, x + w, y + c_y, 0, x + w, y, 0);
rob@100 18124 p.endShape();
rob@100 18125
rob@100 18126 if (doFill) {
rob@100 18127 //temporary workaround to not working fills for bezier -- will fix later
rob@100 18128 var xAv = 0, yAv = 0, i, j;
rob@100 18129 for (i = 0; i < vertArray.length; i++) {
rob@100 18130 xAv += vertArray[i][0];
rob@100 18131 yAv += vertArray[i][1];
rob@100 18132 }
rob@100 18133 xAv /= vertArray.length;
rob@100 18134 yAv /= vertArray.length;
rob@100 18135 var vert = [],
rob@100 18136 fillVertArray = [],
rob@100 18137 colorVertArray = [];
rob@100 18138 vert[0] = xAv;
rob@100 18139 vert[1] = yAv;
rob@100 18140 vert[2] = 0;
rob@100 18141 vert[3] = 0;
rob@100 18142 vert[4] = 0;
rob@100 18143 vert[5] = fillStyle[0];
rob@100 18144 vert[6] = fillStyle[1];
rob@100 18145 vert[7] = fillStyle[2];
rob@100 18146 vert[8] = fillStyle[3];
rob@100 18147 vert[9] = strokeStyle[0];
rob@100 18148 vert[10] = strokeStyle[1];
rob@100 18149 vert[11] = strokeStyle[2];
rob@100 18150 vert[12] = strokeStyle[3];
rob@100 18151 vert[13] = normalX;
rob@100 18152 vert[14] = normalY;
rob@100 18153 vert[15] = normalZ;
rob@100 18154 vertArray.unshift(vert);
rob@100 18155 for (i = 0; i < vertArray.length; i++) {
rob@100 18156 for (j = 0; j < 3; j++) {
rob@100 18157 fillVertArray.push(vertArray[i][j]);
rob@100 18158 }
rob@100 18159 for (j = 5; j < 9; j++) {
rob@100 18160 colorVertArray.push(vertArray[i][j]);
rob@100 18161 }
rob@100 18162 }
rob@100 18163 fill3D(fillVertArray, "TRIANGLE_FAN", colorVertArray);
rob@100 18164 }
rob@100 18165 };
rob@100 18166
rob@100 18167 /**
rob@100 18168 * Sets the current normal vector. This is for drawing three dimensional shapes and surfaces and
rob@100 18169 * specifies a vector perpendicular to the surface of the shape which determines how lighting affects
rob@100 18170 * it. Processing attempts to automatically assign normals to shapes, but since that's imperfect,
rob@100 18171 * this is a better option when you want more control. This function is identical to glNormal3f() in OpenGL.
rob@100 18172 *
rob@100 18173 * @param {float} nx x direction
rob@100 18174 * @param {float} ny y direction
rob@100 18175 * @param {float} nz z direction
rob@100 18176 *
rob@100 18177 * @see beginShape
rob@100 18178 * @see endShape
rob@100 18179 * @see lights
rob@100 18180 */
rob@100 18181 p.normal = function(nx, ny, nz) {
rob@100 18182 if (arguments.length !== 3 || !(typeof nx === "number" && typeof ny === "number" && typeof nz === "number")) {
rob@100 18183 throw "normal() requires three numeric arguments.";
rob@100 18184 }
rob@100 18185
rob@100 18186 normalX = nx;
rob@100 18187 normalY = ny;
rob@100 18188 normalZ = nz;
rob@100 18189
rob@100 18190 if (curShape !== 0) {
rob@100 18191 if (normalMode === PConstants.NORMAL_MODE_AUTO) {
rob@100 18192 normalMode = PConstants.NORMAL_MODE_SHAPE;
rob@100 18193 } else if (normalMode === PConstants.NORMAL_MODE_SHAPE) {
rob@100 18194 normalMode = PConstants.NORMAL_MODE_VERTEX;
rob@100 18195 }
rob@100 18196 }
rob@100 18197 };
rob@100 18198
rob@100 18199 ////////////////////////////////////////////////////////////////////////////
rob@100 18200 // Raster drawing functions
rob@100 18201 ////////////////////////////////////////////////////////////////////////////
rob@100 18202
rob@100 18203 /**
rob@100 18204 * Saves an image from the display window. Images are saved in TIFF, TARGA, JPEG, and PNG format
rob@100 18205 * depending on the extension within the filename parameter. For example, "image.tif" will have
rob@100 18206 * a TIFF image and "image.png" will save a PNG image. If no extension is included in the filename,
rob@100 18207 * the image will save in TIFF format and .tif will be added to the name. These files are saved to
rob@100 18208 * the sketch's folder, which may be opened by selecting "Show sketch folder" from the "Sketch" menu.
rob@100 18209 * It is not possible to use save() while running the program in a web browser. All images saved
rob@100 18210 * from the main drawing window will be opaque. To save images without a background, use createGraphics().
rob@100 18211 *
rob@100 18212 * @param {String} filename any sequence of letters and numbers
rob@100 18213 *
rob@100 18214 * @see saveFrame
rob@100 18215 * @see createGraphics
rob@100 18216 */
rob@100 18217 p.save = function(file, img) {
rob@100 18218 // file is unused at the moment
rob@100 18219 // may implement this differently in later release
rob@100 18220 if (img !== undef) {
rob@100 18221 return window.open(img.toDataURL(),"_blank");
rob@100 18222 }
rob@100 18223 return window.open(p.externals.canvas.toDataURL(),"_blank");
rob@100 18224 };
rob@100 18225
rob@100 18226 var saveNumber = 0;
rob@100 18227
rob@100 18228 p.saveFrame = function(file) {
rob@100 18229 if(file === undef) {
rob@100 18230 // use default name template if parameter is not specified
rob@100 18231 file = "screen-####.png";
rob@100 18232 }
rob@100 18233 // Increment changeable part: screen-0000.png, screen-0001.png, ...
rob@100 18234 var frameFilename = file.replace(/#+/, function(all) {
rob@100 18235 var s = "" + (saveNumber++);
rob@100 18236 while(s.length < all.length) {
rob@100 18237 s = "0" + s;
rob@100 18238 }
rob@100 18239 return s;
rob@100 18240 });
rob@100 18241 p.save(frameFilename);
rob@100 18242 };
rob@100 18243
rob@100 18244 var utilityContext2d = document.createElement("canvas").getContext("2d");
rob@100 18245
rob@100 18246 var canvasDataCache = [undef, undef, undef]; // we need three for now
rob@100 18247
rob@100 18248 function getCanvasData(obj, w, h) {
rob@100 18249 var canvasData = canvasDataCache.shift();
rob@100 18250
rob@100 18251 if (canvasData === undef) {
rob@100 18252 canvasData = {};
rob@100 18253 canvasData.canvas = document.createElement("canvas");
rob@100 18254 canvasData.context = canvasData.canvas.getContext('2d');
rob@100 18255 }
rob@100 18256
rob@100 18257 canvasDataCache.push(canvasData);
rob@100 18258
rob@100 18259 var canvas = canvasData.canvas, context = canvasData.context,
rob@100 18260 width = w || obj.width, height = h || obj.height;
rob@100 18261
rob@100 18262 canvas.width = width;
rob@100 18263 canvas.height = height;
rob@100 18264
rob@100 18265 if (!obj) {
rob@100 18266 context.clearRect(0, 0, width, height);
rob@100 18267 } else if ("data" in obj) { // ImageData
rob@100 18268 context.putImageData(obj, 0, 0);
rob@100 18269 } else {
rob@100 18270 context.clearRect(0, 0, width, height);
rob@100 18271 context.drawImage(obj, 0, 0, width, height);
rob@100 18272 }
rob@100 18273 return canvasData;
rob@100 18274 }
rob@100 18275
rob@100 18276 /**
rob@100 18277 * Handle the sketch code for pixels[] and pixels.length
rob@100 18278 * parser code converts pixels[] to getPixels()
rob@100 18279 * or setPixels(), .length becomes getLength()
rob@100 18280 */
rob@100 18281 function buildPixelsObject(pImage) {
rob@100 18282 return {
rob@100 18283
rob@100 18284 getLength: (function(aImg) {
rob@100 18285 return function() {
rob@100 18286 if (aImg.isRemote) {
rob@100 18287 throw "Image is loaded remotely. Cannot get length.";
rob@100 18288 } else {
rob@100 18289 return aImg.imageData.data.length ? aImg.imageData.data.length/4 : 0;
rob@100 18290 }
rob@100 18291 };
rob@100 18292 }(pImage)),
rob@100 18293
rob@100 18294 getPixel: (function(aImg) {
rob@100 18295 return function(i) {
rob@100 18296 var offset = i*4,
rob@100 18297 data = aImg.imageData.data;
rob@100 18298
rob@100 18299 if (aImg.isRemote) {
rob@100 18300 throw "Image is loaded remotely. Cannot get pixels.";
rob@100 18301 }
rob@100 18302
rob@100 18303 return (data[offset+3] << 24) & PConstants.ALPHA_MASK |
rob@100 18304 (data[offset] << 16) & PConstants.RED_MASK |
rob@100 18305 (data[offset+1] << 8) & PConstants.GREEN_MASK |
rob@100 18306 data[offset+2] & PConstants.BLUE_MASK;
rob@100 18307 };
rob@100 18308 }(pImage)),
rob@100 18309
rob@100 18310 setPixel: (function(aImg) {
rob@100 18311 return function(i, c) {
rob@100 18312 var offset = i*4,
rob@100 18313 data = aImg.imageData.data;
rob@100 18314
rob@100 18315 if (aImg.isRemote) {
rob@100 18316 throw "Image is loaded remotely. Cannot set pixel.";
rob@100 18317 }
rob@100 18318
rob@100 18319 data[offset+0] = (c & PConstants.RED_MASK) >>> 16;
rob@100 18320 data[offset+1] = (c & PConstants.GREEN_MASK) >>> 8;
rob@100 18321 data[offset+2] = (c & PConstants.BLUE_MASK);
rob@100 18322 data[offset+3] = (c & PConstants.ALPHA_MASK) >>> 24;
rob@100 18323 aImg.__isDirty = true;
rob@100 18324 };
rob@100 18325 }(pImage)),
rob@100 18326
rob@100 18327 toArray: (function(aImg) {
rob@100 18328 return function() {
rob@100 18329 var arr = [],
rob@100 18330 data = aImg.imageData.data,
rob@100 18331 length = aImg.width * aImg.height;
rob@100 18332
rob@100 18333 if (aImg.isRemote) {
rob@100 18334 throw "Image is loaded remotely. Cannot get pixels.";
rob@100 18335 }
rob@100 18336
rob@100 18337 for (var i = 0, offset = 0; i < length; i++, offset += 4) {
rob@100 18338 arr.push( (data[offset+3] << 24) & PConstants.ALPHA_MASK |
rob@100 18339 (data[offset] << 16) & PConstants.RED_MASK |
rob@100 18340 (data[offset+1] << 8) & PConstants.GREEN_MASK |
rob@100 18341 data[offset+2] & PConstants.BLUE_MASK );
rob@100 18342 }
rob@100 18343 return arr;
rob@100 18344 };
rob@100 18345 }(pImage)),
rob@100 18346
rob@100 18347 set: (function(aImg) {
rob@100 18348 return function(arr) {
rob@100 18349 var offset,
rob@100 18350 data,
rob@100 18351 c;
rob@100 18352 if (this.isRemote) {
rob@100 18353 throw "Image is loaded remotely. Cannot set pixels.";
rob@100 18354 }
rob@100 18355
rob@100 18356 data = aImg.imageData.data;
rob@100 18357 for (var i = 0, aL = arr.length; i < aL; i++) {
rob@100 18358 c = arr[i];
rob@100 18359 offset = i*4;
rob@100 18360
rob@100 18361 data[offset+0] = (c & PConstants.RED_MASK) >>> 16;
rob@100 18362 data[offset+1] = (c & PConstants.GREEN_MASK) >>> 8;
rob@100 18363 data[offset+2] = (c & PConstants.BLUE_MASK);
rob@100 18364 data[offset+3] = (c & PConstants.ALPHA_MASK) >>> 24;
rob@100 18365 }
rob@100 18366 aImg.__isDirty = true;
rob@100 18367 };
rob@100 18368 }(pImage))
rob@100 18369
rob@100 18370 };
rob@100 18371 }
rob@100 18372
rob@100 18373 /**
rob@100 18374 * Datatype for storing images. Processing can display .gif, .jpg, .tga, and .png images. Images may be
rob@100 18375 * displayed in 2D and 3D space. Before an image is used, it must be loaded with the loadImage() function.
rob@100 18376 * The PImage object contains fields for the width and height of the image, as well as an array called
rob@100 18377 * pixels[] which contains the values for every pixel in the image. A group of methods, described below,
rob@100 18378 * allow easy access to the image's pixels and alpha channel and simplify the process of compositing.
rob@100 18379 * Before using the pixels[] array, be sure to use the loadPixels() method on the image to make sure that the
rob@100 18380 * pixel data is properly loaded. To create a new image, use the createImage() function (do not use new PImage()).
rob@100 18381 *
rob@100 18382 * @param {int} width image width
rob@100 18383 * @param {int} height image height
rob@100 18384 * @param {MODE} format Either RGB, ARGB, ALPHA (grayscale alpha channel)
rob@100 18385 *
rob@100 18386 * @returns {PImage}
rob@100 18387 *
rob@100 18388 * @see loadImage
rob@100 18389 * @see imageMode
rob@100 18390 * @see createImage
rob@100 18391 */
rob@100 18392 var PImage = function(aWidth, aHeight, aFormat) {
rob@100 18393
rob@100 18394 // Keep track of whether or not the cached imageData has been touched.
rob@100 18395 this.__isDirty = false;
rob@100 18396
rob@100 18397 if (aWidth instanceof HTMLImageElement) {
rob@100 18398 // convert an <img> to a PImage
rob@100 18399 this.fromHTMLImageData(aWidth);
rob@100 18400 } else if (aHeight || aFormat) {
rob@100 18401 this.width = aWidth || 1;
rob@100 18402 this.height = aHeight || 1;
rob@100 18403
rob@100 18404 // Stuff a canvas into sourceImg so image() calls can use drawImage like an <img>
rob@100 18405 var canvas = this.sourceImg = document.createElement("canvas");
rob@100 18406 canvas.width = this.width;
rob@100 18407 canvas.height = this.height;
rob@100 18408
rob@100 18409 var imageData = this.imageData = canvas.getContext('2d').createImageData(this.width, this.height);
rob@100 18410 this.format = (aFormat === PConstants.ARGB || aFormat === PConstants.ALPHA) ? aFormat : PConstants.RGB;
rob@100 18411 if (this.format === PConstants.RGB) {
rob@100 18412 // Set the alpha channel of an RGB image to opaque.
rob@100 18413 for (var i = 3, data = this.imageData.data, len = data.length; i < len; i += 4) {
rob@100 18414 data[i] = 255;
rob@100 18415 }
rob@100 18416 }
rob@100 18417
rob@100 18418 this.__isDirty = true;
rob@100 18419 this.updatePixels();
rob@100 18420 } else {
rob@100 18421 this.width = 0;
rob@100 18422 this.height = 0;
rob@100 18423 this.imageData = utilityContext2d.createImageData(1, 1);
rob@100 18424 this.format = PConstants.ARGB;
rob@100 18425 }
rob@100 18426
rob@100 18427 this.pixels = buildPixelsObject(this);
rob@100 18428 };
rob@100 18429 PImage.prototype = {
rob@100 18430
rob@100 18431 /**
rob@100 18432 * Temporary hack to deal with cross-Processing-instance created PImage. See
rob@100 18433 * tickets #1623 and #1644.
rob@100 18434 */
rob@100 18435 __isPImage: true,
rob@100 18436
rob@100 18437 /**
rob@100 18438 * @member PImage
rob@100 18439 * Updates the image with the data in its pixels[] array. Use in conjunction with loadPixels(). If
rob@100 18440 * you're only reading pixels from the array, there's no need to call updatePixels().
rob@100 18441 * Certain renderers may or may not seem to require loadPixels() or updatePixels(). However, the rule
rob@100 18442 * is that any time you want to manipulate the pixels[] array, you must first call loadPixels(), and
rob@100 18443 * after changes have been made, call updatePixels(). Even if the renderer may not seem to use this
rob@100 18444 * function in the current Processing release, this will always be subject to change.
rob@100 18445 * Currently, none of the renderers use the additional parameters to updatePixels().
rob@100 18446 */
rob@100 18447 updatePixels: function() {
rob@100 18448 var canvas = this.sourceImg;
rob@100 18449 if (canvas && canvas instanceof HTMLCanvasElement && this.__isDirty) {
rob@100 18450 canvas.getContext('2d').putImageData(this.imageData, 0, 0);
rob@100 18451 }
rob@100 18452 this.__isDirty = false;
rob@100 18453 },
rob@100 18454
rob@100 18455 fromHTMLImageData: function(htmlImg) {
rob@100 18456 // convert an <img> to a PImage
rob@100 18457 var canvasData = getCanvasData(htmlImg);
rob@100 18458 try {
rob@100 18459 var imageData = canvasData.context.getImageData(0, 0, htmlImg.width, htmlImg.height);
rob@100 18460 this.fromImageData(imageData);
rob@100 18461 } catch(e) {
rob@100 18462 if (htmlImg.width && htmlImg.height) {
rob@100 18463 this.isRemote = true;
rob@100 18464 this.width = htmlImg.width;
rob@100 18465 this.height = htmlImg.height;
rob@100 18466 }
rob@100 18467 }
rob@100 18468 this.sourceImg = htmlImg;
rob@100 18469 },
rob@100 18470
rob@100 18471 'get': function(x, y, w, h) {
rob@100 18472 if (!arguments.length) {
rob@100 18473 return p.get(this);
rob@100 18474 }
rob@100 18475 if (arguments.length === 2) {
rob@100 18476 return p.get(x, y, this);
rob@100 18477 }
rob@100 18478 if (arguments.length === 4) {
rob@100 18479 return p.get(x, y, w, h, this);
rob@100 18480 }
rob@100 18481 },
rob@100 18482
rob@100 18483 /**
rob@100 18484 * @member PImage
rob@100 18485 * Changes the color of any pixel or writes an image directly into the image. The x and y parameter
rob@100 18486 * specify the pixel or the upper-left corner of the image. The color parameter specifies the color value.
rob@100 18487 * Setting the color of a single pixel with set(x, y) is easy, but not as fast as putting the data
rob@100 18488 * directly into pixels[]. The equivalent statement to "set(x, y, #000000)" using pixels[] is
rob@100 18489 * "pixels[y*width+x] = #000000". Processing requires calling loadPixels() to load the display window
rob@100 18490 * data into the pixels[] array before getting the values and calling updatePixels() to update the window.
rob@100 18491 *
rob@100 18492 * @param {int} x x-coordinate of the pixel or upper-left corner of the image
rob@100 18493 * @param {int} y y-coordinate of the pixel or upper-left corner of the image
rob@100 18494 * @param {color} color any value of the color datatype
rob@100 18495 *
rob@100 18496 * @see get
rob@100 18497 * @see pixels[]
rob@100 18498 * @see copy
rob@100 18499 */
rob@100 18500 'set': function(x, y, c) {
rob@100 18501 p.set(x, y, c, this);
rob@100 18502 this.__isDirty = true;
rob@100 18503 },
rob@100 18504
rob@100 18505 /**
rob@100 18506 * @member PImage
rob@100 18507 * Blends a region of pixels into the image specified by the img parameter. These copies utilize full
rob@100 18508 * alpha channel support and a choice of the following modes to blend the colors of source pixels (A)
rob@100 18509 * with the ones of pixels in the destination image (B):
rob@100 18510 * BLEND - linear interpolation of colours: C = A*factor + B
rob@100 18511 * ADD - additive blending with white clip: C = min(A*factor + B, 255)
rob@100 18512 * SUBTRACT - subtractive blending with black clip: C = max(B - A*factor, 0)
rob@100 18513 * DARKEST - only the darkest colour succeeds: C = min(A*factor, B)
rob@100 18514 * LIGHTEST - only the lightest colour succeeds: C = max(A*factor, B)
rob@100 18515 * DIFFERENCE - subtract colors from underlying image.
rob@100 18516 * EXCLUSION - similar to DIFFERENCE, but less extreme.
rob@100 18517 * MULTIPLY - Multiply the colors, result will always be darker.
rob@100 18518 * SCREEN - Opposite multiply, uses inverse values of the colors.
rob@100 18519 * OVERLAY - A mix of MULTIPLY and SCREEN. Multiplies dark values, and screens light values.
rob@100 18520 * HARD_LIGHT - SCREEN when greater than 50% gray, MULTIPLY when lower.
rob@100 18521 * SOFT_LIGHT - Mix of DARKEST and LIGHTEST. Works like OVERLAY, but not as harsh.
rob@100 18522 * DODGE - Lightens light tones and increases contrast, ignores darks. Called "Color Dodge" in Illustrator and Photoshop.
rob@100 18523 * BURN - Darker areas are applied, increasing contrast, ignores lights. Called "Color Burn" in Illustrator and Photoshop.
rob@100 18524 * All modes use the alpha information (highest byte) of source image pixels as the blending factor.
rob@100 18525 * If the source and destination regions are different sizes, the image will be automatically resized to
rob@100 18526 * match the destination size. If the srcImg parameter is not used, the display window is used as the source image.
rob@100 18527 * This function ignores imageMode().
rob@100 18528 *
rob@100 18529 * @param {int} x X coordinate of the source's upper left corner
rob@100 18530 * @param {int} y Y coordinate of the source's upper left corner
rob@100 18531 * @param {int} width source image width
rob@100 18532 * @param {int} height source image height
rob@100 18533 * @param {int} dx X coordinate of the destinations's upper left corner
rob@100 18534 * @param {int} dy Y coordinate of the destinations's upper left corner
rob@100 18535 * @param {int} dwidth destination image width
rob@100 18536 * @param {int} dheight destination image height
rob@100 18537 * @param {PImage} srcImg an image variable referring to the source image
rob@100 18538 * @param {MODE} MODE Either BLEND, ADD, SUBTRACT, LIGHTEST, DARKEST, DIFFERENCE, EXCLUSION,
rob@100 18539 * MULTIPLY, SCREEN, OVERLAY, HARD_LIGHT, SOFT_LIGHT, DODGE, BURN
rob@100 18540 *
rob@100 18541 * @see alpha
rob@100 18542 * @see copy
rob@100 18543 */
rob@100 18544 blend: function(srcImg, x, y, width, height, dx, dy, dwidth, dheight, MODE) {
rob@100 18545 if (arguments.length === 9) {
rob@100 18546 p.blend(this, srcImg, x, y, width, height, dx, dy, dwidth, dheight, this);
rob@100 18547 } else if (arguments.length === 10) {
rob@100 18548 p.blend(srcImg, x, y, width, height, dx, dy, dwidth, dheight, MODE, this);
rob@100 18549 }
rob@100 18550 delete this.sourceImg;
rob@100 18551 },
rob@100 18552
rob@100 18553 /**
rob@100 18554 * @member PImage
rob@100 18555 * Copies a region of pixels from one image into another. If the source and destination regions
rob@100 18556 * aren't the same size, it will automatically resize source pixels to fit the specified target region.
rob@100 18557 * No alpha information is used in the process, however if the source image has an alpha channel set,
rob@100 18558 * it will be copied as well. This function ignores imageMode().
rob@100 18559 *
rob@100 18560 * @param {int} sx X coordinate of the source's upper left corner
rob@100 18561 * @param {int} sy Y coordinate of the source's upper left corner
rob@100 18562 * @param {int} swidth source image width
rob@100 18563 * @param {int} sheight source image height
rob@100 18564 * @param {int} dx X coordinate of the destinations's upper left corner
rob@100 18565 * @param {int} dy Y coordinate of the destinations's upper left corner
rob@100 18566 * @param {int} dwidth destination image width
rob@100 18567 * @param {int} dheight destination image height
rob@100 18568 * @param {PImage} srcImg an image variable referring to the source image
rob@100 18569 *
rob@100 18570 * @see alpha
rob@100 18571 * @see blend
rob@100 18572 */
rob@100 18573 copy: function(srcImg, sx, sy, swidth, sheight, dx, dy, dwidth, dheight) {
rob@100 18574 if (arguments.length === 8) {
rob@100 18575 p.blend(this, srcImg, sx, sy, swidth, sheight, dx, dy, dwidth, PConstants.REPLACE, this);
rob@100 18576 } else if (arguments.length === 9) {
rob@100 18577 p.blend(srcImg, sx, sy, swidth, sheight, dx, dy, dwidth, dheight, PConstants.REPLACE, this);
rob@100 18578 }
rob@100 18579 delete this.sourceImg;
rob@100 18580 },
rob@100 18581
rob@100 18582 /**
rob@100 18583 * @member PImage
rob@100 18584 * Filters an image as defined by one of the following modes:
rob@100 18585 * THRESHOLD - converts the image to black and white pixels depending if they are above or below
rob@100 18586 * the threshold defined by the level parameter. The level must be between 0.0 (black) and 1.0(white).
rob@100 18587 * If no level is specified, 0.5 is used.
rob@100 18588 * GRAY - converts any colors in the image to grayscale equivalents
rob@100 18589 * INVERT - sets each pixel to its inverse value
rob@100 18590 * POSTERIZE - limits each channel of the image to the number of colors specified as the level parameter
rob@100 18591 * BLUR - executes a Guassian blur with the level parameter specifying the extent of the blurring.
rob@100 18592 * If no level parameter is used, the blur is equivalent to Guassian blur of radius 1.
rob@100 18593 * OPAQUE - sets the alpha channel to entirely opaque.
rob@100 18594 * ERODE - reduces the light areas with the amount defined by the level parameter.
rob@100 18595 * DILATE - increases the light areas with the amount defined by the level parameter
rob@100 18596 *
rob@100 18597 * @param {MODE} MODE Either THRESHOLD, GRAY, INVERT, POSTERIZE, BLUR, OPAQUE, ERODE, or DILATE
rob@100 18598 * @param {int|float} param in the range from 0 to 1
rob@100 18599 */
rob@100 18600 filter: function(mode, param) {
rob@100 18601 if (arguments.length === 2) {
rob@100 18602 p.filter(mode, param, this);
rob@100 18603 } else if (arguments.length === 1) {
rob@100 18604 // no param specified, send null to show its invalid
rob@100 18605 p.filter(mode, null, this);
rob@100 18606 }
rob@100 18607 delete this.sourceImg;
rob@100 18608 },
rob@100 18609
rob@100 18610 /**
rob@100 18611 * @member PImage
rob@100 18612 * Saves the image into a file. Images are saved in TIFF, TARGA, JPEG, and PNG format depending on
rob@100 18613 * the extension within the filename parameter. For example, "image.tif" will have a TIFF image and
rob@100 18614 * "image.png" will save a PNG image. If no extension is included in the filename, the image will save
rob@100 18615 * in TIFF format and .tif will be added to the name. These files are saved to the sketch's folder,
rob@100 18616 * which may be opened by selecting "Show sketch folder" from the "Sketch" menu. It is not possible to
rob@100 18617 * use save() while running the program in a web browser.
rob@100 18618 * To save an image created within the code, rather than through loading, it's necessary to make the
rob@100 18619 * image with the createImage() function so it is aware of the location of the program and can therefore
rob@100 18620 * save the file to the right place. See the createImage() reference for more information.
rob@100 18621 *
rob@100 18622 * @param {String} filename a sequence of letters and numbers
rob@100 18623 */
rob@100 18624 save: function(file){
rob@100 18625 p.save(file,this);
rob@100 18626 },
rob@100 18627
rob@100 18628 /**
rob@100 18629 * @member PImage
rob@100 18630 * Resize the image to a new width and height. To make the image scale proportionally, use 0 as the
rob@100 18631 * value for the wide or high parameter.
rob@100 18632 *
rob@100 18633 * @param {int} wide the resized image width
rob@100 18634 * @param {int} high the resized image height
rob@100 18635 *
rob@100 18636 * @see get
rob@100 18637 */
rob@100 18638 resize: function(w, h) {
rob@100 18639 if (this.isRemote) { // Remote images cannot access imageData
rob@100 18640 throw "Image is loaded remotely. Cannot resize.";
rob@100 18641 }
rob@100 18642 if (this.width !== 0 || this.height !== 0) {
rob@100 18643 // make aspect ratio if w or h is 0
rob@100 18644 if (w === 0 && h !== 0) {
rob@100 18645 w = Math.floor(this.width / this.height * h);
rob@100 18646 } else if (h === 0 && w !== 0) {
rob@100 18647 h = Math.floor(this.height / this.width * w);
rob@100 18648 }
rob@100 18649 // put 'this.imageData' into a new canvas
rob@100 18650 var canvas = getCanvasData(this.imageData).canvas;
rob@100 18651 // pull imageData object out of canvas into ImageData object
rob@100 18652 var imageData = getCanvasData(canvas, w, h).context.getImageData(0, 0, w, h);
rob@100 18653 // set this as new pimage
rob@100 18654 this.fromImageData(imageData);
rob@100 18655 }
rob@100 18656 },
rob@100 18657
rob@100 18658 /**
rob@100 18659 * @member PImage
rob@100 18660 * Masks part of an image from displaying by loading another image and using it as an alpha channel.
rob@100 18661 * This mask image should only contain grayscale data, but only the blue color channel is used. The
rob@100 18662 * mask image needs to be the same size as the image to which it is applied.
rob@100 18663 * In addition to using a mask image, an integer array containing the alpha channel data can be
rob@100 18664 * specified directly. This method is useful for creating dynamically generated alpha masks. This
rob@100 18665 * array must be of the same length as the target image's pixels array and should contain only grayscale
rob@100 18666 * data of values between 0-255.
rob@100 18667 *
rob@100 18668 * @param {PImage} maskImg any PImage object used as the alpha channel for "img", needs to be same
rob@100 18669 * size as "img"
rob@100 18670 * @param {int[]} maskArray any array of Integer numbers used as the alpha channel, needs to be same
rob@100 18671 * length as the image's pixel array
rob@100 18672 */
rob@100 18673 mask: function(mask) {
rob@100 18674 var obj = this.toImageData(),
rob@100 18675 i,
rob@100 18676 size;
rob@100 18677
rob@100 18678 if (mask instanceof PImage || mask.__isPImage) {
rob@100 18679 if (mask.width === this.width && mask.height === this.height) {
rob@100 18680 mask = mask.toImageData();
rob@100 18681
rob@100 18682 for (i = 2, size = this.width * this.height * 4; i < size; i += 4) {
rob@100 18683 // using it as an alpha channel
rob@100 18684 obj.data[i + 1] = mask.data[i];
rob@100 18685 // but only the blue color channel
rob@100 18686 }
rob@100 18687 } else {
rob@100 18688 throw "mask must have the same dimensions as PImage.";
rob@100 18689 }
rob@100 18690 } else if (mask instanceof Array) {
rob@100 18691 if (this.width * this.height === mask.length) {
rob@100 18692 for (i = 0, size = mask.length; i < size; ++i) {
rob@100 18693 obj.data[i * 4 + 3] = mask[i];
rob@100 18694 }
rob@100 18695 } else {
rob@100 18696 throw "mask array must be the same length as PImage pixels array.";
rob@100 18697 }
rob@100 18698 }
rob@100 18699
rob@100 18700 this.fromImageData(obj);
rob@100 18701 },
rob@100 18702
rob@100 18703 // These are intentionally left blank for PImages, we work live with pixels and draw as necessary
rob@100 18704 /**
rob@100 18705 * @member PImage
rob@100 18706 * Loads the pixel data for the image into its pixels[] array. This function must always be called
rob@100 18707 * before reading from or writing to pixels[].
rob@100 18708 * Certain renderers may or may not seem to require loadPixels() or updatePixels(). However, the
rob@100 18709 * rule is that any time you want to manipulate the pixels[] array, you must first call loadPixels(),
rob@100 18710 * and after changes have been made, call updatePixels(). Even if the renderer may not seem to use
rob@100 18711 * this function in the current Processing release, this will always be subject to change.
rob@100 18712 */
rob@100 18713 loadPixels: noop,
rob@100 18714
rob@100 18715 toImageData: function() {
rob@100 18716 if (this.isRemote) {
rob@100 18717 return this.sourceImg;
rob@100 18718 }
rob@100 18719
rob@100 18720 if (!this.__isDirty) {
rob@100 18721 return this.imageData;
rob@100 18722 }
rob@100 18723
rob@100 18724 var canvasData = getCanvasData(this.sourceImg);
rob@100 18725 return canvasData.context.getImageData(0, 0, this.width, this.height);
rob@100 18726 },
rob@100 18727
rob@100 18728 toDataURL: function() {
rob@100 18729 if (this.isRemote) { // Remote images cannot access imageData
rob@100 18730 throw "Image is loaded remotely. Cannot create dataURI.";
rob@100 18731 }
rob@100 18732 var canvasData = getCanvasData(this.imageData);
rob@100 18733 return canvasData.canvas.toDataURL();
rob@100 18734 },
rob@100 18735
rob@100 18736 fromImageData: function(canvasImg) {
rob@100 18737 var w = canvasImg.width,
rob@100 18738 h = canvasImg.height,
rob@100 18739 canvas = document.createElement('canvas'),
rob@100 18740 ctx = canvas.getContext('2d');
rob@100 18741
rob@100 18742 this.width = canvas.width = w;
rob@100 18743 this.height = canvas.height = h;
rob@100 18744
rob@100 18745 ctx.putImageData(canvasImg, 0, 0);
rob@100 18746
rob@100 18747 // changed for 0.9
rob@100 18748 this.format = PConstants.ARGB;
rob@100 18749
rob@100 18750 this.imageData = canvasImg;
rob@100 18751 this.sourceImg = canvas;
rob@100 18752 }
rob@100 18753 };
rob@100 18754
rob@100 18755 p.PImage = PImage;
rob@100 18756
rob@100 18757 /**
rob@100 18758 * Creates a new PImage (the datatype for storing images). This provides a fresh buffer of pixels to play
rob@100 18759 * with. Set the size of the buffer with the width and height parameters. The format parameter defines how
rob@100 18760 * the pixels are stored. See the PImage reference for more information.
rob@100 18761 * Be sure to include all three parameters, specifying only the width and height (but no format) will
rob@100 18762 * produce a strange error.
rob@100 18763 * Advanced users please note that createImage() should be used instead of the syntax new PImage().
rob@100 18764 *
rob@100 18765 * @param {int} width image width
rob@100 18766 * @param {int} height image height
rob@100 18767 * @param {MODE} format Either RGB, ARGB, ALPHA (grayscale alpha channel)
rob@100 18768 *
rob@100 18769 * @returns {PImage}
rob@100 18770 *
rob@100 18771 * @see PImage
rob@100 18772 * @see PGraphics
rob@100 18773 */
rob@100 18774 p.createImage = function(w, h, mode) {
rob@100 18775 return new PImage(w,h,mode);
rob@100 18776 };
rob@100 18777
rob@100 18778 // Loads an image for display. Type is an extension. Callback is fired on load.
rob@100 18779 /**
rob@100 18780 * Loads an image into a variable of type PImage. Four types of images ( .gif, .jpg, .tga, .png) images may
rob@100 18781 * be loaded. To load correctly, images must be located in the data directory of the current sketch. In most
rob@100 18782 * cases, load all images in setup() to preload them at the start of the program. Loading images inside draw()
rob@100 18783 * will reduce the speed of a program.
rob@100 18784 * The filename parameter can also be a URL to a file found online. For security reasons, a Processing sketch
rob@100 18785 * found online can only download files from the same server from which it came. Getting around this restriction
rob@100 18786 * requires a signed applet.
rob@100 18787 * The extension parameter is used to determine the image type in cases where the image filename does not end
rob@100 18788 * with a proper extension. Specify the extension as the second parameter to loadImage(), as shown in the
rob@100 18789 * third example on this page.
rob@100 18790 * If an image is not loaded successfully, the null value is returned and an error message will be printed to
rob@100 18791 * the console. The error message does not halt the program, however the null value may cause a NullPointerException
rob@100 18792 * if your code does not check whether the value returned from loadImage() is null.
rob@100 18793 * Depending on the type of error, a PImage object may still be returned, but the width and height of the image
rob@100 18794 * will be set to -1. This happens if bad image data is returned or cannot be decoded properly. Sometimes this happens
rob@100 18795 * with image URLs that produce a 403 error or that redirect to a password prompt, because loadImage() will attempt
rob@100 18796 * to interpret the HTML as image data.
rob@100 18797 *
rob@100 18798 * @param {String} filename name of file to load, can be .gif, .jpg, .tga, or a handful of other image
rob@100 18799 * types depending on your platform.
rob@100 18800 * @param {String} extension the type of image to load, for example "png", "gif", "jpg"
rob@100 18801 *
rob@100 18802 * @returns {PImage}
rob@100 18803 *
rob@100 18804 * @see PImage
rob@100 18805 * @see image
rob@100 18806 * @see imageMode
rob@100 18807 * @see background
rob@100 18808 */
rob@100 18809 p.loadImage = function(file, type, callback) {
rob@100 18810 // if type is specified, we just ignore it
rob@100 18811
rob@100 18812 var pimg;
rob@100 18813 // if image is in the preloader cache return a new PImage
rob@100 18814 if (curSketch.imageCache.images[file]) {
rob@100 18815 pimg = new PImage(curSketch.imageCache.images[file]);
rob@100 18816 pimg.loaded = true;
rob@100 18817 return pimg;
rob@100 18818 }
rob@100 18819 // else async load it
rob@100 18820 pimg = new PImage();
rob@100 18821 var img = document.createElement('img');
rob@100 18822
rob@100 18823 pimg.sourceImg = img;
rob@100 18824
rob@100 18825 img.onload = (function(aImage, aPImage, aCallback) {
rob@100 18826 var image = aImage;
rob@100 18827 var pimg = aPImage;
rob@100 18828 var callback = aCallback;
rob@100 18829 return function() {
rob@100 18830 // change the <img> object into a PImage now that its loaded
rob@100 18831 pimg.fromHTMLImageData(image);
rob@100 18832 pimg.loaded = true;
rob@100 18833 if (callback) {
rob@100 18834 callback();
rob@100 18835 }
rob@100 18836 };
rob@100 18837 }(img, pimg, callback));
rob@100 18838
rob@100 18839 img.src = file; // needs to be called after the img.onload function is declared or it wont work in opera
rob@100 18840 return pimg;
rob@100 18841 };
rob@100 18842
rob@100 18843 // async loading of large images, same functionality as loadImage above
rob@100 18844 /**
rob@100 18845 * This function load images on a separate thread so that your sketch does not freeze while images load during
rob@100 18846 * setup(). While the image is loading, its width and height will be 0. If an error occurs while loading the image,
rob@100 18847 * its width and height will be set to -1. You'll know when the image has loaded properly because its width and
rob@100 18848 * height will be greater than 0. Asynchronous image loading (particularly when downloading from a server) can
rob@100 18849 * dramatically improve performance.
rob@100 18850 * The extension parameter is used to determine the image type in cases where the image filename does not end
rob@100 18851 * with a proper extension. Specify the extension as the second parameter to requestImage().
rob@100 18852 *
rob@100 18853 * @param {String} filename name of file to load, can be .gif, .jpg, .tga, or a handful of other image
rob@100 18854 * types depending on your platform.
rob@100 18855 * @param {String} extension the type of image to load, for example "png", "gif", "jpg"
rob@100 18856 *
rob@100 18857 * @returns {PImage}
rob@100 18858 *
rob@100 18859 * @see PImage
rob@100 18860 * @see loadImage
rob@100 18861 */
rob@100 18862 p.requestImage = p.loadImage;
rob@100 18863
rob@100 18864 function get$2(x,y) {
rob@100 18865 var data;
rob@100 18866 // return the color at x,y (int) of curContext
rob@100 18867 if (x >= p.width || x < 0 || y < 0 || y >= p.height) {
rob@100 18868 // x,y is outside image return transparent black
rob@100 18869 return 0;
rob@100 18870 }
rob@100 18871
rob@100 18872 // loadPixels() has been called
rob@100 18873 if (isContextReplaced) {
rob@100 18874 var offset = ((0|x) + p.width * (0|y)) * 4;
rob@100 18875 data = p.imageData.data;
rob@100 18876 return (data[offset + 3] << 24) & PConstants.ALPHA_MASK |
rob@100 18877 (data[offset] << 16) & PConstants.RED_MASK |
rob@100 18878 (data[offset + 1] << 8) & PConstants.GREEN_MASK |
rob@100 18879 data[offset + 2] & PConstants.BLUE_MASK;
rob@100 18880 }
rob@100 18881
rob@100 18882 // x,y is inside canvas space
rob@100 18883 data = p.toImageData(0|x, 0|y, 1, 1).data;
rob@100 18884 return (data[3] << 24) & PConstants.ALPHA_MASK |
rob@100 18885 (data[0] << 16) & PConstants.RED_MASK |
rob@100 18886 (data[1] << 8) & PConstants.GREEN_MASK |
rob@100 18887 data[2] & PConstants.BLUE_MASK;
rob@100 18888 }
rob@100 18889 function get$3(x,y,img) {
rob@100 18890 if (img.isRemote) { // Remote images cannot access imageData
rob@100 18891 throw "Image is loaded remotely. Cannot get x,y.";
rob@100 18892 }
rob@100 18893 // PImage.get(x,y) was called, return the color (int) at x,y of img
rob@100 18894 var offset = y * img.width * 4 + (x * 4),
rob@100 18895 data = img.imageData.data;
rob@100 18896 return (data[offset + 3] << 24) & PConstants.ALPHA_MASK |
rob@100 18897 (data[offset] << 16) & PConstants.RED_MASK |
rob@100 18898 (data[offset + 1] << 8) & PConstants.GREEN_MASK |
rob@100 18899 data[offset + 2] & PConstants.BLUE_MASK;
rob@100 18900 }
rob@100 18901 function get$4(x, y, w, h) {
rob@100 18902 // return a PImage of w and h from cood x,y of curContext
rob@100 18903 var c = new PImage(w, h, PConstants.ARGB);
rob@100 18904 c.fromImageData(p.toImageData(x, y, w, h));
rob@100 18905 return c;
rob@100 18906 }
rob@100 18907 function get$5(x, y, w, h, img) {
rob@100 18908 if (img.isRemote) { // Remote images cannot access imageData
rob@100 18909 throw "Image is loaded remotely. Cannot get x,y,w,h.";
rob@100 18910 }
rob@100 18911 // PImage.get(x,y,w,h) was called, return x,y,w,h PImage of img
rob@100 18912 // offset start point needs to be *4
rob@100 18913 var c = new PImage(w, h, PConstants.ARGB), cData = c.imageData.data,
rob@100 18914 imgWidth = img.width, imgHeight = img.height, imgData = img.imageData.data;
rob@100 18915 // Don't need to copy pixels from the image outside ranges.
rob@100 18916 var startRow = Math.max(0, -y), startColumn = Math.max(0, -x),
rob@100 18917 stopRow = Math.min(h, imgHeight - y), stopColumn = Math.min(w, imgWidth - x);
rob@100 18918 for (var i = startRow; i < stopRow; ++i) {
rob@100 18919 var sourceOffset = ((y + i) * imgWidth + (x + startColumn)) * 4;
rob@100 18920 var targetOffset = (i * w + startColumn) * 4;
rob@100 18921 for (var j = startColumn; j < stopColumn; ++j) {
rob@100 18922 cData[targetOffset++] = imgData[sourceOffset++];
rob@100 18923 cData[targetOffset++] = imgData[sourceOffset++];
rob@100 18924 cData[targetOffset++] = imgData[sourceOffset++];
rob@100 18925 cData[targetOffset++] = imgData[sourceOffset++];
rob@100 18926 }
rob@100 18927 }
rob@100 18928 c.__isDirty = true;
rob@100 18929 return c;
rob@100 18930 }
rob@100 18931
rob@100 18932 // Gets a single pixel or block of pixels from the current Canvas Context or a PImage
rob@100 18933 /**
rob@100 18934 * Reads the color of any pixel or grabs a section of an image. If no parameters are specified, the entire
rob@100 18935 * image is returned. Get the value of one pixel by specifying an x,y coordinate. Get a section of the display
rob@100 18936 * window by specifying an additional width and height parameter. If the pixel requested is outside of the image
rob@100 18937 * window, black is returned. The numbers returned are scaled according to the current color ranges, but only RGB
rob@100 18938 * values are returned by this function. For example, even though you may have drawn a shape with colorMode(HSB),
rob@100 18939 * the numbers returned will be in RGB.
rob@100 18940 * Getting the color of a single pixel with get(x, y) is easy, but not as fast as grabbing the data directly
rob@100 18941 * from pixels[]. The equivalent statement to "get(x, y)" using pixels[] is "pixels[y*width+x]". Processing
rob@100 18942 * requires calling loadPixels() to load the display window data into the pixels[] array before getting the values.
rob@100 18943 * This function ignores imageMode().
rob@100 18944 *
rob@100 18945 * @param {int} x x-coordinate of the pixel
rob@100 18946 * @param {int} y y-coordinate of the pixel
rob@100 18947 * @param {int} width width of pixel rectangle to get
rob@100 18948 * @param {int} height height of pixel rectangle to get
rob@100 18949 *
rob@100 18950 * @returns {Color|PImage}
rob@100 18951 *
rob@100 18952 * @see set
rob@100 18953 * @see pixels[]
rob@100 18954 * @see imageMode
rob@100 18955 */
rob@100 18956 p.get = function(x, y, w, h, img) {
rob@100 18957 // for 0 2 and 4 arguments use curContext, otherwise PImage.get was called
rob@100 18958 if (img !== undefined) {
rob@100 18959 return get$5(x, y, w, h, img);
rob@100 18960 }
rob@100 18961 if (h !== undefined) {
rob@100 18962 return get$4(x, y, w, h);
rob@100 18963 }
rob@100 18964 if (w !== undefined) {
rob@100 18965 return get$3(x, y, w);
rob@100 18966 }
rob@100 18967 if (y !== undefined) {
rob@100 18968 return get$2(x, y);
rob@100 18969 }
rob@100 18970 if (x !== undefined) {
rob@100 18971 // PImage.get() was called, return a new PImage
rob@100 18972 return get$5(0, 0, x.width, x.height, x);
rob@100 18973 }
rob@100 18974
rob@100 18975 return get$4(0, 0, p.width, p.height);
rob@100 18976 };
rob@100 18977
rob@100 18978 /**
rob@100 18979 * Creates and returns a new <b>PGraphics</b> object of the types P2D, P3D, and JAVA2D. Use this class if you need to draw
rob@100 18980 * into an off-screen graphics buffer. It's not possible to use <b>createGraphics()</b> with OPENGL, because it doesn't
rob@100 18981 * allow offscreen use. The DXF and PDF renderers require the filename parameter. <br /><br /> It's important to call
rob@100 18982 * any drawing commands between beginDraw() and endDraw() statements. This is also true for any commands that affect
rob@100 18983 * drawing, such as smooth() or colorMode().<br /><br /> Unlike the main drawing surface which is completely opaque,
rob@100 18984 * surfaces created with createGraphics() can have transparency. This makes it possible to draw into a graphics and
rob@100 18985 * maintain the alpha channel.
rob@100 18986 *
rob@100 18987 * @param {int} width width in pixels
rob@100 18988 * @param {int} height height in pixels
rob@100 18989 * @param {int} renderer Either P2D, P3D, JAVA2D, PDF, DXF
rob@100 18990 * @param {String} filename the name of the file (not supported yet)
rob@100 18991 */
rob@100 18992 p.createGraphics = function(w, h, render) {
rob@100 18993 var pg = new Processing();
rob@100 18994 pg.size(w, h, render);
rob@100 18995 pg.background(0,0);
rob@100 18996 return pg;
rob@100 18997 };
rob@100 18998
rob@100 18999 // pixels caching
rob@100 19000 function resetContext() {
rob@100 19001 if(isContextReplaced) {
rob@100 19002 curContext = originalContext;
rob@100 19003 isContextReplaced = false;
rob@100 19004
rob@100 19005 p.updatePixels();
rob@100 19006 }
rob@100 19007 }
rob@100 19008 function SetPixelContextWrapper() {
rob@100 19009 function wrapFunction(newContext, name) {
rob@100 19010 function wrapper() {
rob@100 19011 resetContext();
rob@100 19012 curContext[name].apply(curContext, arguments);
rob@100 19013 }
rob@100 19014 newContext[name] = wrapper;
rob@100 19015 }
rob@100 19016 function wrapProperty(newContext, name) {
rob@100 19017 function getter() {
rob@100 19018 resetContext();
rob@100 19019 return curContext[name];
rob@100 19020 }
rob@100 19021 function setter(value) {
rob@100 19022 resetContext();
rob@100 19023 curContext[name] = value;
rob@100 19024 }
rob@100 19025 p.defineProperty(newContext, name, { get: getter, set: setter });
rob@100 19026 }
rob@100 19027 for(var n in curContext) {
rob@100 19028 if(typeof curContext[n] === 'function') {
rob@100 19029 wrapFunction(this, n);
rob@100 19030 } else {
rob@100 19031 wrapProperty(this, n);
rob@100 19032 }
rob@100 19033 }
rob@100 19034 }
rob@100 19035 function replaceContext() {
rob@100 19036 if(isContextReplaced) {
rob@100 19037 return;
rob@100 19038 }
rob@100 19039 p.loadPixels();
rob@100 19040 if(proxyContext === null) {
rob@100 19041 originalContext = curContext;
rob@100 19042 proxyContext = new SetPixelContextWrapper();
rob@100 19043 }
rob@100 19044 isContextReplaced = true;
rob@100 19045 curContext = proxyContext;
rob@100 19046 setPixelsCached = 0;
rob@100 19047 }
rob@100 19048
rob@100 19049 function set$3(x, y, c) {
rob@100 19050 if (x < p.width && x >= 0 && y >= 0 && y < p.height) {
rob@100 19051 replaceContext();
rob@100 19052 p.pixels.setPixel((0|x)+p.width*(0|y), c);
rob@100 19053 if(++setPixelsCached > maxPixelsCached) {
rob@100 19054 resetContext();
rob@100 19055 }
rob@100 19056 }
rob@100 19057 }
rob@100 19058 function set$4(x, y, obj, img) {
rob@100 19059 if (img.isRemote) { // Remote images cannot access imageData
rob@100 19060 throw "Image is loaded remotely. Cannot set x,y.";
rob@100 19061 }
rob@100 19062 var c = p.color.toArray(obj);
rob@100 19063 var offset = y * img.width * 4 + (x*4);
rob@100 19064 var data = img.imageData.data;
rob@100 19065 data[offset] = c[0];
rob@100 19066 data[offset+1] = c[1];
rob@100 19067 data[offset+2] = c[2];
rob@100 19068 data[offset+3] = c[3];
rob@100 19069 }
rob@100 19070
rob@100 19071 // Paints a pixel array into the canvas
rob@100 19072 /**
rob@100 19073 * Changes the color of any pixel or writes an image directly into the display window. The x and y parameters
rob@100 19074 * specify the pixel to change and the color parameter specifies the color value. The color parameter is affected
rob@100 19075 * by the current color mode (the default is RGB values from 0 to 255). When setting an image, the x and y
rob@100 19076 * parameters define the coordinates for the upper-left corner of the image.
rob@100 19077 * Setting the color of a single pixel with set(x, y) is easy, but not as fast as putting the data directly
rob@100 19078 * into pixels[]. The equivalent statement to "set(x, y, #000000)" using pixels[] is "pixels[y*width+x] = #000000".
rob@100 19079 * You must call loadPixels() to load the display window data into the pixels[] array before setting the values
rob@100 19080 * and calling updatePixels() to update the window with any changes. This function ignores imageMode().
rob@100 19081 *
rob@100 19082 * @param {int} x x-coordinate of the pixel
rob@100 19083 * @param {int} y y-coordinate of the pixel
rob@100 19084 * @param {Color} obj any value of the color datatype
rob@100 19085 * @param {PImage} img any valid variable of type PImage
rob@100 19086 *
rob@100 19087 * @see get
rob@100 19088 * @see pixels[]
rob@100 19089 * @see imageMode
rob@100 19090 */
rob@100 19091 p.set = function(x, y, obj, img) {
rob@100 19092 var color, oldFill;
rob@100 19093 if (arguments.length === 3) {
rob@100 19094 // called p.set(), was it with a color or a img ?
rob@100 19095 if (typeof obj === "number") {
rob@100 19096 set$3(x, y, obj);
rob@100 19097 } else if (obj instanceof PImage || obj.__isPImage) {
rob@100 19098 p.image(obj, x, y);
rob@100 19099 }
rob@100 19100 } else if (arguments.length === 4) {
rob@100 19101 // PImage.set(x,y,c) was called, set coordinate x,y color to c of img
rob@100 19102 set$4(x, y, obj, img);
rob@100 19103 }
rob@100 19104 };
rob@100 19105 p.imageData = {};
rob@100 19106
rob@100 19107 // handle the sketch code for pixels[]
rob@100 19108 // parser code converts pixels[] to getPixels() or setPixels(),
rob@100 19109 // .length becomes getLength()
rob@100 19110 /**
rob@100 19111 * Array containing the values for all the pixels in the display window. These values are of the color datatype.
rob@100 19112 * This array is the size of the display window. For example, if the image is 100x100 pixels, there will be 10000
rob@100 19113 * values and if the window is 200x300 pixels, there will be 60000 values. The index value defines the position
rob@100 19114 * of a value within the array. For example, the statment color b = pixels[230] will set the variable b to be
rob@100 19115 * equal to the value at that location in the array.
rob@100 19116 * Before accessing this array, the data must loaded with the loadPixels() function. After the array data has
rob@100 19117 * been modified, the updatePixels() function must be run to update the changes.
rob@100 19118 *
rob@100 19119 * @param {int} index must not exceed the size of the array
rob@100 19120 *
rob@100 19121 * @see loadPixels
rob@100 19122 * @see updatePixels
rob@100 19123 * @see get
rob@100 19124 * @see set
rob@100 19125 * @see PImage
rob@100 19126 */
rob@100 19127 p.pixels = {
rob@100 19128 getLength: function() { return p.imageData.data.length ? p.imageData.data.length/4 : 0; },
rob@100 19129 getPixel: function(i) {
rob@100 19130 var offset = i*4, data = p.imageData.data;
rob@100 19131 return (data[offset+3] << 24) & 0xff000000 |
rob@100 19132 (data[offset+0] << 16) & 0x00ff0000 |
rob@100 19133 (data[offset+1] << 8) & 0x0000ff00 |
rob@100 19134 data[offset+2] & 0x000000ff;
rob@100 19135 },
rob@100 19136 setPixel: function(i,c) {
rob@100 19137 var offset = i*4, data = p.imageData.data;
rob@100 19138 data[offset+0] = (c & 0x00ff0000) >>> 16; // RED_MASK
rob@100 19139 data[offset+1] = (c & 0x0000ff00) >>> 8; // GREEN_MASK
rob@100 19140 data[offset+2] = (c & 0x000000ff); // BLUE_MASK
rob@100 19141 data[offset+3] = (c & 0xff000000) >>> 24; // ALPHA_MASK
rob@100 19142 },
rob@100 19143 toArray: function() {
rob@100 19144 var arr = [], length = p.imageData.width * p.imageData.height, data = p.imageData.data;
rob@100 19145 for (var i = 0, offset = 0; i < length; i++, offset += 4) {
rob@100 19146 arr.push((data[offset+3] << 24) & 0xff000000 |
rob@100 19147 (data[offset+0] << 16) & 0x00ff0000 |
rob@100 19148 (data[offset+1] << 8) & 0x0000ff00 |
rob@100 19149 data[offset+2] & 0x000000ff);
rob@100 19150 }
rob@100 19151 return arr;
rob@100 19152 },
rob@100 19153 set: function(arr) {
rob@100 19154 for (var i = 0, aL = arr.length; i < aL; i++) {
rob@100 19155 this.setPixel(i, arr[i]);
rob@100 19156 }
rob@100 19157 }
rob@100 19158 };
rob@100 19159
rob@100 19160 // Gets a 1-Dimensional pixel array from Canvas
rob@100 19161 /**
rob@100 19162 * Loads the pixel data for the display window into the pixels[] array. This function must always be called
rob@100 19163 * before reading from or writing to pixels[].
rob@100 19164 * Certain renderers may or may not seem to require loadPixels() or updatePixels(). However, the rule is that
rob@100 19165 * any time you want to manipulate the pixels[] array, you must first call loadPixels(), and after changes
rob@100 19166 * have been made, call updatePixels(). Even if the renderer may not seem to use this function in the current
rob@100 19167 * Processing release, this will always be subject to change.
rob@100 19168 *
rob@100 19169 * @see pixels[]
rob@100 19170 * @see updatePixels
rob@100 19171 */
rob@100 19172 p.loadPixels = function() {
rob@100 19173 p.imageData = drawing.$ensureContext().getImageData(0, 0, p.width, p.height);
rob@100 19174 };
rob@100 19175
rob@100 19176 // Draws a 1-Dimensional pixel array to Canvas
rob@100 19177 /**
rob@100 19178 * Updates the display window with the data in the pixels[] array. Use in conjunction with loadPixels(). If
rob@100 19179 * you're only reading pixels from the array, there's no need to call updatePixels() unless there are changes.
rob@100 19180 * Certain renderers may or may not seem to require loadPixels() or updatePixels(). However, the rule is that
rob@100 19181 * any time you want to manipulate the pixels[] array, you must first call loadPixels(), and after changes
rob@100 19182 * have been made, call updatePixels(). Even if the renderer may not seem to use this function in the current
rob@100 19183 * Processing release, this will always be subject to change.
rob@100 19184 * Currently, none of the renderers use the additional parameters to updatePixels(), however this may be
rob@100 19185 * implemented in the future.
rob@100 19186 *
rob@100 19187 * @see loadPixels
rob@100 19188 * @see pixels[]
rob@100 19189 */
rob@100 19190 p.updatePixels = function() {
rob@100 19191 if (p.imageData) {
rob@100 19192 drawing.$ensureContext().putImageData(p.imageData, 0, 0);
rob@100 19193 }
rob@100 19194 };
rob@100 19195
rob@100 19196 /**
rob@100 19197 * Set various hints and hacks for the renderer. This is used to handle obscure rendering features that cannot be
rob@100 19198 * implemented in a consistent manner across renderers. Many options will often graduate to standard features
rob@100 19199 * instead of hints over time.
rob@100 19200 * hint(ENABLE_OPENGL_4X_SMOOTH) - Enable 4x anti-aliasing for OpenGL. This can help force anti-aliasing if
rob@100 19201 * it has not been enabled by the user. On some graphics cards, this can also be set by the graphics driver's
rob@100 19202 * control panel, however not all cards make this available. This hint must be called immediately after the
rob@100 19203 * size() command because it resets the renderer, obliterating any settings and anything drawn (and like size(),
rob@100 19204 * re-running the code that came before it again).
rob@100 19205 * hint(DISABLE_OPENGL_2X_SMOOTH) - In Processing 1.0, Processing always enables 2x smoothing when the OpenGL
rob@100 19206 * renderer is used. This hint disables the default 2x smoothing and returns the smoothing behavior found in
rob@100 19207 * earlier releases, where smooth() and noSmooth() could be used to enable and disable smoothing, though the
rob@100 19208 * quality was inferior.
rob@100 19209 * hint(ENABLE_NATIVE_FONTS) - Use the native version fonts when they are installed, rather than the bitmapped
rob@100 19210 * version from a .vlw file. This is useful with the JAVA2D renderer setting, as it will improve font rendering
rob@100 19211 * speed. This is not enabled by default, because it can be misleading while testing because the type will look
rob@100 19212 * great on your machine (because you have the font installed) but lousy on others' machines if the identical
rob@100 19213 * font is unavailable. This option can only be set per-sketch, and must be called before any use of textFont().
rob@100 19214 * hint(DISABLE_DEPTH_TEST) - Disable the zbuffer, allowing you to draw on top of everything at will. When depth
rob@100 19215 * testing is disabled, items will be drawn to the screen sequentially, like a painting. This hint is most often
rob@100 19216 * used to draw in 3D, then draw in 2D on top of it (for instance, to draw GUI controls in 2D on top of a 3D
rob@100 19217 * interface). Starting in release 0149, this will also clear the depth buffer. Restore the default with
rob@100 19218 * hint(ENABLE_DEPTH_TEST), but note that with the depth buffer cleared, any 3D drawing that happens later in
rob@100 19219 * draw() will ignore existing shapes on the screen.
rob@100 19220 * hint(ENABLE_DEPTH_SORT) - Enable primitive z-sorting of triangles and lines in P3D and OPENGL. This can slow
rob@100 19221 * performance considerably, and the algorithm is not yet perfect. Restore the default with hint(DISABLE_DEPTH_SORT).
rob@100 19222 * hint(DISABLE_OPENGL_ERROR_REPORT) - Speeds up the OPENGL renderer setting by not checking for errors while
rob@100 19223 * running. Undo with hint(ENABLE_OPENGL_ERROR_REPORT).
rob@100 19224 * As of release 0149, unhint() has been removed in favor of adding additional ENABLE/DISABLE constants to reset
rob@100 19225 * the default behavior. This prevents the double negatives, and also reinforces which hints can be enabled or disabled.
rob@100 19226 *
rob@100 19227 * @param {MODE} item constant: name of the hint to be enabled or disabled
rob@100 19228 *
rob@100 19229 * @see PGraphics
rob@100 19230 * @see createGraphics
rob@100 19231 * @see size
rob@100 19232 */
rob@100 19233 p.hint = function(which) {
rob@100 19234 var curContext = drawing.$ensureContext();
rob@100 19235 if (which === PConstants.DISABLE_DEPTH_TEST) {
rob@100 19236 curContext.disable(curContext.DEPTH_TEST);
rob@100 19237 curContext.depthMask(false);
rob@100 19238 curContext.clear(curContext.DEPTH_BUFFER_BIT);
rob@100 19239 }
rob@100 19240 else if (which === PConstants.ENABLE_DEPTH_TEST) {
rob@100 19241 curContext.enable(curContext.DEPTH_TEST);
rob@100 19242 curContext.depthMask(true);
rob@100 19243 }
rob@100 19244 else if (which === PConstants.ENABLE_OPENGL_2X_SMOOTH ||
rob@100 19245 which === PConstants.ENABLE_OPENGL_4X_SMOOTH){
rob@100 19246 renderSmooth = true;
rob@100 19247 }
rob@100 19248 else if (which === PConstants.DISABLE_OPENGL_2X_SMOOTH){
rob@100 19249 renderSmooth = false;
rob@100 19250 }
rob@100 19251 };
rob@100 19252
rob@100 19253 /**
rob@100 19254 * The background() function sets the color used for the background of the Processing window.
rob@100 19255 * The default background is light gray. In the <b>draw()</b> function, the background color is used to clear the display window at the beginning of each frame.
rob@100 19256 * An image can also be used as the background for a sketch, however its width and height must be the same size as the sketch window.
rob@100 19257 * To resize an image 'b' to the size of the sketch window, use b.resize(width, height).
rob@100 19258 * Images used as background will ignore the current <b>tint()</b> setting.
rob@100 19259 * For the main drawing surface, the alpha value will be ignored. However,
rob@100 19260 * alpha can be used on PGraphics objects from <b>createGraphics()</b>. This is
rob@100 19261 * the only way to set all the pixels partially transparent, for instance.
rob@100 19262 * If the 'gray' parameter is passed in the function sets the background to a grayscale value, based on the
rob@100 19263 * current colorMode.
rob@100 19264 * <p>
rob@100 19265 * Note that background() should be called before any transformations occur,
rob@100 19266 * because some implementations may require the current transformation matrix
rob@100 19267 * to be identity before drawing.
rob@100 19268 *
rob@100 19269 * @param {int|float} gray specifies a value between white and black
rob@100 19270 * @param {int|float} value1 red or hue value (depending on the current color mode)
rob@100 19271 * @param {int|float} value2 green or saturation value (depending on the current color mode)
rob@100 19272 * @param {int|float} value3 blue or brightness value (depending on the current color mode)
rob@100 19273 * @param {int|float} alpha opacity of the background
rob@100 19274 * @param {Color} color any value of the color datatype
rob@100 19275 * @param {int} hex color value in hexadecimal notation (i.e. #FFCC00 or 0xFFFFCC00)
rob@100 19276 * @param {PImage} image an instance of a PImage to use as a background
rob@100 19277 *
rob@100 19278 * @see #stroke()
rob@100 19279 * @see #fill()
rob@100 19280 * @see #tint()
rob@100 19281 * @see #colorMode()
rob@100 19282 */
rob@100 19283 var backgroundHelper = function(arg1, arg2, arg3, arg4) {
rob@100 19284 var obj;
rob@100 19285
rob@100 19286 if (arg1 instanceof PImage || arg1.__isPImage) {
rob@100 19287 obj = arg1;
rob@100 19288
rob@100 19289 if (!obj.loaded) {
rob@100 19290 throw "Error using image in background(): PImage not loaded.";
rob@100 19291 }
rob@100 19292 if(obj.width !== p.width || obj.height !== p.height){
rob@100 19293 throw "Background image must be the same dimensions as the canvas.";
rob@100 19294 }
rob@100 19295 } else {
rob@100 19296 obj = p.color(arg1, arg2, arg3, arg4);
rob@100 19297 }
rob@100 19298
rob@100 19299 backgroundObj = obj;
rob@100 19300 };
rob@100 19301
rob@100 19302 Drawing2D.prototype.background = function(arg1, arg2, arg3, arg4) {
rob@100 19303 if (arg1 !== undef) {
rob@100 19304 backgroundHelper(arg1, arg2, arg3, arg4);
rob@100 19305 }
rob@100 19306
rob@100 19307 if (backgroundObj instanceof PImage || backgroundObj.__isPImage) {
rob@100 19308 saveContext();
rob@100 19309 curContext.setTransform(1, 0, 0, 1, 0, 0);
rob@100 19310 p.image(backgroundObj, 0, 0);
rob@100 19311 restoreContext();
rob@100 19312 } else {
rob@100 19313 saveContext();
rob@100 19314 curContext.setTransform(1, 0, 0, 1, 0, 0);
rob@100 19315
rob@100 19316 // If the background is transparent
rob@100 19317 if (p.alpha(backgroundObj) !== colorModeA) {
rob@100 19318 curContext.clearRect(0,0, p.width, p.height);
rob@100 19319 }
rob@100 19320 curContext.fillStyle = p.color.toString(backgroundObj);
rob@100 19321 curContext.fillRect(0, 0, p.width, p.height);
rob@100 19322 isFillDirty = true;
rob@100 19323 restoreContext();
rob@100 19324 }
rob@100 19325 };
rob@100 19326
rob@100 19327 Drawing3D.prototype.background = function(arg1, arg2, arg3, arg4) {
rob@100 19328 if (arguments.length > 0) {
rob@100 19329 backgroundHelper(arg1, arg2, arg3, arg4);
rob@100 19330 }
rob@100 19331
rob@100 19332 var c = p.color.toGLArray(backgroundObj);
rob@100 19333 curContext.clearColor(c[0], c[1], c[2], c[3]);
rob@100 19334 curContext.clear(curContext.COLOR_BUFFER_BIT | curContext.DEPTH_BUFFER_BIT);
rob@100 19335
rob@100 19336 // An image as a background in 3D is not implemented yet
rob@100 19337 };
rob@100 19338
rob@100 19339 // Draws an image to the Canvas
rob@100 19340 /**
rob@100 19341 * Displays images to the screen. The images must be in the sketch's "data" directory to load correctly. Select "Add
rob@100 19342 * file..." from the "Sketch" menu to add the image. Processing currently works with GIF, JPEG, and Targa images. The
rob@100 19343 * color of an image may be modified with the tint() function and if a GIF has transparency, it will maintain its
rob@100 19344 * transparency. The img parameter specifies the image to display and the x and y parameters define the location of
rob@100 19345 * the image from its upper-left corner. The image is displayed at its original size unless the width and height
rob@100 19346 * parameters specify a different size. The imageMode() function changes the way the parameters work. A call to
rob@100 19347 * imageMode(CORNERS) will change the width and height parameters to define the x and y values of the opposite
rob@100 19348 * corner of the image.
rob@100 19349 *
rob@100 19350 * @param {PImage} img the image to display
rob@100 19351 * @param {int|float} x x-coordinate of the image
rob@100 19352 * @param {int|float} y y-coordinate of the image
rob@100 19353 * @param {int|float} width width to display the image
rob@100 19354 * @param {int|float} height height to display the image
rob@100 19355 *
rob@100 19356 * @see loadImage
rob@100 19357 * @see PImage
rob@100 19358 * @see imageMode
rob@100 19359 * @see tint
rob@100 19360 * @see background
rob@100 19361 * @see alpha
rob@100 19362 */
rob@100 19363 Drawing2D.prototype.image = function(img, x, y, w, h) {
rob@100 19364 // Fix fractional positions
rob@100 19365 x = Math.round(x);
rob@100 19366 y = Math.round(y);
rob@100 19367
rob@100 19368 if (img.width > 0) {
rob@100 19369 var wid = w || img.width;
rob@100 19370 var hgt = h || img.height;
rob@100 19371
rob@100 19372 var bounds = imageModeConvert(x || 0, y || 0, w || img.width, h || img.height, arguments.length < 4);
rob@100 19373 var fastImage = !!img.sourceImg && curTint === null;
rob@100 19374 if (fastImage) {
rob@100 19375 var htmlElement = img.sourceImg;
rob@100 19376 if (img.__isDirty) {
rob@100 19377 img.updatePixels();
rob@100 19378 }
rob@100 19379 // Using HTML element's width and height in case if the image was resized.
rob@100 19380 curContext.drawImage(htmlElement, 0, 0,
rob@100 19381 htmlElement.width, htmlElement.height, bounds.x, bounds.y, bounds.w, bounds.h);
rob@100 19382 } else {
rob@100 19383 var obj = img.toImageData();
rob@100 19384
rob@100 19385 // Tint the image
rob@100 19386 if (curTint !== null) {
rob@100 19387 curTint(obj);
rob@100 19388 img.__isDirty = true;
rob@100 19389 }
rob@100 19390
rob@100 19391 curContext.drawImage(getCanvasData(obj).canvas, 0, 0,
rob@100 19392 img.width, img.height, bounds.x, bounds.y, bounds.w, bounds.h);
rob@100 19393 }
rob@100 19394 }
rob@100 19395 };
rob@100 19396
rob@100 19397 Drawing3D.prototype.image = function(img, x, y, w, h) {
rob@100 19398 if (img.width > 0) {
rob@100 19399 // Fix fractional positions
rob@100 19400 x = Math.round(x);
rob@100 19401 y = Math.round(y);
rob@100 19402 w = w || img.width;
rob@100 19403 h = h || img.height;
rob@100 19404
rob@100 19405 p.beginShape(p.QUADS);
rob@100 19406 p.texture(img);
rob@100 19407 p.vertex(x, y, 0, 0, 0);
rob@100 19408 p.vertex(x, y+h, 0, 0, h);
rob@100 19409 p.vertex(x+w, y+h, 0, w, h);
rob@100 19410 p.vertex(x+w, y, 0, w, 0);
rob@100 19411 p.endShape();
rob@100 19412 }
rob@100 19413 };
rob@100 19414
rob@100 19415 /**
rob@100 19416 * The tint() function sets the fill value for displaying images. Images can be tinted to
rob@100 19417 * specified colors or made transparent by setting the alpha.
rob@100 19418 * <br><br>To make an image transparent, but not change it's color,
rob@100 19419 * use white as the tint color and specify an alpha value. For instance,
rob@100 19420 * tint(255, 128) will make an image 50% transparent (unless
rob@100 19421 * <b>colorMode()</b> has been used).
rob@100 19422 *
rob@100 19423 * <br><br>When using hexadecimal notation to specify a color, use "#" or
rob@100 19424 * "0x" before the values (e.g. #CCFFAA, 0xFFCCFFAA). The # syntax uses six
rob@100 19425 * digits to specify a color (the way colors are specified in HTML and CSS).
rob@100 19426 * When using the hexadecimal notation starting with "0x", the hexadecimal
rob@100 19427 * value must be specified with eight characters; the first two characters
rob@100 19428 * define the alpha component and the remainder the red, green, and blue
rob@100 19429 * components.
rob@100 19430 * <br><br>The value for the parameter "gray" must be less than or equal
rob@100 19431 * to the current maximum value as specified by <b>colorMode()</b>.
rob@100 19432 * The default maximum value is 255.
rob@100 19433 * <br><br>The tint() method is also used to control the coloring of
rob@100 19434 * textures in 3D.
rob@100 19435 *
rob@100 19436 * @param {int|float} gray any valid number
rob@100 19437 * @param {int|float} alpha opacity of the image
rob@100 19438 * @param {int|float} value1 red or hue value
rob@100 19439 * @param {int|float} value2 green or saturation value
rob@100 19440 * @param {int|float} value3 blue or brightness value
rob@100 19441 * @param {int|float} color any value of the color datatype
rob@100 19442 * @param {int} hex color value in hexadecimal notation (i.e. #FFCC00 or 0xFFFFCC00)
rob@100 19443 *
rob@100 19444 * @see #noTint()
rob@100 19445 * @see #image()
rob@100 19446 */
rob@100 19447 p.tint = function(a1, a2, a3, a4) {
rob@100 19448 var tintColor = p.color(a1, a2, a3, a4);
rob@100 19449 var r = p.red(tintColor) / colorModeX;
rob@100 19450 var g = p.green(tintColor) / colorModeY;
rob@100 19451 var b = p.blue(tintColor) / colorModeZ;
rob@100 19452 var a = p.alpha(tintColor) / colorModeA;
rob@100 19453 curTint = function(obj) {
rob@100 19454 var data = obj.data,
rob@100 19455 length = 4 * obj.width * obj.height;
rob@100 19456 for (var i = 0; i < length;) {
rob@100 19457 data[i++] *= r;
rob@100 19458 data[i++] *= g;
rob@100 19459 data[i++] *= b;
rob@100 19460 data[i++] *= a;
rob@100 19461 }
rob@100 19462 };
rob@100 19463 // for overriding the color buffer when 3d rendering
rob@100 19464 curTint3d = function(data){
rob@100 19465 for (var i = 0; i < data.length;) {
rob@100 19466 data[i++] = r;
rob@100 19467 data[i++] = g;
rob@100 19468 data[i++] = b;
rob@100 19469 data[i++] = a;
rob@100 19470 }
rob@100 19471 };
rob@100 19472 };
rob@100 19473
rob@100 19474 /**
rob@100 19475 * The noTint() function removes the current fill value for displaying images and reverts to displaying images with their original hues.
rob@100 19476 *
rob@100 19477 * @see #tint()
rob@100 19478 * @see #image()
rob@100 19479 */
rob@100 19480 p.noTint = function() {
rob@100 19481 curTint = null;
rob@100 19482 curTint3d = null;
rob@100 19483 };
rob@100 19484
rob@100 19485 /**
rob@100 19486 * Copies a region of pixels from the display window to another area of the display window and copies a region of pixels from an
rob@100 19487 * image used as the srcImg parameter into the display window. If the source and destination regions aren't the same size, it will
rob@100 19488 * automatically resize the source pixels to fit the specified target region. No alpha information is used in the process, however
rob@100 19489 * if the source image has an alpha channel set, it will be copied as well. This function ignores imageMode().
rob@100 19490 *
rob@100 19491 * @param {int} x X coordinate of the source's upper left corner
rob@100 19492 * @param {int} y Y coordinate of the source's upper left corner
rob@100 19493 * @param {int} width source image width
rob@100 19494 * @param {int} height source image height
rob@100 19495 * @param {int} dx X coordinate of the destination's upper left corner
rob@100 19496 * @param {int} dy Y coordinate of the destination's upper left corner
rob@100 19497 * @param {int} dwidth destination image width
rob@100 19498 * @param {int} dheight destination image height
rob@100 19499 * @param {PImage} srcImg image variable referring to the source image
rob@100 19500 *
rob@100 19501 * @see blend
rob@100 19502 * @see get
rob@100 19503 */
rob@100 19504 p.copy = function(src, sx, sy, sw, sh, dx, dy, dw, dh) {
rob@100 19505 if (dh === undef) {
rob@100 19506 // shift everything, and introduce p
rob@100 19507 dh = dw;
rob@100 19508 dw = dy;
rob@100 19509 dy = dx;
rob@100 19510 dx = sh;
rob@100 19511 sh = sw;
rob@100 19512 sw = sy;
rob@100 19513 sy = sx;
rob@100 19514 sx = src;
rob@100 19515 src = p;
rob@100 19516 }
rob@100 19517 p.blend(src, sx, sy, sw, sh, dx, dy, dw, dh, PConstants.REPLACE);
rob@100 19518 };
rob@100 19519
rob@100 19520 /**
rob@100 19521 * Blends a region of pixels from one image into another (or in itself again) with full alpha channel support. There
rob@100 19522 * is a choice of the following modes to blend the source pixels (A) with the ones of pixels in the destination image (B):
rob@100 19523 * BLEND - linear interpolation of colours: C = A*factor + B
rob@100 19524 * ADD - additive blending with white clip: C = min(A*factor + B, 255)
rob@100 19525 * SUBTRACT - subtractive blending with black clip: C = max(B - A*factor, 0)
rob@100 19526 * DARKEST - only the darkest colour succeeds: C = min(A*factor, B)
rob@100 19527 * LIGHTEST - only the lightest colour succeeds: C = max(A*factor, B)
rob@100 19528 * DIFFERENCE - subtract colors from underlying image.
rob@100 19529 * EXCLUSION - similar to DIFFERENCE, but less extreme.
rob@100 19530 * MULTIPLY - Multiply the colors, result will always be darker.
rob@100 19531 * SCREEN - Opposite multiply, uses inverse values of the colors.
rob@100 19532 * OVERLAY - A mix of MULTIPLY and SCREEN. Multiplies dark values, and screens light values.
rob@100 19533 * HARD_LIGHT - SCREEN when greater than 50% gray, MULTIPLY when lower.
rob@100 19534 * SOFT_LIGHT - Mix of DARKEST and LIGHTEST. Works like OVERLAY, but not as harsh.
rob@100 19535 * DODGE - Lightens light tones and increases contrast, ignores darks. Called "Color Dodge" in Illustrator and Photoshop.
rob@100 19536 * BURN - Darker areas are applied, increasing contrast, ignores lights. Called "Color Burn" in Illustrator and Photoshop.
rob@100 19537 * All modes use the alpha information (highest byte) of source image pixels as the blending factor. If the source and
rob@100 19538 * destination regions are different sizes, the image will be automatically resized to match the destination size. If the
rob@100 19539 * srcImg parameter is not used, the display window is used as the source image. This function ignores imageMode().
rob@100 19540 *
rob@100 19541 * @param {int} x X coordinate of the source's upper left corner
rob@100 19542 * @param {int} y Y coordinate of the source's upper left corner
rob@100 19543 * @param {int} width source image width
rob@100 19544 * @param {int} height source image height
rob@100 19545 * @param {int} dx X coordinate of the destination's upper left corner
rob@100 19546 * @param {int} dy Y coordinate of the destination's upper left corner
rob@100 19547 * @param {int} dwidth destination image width
rob@100 19548 * @param {int} dheight destination image height
rob@100 19549 * @param {PImage} srcImg image variable referring to the source image
rob@100 19550 * @param {PImage} MODE Either BLEND, ADD, SUBTRACT, LIGHTEST, DARKEST, DIFFERENCE, EXCLUSION, MULTIPLY, SCREEN,
rob@100 19551 * OVERLAY, HARD_LIGHT, SOFT_LIGHT, DODGE, BURN
rob@100 19552 * @see filter
rob@100 19553 */
rob@100 19554 p.blend = function(src, sx, sy, sw, sh, dx, dy, dw, dh, mode, pimgdest) {
rob@100 19555 if (src.isRemote) {
rob@100 19556 throw "Image is loaded remotely. Cannot blend image.";
rob@100 19557 }
rob@100 19558
rob@100 19559 if (mode === undef) {
rob@100 19560 // shift everything, and introduce p
rob@100 19561 mode = dh;
rob@100 19562 dh = dw;
rob@100 19563 dw = dy;
rob@100 19564 dy = dx;
rob@100 19565 dx = sh;
rob@100 19566 sh = sw;
rob@100 19567 sw = sy;
rob@100 19568 sy = sx;
rob@100 19569 sx = src;
rob@100 19570 src = p;
rob@100 19571 }
rob@100 19572
rob@100 19573 var sx2 = sx + sw,
rob@100 19574 sy2 = sy + sh,
rob@100 19575 dx2 = dx + dw,
rob@100 19576 dy2 = dy + dh,
rob@100 19577 dest = pimgdest || p;
rob@100 19578
rob@100 19579 // check if pimgdest is there and pixels, if so this was a call from pimg.blend
rob@100 19580 if (pimgdest === undef || mode === undef) {
rob@100 19581 p.loadPixels();
rob@100 19582 }
rob@100 19583
rob@100 19584 src.loadPixels();
rob@100 19585
rob@100 19586 if (src === p && p.intersect(sx, sy, sx2, sy2, dx, dy, dx2, dy2)) {
rob@100 19587 p.blit_resize(p.get(sx, sy, sx2 - sx, sy2 - sy), 0, 0, sx2 - sx - 1, sy2 - sy - 1,
rob@100 19588 dest.imageData.data, dest.width, dest.height, dx, dy, dx2, dy2, mode);
rob@100 19589 } else {
rob@100 19590 p.blit_resize(src, sx, sy, sx2, sy2, dest.imageData.data, dest.width, dest.height, dx, dy, dx2, dy2, mode);
rob@100 19591 }
rob@100 19592
rob@100 19593 if (pimgdest === undef) {
rob@100 19594 p.updatePixels();
rob@100 19595 }
rob@100 19596 };
rob@100 19597
rob@100 19598 // helper function for filter()
rob@100 19599 var buildBlurKernel = function(r) {
rob@100 19600 var radius = p.floor(r * 3.5), i, radiusi;
rob@100 19601 radius = (radius < 1) ? 1 : ((radius < 248) ? radius : 248);
rob@100 19602 if (p.shared.blurRadius !== radius) {
rob@100 19603 p.shared.blurRadius = radius;
rob@100 19604 p.shared.blurKernelSize = 1 + (p.shared.blurRadius<<1);
rob@100 19605 p.shared.blurKernel = new Float32Array(p.shared.blurKernelSize);
rob@100 19606 var sharedBlurKernal = p.shared.blurKernel;
rob@100 19607 var sharedBlurKernelSize = p.shared.blurKernelSize;
rob@100 19608 var sharedBlurRadius = p.shared.blurRadius;
rob@100 19609 // init blurKernel
rob@100 19610 for (i = 0; i < sharedBlurKernelSize; i++) {
rob@100 19611 sharedBlurKernal[i] = 0;
rob@100 19612 }
rob@100 19613 var radiusiSquared = (radius - 1) * (radius - 1);
rob@100 19614 for (i = 1; i < radius; i++) {
rob@100 19615 sharedBlurKernal[radius + i] = sharedBlurKernal[radiusi] = radiusiSquared;
rob@100 19616 }
rob@100 19617 sharedBlurKernal[radius] = radius * radius;
rob@100 19618 }
rob@100 19619 };
rob@100 19620
rob@100 19621 var blurARGB = function(r, aImg) {
rob@100 19622 var sum, cr, cg, cb, ca, c, m;
rob@100 19623 var read, ri, ym, ymi, bk0;
rob@100 19624 var wh = aImg.pixels.getLength();
rob@100 19625 var r2 = new Float32Array(wh);
rob@100 19626 var g2 = new Float32Array(wh);
rob@100 19627 var b2 = new Float32Array(wh);
rob@100 19628 var a2 = new Float32Array(wh);
rob@100 19629 var yi = 0;
rob@100 19630 var x, y, i, offset;
rob@100 19631
rob@100 19632 buildBlurKernel(r);
rob@100 19633
rob@100 19634 var aImgHeight = aImg.height;
rob@100 19635 var aImgWidth = aImg.width;
rob@100 19636 var sharedBlurKernelSize = p.shared.blurKernelSize;
rob@100 19637 var sharedBlurRadius = p.shared.blurRadius;
rob@100 19638 var sharedBlurKernal = p.shared.blurKernel;
rob@100 19639 var pix = aImg.imageData.data;
rob@100 19640
rob@100 19641 for (y = 0; y < aImgHeight; y++) {
rob@100 19642 for (x = 0; x < aImgWidth; x++) {
rob@100 19643 cb = cg = cr = ca = sum = 0;
rob@100 19644 read = x - sharedBlurRadius;
rob@100 19645 if (read<0) {
rob@100 19646 bk0 = -read;
rob@100 19647 read = 0;
rob@100 19648 } else {
rob@100 19649 if (read >= aImgWidth) {
rob@100 19650 break;
rob@100 19651 }
rob@100 19652 bk0=0;
rob@100 19653 }
rob@100 19654 for (i = bk0; i < sharedBlurKernelSize; i++) {
rob@100 19655 if (read >= aImgWidth) {
rob@100 19656 break;
rob@100 19657 }
rob@100 19658 offset = (read + yi) *4;
rob@100 19659 m = sharedBlurKernal[i];
rob@100 19660 ca += m * pix[offset + 3];
rob@100 19661 cr += m * pix[offset];
rob@100 19662 cg += m * pix[offset + 1];
rob@100 19663 cb += m * pix[offset + 2];
rob@100 19664 sum += m;
rob@100 19665 read++;
rob@100 19666 }
rob@100 19667 ri = yi + x;
rob@100 19668 a2[ri] = ca / sum;
rob@100 19669 r2[ri] = cr / sum;
rob@100 19670 g2[ri] = cg / sum;
rob@100 19671 b2[ri] = cb / sum;
rob@100 19672 }
rob@100 19673 yi += aImgWidth;
rob@100 19674 }
rob@100 19675
rob@100 19676 yi = 0;
rob@100 19677 ym = -sharedBlurRadius;
rob@100 19678 ymi = ym*aImgWidth;
rob@100 19679
rob@100 19680 for (y = 0; y < aImgHeight; y++) {
rob@100 19681 for (x = 0; x < aImgWidth; x++) {
rob@100 19682 cb = cg = cr = ca = sum = 0;
rob@100 19683 if (ym<0) {
rob@100 19684 bk0 = ri = -ym;
rob@100 19685 read = x;
rob@100 19686 } else {
rob@100 19687 if (ym >= aImgHeight) {
rob@100 19688 break;
rob@100 19689 }
rob@100 19690 bk0 = 0;
rob@100 19691 ri = ym;
rob@100 19692 read = x + ymi;
rob@100 19693 }
rob@100 19694 for (i = bk0; i < sharedBlurKernelSize; i++) {
rob@100 19695 if (ri >= aImgHeight) {
rob@100 19696 break;
rob@100 19697 }
rob@100 19698 m = sharedBlurKernal[i];
rob@100 19699 ca += m * a2[read];
rob@100 19700 cr += m * r2[read];
rob@100 19701 cg += m * g2[read];
rob@100 19702 cb += m * b2[read];
rob@100 19703 sum += m;
rob@100 19704 ri++;
rob@100 19705 read += aImgWidth;
rob@100 19706 }
rob@100 19707 offset = (x + yi) *4;
rob@100 19708 pix[offset] = cr / sum;
rob@100 19709 pix[offset + 1] = cg / sum;
rob@100 19710 pix[offset + 2] = cb / sum;
rob@100 19711 pix[offset + 3] = ca / sum;
rob@100 19712 }
rob@100 19713 yi += aImgWidth;
rob@100 19714 ymi += aImgWidth;
rob@100 19715 ym++;
rob@100 19716 }
rob@100 19717 };
rob@100 19718
rob@100 19719 // helper funtion for ERODE and DILATE modes of filter()
rob@100 19720 var dilate = function(isInverted, aImg) {
rob@100 19721 var currIdx = 0;
rob@100 19722 var maxIdx = aImg.pixels.getLength();
rob@100 19723 var out = new Int32Array(maxIdx);
rob@100 19724 var currRowIdx, maxRowIdx, colOrig, colOut, currLum;
rob@100 19725 var idxRight, idxLeft, idxUp, idxDown,
rob@100 19726 colRight, colLeft, colUp, colDown,
rob@100 19727 lumRight, lumLeft, lumUp, lumDown;
rob@100 19728
rob@100 19729 if (!isInverted) {
rob@100 19730 // erosion (grow light areas)
rob@100 19731 while (currIdx<maxIdx) {
rob@100 19732 currRowIdx = currIdx;
rob@100 19733 maxRowIdx = currIdx + aImg.width;
rob@100 19734 while (currIdx < maxRowIdx) {
rob@100 19735 colOrig = colOut = aImg.pixels.getPixel(currIdx);
rob@100 19736 idxLeft = currIdx - 1;
rob@100 19737 idxRight = currIdx + 1;
rob@100 19738 idxUp = currIdx - aImg.width;
rob@100 19739 idxDown = currIdx + aImg.width;
rob@100 19740 if (idxLeft < currRowIdx) {
rob@100 19741 idxLeft = currIdx;
rob@100 19742 }
rob@100 19743 if (idxRight >= maxRowIdx) {
rob@100 19744 idxRight = currIdx;
rob@100 19745 }
rob@100 19746 if (idxUp < 0) {
rob@100 19747 idxUp = 0;
rob@100 19748 }
rob@100 19749 if (idxDown >= maxIdx) {
rob@100 19750 idxDown = currIdx;
rob@100 19751 }
rob@100 19752 colUp = aImg.pixels.getPixel(idxUp);
rob@100 19753 colLeft = aImg.pixels.getPixel(idxLeft);
rob@100 19754 colDown = aImg.pixels.getPixel(idxDown);
rob@100 19755 colRight = aImg.pixels.getPixel(idxRight);
rob@100 19756
rob@100 19757 // compute luminance
rob@100 19758 currLum = 77*(colOrig>>16&0xff) + 151*(colOrig>>8&0xff) + 28*(colOrig&0xff);
rob@100 19759 lumLeft = 77*(colLeft>>16&0xff) + 151*(colLeft>>8&0xff) + 28*(colLeft&0xff);
rob@100 19760 lumRight = 77*(colRight>>16&0xff) + 151*(colRight>>8&0xff) + 28*(colRight&0xff);
rob@100 19761 lumUp = 77*(colUp>>16&0xff) + 151*(colUp>>8&0xff) + 28*(colUp&0xff);
rob@100 19762 lumDown = 77*(colDown>>16&0xff) + 151*(colDown>>8&0xff) + 28*(colDown&0xff);
rob@100 19763
rob@100 19764 if (lumLeft > currLum) {
rob@100 19765 colOut = colLeft;
rob@100 19766 currLum = lumLeft;
rob@100 19767 }
rob@100 19768 if (lumRight > currLum) {
rob@100 19769 colOut = colRight;
rob@100 19770 currLum = lumRight;
rob@100 19771 }
rob@100 19772 if (lumUp > currLum) {
rob@100 19773 colOut = colUp;
rob@100 19774 currLum = lumUp;
rob@100 19775 }
rob@100 19776 if (lumDown > currLum) {
rob@100 19777 colOut = colDown;
rob@100 19778 currLum = lumDown;
rob@100 19779 }
rob@100 19780 out[currIdx++] = colOut;
rob@100 19781 }
rob@100 19782 }
rob@100 19783 } else {
rob@100 19784 // dilate (grow dark areas)
rob@100 19785 while (currIdx < maxIdx) {
rob@100 19786 currRowIdx = currIdx;
rob@100 19787 maxRowIdx = currIdx + aImg.width;
rob@100 19788 while (currIdx < maxRowIdx) {
rob@100 19789 colOrig = colOut = aImg.pixels.getPixel(currIdx);
rob@100 19790 idxLeft = currIdx - 1;
rob@100 19791 idxRight = currIdx + 1;
rob@100 19792 idxUp = currIdx - aImg.width;
rob@100 19793 idxDown = currIdx + aImg.width;
rob@100 19794 if (idxLeft < currRowIdx) {
rob@100 19795 idxLeft = currIdx;
rob@100 19796 }
rob@100 19797 if (idxRight >= maxRowIdx) {
rob@100 19798 idxRight = currIdx;
rob@100 19799 }
rob@100 19800 if (idxUp < 0) {
rob@100 19801 idxUp = 0;
rob@100 19802 }
rob@100 19803 if (idxDown >= maxIdx) {
rob@100 19804 idxDown = currIdx;
rob@100 19805 }
rob@100 19806 colUp = aImg.pixels.getPixel(idxUp);
rob@100 19807 colLeft = aImg.pixels.getPixel(idxLeft);
rob@100 19808 colDown = aImg.pixels.getPixel(idxDown);
rob@100 19809 colRight = aImg.pixels.getPixel(idxRight);
rob@100 19810
rob@100 19811 // compute luminance
rob@100 19812 currLum = 77*(colOrig>>16&0xff) + 151*(colOrig>>8&0xff) + 28*(colOrig&0xff);
rob@100 19813 lumLeft = 77*(colLeft>>16&0xff) + 151*(colLeft>>8&0xff) + 28*(colLeft&0xff);
rob@100 19814 lumRight = 77*(colRight>>16&0xff) + 151*(colRight>>8&0xff) + 28*(colRight&0xff);
rob@100 19815 lumUp = 77*(colUp>>16&0xff) + 151*(colUp>>8&0xff) + 28*(colUp&0xff);
rob@100 19816 lumDown = 77*(colDown>>16&0xff) + 151*(colDown>>8&0xff) + 28*(colDown&0xff);
rob@100 19817
rob@100 19818 if (lumLeft < currLum) {
rob@100 19819 colOut = colLeft;
rob@100 19820 currLum = lumLeft;
rob@100 19821 }
rob@100 19822 if (lumRight < currLum) {
rob@100 19823 colOut = colRight;
rob@100 19824 currLum = lumRight;
rob@100 19825 }
rob@100 19826 if (lumUp < currLum) {
rob@100 19827 colOut = colUp;
rob@100 19828 currLum = lumUp;
rob@100 19829 }
rob@100 19830 if (lumDown < currLum) {
rob@100 19831 colOut = colDown;
rob@100 19832 currLum = lumDown;
rob@100 19833 }
rob@100 19834 out[currIdx++]=colOut;
rob@100 19835 }
rob@100 19836 }
rob@100 19837 }
rob@100 19838 aImg.pixels.set(out);
rob@100 19839 //p.arraycopy(out,0,pixels,0,maxIdx);
rob@100 19840 };
rob@100 19841
rob@100 19842 /**
rob@100 19843 * Filters the display window as defined by one of the following modes:
rob@100 19844 * THRESHOLD - converts the image to black and white pixels depending if they are above or below the threshold
rob@100 19845 * defined by the level parameter. The level must be between 0.0 (black) and 1.0(white). If no level is specified, 0.5 is used.
rob@100 19846 * GRAY - converts any colors in the image to grayscale equivalents
rob@100 19847 * INVERT - sets each pixel to its inverse value
rob@100 19848 * POSTERIZE - limits each channel of the image to the number of colors specified as the level parameter
rob@100 19849 * BLUR - executes a Guassian blur with the level parameter specifying the extent of the blurring. If no level parameter is
rob@100 19850 * used, the blur is equivalent to Guassian blur of radius 1.
rob@100 19851 * OPAQUE - sets the alpha channel to entirely opaque.
rob@100 19852 * ERODE - reduces the light areas with the amount defined by the level parameter.
rob@100 19853 * DILATE - increases the light areas with the amount defined by the level parameter.
rob@100 19854 *
rob@100 19855 * @param {MODE} MODE Either THRESHOLD, GRAY, INVERT, POSTERIZE, BLUR, OPAQUE, ERODE, or DILATE
rob@100 19856 * @param {int|float} level defines the quality of the filter
rob@100 19857 *
rob@100 19858 * @see blend
rob@100 19859 */
rob@100 19860 p.filter = function(kind, param, aImg){
rob@100 19861 var img, col, lum, i;
rob@100 19862
rob@100 19863 if (arguments.length === 3) {
rob@100 19864 aImg.loadPixels();
rob@100 19865 img = aImg;
rob@100 19866 } else {
rob@100 19867 p.loadPixels();
rob@100 19868 img = p;
rob@100 19869 }
rob@100 19870
rob@100 19871 if (param === undef) {
rob@100 19872 param = null;
rob@100 19873 }
rob@100 19874 if (img.isRemote) { // Remote images cannot access imageData
rob@100 19875 throw "Image is loaded remotely. Cannot filter image.";
rob@100 19876 }
rob@100 19877 // begin filter process
rob@100 19878 var imglen = img.pixels.getLength();
rob@100 19879 switch (kind) {
rob@100 19880 case PConstants.BLUR:
rob@100 19881 var radius = param || 1; // if no param specified, use 1 (default for p5)
rob@100 19882 blurARGB(radius, img);
rob@100 19883 break;
rob@100 19884
rob@100 19885 case PConstants.GRAY:
rob@100 19886 if (img.format === PConstants.ALPHA) { //trouble
rob@100 19887 // for an alpha image, convert it to an opaque grayscale
rob@100 19888 for (i = 0; i < imglen; i++) {
rob@100 19889 col = 255 - img.pixels.getPixel(i);
rob@100 19890 img.pixels.setPixel(i,(0xff000000 | (col << 16) | (col << 8) | col));
rob@100 19891 }
rob@100 19892 img.format = PConstants.RGB; //trouble
rob@100 19893 } else {
rob@100 19894 for (i = 0; i < imglen; i++) {
rob@100 19895 col = img.pixels.getPixel(i);
rob@100 19896 lum = (77*(col>>16&0xff) + 151*(col>>8&0xff) + 28*(col&0xff))>>8;
rob@100 19897 img.pixels.setPixel(i,((col & PConstants.ALPHA_MASK) | lum<<16 | lum<<8 | lum));
rob@100 19898 }
rob@100 19899 }
rob@100 19900 break;
rob@100 19901
rob@100 19902 case PConstants.INVERT:
rob@100 19903 for (i = 0; i < imglen; i++) {
rob@100 19904 img.pixels.setPixel(i, (img.pixels.getPixel(i) ^ 0xffffff));
rob@100 19905 }
rob@100 19906 break;
rob@100 19907
rob@100 19908 case PConstants.POSTERIZE:
rob@100 19909 if (param === null) {
rob@100 19910 throw "Use filter(POSTERIZE, int levels) instead of filter(POSTERIZE)";
rob@100 19911 }
rob@100 19912 var levels = p.floor(param);
rob@100 19913 if ((levels < 2) || (levels > 255)) {
rob@100 19914 throw "Levels must be between 2 and 255 for filter(POSTERIZE, levels)";
rob@100 19915 }
rob@100 19916 var levels1 = levels - 1;
rob@100 19917 for (i = 0; i < imglen; i++) {
rob@100 19918 var rlevel = (img.pixels.getPixel(i) >> 16) & 0xff;
rob@100 19919 var glevel = (img.pixels.getPixel(i) >> 8) & 0xff;
rob@100 19920 var blevel = img.pixels.getPixel(i) & 0xff;
rob@100 19921 rlevel = (((rlevel * levels) >> 8) * 255) / levels1;
rob@100 19922 glevel = (((glevel * levels) >> 8) * 255) / levels1;
rob@100 19923 blevel = (((blevel * levels) >> 8) * 255) / levels1;
rob@100 19924 img.pixels.setPixel(i, ((0xff000000 & img.pixels.getPixel(i)) | (rlevel << 16) | (glevel << 8) | blevel));
rob@100 19925 }
rob@100 19926 break;
rob@100 19927
rob@100 19928 case PConstants.OPAQUE:
rob@100 19929 for (i = 0; i < imglen; i++) {
rob@100 19930 img.pixels.setPixel(i, (img.pixels.getPixel(i) | 0xff000000));
rob@100 19931 }
rob@100 19932 img.format = PConstants.RGB; //trouble
rob@100 19933 break;
rob@100 19934
rob@100 19935 case PConstants.THRESHOLD:
rob@100 19936 if (param === null) {
rob@100 19937 param = 0.5;
rob@100 19938 }
rob@100 19939 if ((param < 0) || (param > 1)) {
rob@100 19940 throw "Level must be between 0 and 1 for filter(THRESHOLD, level)";
rob@100 19941 }
rob@100 19942 var thresh = p.floor(param * 255);
rob@100 19943 for (i = 0; i < imglen; i++) {
rob@100 19944 var max = p.max((img.pixels.getPixel(i) & PConstants.RED_MASK) >> 16, p.max((img.pixels.getPixel(i) & PConstants.GREEN_MASK) >> 8, (img.pixels.getPixel(i) & PConstants.BLUE_MASK)));
rob@100 19945 img.pixels.setPixel(i, ((img.pixels.getPixel(i) & PConstants.ALPHA_MASK) | ((max < thresh) ? 0x000000 : 0xffffff)));
rob@100 19946 }
rob@100 19947 break;
rob@100 19948
rob@100 19949 case PConstants.ERODE:
rob@100 19950 dilate(true, img);
rob@100 19951 break;
rob@100 19952
rob@100 19953 case PConstants.DILATE:
rob@100 19954 dilate(false, img);
rob@100 19955 break;
rob@100 19956 }
rob@100 19957 img.updatePixels();
rob@100 19958 };
rob@100 19959
rob@100 19960
rob@100 19961 // shared variables for blit_resize(), filter_new_scanline(), filter_bilinear(), filter()
rob@100 19962 // change this in the future to not be exposed to p
rob@100 19963 p.shared = {
rob@100 19964 fracU: 0,
rob@100 19965 ifU: 0,
rob@100 19966 fracV: 0,
rob@100 19967 ifV: 0,
rob@100 19968 u1: 0,
rob@100 19969 u2: 0,
rob@100 19970 v1: 0,
rob@100 19971 v2: 0,
rob@100 19972 sX: 0,
rob@100 19973 sY: 0,
rob@100 19974 iw: 0,
rob@100 19975 iw1: 0,
rob@100 19976 ih1: 0,
rob@100 19977 ul: 0,
rob@100 19978 ll: 0,
rob@100 19979 ur: 0,
rob@100 19980 lr: 0,
rob@100 19981 cUL: 0,
rob@100 19982 cLL: 0,
rob@100 19983 cUR: 0,
rob@100 19984 cLR: 0,
rob@100 19985 srcXOffset: 0,
rob@100 19986 srcYOffset: 0,
rob@100 19987 r: 0,
rob@100 19988 g: 0,
rob@100 19989 b: 0,
rob@100 19990 a: 0,
rob@100 19991 srcBuffer: null,
rob@100 19992 blurRadius: 0,
rob@100 19993 blurKernelSize: 0,
rob@100 19994 blurKernel: null
rob@100 19995 };
rob@100 19996
rob@100 19997 p.intersect = function(sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2) {
rob@100 19998 var sw = sx2 - sx1 + 1;
rob@100 19999 var sh = sy2 - sy1 + 1;
rob@100 20000 var dw = dx2 - dx1 + 1;
rob@100 20001 var dh = dy2 - dy1 + 1;
rob@100 20002 if (dx1 < sx1) {
rob@100 20003 dw += dx1 - sx1;
rob@100 20004 if (dw > sw) {
rob@100 20005 dw = sw;
rob@100 20006 }
rob@100 20007 } else {
rob@100 20008 var w = sw + sx1 - dx1;
rob@100 20009 if (dw > w) {
rob@100 20010 dw = w;
rob@100 20011 }
rob@100 20012 }
rob@100 20013 if (dy1 < sy1) {
rob@100 20014 dh += dy1 - sy1;
rob@100 20015 if (dh > sh) {
rob@100 20016 dh = sh;
rob@100 20017 }
rob@100 20018 } else {
rob@100 20019 var h = sh + sy1 - dy1;
rob@100 20020 if (dh > h) {
rob@100 20021 dh = h;
rob@100 20022 }
rob@100 20023 }
rob@100 20024 return ! (dw <= 0 || dh <= 0);
rob@100 20025 };
rob@100 20026
rob@100 20027 var blendFuncs = {};
rob@100 20028 blendFuncs[PConstants.BLEND] = p.modes.blend;
rob@100 20029 blendFuncs[PConstants.ADD] = p.modes.add;
rob@100 20030 blendFuncs[PConstants.SUBTRACT] = p.modes.subtract;
rob@100 20031 blendFuncs[PConstants.LIGHTEST] = p.modes.lightest;
rob@100 20032 blendFuncs[PConstants.DARKEST] = p.modes.darkest;
rob@100 20033 blendFuncs[PConstants.REPLACE] = p.modes.replace;
rob@100 20034 blendFuncs[PConstants.DIFFERENCE] = p.modes.difference;
rob@100 20035 blendFuncs[PConstants.EXCLUSION] = p.modes.exclusion;
rob@100 20036 blendFuncs[PConstants.MULTIPLY] = p.modes.multiply;
rob@100 20037 blendFuncs[PConstants.SCREEN] = p.modes.screen;
rob@100 20038 blendFuncs[PConstants.OVERLAY] = p.modes.overlay;
rob@100 20039 blendFuncs[PConstants.HARD_LIGHT] = p.modes.hard_light;
rob@100 20040 blendFuncs[PConstants.SOFT_LIGHT] = p.modes.soft_light;
rob@100 20041 blendFuncs[PConstants.DODGE] = p.modes.dodge;
rob@100 20042 blendFuncs[PConstants.BURN] = p.modes.burn;
rob@100 20043
rob@100 20044 p.blit_resize = function(img, srcX1, srcY1, srcX2, srcY2, destPixels,
rob@100 20045 screenW, screenH, destX1, destY1, destX2, destY2, mode) {
rob@100 20046 var x, y;
rob@100 20047 if (srcX1 < 0) {
rob@100 20048 srcX1 = 0;
rob@100 20049 }
rob@100 20050 if (srcY1 < 0) {
rob@100 20051 srcY1 = 0;
rob@100 20052 }
rob@100 20053 if (srcX2 >= img.width) {
rob@100 20054 srcX2 = img.width - 1;
rob@100 20055 }
rob@100 20056 if (srcY2 >= img.height) {
rob@100 20057 srcY2 = img.height - 1;
rob@100 20058 }
rob@100 20059 var srcW = srcX2 - srcX1;
rob@100 20060 var srcH = srcY2 - srcY1;
rob@100 20061 var destW = destX2 - destX1;
rob@100 20062 var destH = destY2 - destY1;
rob@100 20063
rob@100 20064 if (destW <= 0 || destH <= 0 || srcW <= 0 || srcH <= 0 || destX1 >= screenW ||
rob@100 20065 destY1 >= screenH || srcX1 >= img.width || srcY1 >= img.height) {
rob@100 20066 return;
rob@100 20067 }
rob@100 20068
rob@100 20069 var dx = Math.floor(srcW / destW * PConstants.PRECISIONF);
rob@100 20070 var dy = Math.floor(srcH / destH * PConstants.PRECISIONF);
rob@100 20071
rob@100 20072 var pshared = p.shared;
rob@100 20073
rob@100 20074 pshared.srcXOffset = Math.floor(destX1 < 0 ? -destX1 * dx : srcX1 * PConstants.PRECISIONF);
rob@100 20075 pshared.srcYOffset = Math.floor(destY1 < 0 ? -destY1 * dy : srcY1 * PConstants.PRECISIONF);
rob@100 20076 if (destX1 < 0) {
rob@100 20077 destW += destX1;
rob@100 20078 destX1 = 0;
rob@100 20079 }
rob@100 20080 if (destY1 < 0) {
rob@100 20081 destH += destY1;
rob@100 20082 destY1 = 0;
rob@100 20083 }
rob@100 20084 destW = Math.min(destW, screenW - destX1);
rob@100 20085 destH = Math.min(destH, screenH - destY1);
rob@100 20086
rob@100 20087 var destOffset = destY1 * screenW + destX1;
rob@100 20088 var destColor;
rob@100 20089
rob@100 20090 pshared.srcBuffer = img.imageData.data;
rob@100 20091 pshared.iw = img.width;
rob@100 20092 pshared.iw1 = img.width - 1;
rob@100 20093 pshared.ih1 = img.height - 1;
rob@100 20094
rob@100 20095 // cache for speed
rob@100 20096 var filterBilinear = p.filter_bilinear,
rob@100 20097 filterNewScanline = p.filter_new_scanline,
rob@100 20098 blendFunc = blendFuncs[mode],
rob@100 20099 blendedColor,
rob@100 20100 idx,
rob@100 20101 cULoffset,
rob@100 20102 cURoffset,
rob@100 20103 cLLoffset,
rob@100 20104 cLRoffset,
rob@100 20105 ALPHA_MASK = PConstants.ALPHA_MASK,
rob@100 20106 RED_MASK = PConstants.RED_MASK,
rob@100 20107 GREEN_MASK = PConstants.GREEN_MASK,
rob@100 20108 BLUE_MASK = PConstants.BLUE_MASK,
rob@100 20109 PREC_MAXVAL = PConstants.PREC_MAXVAL,
rob@100 20110 PRECISIONB = PConstants.PRECISIONB,
rob@100 20111 PREC_RED_SHIFT = PConstants.PREC_RED_SHIFT,
rob@100 20112 PREC_ALPHA_SHIFT = PConstants.PREC_ALPHA_SHIFT,
rob@100 20113 srcBuffer = pshared.srcBuffer,
rob@100 20114 min = Math.min;
rob@100 20115
rob@100 20116 for (y = 0; y < destH; y++) {
rob@100 20117
rob@100 20118 pshared.sX = pshared.srcXOffset;
rob@100 20119 pshared.fracV = pshared.srcYOffset & PREC_MAXVAL;
rob@100 20120 pshared.ifV = PREC_MAXVAL - pshared.fracV;
rob@100 20121 pshared.v1 = (pshared.srcYOffset >> PRECISIONB) * pshared.iw;
rob@100 20122 pshared.v2 = min((pshared.srcYOffset >> PRECISIONB) + 1, pshared.ih1) * pshared.iw;
rob@100 20123
rob@100 20124 for (x = 0; x < destW; x++) {
rob@100 20125 idx = (destOffset + x) * 4;
rob@100 20126
rob@100 20127 destColor = (destPixels[idx + 3] << 24) &
rob@100 20128 ALPHA_MASK | (destPixels[idx] << 16) &
rob@100 20129 RED_MASK | (destPixels[idx + 1] << 8) &
rob@100 20130 GREEN_MASK | destPixels[idx + 2] & BLUE_MASK;
rob@100 20131
rob@100 20132 pshared.fracU = pshared.sX & PREC_MAXVAL;
rob@100 20133 pshared.ifU = PREC_MAXVAL - pshared.fracU;
rob@100 20134 pshared.ul = (pshared.ifU * pshared.ifV) >> PRECISIONB;
rob@100 20135 pshared.ll = (pshared.ifU * pshared.fracV) >> PRECISIONB;
rob@100 20136 pshared.ur = (pshared.fracU * pshared.ifV) >> PRECISIONB;
rob@100 20137 pshared.lr = (pshared.fracU * pshared.fracV) >> PRECISIONB;
rob@100 20138 pshared.u1 = (pshared.sX >> PRECISIONB);
rob@100 20139 pshared.u2 = min(pshared.u1 + 1, pshared.iw1);
rob@100 20140
rob@100 20141 cULoffset = (pshared.v1 + pshared.u1) * 4;
rob@100 20142 cURoffset = (pshared.v1 + pshared.u2) * 4;
rob@100 20143 cLLoffset = (pshared.v2 + pshared.u1) * 4;
rob@100 20144 cLRoffset = (pshared.v2 + pshared.u2) * 4;
rob@100 20145
rob@100 20146 pshared.cUL = (srcBuffer[cULoffset + 3] << 24) &
rob@100 20147 ALPHA_MASK | (srcBuffer[cULoffset] << 16) &
rob@100 20148 RED_MASK | (srcBuffer[cULoffset + 1] << 8) &
rob@100 20149 GREEN_MASK | srcBuffer[cULoffset + 2] & BLUE_MASK;
rob@100 20150
rob@100 20151 pshared.cUR = (srcBuffer[cURoffset + 3] << 24) &
rob@100 20152 ALPHA_MASK | (srcBuffer[cURoffset] << 16) &
rob@100 20153 RED_MASK | (srcBuffer[cURoffset + 1] << 8) &
rob@100 20154 GREEN_MASK | srcBuffer[cURoffset + 2] & BLUE_MASK;
rob@100 20155
rob@100 20156 pshared.cLL = (srcBuffer[cLLoffset + 3] << 24) &
rob@100 20157 ALPHA_MASK | (srcBuffer[cLLoffset] << 16) &
rob@100 20158 RED_MASK | (srcBuffer[cLLoffset + 1] << 8) &
rob@100 20159 GREEN_MASK | srcBuffer[cLLoffset + 2] & BLUE_MASK;
rob@100 20160
rob@100 20161 pshared.cLR = (srcBuffer[cLRoffset + 3] << 24) &
rob@100 20162 ALPHA_MASK | (srcBuffer[cLRoffset] << 16) &
rob@100 20163 RED_MASK | (srcBuffer[cLRoffset + 1] << 8) &
rob@100 20164 GREEN_MASK | srcBuffer[cLRoffset + 2] & BLUE_MASK;
rob@100 20165
rob@100 20166 pshared.r = ((pshared.ul * ((pshared.cUL & RED_MASK) >> 16) +
rob@100 20167 pshared.ll * ((pshared.cLL & RED_MASK) >> 16) +
rob@100 20168 pshared.ur * ((pshared.cUR & RED_MASK) >> 16) +
rob@100 20169 pshared.lr * ((pshared.cLR & RED_MASK) >> 16)) << PREC_RED_SHIFT) & RED_MASK;
rob@100 20170 pshared.g = ((pshared.ul * (pshared.cUL & GREEN_MASK) +
rob@100 20171 pshared.ll * (pshared.cLL & GREEN_MASK) +
rob@100 20172 pshared.ur * (pshared.cUR & GREEN_MASK) +
rob@100 20173 pshared.lr * (pshared.cLR & GREEN_MASK)) >>> PRECISIONB) & GREEN_MASK;
rob@100 20174 pshared.b = (pshared.ul * (pshared.cUL & BLUE_MASK) +
rob@100 20175 pshared.ll * (pshared.cLL & BLUE_MASK) +
rob@100 20176 pshared.ur * (pshared.cUR & BLUE_MASK) +
rob@100 20177 pshared.lr * (pshared.cLR & BLUE_MASK)) >>> PRECISIONB;
rob@100 20178 pshared.a = ((pshared.ul * ((pshared.cUL & ALPHA_MASK) >>> 24) +
rob@100 20179 pshared.ll * ((pshared.cLL & ALPHA_MASK) >>> 24) +
rob@100 20180 pshared.ur * ((pshared.cUR & ALPHA_MASK) >>> 24) +
rob@100 20181 pshared.lr * ((pshared.cLR & ALPHA_MASK) >>> 24)) << PREC_ALPHA_SHIFT) & ALPHA_MASK;
rob@100 20182
rob@100 20183 blendedColor = blendFunc(destColor, (pshared.a | pshared.r | pshared.g | pshared.b));
rob@100 20184
rob@100 20185 destPixels[idx] = (blendedColor & RED_MASK) >>> 16;
rob@100 20186 destPixels[idx + 1] = (blendedColor & GREEN_MASK) >>> 8;
rob@100 20187 destPixels[idx + 2] = (blendedColor & BLUE_MASK);
rob@100 20188 destPixels[idx + 3] = (blendedColor & ALPHA_MASK) >>> 24;
rob@100 20189
rob@100 20190 pshared.sX += dx;
rob@100 20191 }
rob@100 20192 destOffset += screenW;
rob@100 20193 pshared.srcYOffset += dy;
rob@100 20194 }
rob@100 20195 };
rob@100 20196
rob@100 20197 ////////////////////////////////////////////////////////////////////////////
rob@100 20198 // Font handling
rob@100 20199 ////////////////////////////////////////////////////////////////////////////
rob@100 20200
rob@100 20201 /**
rob@100 20202 * loadFont() Loads a font into a variable of type PFont.
rob@100 20203 *
rob@100 20204 * @param {String} name filename of the font to load
rob@100 20205 * @param {int|float} size option font size (used internally)
rob@100 20206 *
rob@100 20207 * @returns {PFont} new PFont object
rob@100 20208 *
rob@100 20209 * @see #PFont
rob@100 20210 * @see #textFont
rob@100 20211 * @see #text
rob@100 20212 * @see #createFont
rob@100 20213 */
rob@100 20214 p.loadFont = function(name, size) {
rob@100 20215 if (name === undef) {
rob@100 20216 throw("font name required in loadFont.");
rob@100 20217 }
rob@100 20218 if (name.indexOf(".svg") === -1) {
rob@100 20219 if (size === undef) {
rob@100 20220 size = curTextFont.size;
rob@100 20221 }
rob@100 20222 return PFont.get(name, size);
rob@100 20223 }
rob@100 20224 // If the font is a glyph, calculate by SVG table
rob@100 20225 var font = p.loadGlyphs(name);
rob@100 20226
rob@100 20227 return {
rob@100 20228 name: name,
rob@100 20229 css: '12px sans-serif',
rob@100 20230 glyph: true,
rob@100 20231 units_per_em: font.units_per_em,
rob@100 20232 horiz_adv_x: 1 / font.units_per_em * font.horiz_adv_x,
rob@100 20233 ascent: font.ascent,
rob@100 20234 descent: font.descent,
rob@100 20235 width: function(str) {
rob@100 20236 var width = 0;
rob@100 20237 var len = str.length;
rob@100 20238 for (var i = 0; i < len; i++) {
rob@100 20239 try {
rob@100 20240 width += parseFloat(p.glyphLook(p.glyphTable[name], str[i]).horiz_adv_x);
rob@100 20241 }
rob@100 20242 catch(e) {
rob@100 20243 Processing.debug(e);
rob@100 20244 }
rob@100 20245 }
rob@100 20246 return width / p.glyphTable[name].units_per_em;
rob@100 20247 }
rob@100 20248 };
rob@100 20249 };
rob@100 20250
rob@100 20251 /**
rob@100 20252 * createFont() Loads a font into a variable of type PFont.
rob@100 20253 * Smooth and charset are ignored in Processing.js.
rob@100 20254 *
rob@100 20255 * @param {String} name filename of the font to load
rob@100 20256 * @param {int|float} size font size in pixels
rob@100 20257 * @param {boolean} smooth not used in Processing.js
rob@100 20258 * @param {char[]} charset not used in Processing.js
rob@100 20259 *
rob@100 20260 * @returns {PFont} new PFont object
rob@100 20261 *
rob@100 20262 * @see #PFont
rob@100 20263 * @see #textFont
rob@100 20264 * @see #text
rob@100 20265 * @see #loadFont
rob@100 20266 */
rob@100 20267 p.createFont = function(name, size) {
rob@100 20268 // because Processing.js only deals with real fonts,
rob@100 20269 // createFont is simply a wrapper for loadFont/2
rob@100 20270 return p.loadFont(name, size);
rob@100 20271 };
rob@100 20272
rob@100 20273 /**
rob@100 20274 * textFont() Sets the current font.
rob@100 20275 *
rob@100 20276 * @param {PFont} pfont the PFont to load as current text font
rob@100 20277 * @param {int|float} size optional font size in pixels
rob@100 20278 *
rob@100 20279 * @see #createFont
rob@100 20280 * @see #loadFont
rob@100 20281 * @see #PFont
rob@100 20282 * @see #text
rob@100 20283 */
rob@100 20284 p.textFont = function(pfont, size) {
rob@100 20285 if (size !== undef) {
rob@100 20286 // If we're using an SVG glyph font, don't load from cache
rob@100 20287 if (!pfont.glyph) {
rob@100 20288 pfont = PFont.get(pfont.name, size);
rob@100 20289 }
rob@100 20290 curTextSize = size;
rob@100 20291 }
rob@100 20292 curTextFont = pfont;
rob@100 20293 curFontName = curTextFont.name;
rob@100 20294 curTextAscent = curTextFont.ascent;
rob@100 20295 curTextDescent = curTextFont.descent;
rob@100 20296 curTextLeading = curTextFont.leading;
rob@100 20297 var curContext = drawing.$ensureContext();
rob@100 20298 curContext.font = curTextFont.css;
rob@100 20299 };
rob@100 20300
rob@100 20301 /**
rob@100 20302 * textSize() Sets the current font size in pixels.
rob@100 20303 *
rob@100 20304 * @param {int|float} size font size in pixels
rob@100 20305 *
rob@100 20306 * @see #textFont
rob@100 20307 * @see #loadFont
rob@100 20308 * @see #PFont
rob@100 20309 * @see #text
rob@100 20310 */
rob@100 20311 p.textSize = function(size) {
rob@100 20312 curTextFont = PFont.get(curFontName, size);
rob@100 20313 curTextSize = size;
rob@100 20314 // recache metrics
rob@100 20315 curTextAscent = curTextFont.ascent;
rob@100 20316 curTextDescent = curTextFont.descent;
rob@100 20317 curTextLeading = curTextFont.leading;
rob@100 20318 var curContext = drawing.$ensureContext();
rob@100 20319 curContext.font = curTextFont.css;
rob@100 20320 };
rob@100 20321
rob@100 20322 /**
rob@100 20323 * textAscent() returns the maximum height a character extends above the baseline of the
rob@100 20324 * current font at its current size, in pixels.
rob@100 20325 *
rob@100 20326 * @returns {float} height of the current font above the baseline, at its current size, in pixels
rob@100 20327 *
rob@100 20328 * @see #textDescent
rob@100 20329 */
rob@100 20330 p.textAscent = function() {
rob@100 20331 return curTextAscent;
rob@100 20332 };
rob@100 20333
rob@100 20334 /**
rob@100 20335 * textDescent() returns the maximum depth a character will protrude below the baseline of
rob@100 20336 * the current font at its current size, in pixels.
rob@100 20337 *
rob@100 20338 * @returns {float} depth of the current font below the baseline, at its current size, in pixels
rob@100 20339 *
rob@100 20340 * @see #textAscent
rob@100 20341 */
rob@100 20342 p.textDescent = function() {
rob@100 20343 return curTextDescent;
rob@100 20344 };
rob@100 20345
rob@100 20346 /**
rob@100 20347 * textLeading() Sets the current font's leading, which is the distance
rob@100 20348 * from baseline to baseline over consecutive lines, with additional vertical
rob@100 20349 * spacing taking into account. Usually this value is 1.2 or 1.25 times the
rob@100 20350 * textsize, but this value can be changed to effect vertically compressed
rob@100 20351 * or stretched text.
rob@100 20352 *
rob@100 20353 * @param {int|float} the desired baseline-to-baseline size in pixels
rob@100 20354 */
rob@100 20355 p.textLeading = function(leading) {
rob@100 20356 curTextLeading = leading;
rob@100 20357 };
rob@100 20358
rob@100 20359 /**
rob@100 20360 * textAlign() Sets the current alignment for drawing text.
rob@100 20361 *
rob@100 20362 * @param {int} ALIGN Horizontal alignment, either LEFT, CENTER, or RIGHT
rob@100 20363 * @param {int} YALIGN optional vertical alignment, either TOP, BOTTOM, CENTER, or BASELINE
rob@100 20364 *
rob@100 20365 * @see #loadFont
rob@100 20366 * @see #PFont
rob@100 20367 * @see #text
rob@100 20368 */
rob@100 20369 p.textAlign = function(xalign, yalign) {
rob@100 20370 horizontalTextAlignment = xalign;
rob@100 20371 verticalTextAlignment = yalign || PConstants.BASELINE;
rob@100 20372 };
rob@100 20373
rob@100 20374 /**
rob@100 20375 * toP5String converts things with arbitrary data type into
rob@100 20376 * string values, for text rendering.
rob@100 20377 *
rob@100 20378 * @param {any} any object that can be converted into a string
rob@100 20379 *
rob@100 20380 * @return {String} the string representation of the input
rob@100 20381 */
rob@100 20382 function toP5String(obj) {
rob@100 20383 if(obj instanceof String) {
rob@100 20384 return obj;
rob@100 20385 }
rob@100 20386 if(typeof obj === 'number') {
rob@100 20387 // check if an int
rob@100 20388 if(obj === (0 | obj)) {
rob@100 20389 return obj.toString();
rob@100 20390 }
rob@100 20391 return p.nf(obj, 0, 3);
rob@100 20392 }
rob@100 20393 if(obj === null || obj === undef) {
rob@100 20394 return "";
rob@100 20395 }
rob@100 20396 return obj.toString();
rob@100 20397 }
rob@100 20398
rob@100 20399 /**
rob@100 20400 * textWidth() Calculates and returns the width of any character or text string in pixels.
rob@100 20401 *
rob@100 20402 * @param {char|String} str char or String to be measured
rob@100 20403 *
rob@100 20404 * @return {float} width of char or String in pixels
rob@100 20405 *
rob@100 20406 * @see #loadFont
rob@100 20407 * @see #PFont
rob@100 20408 * @see #text
rob@100 20409 * @see #textFont
rob@100 20410 */
rob@100 20411 Drawing2D.prototype.textWidth = function(str) {
rob@100 20412 var lines = toP5String(str).split(/\r?\n/g), width = 0;
rob@100 20413 var i, linesCount = lines.length;
rob@100 20414
rob@100 20415 curContext.font = curTextFont.css;
rob@100 20416 for (i = 0; i < linesCount; ++i) {
rob@100 20417 width = Math.max(width, curTextFont.measureTextWidth(lines[i]));
rob@100 20418 }
rob@100 20419 return width | 0;
rob@100 20420 };
rob@100 20421
rob@100 20422 Drawing3D.prototype.textWidth = function(str) {
rob@100 20423 var lines = toP5String(str).split(/\r?\n/g), width = 0;
rob@100 20424 var i, linesCount = lines.length;
rob@100 20425 if (textcanvas === undef) {
rob@100 20426 textcanvas = document.createElement("canvas");
rob@100 20427 }
rob@100 20428
rob@100 20429 var textContext = textcanvas.getContext("2d");
rob@100 20430 textContext.font = curTextFont.css;
rob@100 20431
rob@100 20432 for (i = 0; i < linesCount; ++i) {
rob@100 20433 width = Math.max(width, textContext.measureText(lines[i]).width);
rob@100 20434 }
rob@100 20435 return width | 0;
rob@100 20436 };
rob@100 20437
rob@100 20438 // A lookup table for characters that can not be referenced by Object
rob@100 20439 p.glyphLook = function(font, chr) {
rob@100 20440 try {
rob@100 20441 switch (chr) {
rob@100 20442 case "1":
rob@100 20443 return font.one;
rob@100 20444 case "2":
rob@100 20445 return font.two;
rob@100 20446 case "3":
rob@100 20447 return font.three;
rob@100 20448 case "4":
rob@100 20449 return font.four;
rob@100 20450 case "5":
rob@100 20451 return font.five;
rob@100 20452 case "6":
rob@100 20453 return font.six;
rob@100 20454 case "7":
rob@100 20455 return font.seven;
rob@100 20456 case "8":
rob@100 20457 return font.eight;
rob@100 20458 case "9":
rob@100 20459 return font.nine;
rob@100 20460 case "0":
rob@100 20461 return font.zero;
rob@100 20462 case " ":
rob@100 20463 return font.space;
rob@100 20464 case "$":
rob@100 20465 return font.dollar;
rob@100 20466 case "!":
rob@100 20467 return font.exclam;
rob@100 20468 case '"':
rob@100 20469 return font.quotedbl;
rob@100 20470 case "#":
rob@100 20471 return font.numbersign;
rob@100 20472 case "%":
rob@100 20473 return font.percent;
rob@100 20474 case "&":
rob@100 20475 return font.ampersand;
rob@100 20476 case "'":
rob@100 20477 return font.quotesingle;
rob@100 20478 case "(":
rob@100 20479 return font.parenleft;
rob@100 20480 case ")":
rob@100 20481 return font.parenright;
rob@100 20482 case "*":
rob@100 20483 return font.asterisk;
rob@100 20484 case "+":
rob@100 20485 return font.plus;
rob@100 20486 case ",":
rob@100 20487 return font.comma;
rob@100 20488 case "-":
rob@100 20489 return font.hyphen;
rob@100 20490 case ".":
rob@100 20491 return font.period;
rob@100 20492 case "/":
rob@100 20493 return font.slash;
rob@100 20494 case "_":
rob@100 20495 return font.underscore;
rob@100 20496 case ":":
rob@100 20497 return font.colon;
rob@100 20498 case ";":
rob@100 20499 return font.semicolon;
rob@100 20500 case "<":
rob@100 20501 return font.less;
rob@100 20502 case "=":
rob@100 20503 return font.equal;
rob@100 20504 case ">":
rob@100 20505 return font.greater;
rob@100 20506 case "?":
rob@100 20507 return font.question;
rob@100 20508 case "@":
rob@100 20509 return font.at;
rob@100 20510 case "[":
rob@100 20511 return font.bracketleft;
rob@100 20512 case "\\":
rob@100 20513 return font.backslash;
rob@100 20514 case "]":
rob@100 20515 return font.bracketright;
rob@100 20516 case "^":
rob@100 20517 return font.asciicircum;
rob@100 20518 case "`":
rob@100 20519 return font.grave;
rob@100 20520 case "{":
rob@100 20521 return font.braceleft;
rob@100 20522 case "|":
rob@100 20523 return font.bar;
rob@100 20524 case "}":
rob@100 20525 return font.braceright;
rob@100 20526 case "~":
rob@100 20527 return font.asciitilde;
rob@100 20528 // If the character is not 'special', access it by object reference
rob@100 20529 default:
rob@100 20530 return font[chr];
rob@100 20531 }
rob@100 20532 } catch(e) {
rob@100 20533 Processing.debug(e);
rob@100 20534 }
rob@100 20535 };
rob@100 20536
rob@100 20537 // Print some text to the Canvas
rob@100 20538 Drawing2D.prototype.text$line = function(str, x, y, z, align) {
rob@100 20539 var textWidth = 0, xOffset = 0;
rob@100 20540 // If the font is a standard Canvas font...
rob@100 20541 if (!curTextFont.glyph) {
rob@100 20542 if (str && ("fillText" in curContext)) {
rob@100 20543 if (isFillDirty) {
rob@100 20544 curContext.fillStyle = p.color.toString(currentFillColor);
rob@100 20545 isFillDirty = false;
rob@100 20546 }
rob@100 20547
rob@100 20548 // horizontal offset/alignment
rob@100 20549 if(align === PConstants.RIGHT || align === PConstants.CENTER) {
rob@100 20550 textWidth = curTextFont.measureTextWidth(str);
rob@100 20551
rob@100 20552 if(align === PConstants.RIGHT) {
rob@100 20553 xOffset = -textWidth;
rob@100 20554 } else { // if(align === PConstants.CENTER)
rob@100 20555 xOffset = -textWidth/2;
rob@100 20556 }
rob@100 20557 }
rob@100 20558
rob@100 20559 curContext.fillText(str, x+xOffset, y);
rob@100 20560 }
rob@100 20561 } else {
rob@100 20562 // If the font is a Batik SVG font...
rob@100 20563 var font = p.glyphTable[curFontName];
rob@100 20564 saveContext();
rob@100 20565 curContext.translate(x, y + curTextSize);
rob@100 20566
rob@100 20567 // horizontal offset/alignment
rob@100 20568 if(align === PConstants.RIGHT || align === PConstants.CENTER) {
rob@100 20569 textWidth = font.width(str);
rob@100 20570
rob@100 20571 if(align === PConstants.RIGHT) {
rob@100 20572 xOffset = -textWidth;
rob@100 20573 } else { // if(align === PConstants.CENTER)
rob@100 20574 xOffset = -textWidth/2;
rob@100 20575 }
rob@100 20576 }
rob@100 20577
rob@100 20578 var upem = font.units_per_em,
rob@100 20579 newScale = 1 / upem * curTextSize;
rob@100 20580
rob@100 20581 curContext.scale(newScale, newScale);
rob@100 20582
rob@100 20583 for (var i=0, len=str.length; i < len; i++) {
rob@100 20584 // Test character against glyph table
rob@100 20585 try {
rob@100 20586 p.glyphLook(font, str[i]).draw();
rob@100 20587 } catch(e) {
rob@100 20588 Processing.debug(e);
rob@100 20589 }
rob@100 20590 }
rob@100 20591 restoreContext();
rob@100 20592 }
rob@100 20593 };
rob@100 20594
rob@100 20595 Drawing3D.prototype.text$line = function(str, x, y, z, align) {
rob@100 20596 // handle case for 3d text
rob@100 20597 if (textcanvas === undef) {
rob@100 20598 textcanvas = document.createElement("canvas");
rob@100 20599 }
rob@100 20600 var oldContext = curContext;
rob@100 20601 curContext = textcanvas.getContext("2d");
rob@100 20602 curContext.font = curTextFont.css;
rob@100 20603 var textWidth = curTextFont.measureTextWidth(str);
rob@100 20604 textcanvas.width = textWidth;
rob@100 20605 textcanvas.height = curTextSize;
rob@100 20606 curContext = textcanvas.getContext("2d"); // refreshes curContext
rob@100 20607 curContext.font = curTextFont.css;
rob@100 20608 curContext.textBaseline="top";
rob@100 20609
rob@100 20610 // paint on 2D canvas
rob@100 20611 Drawing2D.prototype.text$line(str,0,0,0,PConstants.LEFT);
rob@100 20612
rob@100 20613 // use it as a texture
rob@100 20614 var aspect = textcanvas.width/textcanvas.height;
rob@100 20615 curContext = oldContext;
rob@100 20616
rob@100 20617 curContext.bindTexture(curContext.TEXTURE_2D, textTex);
rob@100 20618 curContext.texImage2D(curContext.TEXTURE_2D, 0, curContext.RGBA, curContext.RGBA, curContext.UNSIGNED_BYTE, textcanvas);
rob@100 20619 curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_MAG_FILTER, curContext.LINEAR);
rob@100 20620 curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_MIN_FILTER, curContext.LINEAR);
rob@100 20621 curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_WRAP_T, curContext.CLAMP_TO_EDGE);
rob@100 20622 curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_WRAP_S, curContext.CLAMP_TO_EDGE);
rob@100 20623 // If we don't have a power of two texture, we can't mipmap it.
rob@100 20624 // curContext.generateMipmap(curContext.TEXTURE_2D);
rob@100 20625
rob@100 20626 // horizontal offset/alignment
rob@100 20627 var xOffset = 0;
rob@100 20628 if (align === PConstants.RIGHT) {
rob@100 20629 xOffset = -textWidth;
rob@100 20630 } else if(align === PConstants.CENTER) {
rob@100 20631 xOffset = -textWidth/2;
rob@100 20632 }
rob@100 20633 var model = new PMatrix3D();
rob@100 20634 var scalefactor = curTextSize * 0.5;
rob@100 20635 model.translate(x+xOffset-scalefactor/2, y-scalefactor, z);
rob@100 20636 model.scale(-aspect*scalefactor, -scalefactor, scalefactor);
rob@100 20637 model.translate(-1, -1, -1);
rob@100 20638 model.transpose();
rob@100 20639
rob@100 20640 var view = new PMatrix3D();
rob@100 20641 view.scale(1, -1, 1);
rob@100 20642 view.apply(modelView.array());
rob@100 20643 view.transpose();
rob@100 20644
rob@100 20645 curContext.useProgram(programObject2D);
rob@100 20646 vertexAttribPointer("aVertex2d", programObject2D, "aVertex", 3, textBuffer);
rob@100 20647 vertexAttribPointer("aTextureCoord2d", programObject2D, "aTextureCoord", 2, textureBuffer);
rob@100 20648 uniformi("uSampler2d", programObject2D, "uSampler", [0]);
rob@100 20649
rob@100 20650 uniformi("uIsDrawingText2d", programObject2D, "uIsDrawingText", true);
rob@100 20651
rob@100 20652 uniformMatrix("uModel2d", programObject2D, "uModel", false, model.array());
rob@100 20653 uniformMatrix("uView2d", programObject2D, "uView", false, view.array());
rob@100 20654 uniformf("uColor2d", programObject2D, "uColor", fillStyle);
rob@100 20655 curContext.bindBuffer(curContext.ELEMENT_ARRAY_BUFFER, indexBuffer);
rob@100 20656 curContext.drawElements(curContext.TRIANGLES, 6, curContext.UNSIGNED_SHORT, 0);
rob@100 20657 };
rob@100 20658
rob@100 20659
rob@100 20660 /**
rob@100 20661 * unbounded text function (z is an optional argument)
rob@100 20662 */
rob@100 20663 function text$4(str, x, y, z) {
rob@100 20664 var lines, linesCount;
rob@100 20665 if(str.indexOf('\n') < 0) {
rob@100 20666 lines = [str];
rob@100 20667 linesCount = 1;
rob@100 20668 } else {
rob@100 20669 lines = str.split(/\r?\n/g);
rob@100 20670 linesCount = lines.length;
rob@100 20671 }
rob@100 20672 // handle text line-by-line
rob@100 20673
rob@100 20674 var yOffset = 0;
rob@100 20675 if(verticalTextAlignment === PConstants.TOP) {
rob@100 20676 yOffset = curTextAscent + curTextDescent;
rob@100 20677 } else if(verticalTextAlignment === PConstants.CENTER) {
rob@100 20678 yOffset = curTextAscent/2 - (linesCount-1)*curTextLeading/2;
rob@100 20679 } else if(verticalTextAlignment === PConstants.BOTTOM) {
rob@100 20680 yOffset = -(curTextDescent + (linesCount-1)*curTextLeading);
rob@100 20681 }
rob@100 20682
rob@100 20683 for(var i=0;i<linesCount;++i) {
rob@100 20684 var line = lines[i];
rob@100 20685 drawing.text$line(line, x, y + yOffset, z, horizontalTextAlignment);
rob@100 20686 yOffset += curTextLeading;
rob@100 20687 }
rob@100 20688 }
rob@100 20689
rob@100 20690
rob@100 20691 /**
rob@100 20692 * box-bounded text function (z is an optional argument)
rob@100 20693 */
rob@100 20694 function text$6(str, x, y, width, height, z) {
rob@100 20695 // 'fail' on 0-valued dimensions
rob@100 20696 if (str.length === 0 || width === 0 || height === 0) {
rob@100 20697 return;
rob@100 20698 }
rob@100 20699 // also 'fail' if the text height is larger than the bounding height
rob@100 20700 if(curTextSize > height) {
rob@100 20701 return;
rob@100 20702 }
rob@100 20703
rob@100 20704 var spaceMark = -1;
rob@100 20705 var start = 0;
rob@100 20706 var lineWidth = 0;
rob@100 20707 var drawCommands = [];
rob@100 20708
rob@100 20709 // run through text, character-by-character
rob@100 20710 for (var charPos=0, len=str.length; charPos < len; charPos++)
rob@100 20711 {
rob@100 20712 var currentChar = str[charPos];
rob@100 20713 var spaceChar = (currentChar === " ");
rob@100 20714 var letterWidth = curTextFont.measureTextWidth(currentChar);
rob@100 20715
rob@100 20716 // if we aren't looking at a newline, and the text still fits, keep processing
rob@100 20717 if (currentChar !== "\n" && (lineWidth + letterWidth <= width)) {
rob@100 20718 if (spaceChar) { spaceMark = charPos; }
rob@100 20719 lineWidth += letterWidth;
rob@100 20720 }
rob@100 20721
rob@100 20722 // if we're looking at a newline, or the text no longer fits, push the section that fit into the drawcommand list
rob@100 20723 else
rob@100 20724 {
rob@100 20725 if (spaceMark + 1 === start) {
rob@100 20726 if(charPos>0) {
rob@100 20727 // Whole line without spaces so far.
rob@100 20728 spaceMark = charPos;
rob@100 20729 } else {
rob@100 20730 // 'fail', because the line can't even fit the first character
rob@100 20731 return;
rob@100 20732 }
rob@100 20733 }
rob@100 20734
rob@100 20735 if (currentChar === "\n") {
rob@100 20736 drawCommands.push({text:str.substring(start, charPos), width: lineWidth});
rob@100 20737 start = charPos + 1;
rob@100 20738 } else {
rob@100 20739 // current is not a newline, which means the line doesn't fit in box. push text.
rob@100 20740 // In Processing 1.5.1, the space is also pushed, so we push up to spaceMark+1,
rob@100 20741 // rather than up to spaceMark, as was the case for Processing 1.5 and earlier.
rob@100 20742 drawCommands.push({text:str.substring(start, spaceMark+1), width: lineWidth});
rob@100 20743 start = spaceMark + 1;
rob@100 20744 }
rob@100 20745
rob@100 20746 // newline + return
rob@100 20747 lineWidth = 0;
rob@100 20748 charPos = start - 1;
rob@100 20749 }
rob@100 20750 }
rob@100 20751
rob@100 20752 // push the remaining text
rob@100 20753 if (start < len) {
rob@100 20754 drawCommands.push({text:str.substring(start), width: lineWidth});
rob@100 20755 }
rob@100 20756
rob@100 20757 // resolve horizontal alignment
rob@100 20758 var xOffset = 1,
rob@100 20759 yOffset = curTextAscent;
rob@100 20760 if (horizontalTextAlignment === PConstants.CENTER) {
rob@100 20761 xOffset = width/2;
rob@100 20762 } else if (horizontalTextAlignment === PConstants.RIGHT) {
rob@100 20763 xOffset = width;
rob@100 20764 }
rob@100 20765
rob@100 20766 // resolve vertical alignment
rob@100 20767 var linesCount = drawCommands.length,
rob@100 20768 visibleLines = Math.min(linesCount, Math.floor(height/curTextLeading));
rob@100 20769 if(verticalTextAlignment === PConstants.TOP) {
rob@100 20770 yOffset = curTextAscent + curTextDescent;
rob@100 20771 } else if(verticalTextAlignment === PConstants.CENTER) {
rob@100 20772 yOffset = (height/2) - curTextLeading * (visibleLines/2 - 1);
rob@100 20773 } else if(verticalTextAlignment === PConstants.BOTTOM) {
rob@100 20774 yOffset = curTextDescent + curTextLeading;
rob@100 20775 }
rob@100 20776
rob@100 20777 var command,
rob@100 20778 drawCommand,
rob@100 20779 leading;
rob@100 20780 for (command = 0; command < linesCount; command++) {
rob@100 20781 leading = command * curTextLeading;
rob@100 20782 // stop if not enough space for one more line draw
rob@100 20783 if (yOffset + leading > height - curTextDescent) {
rob@100 20784 break;
rob@100 20785 }
rob@100 20786 drawCommand = drawCommands[command];
rob@100 20787 drawing.text$line(drawCommand.text, x + xOffset, y + yOffset + leading, z, horizontalTextAlignment);
rob@100 20788 }
rob@100 20789 }
rob@100 20790
rob@100 20791 /**
rob@100 20792 * text() Draws text to the screen.
rob@100 20793 *
rob@100 20794 * @param {String|char|int|float} data the alphanumeric symbols to be displayed
rob@100 20795 * @param {int|float} x x-coordinate of text
rob@100 20796 * @param {int|float} y y-coordinate of text
rob@100 20797 * @param {int|float} z optional z-coordinate of text
rob@100 20798 * @param {String} stringdata optional letters to be displayed
rob@100 20799 * @param {int|float} width optional width of text box
rob@100 20800 * @param {int|float} height optional height of text box
rob@100 20801 *
rob@100 20802 * @see #textAlign
rob@100 20803 * @see #textMode
rob@100 20804 * @see #loadFont
rob@100 20805 * @see #PFont
rob@100 20806 * @see #textFont
rob@100 20807 */
rob@100 20808 p.text = function() {
rob@100 20809 if (textMode === PConstants.SHAPE) {
rob@100 20810 // TODO: requires beginRaw function
rob@100 20811 return;
rob@100 20812 }
rob@100 20813 if (arguments.length === 3) { // for text( str, x, y)
rob@100 20814 text$4(toP5String(arguments[0]), arguments[1], arguments[2], 0);
rob@100 20815 } else if (arguments.length === 4) { // for text( str, x, y, z)
rob@100 20816 text$4(toP5String(arguments[0]), arguments[1], arguments[2], arguments[3]);
rob@100 20817 } else if (arguments.length === 5) { // for text( str, x, y , width, height)
rob@100 20818 text$6(toP5String(arguments[0]), arguments[1], arguments[2], arguments[3], arguments[4], 0);
rob@100 20819 } else if (arguments.length === 6) { // for text( stringdata, x, y , width, height, z)
rob@100 20820 text$6(toP5String(arguments[0]), arguments[1], arguments[2], arguments[3], arguments[4], arguments[5]);
rob@100 20821 }
rob@100 20822 };
rob@100 20823
rob@100 20824 /**
rob@100 20825 * Sets the way text draws to the screen. In the default configuration (the MODEL mode), it's possible to rotate,
rob@100 20826 * scale, and place letters in two and three dimensional space. <br /><br /> Changing to SCREEN mode draws letters
rob@100 20827 * directly to the front of the window and greatly increases rendering quality and speed when used with the P2D and
rob@100 20828 * P3D renderers. textMode(SCREEN) with OPENGL and JAVA2D (the default) renderers will generally be slower, though
rob@100 20829 * pixel accurate with P2D and P3D. With textMode(SCREEN), the letters draw at the actual size of the font (in pixels)
rob@100 20830 * and therefore calls to <b>textSize()</b> will not affect the size of the letters. To create a font at the size you
rob@100 20831 * desire, use the "Create font..." option in the Tools menu, or use the createFont() function. When using textMode(SCREEN),
rob@100 20832 * any z-coordinate passed to a text() command will be ignored, because your computer screen is...flat!
rob@100 20833 *
rob@100 20834 * @param {int} MODE Either MODEL, SCREEN or SHAPE (not yet supported)
rob@100 20835 *
rob@100 20836 * @see loadFont
rob@100 20837 * @see PFont
rob@100 20838 * @see text
rob@100 20839 * @see textFont
rob@100 20840 * @see createFont
rob@100 20841 */
rob@100 20842 p.textMode = function(mode){
rob@100 20843 textMode = mode;
rob@100 20844 };
rob@100 20845
rob@100 20846 // Load Batik SVG Fonts and parse to pre-def objects for quick rendering
rob@100 20847 p.loadGlyphs = function(url) {
rob@100 20848 var x, y, cx, cy, nx, ny, d, a, lastCom, lenC, horiz_adv_x, getXY = '[0-9\\-]+', path;
rob@100 20849
rob@100 20850 // Return arrays of SVG commands and coords
rob@100 20851 // get this to use p.matchAll() - will need to work around the lack of null return
rob@100 20852 var regex = function(needle, hay) {
rob@100 20853 var i = 0,
rob@100 20854 results = [],
rob@100 20855 latest, regexp = new RegExp(needle, "g");
rob@100 20856 latest = results[i] = regexp.exec(hay);
rob@100 20857 while (latest) {
rob@100 20858 i++;
rob@100 20859 latest = results[i] = regexp.exec(hay);
rob@100 20860 }
rob@100 20861 return results;
rob@100 20862 };
rob@100 20863
rob@100 20864 var buildPath = function(d) {
rob@100 20865 var c = regex("[A-Za-z][0-9\\- ]+|Z", d);
rob@100 20866 var beforePathDraw = function() {
rob@100 20867 saveContext();
rob@100 20868 return drawing.$ensureContext();
rob@100 20869 };
rob@100 20870 var afterPathDraw = function() {
rob@100 20871 executeContextFill();
rob@100 20872 executeContextStroke();
rob@100 20873 restoreContext();
rob@100 20874 };
rob@100 20875
rob@100 20876 // Begin storing path object
rob@100 20877 path = "return {draw:function(){var curContext=beforePathDraw();curContext.beginPath();";
rob@100 20878
rob@100 20879 x = 0;
rob@100 20880 y = 0;
rob@100 20881 cx = 0;
rob@100 20882 cy = 0;
rob@100 20883 nx = 0;
rob@100 20884 ny = 0;
rob@100 20885 d = 0;
rob@100 20886 a = 0;
rob@100 20887 lastCom = "";
rob@100 20888 lenC = c.length - 1;
rob@100 20889
rob@100 20890 // Loop through SVG commands translating to canvas eqivs functions in path object
rob@100 20891 for (var j = 0; j < lenC; j++) {
rob@100 20892 var com = c[j][0], xy = regex(getXY, com);
rob@100 20893
rob@100 20894 switch (com[0]) {
rob@100 20895 case "M":
rob@100 20896 //curContext.moveTo(x,-y);
rob@100 20897 x = parseFloat(xy[0][0]);
rob@100 20898 y = parseFloat(xy[1][0]);
rob@100 20899 path += "curContext.moveTo(" + x + "," + (-y) + ");";
rob@100 20900 break;
rob@100 20901
rob@100 20902 case "L":
rob@100 20903 //curContext.lineTo(x,-y);
rob@100 20904 x = parseFloat(xy[0][0]);
rob@100 20905 y = parseFloat(xy[1][0]);
rob@100 20906 path += "curContext.lineTo(" + x + "," + (-y) + ");";
rob@100 20907 break;
rob@100 20908
rob@100 20909 case "H":
rob@100 20910 //curContext.lineTo(x,-y)
rob@100 20911 x = parseFloat(xy[0][0]);
rob@100 20912 path += "curContext.lineTo(" + x + "," + (-y) + ");";
rob@100 20913 break;
rob@100 20914
rob@100 20915 case "V":
rob@100 20916 //curContext.lineTo(x,-y);
rob@100 20917 y = parseFloat(xy[0][0]);
rob@100 20918 path += "curContext.lineTo(" + x + "," + (-y) + ");";
rob@100 20919 break;
rob@100 20920
rob@100 20921 case "T":
rob@100 20922 //curContext.quadraticCurveTo(cx,-cy,nx,-ny);
rob@100 20923 nx = parseFloat(xy[0][0]);
rob@100 20924 ny = parseFloat(xy[1][0]);
rob@100 20925
rob@100 20926 if (lastCom === "Q" || lastCom === "T") {
rob@100 20927 d = Math.sqrt(Math.pow(x - cx, 2) + Math.pow(cy - y, 2));
rob@100 20928 a = Math.PI + Math.atan2(cx - x, cy - y);
rob@100 20929 cx = x + (Math.sin(a) * (d));
rob@100 20930 cy = y + (Math.cos(a) * (d));
rob@100 20931 } else {
rob@100 20932 cx = x;
rob@100 20933 cy = y;
rob@100 20934 }
rob@100 20935
rob@100 20936 path += "curContext.quadraticCurveTo(" + cx + "," + (-cy) + "," + nx + "," + (-ny) + ");";
rob@100 20937 x = nx;
rob@100 20938 y = ny;
rob@100 20939 break;
rob@100 20940
rob@100 20941 case "Q":
rob@100 20942 //curContext.quadraticCurveTo(cx,-cy,nx,-ny);
rob@100 20943 cx = parseFloat(xy[0][0]);
rob@100 20944 cy = parseFloat(xy[1][0]);
rob@100 20945 nx = parseFloat(xy[2][0]);
rob@100 20946 ny = parseFloat(xy[3][0]);
rob@100 20947 path += "curContext.quadraticCurveTo(" + cx + "," + (-cy) + "," + nx + "," + (-ny) + ");";
rob@100 20948 x = nx;
rob@100 20949 y = ny;
rob@100 20950 break;
rob@100 20951
rob@100 20952 case "Z":
rob@100 20953 //curContext.closePath();
rob@100 20954 path += "curContext.closePath();";
rob@100 20955 break;
rob@100 20956 }
rob@100 20957 lastCom = com[0];
rob@100 20958 }
rob@100 20959
rob@100 20960 path += "afterPathDraw();";
rob@100 20961 path += "curContext.translate(" + horiz_adv_x + ",0);";
rob@100 20962 path += "}}";
rob@100 20963
rob@100 20964 return ((new Function("beforePathDraw", "afterPathDraw", path))(beforePathDraw, afterPathDraw));
rob@100 20965 };
rob@100 20966
rob@100 20967 // Parse SVG font-file into block of Canvas commands
rob@100 20968 var parseSVGFont = function(svg) {
rob@100 20969 // Store font attributes
rob@100 20970 var font = svg.getElementsByTagName("font");
rob@100 20971 p.glyphTable[url].horiz_adv_x = font[0].getAttribute("horiz-adv-x");
rob@100 20972
rob@100 20973 var font_face = svg.getElementsByTagName("font-face")[0];
rob@100 20974 p.glyphTable[url].units_per_em = parseFloat(font_face.getAttribute("units-per-em"));
rob@100 20975 p.glyphTable[url].ascent = parseFloat(font_face.getAttribute("ascent"));
rob@100 20976 p.glyphTable[url].descent = parseFloat(font_face.getAttribute("descent"));
rob@100 20977
rob@100 20978 var glyph = svg.getElementsByTagName("glyph"),
rob@100 20979 len = glyph.length;
rob@100 20980
rob@100 20981 // Loop through each glyph in the SVG
rob@100 20982 for (var i = 0; i < len; i++) {
rob@100 20983 // Store attributes for this glyph
rob@100 20984 var unicode = glyph[i].getAttribute("unicode");
rob@100 20985 var name = glyph[i].getAttribute("glyph-name");
rob@100 20986 horiz_adv_x = glyph[i].getAttribute("horiz-adv-x");
rob@100 20987 if (horiz_adv_x === null) {
rob@100 20988 horiz_adv_x = p.glyphTable[url].horiz_adv_x;
rob@100 20989 }
rob@100 20990 d = glyph[i].getAttribute("d");
rob@100 20991 // Split path commands in glpyh
rob@100 20992 if (d !== undef) {
rob@100 20993 path = buildPath(d);
rob@100 20994 // Store glyph data to table object
rob@100 20995 p.glyphTable[url][name] = {
rob@100 20996 name: name,
rob@100 20997 unicode: unicode,
rob@100 20998 horiz_adv_x: horiz_adv_x,
rob@100 20999 draw: path.draw
rob@100 21000 };
rob@100 21001 }
rob@100 21002 } // finished adding glyphs to table
rob@100 21003 };
rob@100 21004
rob@100 21005 // Load and parse Batik SVG font as XML into a Processing Glyph object
rob@100 21006 var loadXML = function() {
rob@100 21007 var xmlDoc;
rob@100 21008
rob@100 21009 try {
rob@100 21010 xmlDoc = document.implementation.createDocument("", "", null);
rob@100 21011 }
rob@100 21012 catch(e_fx_op) {
rob@100 21013 Processing.debug(e_fx_op.message);
rob@100 21014 return;
rob@100 21015 }
rob@100 21016
rob@100 21017 try {
rob@100 21018 xmlDoc.async = false;
rob@100 21019 xmlDoc.load(url);
rob@100 21020 parseSVGFont(xmlDoc.getElementsByTagName("svg")[0]);
rob@100 21021 }
rob@100 21022 catch(e_sf_ch) {
rob@100 21023 // Google Chrome, Safari etc.
rob@100 21024 Processing.debug(e_sf_ch);
rob@100 21025 try {
rob@100 21026 var xmlhttp = new window.XMLHttpRequest();
rob@100 21027 xmlhttp.open("GET", url, false);
rob@100 21028 xmlhttp.send(null);
rob@100 21029 parseSVGFont(xmlhttp.responseXML.documentElement);
rob@100 21030 }
rob@100 21031 catch(e) {
rob@100 21032 Processing.debug(e_sf_ch);
rob@100 21033 }
rob@100 21034 }
rob@100 21035 };
rob@100 21036
rob@100 21037 // Create a new object in glyphTable to store this font
rob@100 21038 p.glyphTable[url] = {};
rob@100 21039
rob@100 21040 // Begin loading the Batik SVG font...
rob@100 21041 loadXML(url);
rob@100 21042
rob@100 21043 // Return the loaded font for attribute grabbing
rob@100 21044 return p.glyphTable[url];
rob@100 21045 };
rob@100 21046
rob@100 21047 /**
rob@100 21048 * Gets the sketch parameter value. The parameter can be defined as the canvas attribute with
rob@100 21049 * the "data-processing-" prefix or provided in the pjs directive (e.g. param-test="52").
rob@100 21050 * The function tries the canvas attributes, then the pjs directive content.
rob@100 21051 *
rob@100 21052 * @param {String} name The name of the param to read.
rob@100 21053 *
rob@100 21054 * @returns {String} The parameter value, or null if parameter is not defined.
rob@100 21055 */
rob@100 21056 p.param = function(name) {
rob@100 21057 // trying attribute that was specified in CANVAS
rob@100 21058 var attributeName = "data-processing-" + name;
rob@100 21059 if (curElement.hasAttribute(attributeName)) {
rob@100 21060 return curElement.getAttribute(attributeName);
rob@100 21061 }
rob@100 21062 // trying child PARAM elements of the CANVAS
rob@100 21063 for (var i = 0, len = curElement.childNodes.length; i < len; ++i) {
rob@100 21064 var item = curElement.childNodes.item(i);
rob@100 21065 if (item.nodeType !== 1 || item.tagName.toLowerCase() !== "param") {
rob@100 21066 continue;
rob@100 21067 }
rob@100 21068 if (item.getAttribute("name") === name) {
rob@100 21069 return item.getAttribute("value");
rob@100 21070 }
rob@100 21071 }
rob@100 21072 // fallback to default params
rob@100 21073 if (curSketch.params.hasOwnProperty(name)) {
rob@100 21074 return curSketch.params[name];
rob@100 21075 }
rob@100 21076 return null;
rob@100 21077 };
rob@100 21078
rob@100 21079 ////////////////////////////////////////////////////////////////////////////
rob@100 21080 // 2D/3D methods wiring utils
rob@100 21081 ////////////////////////////////////////////////////////////////////////////
rob@100 21082 function wireDimensionalFunctions(mode) {
rob@100 21083 // Drawing2D/Drawing3D
rob@100 21084 if (mode === '3D') {
rob@100 21085 drawing = new Drawing3D();
rob@100 21086 } else if (mode === '2D') {
rob@100 21087 drawing = new Drawing2D();
rob@100 21088 } else {
rob@100 21089 drawing = new DrawingPre();
rob@100 21090 }
rob@100 21091
rob@100 21092 // Wire up functions (Use DrawingPre properties names)
rob@100 21093 for (var i in DrawingPre.prototype) {
rob@100 21094 if (DrawingPre.prototype.hasOwnProperty(i) && i.indexOf("$") < 0) {
rob@100 21095 p[i] = drawing[i];
rob@100 21096 }
rob@100 21097 }
rob@100 21098
rob@100 21099 // Run initialization
rob@100 21100 drawing.$init();
rob@100 21101 }
rob@100 21102
rob@100 21103 function createDrawingPreFunction(name) {
rob@100 21104 return function() {
rob@100 21105 wireDimensionalFunctions("2D");
rob@100 21106 return drawing[name].apply(this, arguments);
rob@100 21107 };
rob@100 21108 }
rob@100 21109 DrawingPre.prototype.translate = createDrawingPreFunction("translate");
rob@100 21110 DrawingPre.prototype.transform = createDrawingPreFunction("transform");
rob@100 21111 DrawingPre.prototype.scale = createDrawingPreFunction("scale");
rob@100 21112 DrawingPre.prototype.pushMatrix = createDrawingPreFunction("pushMatrix");
rob@100 21113 DrawingPre.prototype.popMatrix = createDrawingPreFunction("popMatrix");
rob@100 21114 DrawingPre.prototype.resetMatrix = createDrawingPreFunction("resetMatrix");
rob@100 21115 DrawingPre.prototype.applyMatrix = createDrawingPreFunction("applyMatrix");
rob@100 21116 DrawingPre.prototype.rotate = createDrawingPreFunction("rotate");
rob@100 21117 DrawingPre.prototype.rotateZ = createDrawingPreFunction("rotateZ");
rob@100 21118 DrawingPre.prototype.shearX = createDrawingPreFunction("shearX");
rob@100 21119 DrawingPre.prototype.shearY = createDrawingPreFunction("shearY");
rob@100 21120 DrawingPre.prototype.redraw = createDrawingPreFunction("redraw");
rob@100 21121 DrawingPre.prototype.toImageData = createDrawingPreFunction("toImageData");
rob@100 21122 DrawingPre.prototype.ambientLight = createDrawingPreFunction("ambientLight");
rob@100 21123 DrawingPre.prototype.directionalLight = createDrawingPreFunction("directionalLight");
rob@100 21124 DrawingPre.prototype.lightFalloff = createDrawingPreFunction("lightFalloff");
rob@100 21125 DrawingPre.prototype.lightSpecular = createDrawingPreFunction("lightSpecular");
rob@100 21126 DrawingPre.prototype.pointLight = createDrawingPreFunction("pointLight");
rob@100 21127 DrawingPre.prototype.noLights = createDrawingPreFunction("noLights");
rob@100 21128 DrawingPre.prototype.spotLight = createDrawingPreFunction("spotLight");
rob@100 21129 DrawingPre.prototype.beginCamera = createDrawingPreFunction("beginCamera");
rob@100 21130 DrawingPre.prototype.endCamera = createDrawingPreFunction("endCamera");
rob@100 21131 DrawingPre.prototype.frustum = createDrawingPreFunction("frustum");
rob@100 21132 DrawingPre.prototype.box = createDrawingPreFunction("box");
rob@100 21133 DrawingPre.prototype.sphere = createDrawingPreFunction("sphere");
rob@100 21134 DrawingPre.prototype.ambient = createDrawingPreFunction("ambient");
rob@100 21135 DrawingPre.prototype.emissive = createDrawingPreFunction("emissive");
rob@100 21136 DrawingPre.prototype.shininess = createDrawingPreFunction("shininess");
rob@100 21137 DrawingPre.prototype.specular = createDrawingPreFunction("specular");
rob@100 21138 DrawingPre.prototype.fill = createDrawingPreFunction("fill");
rob@100 21139 DrawingPre.prototype.stroke = createDrawingPreFunction("stroke");
rob@100 21140 DrawingPre.prototype.strokeWeight = createDrawingPreFunction("strokeWeight");
rob@100 21141 DrawingPre.prototype.smooth = createDrawingPreFunction("smooth");
rob@100 21142 DrawingPre.prototype.noSmooth = createDrawingPreFunction("noSmooth");
rob@100 21143 DrawingPre.prototype.point = createDrawingPreFunction("point");
rob@100 21144 DrawingPre.prototype.vertex = createDrawingPreFunction("vertex");
rob@100 21145 DrawingPre.prototype.endShape = createDrawingPreFunction("endShape");
rob@100 21146 DrawingPre.prototype.bezierVertex = createDrawingPreFunction("bezierVertex");
rob@100 21147 DrawingPre.prototype.curveVertex = createDrawingPreFunction("curveVertex");
rob@100 21148 DrawingPre.prototype.curve = createDrawingPreFunction("curve");
rob@100 21149 DrawingPre.prototype.line = createDrawingPreFunction("line");
rob@100 21150 DrawingPre.prototype.bezier = createDrawingPreFunction("bezier");
rob@100 21151 DrawingPre.prototype.rect = createDrawingPreFunction("rect");
rob@100 21152 DrawingPre.prototype.ellipse = createDrawingPreFunction("ellipse");
rob@100 21153 DrawingPre.prototype.background = createDrawingPreFunction("background");
rob@100 21154 DrawingPre.prototype.image = createDrawingPreFunction("image");
rob@100 21155 DrawingPre.prototype.textWidth = createDrawingPreFunction("textWidth");
rob@100 21156 DrawingPre.prototype.text$line = createDrawingPreFunction("text$line");
rob@100 21157 DrawingPre.prototype.$ensureContext = createDrawingPreFunction("$ensureContext");
rob@100 21158 DrawingPre.prototype.$newPMatrix = createDrawingPreFunction("$newPMatrix");
rob@100 21159
rob@100 21160 DrawingPre.prototype.size = function(aWidth, aHeight, aMode) {
rob@100 21161 wireDimensionalFunctions(aMode === PConstants.WEBGL ? "3D" : "2D");
rob@100 21162 p.size(aWidth, aHeight, aMode);
rob@100 21163 };
rob@100 21164
rob@100 21165 DrawingPre.prototype.$init = noop;
rob@100 21166
rob@100 21167 Drawing2D.prototype.$init = function() {
rob@100 21168 // Setup default 2d canvas context.
rob@100 21169 // Moving this here removes the number of times we need to check the 3D variable
rob@100 21170 p.size(p.width, p.height);
rob@100 21171
rob@100 21172 curContext.lineCap = 'round';
rob@100 21173
rob@100 21174 // Set default stroke and fill color
rob@100 21175 p.noSmooth();
rob@100 21176 p.disableContextMenu();
rob@100 21177 };
rob@100 21178 Drawing3D.prototype.$init = function() {
rob@100 21179 // For ref/perf test compatibility until those are fixed
rob@100 21180 p.use3DContext = true;
rob@100 21181 p.disableContextMenu();
rob@100 21182 };
rob@100 21183
rob@100 21184 DrawingShared.prototype.$ensureContext = function() {
rob@100 21185 return curContext;
rob@100 21186 };
rob@100 21187
rob@100 21188 //////////////////////////////////////////////////////////////////////////
rob@100 21189 // Keyboard Events
rob@100 21190 //////////////////////////////////////////////////////////////////////////
rob@100 21191
rob@100 21192 // In order to catch key events in a canvas, it needs to be "specially focusable",
rob@100 21193 // by assigning it a tabindex. If no tabindex is specified on-page, set this to 0.
rob@100 21194 if (!curElement.getAttribute("tabindex")) {
rob@100 21195 curElement.setAttribute("tabindex", 0);
rob@100 21196 }
rob@100 21197
rob@100 21198 function getKeyCode(e) {
rob@100 21199 var code = e.which || e.keyCode;
rob@100 21200 switch (code) {
rob@100 21201 case 13: // ENTER
rob@100 21202 return 10;
rob@100 21203 case 91: // META L (Saf/Mac)
rob@100 21204 case 93: // META R (Saf/Mac)
rob@100 21205 case 224: // META (FF/Mac)
rob@100 21206 return 157;
rob@100 21207 case 57392: // CONTROL (Op/Mac)
rob@100 21208 return 17;
rob@100 21209 case 46: // DELETE
rob@100 21210 return 127;
rob@100 21211 case 45: // INSERT
rob@100 21212 return 155;
rob@100 21213 }
rob@100 21214 return code;
rob@100 21215 }
rob@100 21216
rob@100 21217 function getKeyChar(e) {
rob@100 21218 var c = e.which || e.keyCode;
rob@100 21219 var anyShiftPressed = e.shiftKey || e.ctrlKey || e.altKey || e.metaKey;
rob@100 21220 switch (c) {
rob@100 21221 case 13:
rob@100 21222 c = anyShiftPressed ? 13 : 10; // RETURN vs ENTER (Mac)
rob@100 21223 break;
rob@100 21224 case 8:
rob@100 21225 c = anyShiftPressed ? 127 : 8; // DELETE vs BACKSPACE (Mac)
rob@100 21226 break;
rob@100 21227 }
rob@100 21228 return new Char(c);
rob@100 21229 }
rob@100 21230
rob@100 21231 function suppressKeyEvent(e) {
rob@100 21232 if (typeof e.preventDefault === "function") {
rob@100 21233 e.preventDefault();
rob@100 21234 } else if (typeof e.stopPropagation === "function") {
rob@100 21235 e.stopPropagation();
rob@100 21236 }
rob@100 21237 return false;
rob@100 21238 }
rob@100 21239
rob@100 21240 function updateKeyPressed() {
rob@100 21241 var ch;
rob@100 21242 for (ch in pressedKeysMap) {
rob@100 21243 if (pressedKeysMap.hasOwnProperty(ch)) {
rob@100 21244 p.__keyPressed = true;
rob@100 21245 return;
rob@100 21246 }
rob@100 21247 }
rob@100 21248 p.__keyPressed = false;
rob@100 21249 }
rob@100 21250
rob@100 21251 function resetKeyPressed() {
rob@100 21252 p.__keyPressed = false;
rob@100 21253 pressedKeysMap = [];
rob@100 21254 lastPressedKeyCode = null;
rob@100 21255 }
rob@100 21256
rob@100 21257 function simulateKeyTyped(code, c) {
rob@100 21258 pressedKeysMap[code] = c;
rob@100 21259 lastPressedKeyCode = null;
rob@100 21260 p.key = c;
rob@100 21261 p.keyCode = code;
rob@100 21262 p.keyPressed();
rob@100 21263 p.keyCode = 0;
rob@100 21264 p.keyTyped();
rob@100 21265 updateKeyPressed();
rob@100 21266 }
rob@100 21267
rob@100 21268 function handleKeydown(e) {
rob@100 21269 var code = getKeyCode(e);
rob@100 21270 if (code === PConstants.DELETE) {
rob@100 21271 simulateKeyTyped(code, new Char(127));
rob@100 21272 return;
rob@100 21273 }
rob@100 21274 if (codedKeys.indexOf(code) < 0) {
rob@100 21275 lastPressedKeyCode = code;
rob@100 21276 return;
rob@100 21277 }
rob@100 21278 var c = new Char(PConstants.CODED);
rob@100 21279 p.key = c;
rob@100 21280 p.keyCode = code;
rob@100 21281 pressedKeysMap[code] = c;
rob@100 21282 p.keyPressed();
rob@100 21283 lastPressedKeyCode = null;
rob@100 21284 updateKeyPressed();
rob@100 21285 return suppressKeyEvent(e);
rob@100 21286 }
rob@100 21287
rob@100 21288 function handleKeypress(e) {
rob@100 21289 if (lastPressedKeyCode === null) {
rob@100 21290 return; // processed in handleKeydown
rob@100 21291 }
rob@100 21292 var code = lastPressedKeyCode, c = getKeyChar(e);
rob@100 21293 simulateKeyTyped(code, c);
rob@100 21294 return suppressKeyEvent(e);
rob@100 21295 }
rob@100 21296
rob@100 21297 function handleKeyup(e) {
rob@100 21298 var code = getKeyCode(e), c = pressedKeysMap[code];
rob@100 21299 if (c === undef) {
rob@100 21300 return; // no keyPressed event was generated.
rob@100 21301 }
rob@100 21302 p.key = c;
rob@100 21303 p.keyCode = code;
rob@100 21304 p.keyReleased();
rob@100 21305 delete pressedKeysMap[code];
rob@100 21306 updateKeyPressed();
rob@100 21307 }
rob@100 21308
rob@100 21309 // Send aCode Processing syntax to be converted to JavaScript
rob@100 21310 if (!pgraphicsMode) {
rob@100 21311 if (aCode instanceof Processing.Sketch) {
rob@100 21312 // Use sketch as is
rob@100 21313 curSketch = aCode;
rob@100 21314 } else if (typeof aCode === "function") {
rob@100 21315 // Wrap function with default sketch parameters
rob@100 21316 curSketch = new Processing.Sketch(aCode);
rob@100 21317 } else if (!aCode) {
rob@100 21318 // Empty sketch
rob@100 21319 curSketch = new Processing.Sketch(function (){});
rob@100 21320 } else {
rob@100 21321 //#if PARSER
rob@100 21322 // Compile the code
rob@100 21323 curSketch = Processing.compile(aCode);
rob@100 21324 //#else
rob@100 21325 // throw "PJS compile is not supported";
rob@100 21326 //#endif
rob@100 21327 }
rob@100 21328
rob@100 21329 // Expose internal field for diagnostics and testing
rob@100 21330 p.externals.sketch = curSketch;
rob@100 21331
rob@100 21332 wireDimensionalFunctions();
rob@100 21333
rob@100 21334 // the onfocus and onblur events are handled in two parts.
rob@100 21335 // 1) the p.focused property is handled per sketch
rob@100 21336 curElement.onfocus = function() {
rob@100 21337 p.focused = true;
rob@100 21338 };
rob@100 21339
rob@100 21340 curElement.onblur = function() {
rob@100 21341 p.focused = false;
rob@100 21342 if (!curSketch.options.globalKeyEvents) {
rob@100 21343 resetKeyPressed();
rob@100 21344 }
rob@100 21345 };
rob@100 21346
rob@100 21347 // 2) looping status is handled per page, based on the pauseOnBlur @pjs directive
rob@100 21348 if (curSketch.options.pauseOnBlur) {
rob@100 21349 attachEventHandler(window, 'focus', function() {
rob@100 21350 if (doLoop) {
rob@100 21351 p.loop();
rob@100 21352 }
rob@100 21353 });
rob@100 21354
rob@100 21355 attachEventHandler(window, 'blur', function() {
rob@100 21356 if (doLoop && loopStarted) {
rob@100 21357 p.noLoop();
rob@100 21358 doLoop = true; // make sure to keep this true after the noLoop call
rob@100 21359 }
rob@100 21360 resetKeyPressed();
rob@100 21361 });
rob@100 21362 }
rob@100 21363
rob@100 21364 // if keyboard events should be handled globally, the listeners should
rob@100 21365 // be bound to the document window, rather than to the current canvas
rob@100 21366 var keyTrigger = curSketch.options.globalKeyEvents ? window : curElement;
rob@100 21367 attachEventHandler(keyTrigger, "keydown", handleKeydown);
rob@100 21368 attachEventHandler(keyTrigger, "keypress", handleKeypress);
rob@100 21369 attachEventHandler(keyTrigger, "keyup", handleKeyup);
rob@100 21370
rob@100 21371 // Step through the libraries that were attached at doc load...
rob@100 21372 for (var i in Processing.lib) {
rob@100 21373 if (Processing.lib.hasOwnProperty(i)) {
rob@100 21374 if(Processing.lib[i].hasOwnProperty("attach")) {
rob@100 21375 // use attach function if present
rob@100 21376 Processing.lib[i].attach(p);
rob@100 21377 } else if(Processing.lib[i] instanceof Function) {
rob@100 21378 // Init the libraries in the context of this p_instance (legacy)
rob@100 21379 Processing.lib[i].call(this);
rob@100 21380 }
rob@100 21381 }
rob@100 21382 }
rob@100 21383
rob@100 21384 // sketch execute test interval, used to reschedule
rob@100 21385 // an execute when preloads have not yet finished.
rob@100 21386 var retryInterval = 100;
rob@100 21387
rob@100 21388 var executeSketch = function(processing) {
rob@100 21389 // Don't start until all specified images and fonts in the cache are preloaded
rob@100 21390 if (!(curSketch.imageCache.pending || PFont.preloading.pending(retryInterval))) {
rob@100 21391 // the opera preload cache can only be cleared once we start
rob@100 21392 if (window.opera) {
rob@100 21393 var link,
rob@100 21394 element,
rob@100 21395 operaCache=curSketch.imageCache.operaCache;
rob@100 21396 for (link in operaCache) {
rob@100 21397 if(operaCache.hasOwnProperty(link)) {
rob@100 21398 element = operaCache[link];
rob@100 21399 if (element !== null) {
rob@100 21400 document.body.removeChild(element);
rob@100 21401 }
rob@100 21402 delete(operaCache[link]);
rob@100 21403 }
rob@100 21404 }
rob@100 21405 }
rob@100 21406
rob@100 21407 curSketch.attach(processing, defaultScope);
rob@100 21408
rob@100 21409 // pass a reference to the p instance for this sketch.
rob@100 21410 curSketch.onLoad(processing);
rob@100 21411
rob@100 21412 // Run void setup()
rob@100 21413 if (processing.setup) {
rob@100 21414 processing.setup();
rob@100 21415 // if any transforms were performed in setup reset to identity matrix
rob@100 21416 // so draw loop is unpolluted
rob@100 21417 processing.resetMatrix();
rob@100 21418 curSketch.onSetup();
rob@100 21419 }
rob@100 21420
rob@100 21421 // some pixels can be cached, flushing
rob@100 21422 resetContext();
rob@100 21423
rob@100 21424 if (processing.draw) {
rob@100 21425 if (!doLoop) {
rob@100 21426 processing.redraw();
rob@100 21427 } else {
rob@100 21428 processing.loop();
rob@100 21429 }
rob@100 21430 }
rob@100 21431 } else {
rob@100 21432 window.setTimeout(function() { executeSketch(processing); }, retryInterval);
rob@100 21433 }
rob@100 21434 };
rob@100 21435
rob@100 21436 // Only store an instance of non-createGraphics instances.
rob@100 21437 addInstance(this);
rob@100 21438
rob@100 21439 // The parser adds custom methods to the processing context
rob@100 21440 // this renames p to processing so these methods will run
rob@100 21441 executeSketch(p);
rob@100 21442 } else {
rob@100 21443 // No executable sketch was specified
rob@100 21444 // or called via createGraphics
rob@100 21445 curSketch = new Processing.Sketch();
rob@100 21446
rob@100 21447 wireDimensionalFunctions();
rob@100 21448
rob@100 21449 // Hack to make PGraphics work again after splitting size()
rob@100 21450 p.size = function(w, h, render) {
rob@100 21451 if (render && render === PConstants.WEBGL) {
rob@100 21452 wireDimensionalFunctions('3D');
rob@100 21453 } else {
rob@100 21454 wireDimensionalFunctions('2D');
rob@100 21455 }
rob@100 21456
rob@100 21457 p.size(w, h, render);
rob@100 21458 };
rob@100 21459 }
rob@100 21460 };
rob@100 21461
rob@100 21462 // Place-holder for overridable debugging function
rob@100 21463 Processing.debug = (function() {
rob@100 21464 if ("console" in window) {
rob@100 21465 return function(msg) {
rob@100 21466 window.console.log('Processing.js: ' + msg);
rob@100 21467 };
rob@100 21468 }
rob@100 21469 return noop;
rob@100 21470 }());
rob@100 21471
rob@100 21472 // bind prototype
rob@100 21473 Processing.prototype = defaultScope;
rob@100 21474
rob@100 21475 /**
rob@100 21476 * instance store and lookup
rob@100 21477 */
rob@100 21478 Processing.instances = processingInstances;
rob@100 21479 Processing.getInstanceById = function(name) {
rob@100 21480 return processingInstances[processingInstanceIds[name]];
rob@100 21481 };
rob@100 21482
rob@100 21483 // Unsupported Processing File and I/O operations.
rob@100 21484 (function(Processing) {
rob@100 21485 var unsupportedP5 = ("open() createOutput() createInput() BufferedReader selectFolder() " +
rob@100 21486 "dataPath() createWriter() selectOutput() beginRecord() " +
rob@100 21487 "saveStream() endRecord() selectInput() saveBytes() createReader() " +
rob@100 21488 "beginRaw() endRaw() PrintWriter delay()").split(" "),
rob@100 21489 count = unsupportedP5.length,
rob@100 21490 prettyName,
rob@100 21491 p5Name;
rob@100 21492
rob@100 21493 function createUnsupportedFunc(n) {
rob@100 21494 return function() {
rob@100 21495 throw "Processing.js does not support " + n + ".";
rob@100 21496 };
rob@100 21497 }
rob@100 21498
rob@100 21499 while (count--) {
rob@100 21500 prettyName = unsupportedP5[count];
rob@100 21501 p5Name = prettyName.replace("()", "");
rob@100 21502 Processing[p5Name] = createUnsupportedFunc(prettyName);
rob@100 21503 }
rob@100 21504 }(defaultScope));
rob@100 21505
rob@100 21506 // we're done. Return our object.
rob@100 21507 return Processing;
rob@100 21508 };
rob@100 21509
rob@100 21510 },{}],27:[function(require,module,exports){
rob@100 21511 // Base source files
rob@100 21512 var source = {
rob@100 21513 virtEquals: require("./Helpers/virtEquals"),
rob@100 21514 virtHashCode: require("./Helpers/virtHashCode"),
rob@100 21515 ObjectIterator: require("./Helpers/ObjectIterator"),
rob@100 21516 PConstants: require("./Helpers/PConstants"),
rob@100 21517 ArrayList: require("./Objects/ArrayList"),
rob@100 21518 HashMap: require("./Objects/HashMap"),
rob@100 21519 PVector: require("./Objects/PVector"),
rob@100 21520 PFont: require("./Objects/PFont"),
rob@100 21521 Char: require("./Objects/Char"),
rob@100 21522 XMLAttribute: require("./Objects/XMLAttribute"),
rob@100 21523 XMLElement: require("./Objects/XMLElement"),
rob@100 21524 PMatrix2D: require("./Objects/PMatrix2D"),
rob@100 21525 PMatrix3D: require("./Objects/PMatrix3D"),
rob@100 21526 PShape: require("./Objects/PShape"),
rob@100 21527 colors: require("./Objects/webcolors"),
rob@100 21528 PShapeSVG: require("./Objects/PShapeSVG"),
rob@100 21529 CommonFunctions: require("./P5Functions/commonFunctions"),
rob@100 21530 defaultScope: require("./Helpers/defaultScope"),
rob@100 21531 Processing: require("./Processing"),
rob@100 21532 setupParser: require("./Parser/Parser"),
rob@100 21533 finalize: require("./Helpers/finalizeProcessing")
rob@100 21534 };
rob@100 21535
rob@100 21536 // Additional code that gets tacked onto "p" during
rob@100 21537 // instantiation of a Processing sketch.
rob@100 21538 source.extend = {
rob@100 21539 withMath: require("./P5Functions/Math.js"),
rob@100 21540 withProxyFunctions: require("./P5Functions/JavaProxyFunctions")(source.virtHashCode, source.virtEquals),
rob@100 21541 withTouch: require("./P5Functions/touchmouse"),
rob@100 21542 withCommonFunctions: source.CommonFunctions.withCommonFunctions
rob@100 21543 };
rob@100 21544
rob@100 21545 /**
rob@100 21546 * Processing.js building function
rob@100 21547 */
rob@100 21548 module.exports = function buildProcessingJS(Browser, testHarness) {
rob@100 21549 var noop = function(){},
rob@100 21550 virtEquals = source.virtEquals,
rob@100 21551 virtHashCode = source.virtHashCode,
rob@100 21552 PConstants = source.PConstants,
rob@100 21553 CommonFunctions = source.CommonFunctions,
rob@100 21554 ObjectIterator = source.ObjectIterator,
rob@100 21555 Char = source.Char,
rob@100 21556 XMLAttribute = source.XMLAttribute(),
rob@100 21557
rob@100 21558 ArrayList = source.ArrayList({
rob@100 21559 virtHashCode: virtHashCode,
rob@100 21560 virtEquals: virtEquals
rob@100 21561 }),
rob@100 21562
rob@100 21563 HashMap = source.HashMap({
rob@100 21564 virtHashCode: virtHashCode,
rob@100 21565 virtEquals: virtEquals
rob@100 21566 }),
rob@100 21567
rob@100 21568 PVector = source.PVector({
rob@100 21569 PConstants: PConstants
rob@100 21570 }),
rob@100 21571
rob@100 21572 PFont = source.PFont({
rob@100 21573 Browser: Browser,
rob@100 21574 noop: noop
rob@100 21575 }),
rob@100 21576
rob@100 21577 XMLElement = source.XMLElement({
rob@100 21578 Browser: Browser,
rob@100 21579 XMLAttribute: XMLAttribute
rob@100 21580 }),
rob@100 21581
rob@100 21582 PMatrix2D = source.PMatrix2D({
rob@100 21583 p:CommonFunctions
rob@100 21584 }),
rob@100 21585
rob@100 21586 PMatrix3D = source.PMatrix3D({
rob@100 21587 p:CommonFunctions
rob@100 21588 }),
rob@100 21589
rob@100 21590 PShape = source.PShape({
rob@100 21591 PConstants: PConstants,
rob@100 21592 PMatrix2D: PMatrix2D,
rob@100 21593 PMatrix3D: PMatrix3D
rob@100 21594 }),
rob@100 21595
rob@100 21596 PShapeSVG = source.PShapeSVG({
rob@100 21597 CommonFunctions: CommonFunctions,
rob@100 21598 PConstants: PConstants,
rob@100 21599 PShape: PShape,
rob@100 21600 XMLElement: XMLElement,
rob@100 21601 colors: source.colors
rob@100 21602 }),
rob@100 21603
rob@100 21604 defaultScope = source.defaultScope({
rob@100 21605 ArrayList: ArrayList,
rob@100 21606 HashMap: HashMap,
rob@100 21607 PVector: PVector,
rob@100 21608 PFont: PFont,
rob@100 21609 PShapeSVG: PShapeSVG,
rob@100 21610 ObjectIterator: ObjectIterator,
rob@100 21611 PConstants: PConstants,
rob@100 21612 Char: Char,
rob@100 21613 XMLElement: XMLElement,
rob@100 21614 XML: XMLElement
rob@100 21615 }),
rob@100 21616
rob@100 21617 Processing = source.Processing({
rob@100 21618 defaultScope: defaultScope,
rob@100 21619 Browser: Browser,
rob@100 21620 extend: source.extend,
rob@100 21621 noop: noop
rob@100 21622 });
rob@100 21623
rob@100 21624 // set up the Processing syntax parser
rob@100 21625 Processing = source.setupParser(Processing, {
rob@100 21626 Browser: Browser,
rob@100 21627 aFunctions: testHarness,
rob@100 21628 defaultScope: defaultScope
rob@100 21629 });
rob@100 21630
rob@100 21631 // finalise the Processing object
rob@100 21632 Processing = source.finalize(Processing, {
rob@100 21633 version: require('../package.json').version,
rob@100 21634 isDomPresent: false || Browser.isDomPresent,
rob@100 21635 window: Browser.window,
rob@100 21636 document: Browser.document,
rob@100 21637 noop: noop
rob@100 21638 });
rob@100 21639
rob@100 21640 // done.
rob@100 21641 return Processing;
rob@100 21642 };
rob@100 21643
rob@100 21644 },{"../package.json":2,"./Helpers/ObjectIterator":3,"./Helpers/PConstants":4,"./Helpers/defaultScope":5,"./Helpers/finalizeProcessing":6,"./Helpers/virtEquals":7,"./Helpers/virtHashCode":8,"./Objects/ArrayList":9,"./Objects/Char":10,"./Objects/HashMap":11,"./Objects/PFont":12,"./Objects/PMatrix2D":13,"./Objects/PMatrix3D":14,"./Objects/PShape":15,"./Objects/PShapeSVG":16,"./Objects/PVector":17,"./Objects/XMLAttribute":18,"./Objects/XMLElement":19,"./Objects/webcolors":20,"./P5Functions/JavaProxyFunctions":21,"./P5Functions/Math.js":22,"./P5Functions/commonFunctions":23,"./P5Functions/touchmouse":24,"./Parser/Parser":25,"./Processing":26}]},{},[1])
rob@100 21645 ;