/*! p5.p5.WebGL.js v0.5.11 July 21, 2017 */ (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.p5 = f()}})(function(){var define,module,exports;return (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);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o
* Note that drawing a full circle (ex: 0 to TWO_PI) will appear blank * because 0 and TWO_PI are the same position on the unit circle. The * best way to handle this is by using the ellipse() function instead * to create a closed ellipse, and to use the arc() function * only to draw parts of an ellipse. * * @method arc * @param {Number} a x-coordinate of the arc's ellipse * @param {Number} b y-coordinate of the arc's ellipse * @param {Number} c width of the arc's ellipse by default * @param {Number} d height of the arc's ellipse by default * @param {Number} start angle to start the arc, specified in radians * @param {Number} stop angle to stop the arc, specified in radians * @param {Constant} [mode] optional parameter to determine the way of drawing * the arc. either CHORD or PIE * @chainable * @example *
* * arc(50, 55, 50, 50, 0, HALF_PI); * noFill(); * arc(50, 55, 60, 60, HALF_PI, PI); * arc(50, 55, 70, 70, PI, PI+QUARTER_PI); * arc(50, 55, 80, 80, PI+QUARTER_PI, TWO_PI); * *
* *
* * arc(50, 50, 80, 80, 0, PI+QUARTER_PI, OPEN); * *
* *
* * arc(50, 50, 80, 80, 0, PI+QUARTER_PI, CHORD); * *
* *
* * arc(50, 50, 80, 80, 0, PI+QUARTER_PI, PIE); * *
* * @alt *shattered outline of an ellipse with a quarter of a white circle bottom-right. *white ellipse with black outline with top right missing. *white ellipse with top right missing with black outline around shape. *white ellipse with top right quarter missing with black outline around the shape. * */ p5.prototype.arc = function(x, y, w, h, start, stop, mode) { var args = new Array(arguments.length); for (var i = 0; i < args.length; ++i) { args[i] = arguments[i]; } if (!this._renderer._doStroke && !this._renderer._doFill) { return this; } if (this._angleMode === constants.DEGREES) { start = this.radians(start); stop = this.radians(stop); } // Make all angles positive... while (start < 0) { start += constants.TWO_PI; } while (stop < 0) { stop += constants.TWO_PI; } // ...and confine them to the interval [0,TWO_PI). start %= constants.TWO_PI; stop %= constants.TWO_PI; // account for full circle if (stop === start) { stop += constants.TWO_PI; } // Adjust angles to counter linear scaling. if (start <= constants.HALF_PI) { start = Math.atan(w / h * Math.tan(start)); } else if (start > constants.HALF_PI && start <= 3 * constants.HALF_PI) { start = Math.atan(w / h * Math.tan(start)) + constants.PI; } else { start = Math.atan(w / h * Math.tan(start)) + constants.TWO_PI; } if (stop <= constants.HALF_PI) { stop = Math.atan(w / h * Math.tan(stop)); } else if (stop > constants.HALF_PI && stop <= 3 * constants.HALF_PI) { stop = Math.atan(w / h * Math.tan(stop)) + constants.PI; } else { stop = Math.atan(w / h * Math.tan(stop)) + constants.TWO_PI; } // Exceed the interval if necessary in order to preserve the size and // orientation of the arc. if (start > stop) { stop += constants.TWO_PI; } // p5 supports negative width and heights for ellipses w = Math.abs(w); h = Math.abs(h); this._renderer.arc(x, y, w, h, start, stop, mode); return this; }; /** * Draws an ellipse (oval) to the screen. An ellipse with equal width and * height is a circle. By default, the first two parameters set the location, * and the third and fourth parameters set the shape's width and height. If * no height is specified, the value of width is used for both the width and * height. If a negative height or width is specified, the absolute value is taken. * The origin may be changed with the ellipseMode() function. * * @method ellipse * @param {Number} x x-coordinate of the ellipse. * @param {Number} y y-coordinate of the ellipse. * @param {Number} w width of the ellipse. * @param {Number} [h] height of the ellipse. * @chainable * @example *
* * ellipse(56, 46, 55, 55); * *
* * @alt *white ellipse with black outline in middle-right of canvas that is 55x55. * */ p5.prototype.ellipse = function() { var args = new Array(arguments.length); for (var i = 0; i < args.length; ++i) { args[i] = arguments[i]; } // Duplicate 3rd argument if only 3 given. if (args.length === 3) { args.push(args[2]); } // p5 supports negative width and heights for rects if (args[2] < 0){args[2] = Math.abs(args[2]);} if (args[3] < 0){args[3] = Math.abs(args[3]);} if (!this._renderer._doStroke && !this._renderer._doFill) { return this; } var vals = canvas.modeAdjust( args[0], args[1], args[2], args[3], this._renderer._ellipseMode); args[0] = vals.x; args[1] = vals.y; args[2] = vals.w; args[3] = vals.h; this._renderer.ellipse(args); return this; }; /** * Draws a line (a direct path between two points) to the screen. The version * of line() with four parameters draws the line in 2D. To color a line, use * the stroke() function. A line cannot be filled, therefore the fill() * function will not affect the color of a line. 2D lines are drawn with a * width of one pixel by default, but this can be changed with the * strokeWeight() function. * * @method line * @param {Number} x1 the x-coordinate of the first point * @param {Number} y1 the y-coordinate of the first point * @param {Number} x2 the x-coordinate of the second point * @param {Number} y2 the y-coordinate of the second point * @chainable * @example *
* * line(30, 20, 85, 75); * *
* *
* * line(30, 20, 85, 20); * stroke(126); * line(85, 20, 85, 75); * stroke(255); * line(85, 75, 30, 75); * *
* * @alt *line 78 pixels long running from mid-top to bottom-right of canvas. *3 lines of various stroke sizes. Form top, bottom and right sides of a square. * */ ////commented out original // p5.prototype.line = function(x1, y1, x2, y2) { // if (!this._renderer._doStroke) { // return this; // } // if(this._renderer.isP3D){ // } else { // this._renderer.line(x1, y1, x2, y2); // } // }; p5.prototype.line = function() { if (!this._renderer._doStroke) { return this; } var args = new Array(arguments.length); for (var i = 0; i < args.length; ++i) { args[i] = arguments[i]; } //check whether we should draw a 3d line or 2d if(this._renderer.isP3D){ this._renderer.line( args[0], args[1], args[2], args[3], args[4], args[5]); } else { this._renderer.line( args[0], args[1], args[2], args[3]); } return this; }; /** * Draws a point, a coordinate in space at the dimension of one pixel. * The first parameter is the horizontal value for the point, the second * value is the vertical value for the point. The color of the point is * determined by the current stroke. * * @method point * @param {Number} x the x-coordinate * @param {Number} y the y-coordinate * @chainable * @example *
* * point(30, 20); * point(85, 20); * point(85, 75); * point(30, 75); * *
* * @alt *4 points centered in the middle-right of the canvas. * */ p5.prototype.point = function() { if (!this._renderer._doStroke) { return this; } var args = new Array(arguments.length); for (var i = 0; i < args.length; ++i) { args[i] = arguments[i]; } //check whether we should draw a 3d line or 2d if(this._renderer.isP3D){ this._renderer.point( args[0], args[1], args[2] ); } else { this._renderer.point( args[0], args[1] ); } return this; }; /** * Draw a quad. A quad is a quadrilateral, a four sided polygon. It is * similar to a rectangle, but the angles between its edges are not * constrained to ninety degrees. The first pair of parameters (x1,y1) * sets the first vertex and the subsequent pairs should proceed * clockwise or counter-clockwise around the defined shape. * * @method quad * @param {Number} x1 the x-coordinate of the first point * @param {Number} y1 the y-coordinate of the first point * @param {Number} x2 the x-coordinate of the second point * @param {Number} y2 the y-coordinate of the second point * @param {Number} x3 the x-coordinate of the third point * @param {Number} y3 the y-coordinate of the third point * @param {Number} x4 the x-coordinate of the fourth point * @param {Number} y4 the y-coordinate of the fourth point * @chainable * @example *
* * quad(38, 31, 86, 20, 69, 63, 30, 76); * *
* * @alt *irregular white quadrilateral shape with black outline mid-right of canvas. * */ /** * @method quad * @param {Number} x1 * @param {Number} y1 * @param {Number} x2 * @param {Number} y2 * @param {Number} x3 * @param {Number} y3 * @param {Number} x4 * @param {Number} y4 * @chainable */ p5.prototype.quad = function() { if (!this._renderer._doStroke && !this._renderer._doFill) { return this; } var args = new Array(arguments.length); for (var i = 0; i < args.length; ++i) { args[i] = arguments[i]; } if(this._renderer.isP3D){ this._renderer.quad( args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10], args[11] ); } else { this._renderer.quad( args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7] ); } return this; }; /** * Draws a rectangle to the screen. A rectangle is a four-sided shape with * every angle at ninety degrees. By default, the first two parameters set * the location of the upper-left corner, the third sets the width, and the * fourth sets the height. The way these parameters are interpreted, however, * may be changed with the rectMode() function. *

* The fifth, sixth, seventh and eighth parameters, if specified, * determine corner radius for the top-right, top-left, lower-right and * lower-left corners, respectively. An omitted corner radius parameter is set * to the value of the previously specified radius value in the parameter list. * * @method rect * @param {Number} x x-coordinate of the rectangle. * @param {Number} y y-coordinate of the rectangle. * @param {Number} w width of the rectangle. * @param {Number} h height of the rectangle. * @param {Number} [tl] optional radius of top-left corner. * @param {Number} [tr] optional radius of top-right corner. * @param {Number} [br] optional radius of bottom-right corner. * @param {Number} [bl] optional radius of bottom-left corner. * @return {p5} the p5 object. * @example *
* * // Draw a rectangle at location (30, 20) with a width and height of 55. * rect(30, 20, 55, 55); * *
* *
* * // Draw a rectangle with rounded corners, each having a radius of 20. * rect(30, 20, 55, 55, 20); * *
* *
* * // Draw a rectangle with rounded corners having the following radii: * // top-left = 20, top-right = 15, bottom-right = 10, bottom-left = 5. * rect(30, 20, 55, 55, 20, 15, 10, 5); * *
* * @alt * 55x55 white rect with black outline in mid-right of canvas. * 55x55 white rect with black outline and rounded edges in mid-right of canvas. * 55x55 white rect with black outline and rounded edges of different radii. */ /** * @method rect * @param {Number} x * @param {Number} y * @param {Number} w * @param {Number} h * @param {Number} [detailX] * @param {Number} [detailY] * @chainable */ p5.prototype.rect = function () { var args = new Array(arguments.length); for (var i = 0; i < args.length; ++i) { args[i] = arguments[i]; } if (!this._renderer._doStroke && !this._renderer._doFill) { return this; } var vals = canvas.modeAdjust( args[0], args[1], args[2], args[3], this._renderer._rectMode); args[0] = vals.x; args[1] = vals.y; args[2] = vals.w; args[3] = vals.h; this._renderer.rect(args); return this; }; /** * A triangle is a plane created by connecting three points. The first two * arguments specify the first point, the middle two arguments specify the * second point, and the last two arguments specify the third point. * * @method triangle * @param {Number} x1 x-coordinate of the first point * @param {Number} y1 y-coordinate of the first point * @param {Number} x2 x-coordinate of the second point * @param {Number} y2 y-coordinate of the second point * @param {Number} x3 x-coordinate of the third point * @param {Number} y3 y-coordinate of the third point * @chainable * @example *
* * triangle(30, 75, 58, 20, 86, 75); * *
* *@alt * white triangle with black outline in mid-right of canvas. * */ p5.prototype.triangle = function() { if (!this._renderer._doStroke && !this._renderer._doFill) { return this; } var args = new Array(arguments.length); for (var i = 0; i < args.length; ++i) { args[i] = arguments[i]; } this._renderer.triangle(args); return this; }; module.exports = p5; },{"./canvas":3,"./constants":4,"./core":5,"./error_helpers":8}],2:[function(_dereq_,module,exports){ /** * @module Shape * @submodule Attributes * @for p5 * @requires core * @requires constants */ 'use strict'; var p5 = _dereq_('./core'); var constants = _dereq_('./constants'); /** * Modifies the location from which ellipses are drawn by changing the way * in which parameters given to ellipse() are interpreted. *

* The default mode is ellipseMode(CENTER), which interprets the first two * parameters of ellipse() as the shape's center point, while the third and * fourth parameters are its width and height. *

* ellipseMode(RADIUS) also uses the first two parameters of ellipse() as * the shape's center point, but uses the third and fourth parameters to * specify half of the shapes's width and height. *

* ellipseMode(CORNER) interprets the first two parameters of ellipse() as * the upper-left corner of the shape, while the third and fourth parameters * are its width and height. *

* ellipseMode(CORNERS) interprets the first two parameters of ellipse() as * the location of one corner of the ellipse's bounding box, and the third * and fourth parameters as the location of the opposite corner. *

* The parameter must be written in ALL CAPS because Javascript is a * case-sensitive language. * * @method ellipseMode * @param {Constant} mode either CENTER, RADIUS, CORNER, or CORNERS * @chainable * @example *
* * ellipseMode(RADIUS); // Set ellipseMode to RADIUS * fill(255); // Set fill to white * ellipse(50, 50, 30, 30); // Draw white ellipse using RADIUS mode * * ellipseMode(CENTER); // Set ellipseMode to CENTER * fill(100); // Set fill to gray * ellipse(50, 50, 30, 30); // Draw gray ellipse using CENTER mode * *
* *
* * ellipseMode(CORNER); // Set ellipseMode is CORNER * fill(255); // Set fill to white * ellipse(25, 25, 50, 50); // Draw white ellipse using CORNER mode * * ellipseMode(CORNERS); // Set ellipseMode to CORNERS * fill(100); // Set fill to gray * ellipse(25, 25, 50, 50); // Draw gray ellipse using CORNERS mode * *
* * @alt * 60x60 white ellipse and 30x30 grey ellipse with black outlines at center. * 60x60 white ellipse @center and 30x30 grey ellipse top-right, black outlines. * */ p5.prototype.ellipseMode = function(m) { if (m === constants.CORNER || m === constants.CORNERS || m === constants.RADIUS || m === constants.CENTER) { this._renderer._ellipseMode = m; } return this; }; /** * Draws all geometry with jagged (aliased) edges. Note that smooth() is * active by default, so it is necessary to call noSmooth() to disable * smoothing of geometry, images, and fonts. * * @method noSmooth * @chainable * @example *
* * background(0); * noStroke(); * smooth(); * ellipse(30, 48, 36, 36); * noSmooth(); * ellipse(70, 48, 36, 36); * *
* * @alt * 2 pixelated 36x36 white ellipses to left & right of center, black background * */ p5.prototype.noSmooth = function() { this._renderer.noSmooth(); return this; }; /** * Modifies the location from which rectangles are drawn by changing the way * in which parameters given to rect() are interpreted. *

* The default mode is rectMode(CORNER), which interprets the first two * parameters of rect() as the upper-left corner of the shape, while the * third and fourth parameters are its width and height. *

* rectMode(CORNERS) interprets the first two parameters of rect() as the * location of one corner, and the third and fourth parameters as the * location of the opposite corner. *

* rectMode(CENTER) interprets the first two parameters of rect() as the * shape's center point, while the third and fourth parameters are its * width and height. *

* rectMode(RADIUS) also uses the first two parameters of rect() as the * shape's center point, but uses the third and fourth parameters to specify * half of the shapes's width and height. *

* The parameter must be written in ALL CAPS because Javascript is a * case-sensitive language. * * @method rectMode * @param {Constant} mode either CORNER, CORNERS, CENTER, or RADIUS * @chainable * @example *
* * rectMode(CORNER); // Default rectMode is CORNER * fill(255); // Set fill to white * rect(25, 25, 50, 50); // Draw white rect using CORNER mode * * rectMode(CORNERS); // Set rectMode to CORNERS * fill(100); // Set fill to gray * rect(25, 25, 50, 50); // Draw gray rect using CORNERS mode * *
* *
* * rectMode(RADIUS); // Set rectMode to RADIUS * fill(255); // Set fill to white * rect(50, 50, 30, 30); // Draw white rect using RADIUS mode * * rectMode(CENTER); // Set rectMode to CENTER * fill(100); // Set fill to gray * rect(50, 50, 30, 30); // Draw gray rect using CENTER mode * *
* * @alt * 50x50 white rect at center and 25x25 grey rect in the top left of the other. * 50x50 white rect at center and 25x25 grey rect in the center of the other. * */ p5.prototype.rectMode = function(m) { if (m === constants.CORNER || m === constants.CORNERS || m === constants.RADIUS || m === constants.CENTER) { this._renderer._rectMode = m; } return this; }; /** * Draws all geometry with smooth (anti-aliased) edges. smooth() will also * improve image quality of resized images. Note that smooth() is active by * default; noSmooth() can be used to disable smoothing of geometry, * images, and fonts. * * @method smooth * @chainable * @example *
* * background(0); * noStroke(); * smooth(); * ellipse(30, 48, 36, 36); * noSmooth(); * ellipse(70, 48, 36, 36); * *
* * @alt * 2 pixelated 36x36 white ellipses one left one right of center. On black. * */ p5.prototype.smooth = function() { this._renderer.smooth(); return this; }; /** * Sets the style for rendering line endings. These ends are either squared, * extended, or rounded, each of which specified with the corresponding * parameters: SQUARE, PROJECT, and ROUND. The default cap is ROUND. * * @method strokeCap * @param {Constant} cap either SQUARE, PROJECT, or ROUND * @chainable * @example *
* * strokeWeight(12.0); * strokeCap(ROUND); * line(20, 30, 80, 30); * strokeCap(SQUARE); * line(20, 50, 80, 50); * strokeCap(PROJECT); * line(20, 70, 80, 70); * *
* * @alt * 3 lines. Top line: rounded ends, mid: squared, bottom:longer squared ends. * */ p5.prototype.strokeCap = function(cap) { if (cap === constants.ROUND || cap === constants.SQUARE || cap === constants.PROJECT) { this._renderer.strokeCap(cap); } return this; }; /** * Sets the style of the joints which connect line segments. These joints * are either mitered, beveled, or rounded and specified with the * corresponding parameters MITER, BEVEL, and ROUND. The default joint is * MITER. * * @method strokeJoin * @param {Constant} join either MITER, BEVEL, ROUND * @chainable * @example *
* * noFill(); * strokeWeight(10.0); * strokeJoin(MITER); * beginShape(); * vertex(35, 20); * vertex(65, 50); * vertex(35, 80); * endShape(); * *
* *
* * noFill(); * strokeWeight(10.0); * strokeJoin(BEVEL); * beginShape(); * vertex(35, 20); * vertex(65, 50); * vertex(35, 80); * endShape(); * *
* *
* * noFill(); * strokeWeight(10.0); * strokeJoin(ROUND); * beginShape(); * vertex(35, 20); * vertex(65, 50); * vertex(35, 80); * endShape(); * *
* * @alt * Right-facing arrowhead shape with pointed tip in center of canvas. * Right-facing arrowhead shape with flat tip in center of canvas. * Right-facing arrowhead shape with rounded tip in center of canvas. * */ p5.prototype.strokeJoin = function(join) { if (join === constants.ROUND || join === constants.BEVEL || join === constants.MITER) { this._renderer.strokeJoin(join); } return this; }; /** * Sets the width of the stroke used for lines, points, and the border * around shapes. All widths are set in units of pixels. * * @method strokeWeight * @param {Number} weight the weight (in pixels) of the stroke * @return {p5} the p5 object * @example *
* * strokeWeight(1); // Default * line(20, 20, 80, 20); * strokeWeight(4); // Thicker * line(20, 40, 80, 40); * strokeWeight(10); // Beastly * line(20, 70, 80, 70); * *
* * @alt * 3 horizontal black lines. Top line: thin, mid: medium, bottom:thick. * */ p5.prototype.strokeWeight = function(w) { this._renderer.strokeWeight(w); return this; }; module.exports = p5; },{"./constants":4,"./core":5}],3:[function(_dereq_,module,exports){ /** * @requires constants */ var constants = _dereq_('./constants'); module.exports = { modeAdjust: function(a, b, c, d, mode) { if (mode === constants.CORNER) { return { x: a, y: b, w: c, h: d }; } else if (mode === constants.CORNERS) { return { x: a, y: b, w: c-a, h: d-b }; } else if (mode === constants.RADIUS) { return { x: a-c, y: b-d, w: 2*c, h: 2*d }; } else if (mode === constants.CENTER) { return { x: a-c*0.5, y: b-d*0.5, w: c, h: d }; } }, arcModeAdjust: function(a, b, c, d, mode) { if (mode === constants.CORNER) { return { x: a+c*0.5, y: b+d*0.5, w: c, h: d }; } else if (mode === constants.CORNERS) { return { x: a, y: b, w: c+a, h: d+b }; } else if (mode === constants.RADIUS) { return { x: a, y: b, w: 2*c, h: 2*d }; } else if (mode === constants.CENTER) { return { x: a, y: b, w: c, h: d }; } } }; },{"./constants":4}],4:[function(_dereq_,module,exports){ /** * @module Constants * @submodule Constants * @for p5 */ var PI = Math.PI; module.exports = { // GRAPHICS RENDERER /** * @property {String} P2D * @final */ P2D: 'p2d', /** * @property {String} WEBGL * @final */ WEBGL: 'webgl', // ENVIRONMENT ARROW: 'default', CROSS: 'crosshair', HAND: 'pointer', MOVE: 'move', TEXT: 'text', WAIT: 'wait', // TRIGONOMETRY /** * HALF_PI is a mathematical constant with the value * 1.57079632679489661923. It is half the ratio of the * circumference of a circle to its diameter. It is useful in * combination with the trigonometric functions sin() and cos(). * * @property {Number} HALF_PI * @final * * @example *
* arc(50, 50, 80, 80, 0, HALF_PI); *
* * @alt * 80x80 white quarter-circle with curve toward bottom right of canvas. * */ HALF_PI: PI / 2, /** * PI is a mathematical constant with the value * 3.14159265358979323846. It is the ratio of the circumference * of a circle to its diameter. It is useful in combination with * the trigonometric functions sin() and cos(). * * @property {Number} PI * @final * * @example *
* arc(50, 50, 80, 80, 0, PI); *
* * @alt * white half-circle with curve toward bottom of canvas. * */ PI: PI, /** * QUARTER_PI is a mathematical constant with the value 0.7853982. * It is one quarter the ratio of the circumference of a circle to * its diameter. It is useful in combination with the trigonometric * functions sin() and cos(). * * @property {Number} QUARTER_PI * @final * * @example *
* arc(50, 50, 80, 80, 0, QUARTER_PI); *
* * @alt * white eighth-circle rotated about 40 degrees with curve bottom right canvas. * */ QUARTER_PI: PI / 4, /** * TAU is an alias for TWO_PI, a mathematical constant with the * value 6.28318530717958647693. It is twice the ratio of the * circumference of a circle to its diameter. It is useful in * combination with the trigonometric functions sin() and cos(). * * @property {Number} TAU * @final * * @example *
* arc(50, 50, 80, 80, 0, TAU); *
* * @alt * 80x80 white ellipse shape in center of canvas. * */ TAU: PI * 2, /** * TWO_PI is a mathematical constant with the value * 6.28318530717958647693. It is twice the ratio of the * circumference of a circle to its diameter. It is useful in * combination with the trigonometric functions sin() and cos(). * * @property {Number} TWO_PI * @final * * @example *
* arc(50, 50, 80, 80, 0, TWO_PI); *
* * @alt * 80x80 white ellipse shape in center of canvas. * */ TWO_PI: PI * 2, /** * @property {String} DEGREES * @final */ DEGREES: 'degrees', /** * @property {String} RADIANS * @final */ RADIANS: 'radians', DEG_TO_RAD: PI / 180.0, RAD_TO_DEG: 180.0 / PI, // SHAPE /** * @property {String} CORNER * @final */ CORNER: 'corner', /** * @property {String} CORNERS * @final */ CORNERS: 'corners', /** * @property {String} RADIUS * @final */ RADIUS: 'radius', /** * @property {String} RIGHT * @final */ RIGHT: 'right', /** * @property {String} LEFT * @final */ LEFT: 'left', /** * @property {String} CENTER * @final */ CENTER: 'center', /** * @property {String} TOP * @final */ TOP: 'top', /** * @property {String} BOTTOM * @final */ BOTTOM: 'bottom', /** * @property {String} BASELINE * @final * @default alphabetic */ BASELINE: 'alphabetic', /** * @property {Number} POINTS * @final * @default 0x0000 */ POINTS: 0x0000, /** * @property {Number} LINES * @final * @default 0x0001 */ LINES: 0x0001, /** * @property {Number} LINE_STRIP * @final * @default 0x0003 */ LINE_STRIP: 0x0003, /** * @property {Number} LINE_LOOP * @final * @default 0x0002 */ LINE_LOOP: 0x0002, /** * @property {Number} TRIANGLES * @final * @default 0x0004 */ TRIANGLES: 0x0004, /** * @property {Number} TRIANGLE_FAN * @final * @default 0x0006 */ TRIANGLE_FAN: 0x0006, /** * @property {Number} TRIANGLE_STRIP * @final * @default 0x0005 */ TRIANGLE_STRIP: 0x0005, /** * @property {String} QUADS * @final */ QUADS: 'quads', /** * @property {String} QUAD_STRIP * @final * @default quad_strip */ QUAD_STRIP: 'quad_strip', /** * @property {String} CLOSE * @final */ CLOSE: 'close', /** * @property {String} OPEN * @final */ OPEN: 'open', /** * @property {String} CHORD * @final */ CHORD: 'chord', /** * @property {String} PIE * @final */ PIE: 'pie', /** * @property {String} PROJECT * @final * @default square */ PROJECT: 'square', // PEND: careful this is counterintuitive /** * @property {String} SQUARE * @final * @default butt */ SQUARE: 'butt', /** * @property {String} ROUND * @final */ ROUND: 'round', /** * @property {String} BEVEL * @final */ BEVEL: 'bevel', /** * @property {String} MITER * @final */ MITER: 'miter', // COLOR /** * @property {String} RGB * @final */ RGB: 'rgb', /** * @property {String} HSB * @final */ HSB: 'hsb', /** * @property {String} HSL * @final */ HSL: 'hsl', // DOM EXTENSION AUTO: 'auto', // INPUT ALT: 18, BACKSPACE: 8, CONTROL: 17, DELETE: 46, DOWN_ARROW: 40, ENTER: 13, ESCAPE: 27, LEFT_ARROW: 37, OPTION: 18, RETURN: 13, RIGHT_ARROW: 39, SHIFT: 16, TAB: 9, UP_ARROW: 38, // RENDERING /** * @property {String} BLEND * @final * @default source-over */ BLEND: 'source-over', /** * @property {String} ADD * @final * @default lighter */ ADD: 'lighter', //ADD: 'add', // //SUBTRACT: 'subtract', // /** * @property {String} DARKEST * @final */ DARKEST: 'darken', /** * @property {String} LIGHTEST * @final * @default lighten */ LIGHTEST: 'lighten', /** * @property {String} DIFFERENCE * @final */ DIFFERENCE: 'difference', /** * @property {String} EXCLUSION * @final */ EXCLUSION: 'exclusion', /** * @property {String} MULTIPLY * @final */ MULTIPLY: 'multiply', /** * @property {String} SCREEN * @final */ SCREEN: 'screen', /** * @property {String} REPLACE * @final * @default copy */ REPLACE: 'copy', /** * @property {String} OVERLAY * @final */ OVERLAY: 'overlay', /** * @property {String} HARD_LIGHT * @final */ HARD_LIGHT: 'hard-light', /** * @property {String} SOFT_LIGHT * @final */ SOFT_LIGHT: 'soft-light', /** * @property {String} DODGE * @final * @default color-dodge */ DODGE: 'color-dodge', /** * @property {String} BURN * @final * @default color-burn */ BURN: 'color-burn', // FILTERS /** * @property {String} THRESHOLD * @final */ THRESHOLD: 'threshold', /** * @property {String} GRAY * @final */ GRAY: 'gray', /** * @property {String} OPAQUE * @final */ OPAQUE: 'opaque', /** * @property {String} INVERT * @final */ INVERT: 'invert', /** * @property {String} POSTERIZE * @final */ POSTERIZE: 'posterize', /** * @property {String} DILATE * @final */ DILATE: 'dilate', /** * @property {String} ERODE * @final */ ERODE: 'erode', /** * @property {String} BLUR * @final */ BLUR: 'blur', // TYPOGRAPHY /** * @property {String} NORMAL * @final */ NORMAL: 'normal', /** * @property {String} ITALIC * @final */ ITALIC: 'italic', /** * @property {String} BOLD * @final */ BOLD: 'bold', // TYPOGRAPHY-INTERNAL _DEFAULT_TEXT_FILL: '#000000', _DEFAULT_LEADMULT: 1.25, _CTX_MIDDLE: 'middle', // VERTICES LINEAR: 'linear', QUADRATIC: 'quadratic', BEZIER: 'bezier', CURVE: 'curve', // DEVICE-ORIENTATION /** * @property {String} LANDSCAPE * @final */ LANDSCAPE: 'landscape', /** * @property {String} PORTRAIT * @final */ PORTRAIT: 'portrait', // DEFAULTS _DEFAULT_STROKE: '#000000', _DEFAULT_FILL: '#FFFFFF' }; },{}],5:[function(_dereq_,module,exports){ /** * @module Structure * @submodule Structure * @for p5 * @requires constants */ 'use strict'; _dereq_('./shim'); // Core needs the PVariables object var constants = _dereq_('./constants'); /** * This is the p5 instance constructor. * * A p5 instance holds all the properties and methods related to * a p5 sketch. It expects an incoming sketch closure and it can also * take an optional node parameter for attaching the generated p5 canvas * to a node. The sketch closure takes the newly created p5 instance as * its sole argument and may optionally set preload(), setup(), and/or * draw() properties on it for running a sketch. * * A p5 sketch can run in "global" or "instance" mode: * "global" - all properties and methods are attached to the window * "instance" - all properties and methods are bound to this p5 object * * @param {function} sketch a closure that can set optional preload(), * setup(), and/or draw() properties on the * given p5 instance * @param {HTMLElement|boolean} [node] element to attach canvas to, if a * boolean is passed in use it as sync * @param {boolean} [sync] start synchronously (optional) * @return {p5} a p5 instance */ var p5 = function(sketch, node, sync) { if (arguments.length === 2 && typeof node === 'boolean') { sync = node; node = undefined; } ////////////////////////////////////////////// // PUBLIC p5 PROPERTIES AND METHODS ////////////////////////////////////////////// /** * Called directly before setup(), the preload() function is used to handle * asynchronous loading of external files. If a preload function is * defined, setup() will wait until any load calls within have finished. * Nothing besides load calls should be inside preload (loadImage, * loadJSON, loadFont, loadStrings, etc).

* By default the text "loading..." will be displayed. To make your own * loading page, include an HTML element with id "p5_loading" in your * page. More information here. * * @method preload * @example *
* var img; * var c; * function preload() { // preload() runs once * img = loadImage('assets/laDefense.jpg'); * } * * function setup() { // setup() waits until preload() is done * img.loadPixels(); * // get color of middle pixel * c = img.get(img.width/2, img.height/2); * } * * function draw() { * background(c); * image(img, 25, 25, 50, 50); * } *
* * @alt * nothing displayed * */ /** * The setup() function is called once when the program starts. It's used to * define initial environment properties such as screen size and background * color and to load media such as images and fonts as the program starts. * There can only be one setup() function for each program and it shouldn't * be called again after its initial execution. *

* Note: Variables declared within setup() are not accessible within other * functions, including draw(). * * @method setup * @example *
* var a = 0; * * function setup() { * background(0); * noStroke(); * fill(102); * } * * function draw() { * rect(a++%width, 10, 2, 80); * } *
* * @alt * nothing displayed * */ /** * Called directly after setup(), the draw() function continuously executes * the lines of code contained inside its block until the program is stopped * or noLoop() is called. Note if noLoop() is called in setup(), draw() will * still be executed once before stopping. draw() is called automatically and * should never be called explicitly. *

* It should always be controlled with noLoop(), redraw() and loop(). After * noLoop() stops the code in draw() from executing, redraw() causes the * code inside draw() to execute once, and loop() will cause the code * inside draw() to resume executing continuously. *

* The number of times draw() executes in each second may be controlled with * the frameRate() function. *

* There can only be one draw() function for each sketch, and draw() must * exist if you want the code to run continuously, or to process events such * as mousePressed(). Sometimes, you might have an empty call to draw() in * your program, as shown in the above example. *

* It is important to note that the drawing coordinate system will be reset * at the beginning of each draw() call. If any transformations are performed * within draw() (ex: scale, rotate, translate, their effects will be * undone at the beginning of draw(), so transformations will not accumulate * over time. On the other hand, styling applied (ex: fill, stroke, etc) will * remain in effect. * * @method draw * @example *
* var yPos = 0; * function setup() { // setup() runs once * frameRate(30); * } * function draw() { // draw() loops forever, until stopped * background(204); * yPos = yPos - 1; * if (yPos < 0) { * yPos = height; * } * line(0, yPos, width, yPos); * } *
* * @alt * nothing displayed * */ ////////////////////////////////////////////// // PRIVATE p5 PROPERTIES AND METHODS ////////////////////////////////////////////// this._setupDone = false; // for handling hidpi this._pixelDensity = Math.ceil(window.devicePixelRatio) || 1; this._userNode = node; this._curElement = null; this._elements = []; this._requestAnimId = 0; this._preloadCount = 0; this._isGlobal = false; this._loop = true; this._styles = []; this._defaultCanvasSize = { width: 100, height: 100 }; this._events = { // keep track of user-events for unregistering later 'mousemove': null, 'mousedown': null, 'mouseup': null, 'dragend': null, 'dragover': null, 'click': null, 'mouseover': null, 'mouseout': null, 'keydown': null, 'keyup': null, 'keypress': null, 'touchstart': null, 'touchmove': null, 'touchend': null, 'resize': null, 'blur': null }; this._events.wheel = null; this._loadingScreenId = 'p5_loading'; if (window.DeviceOrientationEvent) { this._events.deviceorientation = null; } if (window.DeviceMotionEvent && !window._isNodeWebkit) { this._events.devicemotion = null; } this._start = function () { // Find node if id given if (this._userNode) { if (typeof this._userNode === 'string') { this._userNode = document.getElementById(this._userNode); } } var userPreload = this.preload || window.preload; // look for "preload" if (userPreload) { // Setup loading screen // Set loading scfeen into dom if not present // Otherwise displays and removes user provided loading screen var loadingScreen = document.getElementById(this._loadingScreenId); if(!loadingScreen){ loadingScreen = document.createElement('div'); loadingScreen.innerHTML = 'Loading...'; loadingScreen.style.position = 'absolute'; loadingScreen.id = this._loadingScreenId; var node = this._userNode || document.body; node.appendChild(loadingScreen); } // var methods = this._preloadMethods; for (var method in this._preloadMethods){ // default to p5 if no object defined this._preloadMethods[method] = this._preloadMethods[method] || p5; var obj = this._preloadMethods[method]; //it's p5, check if it's global or instance if (obj === p5.prototype || obj === p5){ obj = this._isGlobal ? window : this; } this._registeredPreloadMethods[method] = obj[method]; obj[method] = this._wrapPreload(obj, method); } userPreload(); this._runIfPreloadsAreDone(); } else { this._setup(); this._runFrames(); this._draw(); } }.bind(this); this._runIfPreloadsAreDone = function(){ var context = this._isGlobal ? window : this; if (context._preloadCount === 0) { var loadingScreen = document.getElementById(context._loadingScreenId); if (loadingScreen) { loadingScreen.parentNode.removeChild(loadingScreen); } context._setup(); context._runFrames(); context._draw(); } }; this._decrementPreload = function(){ var context = this._isGlobal ? window : this; if(typeof context.preload === 'function'){ context._setProperty('_preloadCount', context._preloadCount - 1); context._runIfPreloadsAreDone(); } }; this._wrapPreload = function(obj, fnName){ return function(){ //increment counter this._incrementPreload(); //call original function var args = new Array(arguments.length); for (var i = 0; i < args.length; ++i) { args[i] = arguments[i]; } // args.push(this._decrementPreload.bind(this)); return this._registeredPreloadMethods[fnName].apply(obj, args); }.bind(this); }; this._incrementPreload = function(){ var context = this._isGlobal ? window : this; context._setProperty('_preloadCount', context._preloadCount + 1); }; this._setup = function() { // Always create a default canvas. // Later on if the user calls createCanvas, this default one // will be replaced this.createCanvas( this._defaultCanvasSize.width, this._defaultCanvasSize.height, 'p2d', true ); // return preload functions to their normal vals if switched by preload var context = this._isGlobal ? window : this; if (typeof context.preload === 'function') { for (var f in this._preloadMethods) { context[f] = this._preloadMethods[f][f]; if (context[f] && this) { context[f] = context[f].bind(this); } } } // Short-circuit on this, in case someone used the library in "global" // mode earlier if (typeof context.setup === 'function') { context.setup(); } // unhide any hidden canvases that were created var canvases = document.getElementsByTagName('canvas'); for (var i = 0; i < canvases.length; i++) { var k = canvases[i]; if (k.dataset.hidden === 'true') { k.style.visibility = ''; delete(k.dataset.hidden); } } this._setupDone = true; }.bind(this); this._draw = function () { var now = window.performance.now(); var time_since_last = now - this._lastFrameTime; var target_time_between_frames = 1000 / this._targetFrameRate; // only draw if we really need to; don't overextend the browser. // draw if we're within 5ms of when our next frame should paint // (this will prevent us from giving up opportunities to draw // again when it's really about time for us to do so). fixes an // issue where the frameRate is too low if our refresh loop isn't // in sync with the browser. note that we have to draw once even // if looping is off, so we bypass the time delay if that // is the case. var epsilon = 5; if (!this._loop || time_since_last >= target_time_between_frames - epsilon) { //mandatory update values(matrixs and stack) this._setProperty('frameCount', this.frameCount + 1); this.redraw(); this._frameRate = 1000.0/(now - this._lastFrameTime); this._lastFrameTime = now; // If the user is actually using mouse module, then update // coordinates, otherwise skip. We can test this by simply // checking if any of the mouse functions are available or not. // NOTE : This reflects only in complete build or modular build. if(typeof this._updateMouseCoords !== 'undefined') { this._updateMouseCoords(); } } // get notified the next time the browser gives us // an opportunity to draw. if (this._loop) { this._requestAnimId = window.requestAnimationFrame(this._draw); } }.bind(this); this._runFrames = function() { if (this._updateInterval) { clearInterval(this._updateInterval); } }.bind(this); this._setProperty = function(prop, value) { this[prop] = value; if (this._isGlobal) { window[prop] = value; } }.bind(this); /** * Removes the entire p5 sketch. This will remove the canvas and any * elements created by p5.js. It will also stop the draw loop and unbind * any properties or methods from the window global scope. It will * leave a variable p5 in case you wanted to create a new p5 sketch. * If you like, you can set p5 = null to erase it. * @method remove * @example *
* function draw() { * ellipse(50, 50, 10, 10); * } * * function mousePressed() { * remove(); // remove whole sketch on mouse press * } *
* * @alt * nothing displayed * */ this.remove = function() { if (this._curElement) { // stop draw this._loop = false; if (this._requestAnimId) { window.cancelAnimationFrame(this._requestAnimId); } // unregister events sketch-wide for (var ev in this._events) { window.removeEventListener(ev, this._events[ev]); } // remove DOM elements created by p5, and listeners for (var i=0; i
Bezier curves were developed by French * automotive engineer Pierre Bezier, and are commonly used in computer * graphics to define gently sloping curves. See also curve(). * * @method bezier * @param {Number} x1 x-coordinate for the first anchor point * @param {Number} y1 y-coordinate for the first anchor point * @param {Number} x2 x-coordinate for the first control point * @param {Number} y2 y-coordinate for the first control point * @param {Number} x3 x-coordinate for the second control point * @param {Number} y3 y-coordinate for the second control point * @param {Number} x4 x-coordinate for the second anchor point * @param {Number} y4 y-coordinate for the second anchor point * @return {p5} the p5 object * @example *
* * noFill(); * stroke(255, 102, 0); * line(85, 20, 10, 10); * line(90, 90, 15, 80); * stroke(0, 0, 0); * bezier(85, 20, 10, 10, 90, 90, 15, 80); * *
* @alt * stretched black s-shape in center with orange lines extending from end points. * stretched black s-shape with 10 5x5 white ellipses along the shape. * stretched black s-shape with 7 5x5 ellipses and orange lines along the shape. * stretched black s-shape with 17 small orange lines extending from under shape. * horseshoe shape with orange ends facing left and black curved center. * horseshoe shape with orange ends facing left and black curved center. * Line shaped like right-facing arrow,points move with mouse-x and warp shape. * horizontal line that hooks downward on the right and 13 5x5 ellipses along it. * right curving line mid-right of canvas with 7 short lines radiating from it. */ /** * @method bezier * @param {Number} z1 z-coordinate for the first anchor point * @param {Number} z2 z-coordinate for the first control point * @param {Number} z3 z-coordinate for the first anchor point * @param {Number} z4 z-coordinate for the first control point * @chainable * @example *
* *background(0, 0, 0); *noFill(); *stroke(255); *bezier(250,250,0, 100,100,0, 100,0,0, 0,100,0); * *
*/ p5.prototype.bezier = function() { var args = new Array(arguments.length); for (var i = 0; i < args.length; ++i) { args[i] = arguments[i]; } if (!this._renderer._doStroke && !this._renderer._doFill) { return this; } if (this._renderer.isP3D){ args.push(bezierDetail);//adding value of bezier detail to the args array this._renderer.bezier(args); } else{ this._renderer.bezier(args[0],args[1], args[2],args[3], args[4],args[5], args[6],args[7]); } return this; }; /** * Sets the resolution at which Beziers display. * * The default value is 20. * * @param {Number} detail resolution of the curves * @chainable * @example *
* * background(204); * bezierDetail(50); * bezier(85, 20, 10, 10, 90, 90, 15, 80); * *
* * @alt * stretched black s-shape with 7 5x5 ellipses and orange lines along the shape. * */ p5.prototype.bezierDetail = function(d) { bezierDetail = d; return this; }; /** * Evaluates the Bezier at position t for points a, b, c, d. * The parameters a and d are the first and last points * on the curve, and b and c are the control points. * The final parameter t varies between 0 and 1. * This can be done once with the x coordinates and a second time * with the y coordinates to get the location of a bezier curve at t. * * @method bezierPoint * @param {Number} a coordinate of first point on the curve * @param {Number} b coordinate of first control point * @param {Number} c coordinate of second control point * @param {Number} d coordinate of second point on the curve * @param {Number} t value between 0 and 1 * @return {Number} the value of the Bezier at position t * @example *
* * noFill(); * x1 = 85, x2 = 10, x3 = 90, x4 = 15; * y1 = 20, y2 = 10, y3 = 90, y4 = 80; * bezier(x1, y1, x2, y2, x3, y3, x4, y4); * fill(255); * steps = 10; * for (i = 0; i <= steps; i++) { * t = i / steps; * x = bezierPoint(x1, x2, x3, x4, t); * y = bezierPoint(y1, y2, y3, y4, t); * ellipse(x, y, 5, 5); * } * *
* * @alt * stretched black s-shape with 17 small orange lines extending from under shape. * */ p5.prototype.bezierPoint = function(a, b, c, d, t) { var adjustedT = 1-t; return Math.pow(adjustedT,3)*a + 3*(Math.pow(adjustedT,2))*t*b + 3*adjustedT*Math.pow(t,2)*c + Math.pow(t,3)*d; }; /** * Evaluates the tangent to the Bezier at position t for points a, b, c, d. * The parameters a and d are the first and last points * on the curve, and b and c are the control points. * The final parameter t varies between 0 and 1. * * @method bezierTangent * @param {Number} a coordinate of first point on the curve * @param {Number} b coordinate of first control point * @param {Number} c coordinate of second control point * @param {Number} d coordinate of second point on the curve * @param {Number} t value between 0 and 1 * @return {Number} the tangent at position t * @example *
* * noFill(); * bezier(85, 20, 10, 10, 90, 90, 15, 80); * steps = 6; * fill(255); * for (i = 0; i <= steps; i++) { * t = i / steps; * // Get the location of the point * x = bezierPoint(85, 10, 90, 15, t); * y = bezierPoint(20, 10, 90, 80, t); * // Get the tangent points * tx = bezierTangent(85, 10, 90, 15, t); * ty = bezierTangent(20, 10, 90, 80, t); * // Calculate an angle from the tangent points * a = atan2(ty, tx); * a += PI; * stroke(255, 102, 0); * line(x, y, cos(a)*30 + x, sin(a)*30 + y); * // The following line of code makes a line * // inverse of the above line * //line(x, y, cos(a)*-30 + x, sin(a)*-30 + y); * stroke(0); * ellipse(x, y, 5, 5); * } * *
* *
* * noFill(); * bezier(85, 20, 10, 10, 90, 90, 15, 80); * stroke(255, 102, 0); * steps = 16; * for (i = 0; i <= steps; i++) { * t = i / steps; * x = bezierPoint(85, 10, 90, 15, t); * y = bezierPoint(20, 10, 90, 80, t); * tx = bezierTangent(85, 10, 90, 15, t); * ty = bezierTangent(20, 10, 90, 80, t); * a = atan2(ty, tx); * a -= HALF_PI; * line(x, y, cos(a)*8 + x, sin(a)*8 + y); * } * *
* * @alt * s-shaped line with 17 short orange lines extending from underside of shape * */ p5.prototype.bezierTangent = function(a, b, c, d, t) { var adjustedT = 1-t; return 3*d*Math.pow(t,2) - 3*c*Math.pow(t,2) + 6*c*adjustedT*t - 6*b*adjustedT*t + 3*b*Math.pow(adjustedT,2) - 3*a*Math.pow(adjustedT,2); }; /** * Draws a curved line on the screen between two points, given as the * middle four parameters. The first two parameters are a control point, as * if the curve came from this point even though it's not drawn. The last * two parameters similarly describe the other control point.

* Longer curves can be created by putting a series of curve() functions * together or using curveVertex(). An additional function called * curveTightness() provides control for the visual quality of the curve. * The curve() function is an implementation of Catmull-Rom splines. * * @method curve * @param {Number} x1 x-coordinate for the beginning control point * @param {Number} y1 y-coordinate for the beginning control point * @param {Number} x2 x-coordinate for the first point * @param {Number} y2 y-coordinate for the first point * @param {Number} x3 x-coordinate for the second point * @param {Number} y3 y-coordinate for the second point * @param {Number} x4 x-coordinate for the ending control point * @param {Number} y4 y-coordinate for the ending control point * @return {p5} the p5 object * @example *
* * noFill(); * stroke(255, 102, 0); * curve(5, 26, 5, 26, 73, 24, 73, 61); * stroke(0); * curve(5, 26, 73, 24, 73, 61, 15, 65); * stroke(255, 102, 0); * curve(73, 24, 73, 61, 15, 65, 15, 65); * *
*
* * // Define the curve points as JavaScript objects * p1 = {x: 5, y: 26}, p2 = {x: 73, y: 24} * p3 = {x: 73, y: 61}, p4 = {x: 15, y: 65} * noFill(); * stroke(255, 102, 0); * curve(p1.x, p1.y, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y) * stroke(0); * curve(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y) * stroke(255, 102, 0); * curve(p2.x, p2.y, p3.x, p3.y, p4.x, p4.y, p4.x, p4.y) * *
* * @alt * horseshoe shape with orange ends facing left and black curved center. * horseshoe shape with orange ends facing left and black curved center. * */ /** * @method curve * @param {Number} z1 z-coordinate for the beginning control point * @param {Number} z2 z-coordinate for the first point * @param {Number} z3 z-coordinate for the second point * @param {Number} z4 z-coordinate for the ending control point * @chainable * @example *
* * noFill(); * stroke(255, 102, 0); * curve(5,26,0, 5,26,0, 73,24,0, 73,61,0); * stroke(0); * curve(5,26,0, 73,24,0, 73,61,0, 15,65,0); * stroke(255, 102, 0); * curve(73,24,0, 73,61,0, 15,65,0, 15,65,0); * *
* * @alt * curving black and orange lines. */ p5.prototype.curve = function() { var args = new Array(arguments.length); for (var i = 0; i < args.length; ++i) { args[i] = arguments[i]; } if (!this._renderer._doStroke) { return this; } if (this._renderer.isP3D){ args.push(curveDetail); this._renderer.curve(args); } else{ this._renderer.curve(args[0],args[1], args[2],args[3], args[4],args[5], args[6],args[7]); } return this; }; /** * Sets the resolution at which curves display. * * The default value is 20. * * @param {Number} resolution of the curves * @chainable * @example *
* * background(204); * curveDetail(20); * curve(5, 26, 5, 26, 73, 24, 73, 61); * *
* * @alt * white arch shape in top-mid canvas. * */ p5.prototype.curveDetail = function(d) { curveDetail = d; return this; }; /** * Modifies the quality of forms created with curve() and curveVertex(). * The parameter tightness determines how the curve fits to the vertex * points. The value 0.0 is the default value for tightness (this value * defines the curves to be Catmull-Rom splines) and the value 1.0 connects * all the points with straight lines. Values within the range -5.0 and 5.0 * will deform the curves but will leave them recognizable and as values * increase in magnitude, they will continue to deform. * * @method curveTightness * @param {Number} amount of deformation from the original vertices * @chainable * @example *
* * // Move the mouse left and right to see the curve change * * function setup() { * createCanvas(100, 100); * noFill(); * } * * function draw() { * background(204); * var t = map(mouseX, 0, width, -5, 5); * curveTightness(t); * beginShape(); * curveVertex(10, 26); * curveVertex(10, 26); * curveVertex(83, 24); * curveVertex(83, 61); * curveVertex(25, 65); * curveVertex(25, 65); * endShape(); * } * *
* * @alt * Line shaped like right-facing arrow,points move with mouse-x and warp shape. */ p5.prototype.curveTightness = function (t) { this._renderer._curveTightness = t; }; /** * Evaluates the curve at position t for points a, b, c, d. * The parameter t varies between 0 and 1, a and d are points * on the curve, and b and c are the control points. * This can be done once with the x coordinates and a second time * with the y coordinates to get the location of a curve at t. * * @method curvePoint * @param {Number} a coordinate of first point on the curve * @param {Number} b coordinate of first control point * @param {Number} c coordinate of second control point * @param {Number} d coordinate of second point on the curve * @param {Number} t value between 0 and 1 * @return {Number} bezier value at position t * @example *
* * noFill(); * curve(5, 26, 5, 26, 73, 24, 73, 61); * curve(5, 26, 73, 24, 73, 61, 15, 65); * fill(255); * ellipseMode(CENTER); * steps = 6; * for (i = 0; i <= steps; i++) { * t = i / steps; * x = curvePoint(5, 5, 73, 73, t); * y = curvePoint(26, 26, 24, 61, t); * ellipse(x, y, 5, 5); * x = curvePoint(5, 73, 73, 15, t); * y = curvePoint(26, 24, 61, 65, t); * ellipse(x, y, 5, 5); * } * *
* *line hooking down to right-bottom with 13 5x5 white ellipse points */ p5.prototype.curvePoint = function(a, b, c, d, t) { var t3 = t*t*t, t2 = t*t, f1 = -0.5 * t3 + t2 - 0.5 * t, f2 = 1.5 * t3 - 2.5 * t2 + 1.0, f3 = -1.5 * t3 + 2.0 * t2 + 0.5 * t, f4 = 0.5 * t3 - 0.5 * t2; return a*f1 + b*f2 + c*f3 + d*f4; }; /** * Evaluates the tangent to the curve at position t for points a, b, c, d. * The parameter t varies between 0 and 1, a and d are points on the curve, * and b and c are the control points. * * @method curveTangent * @param {Number} a coordinate of first point on the curve * @param {Number} b coordinate of first control point * @param {Number} c coordinate of second control point * @param {Number} d coordinate of second point on the curve * @param {Number} t value between 0 and 1 * @return {Number} the tangent at position t * @example *
* * noFill(); * curve(5, 26, 73, 24, 73, 61, 15, 65); * steps = 6; * for (i = 0; i <= steps; i++) { * t = i / steps; * x = curvePoint(5, 73, 73, 15, t); * y = curvePoint(26, 24, 61, 65, t); * //ellipse(x, y, 5, 5); * tx = curveTangent(5, 73, 73, 15, t); * ty = curveTangent(26, 24, 61, 65, t); * a = atan2(ty, tx); * a -= PI/2.0; * line(x, y, cos(a)*8 + x, sin(a)*8 + y); * } * *
* * @alt *right curving line mid-right of canvas with 7 short lines radiating from it. */ p5.prototype.curveTangent = function(a, b,c, d, t) { var t2 = t*t, f1 = (-3*t2)/2 + 2*t - 0.5, f2 = (9*t2)/2 - 5*t, f3 = (-9*t2)/2 + 4*t + 0.5, f4 = (3*t2)/2 - t; return a*f1 + b*f2 + c*f3 + d*f4; }; module.exports = p5; },{"./core":5,"./error_helpers":8}],7:[function(_dereq_,module,exports){ /** * @module Environment * @submodule Environment * @for p5 * @requires core * @requires constants */ 'use strict'; var p5 = _dereq_('./core'); var C = _dereq_('./constants'); var standardCursors = [C.ARROW, C.CROSS, C.HAND, C.MOVE, C.TEXT, C.WAIT]; p5.prototype._frameRate = 0; p5.prototype._lastFrameTime = window.performance.now(); p5.prototype._targetFrameRate = 60; var _windowPrint = window.print; if (window.console && console.log) { /** * The print() function writes to the console area of your browser. * This function is often helpful for looking at the data a program is * producing. This function creates a new line of text for each call to * the function. Individual elements can be * separated with quotes ("") and joined with the addition operator (+). *

* While print() is similar to console.log(), it does not directly map to * it in order to simulate easier to understand behavior than * console.log(). Due to this, it is slower. For fastest results, use * console.log(). * * @method print * @param {Any} contents any combination of Number, String, Object, Boolean, * Array to print * @example *
* var x = 10; * print("The value of x is " + x); * // prints "The value of x is 10" *
* @alt * default grey canvas */ // Converts passed args into a string and then parses that string to // simulate synchronous behavior. This is a hack and is gross. // Since this will not work on all objects, particularly circular // structures, simply console.log() on error. p5.prototype.print = function(args) { try { if (arguments.length === 0) { _windowPrint(); } else if (arguments.length > 1) { console.log.apply(console, arguments); } else { var newArgs = JSON.parse(JSON.stringify(args)); if (JSON.stringify(newArgs)==='{}'){ console.log(args); } else { console.log(newArgs); } } } catch(err) { console.log(args); } }; } else { p5.prototype.print = function() {}; } /** * The system variable frameCount contains the number of frames that have * been displayed since the program started. Inside setup() the value is 0, * after the first iteration of draw it is 1, etc. * * @property {Number} frameCount * @readOnly * @example *
* function setup() { * frameRate(30); * textSize(20); * textSize(30); * textAlign(CENTER); * } * * function draw() { * background(200); * text(frameCount, width/2, height/2); * } *
* * @alt * numbers rapidly counting upward with frame count set to 30. * */ p5.prototype.frameCount = 0; /** * Confirms if the window a p5.js program is in is "focused," meaning that * the sketch will accept mouse or keyboard input. This variable is * "true" if the window is focused and "false" if not. * * @property {Boolean} focused * @readOnly * @example *
* // To demonstrate, put two windows side by side. * // Click on the window that the p5 sketch isn't in! * function draw() { * background(200); * noStroke(); * fill(0, 200, 0); * ellipse(25, 25, 50, 50); * * if (!focused) { // or "if (focused === false)" * stroke(200,0,0); * line(0, 0, 100, 100); * line(100, 0, 0, 100); * } * } *
* * @alt * green 50x50 ellipse at top left. Red X covers canvas when page focus changes * */ p5.prototype.focused = (document.hasFocus()); /** * Sets the cursor to a predefined symbol or an image, or makes it visible * if already hidden. If you are trying to set an image as the cursor, the * recommended size is 16x16 or 32x32 pixels. It is not possible to load an * image as the cursor if you are exporting your program for the Web, and not * all MODES work with all browsers. The values for parameters x and y must * be less than the dimensions of the image. * * @method cursor * @param {String|Constant} type either ARROW, CROSS, HAND, MOVE, TEXT, or * WAIT, or path for image * @param {Number} [x] the horizontal active spot of the cursor * @param {Number} [y] the vertical active spot of the cursor * @example *
* // Move the mouse left and right across the image * // to see the cursor change from a cross to a hand * function draw() { * line(width/2, 0, width/2, height); * if (mouseX < 50) { * cursor(CROSS); * } else { * cursor(HAND); * } * } *
* * @alt * horizontal line divides canvas. cursor on left is a cross, right is hand. * */ p5.prototype.cursor = function(type, x, y) { var cursor = 'auto'; var canvas = this._curElement.elt; if (standardCursors.indexOf(type) > -1) { // Standard css cursor cursor = type; } else if (typeof type === 'string') { var coords = ''; if (x && y && (typeof x === 'number' && typeof y === 'number')) { // Note that x and y values must be unit-less positive integers < 32 // https://developer.mozilla.org/en-US/docs/Web/CSS/cursor coords = x + ' ' + y; } if ((type.substring(0, 7) === 'http://') || (type.substring(0, 8) === 'https://')) { // Image (absolute url) cursor = 'url(' + type + ') ' + coords + ', auto'; } else if (/\.(cur|jpg|jpeg|gif|png|CUR|JPG|JPEG|GIF|PNG)$/.test(type)) { // Image file (relative path) - Separated for performance reasons cursor = 'url(' + type + ') ' + coords + ', auto'; } else { // Any valid string for the css cursor property cursor = type; } } canvas.style.cursor = cursor; }; /** * Specifies the number of frames to be displayed every second. For example, * the function call frameRate(30) will attempt to refresh 30 times a second. * If the processor is not fast enough to maintain the specified rate, the * frame rate will not be achieved. Setting the frame rate within setup() is * recommended. The default rate is 60 frames per second. This is the same as * setFrameRate(val). *

* Calling frameRate() with no arguments returns the current framerate. The * draw function must run at least once before it will return a value. This * is the same as getFrameRate(). *

* Calling frameRate() with arguments that are not of the type numbers * or are non positive also returns current framerate. * * @method frameRate * @param {Number} fps number of frames to be displayed every second * @chainable */ /** * @method frameRate * @return {Number} current frameRate * @example * *
* var rectX = 0; * var fr = 30; //starting FPS * var clr; * * function setup() { * background(200); * frameRate(fr); // Attempt to refresh at starting FPS * clr = color(255,0,0); * } * * function draw() { * background(200); * rectX = rectX += 1; // Move Rectangle * * if (rectX >= width) { // If you go off screen. * if (fr == 30) { * clr = color(0,0,255); * fr = 10; * frameRate(fr); // make frameRate 10 FPS * } else { * clr = color(255,0,0); * fr = 30; * frameRate(fr); // make frameRate 30 FPS * } * rectX = 0; * } * fill(clr); * rect(rectX, 40, 20,20); * } *
* * @alt * blue rect moves left to right, followed by red rect moving faster. Loops. * */ p5.prototype.frameRate = function(fps) { if (typeof fps !== 'number' || fps < 0) { return this._frameRate; } else { this._setProperty('_targetFrameRate', fps); this._runFrames(); return this; } }; /** * Returns the current framerate. * * @return {Number} current frameRate */ p5.prototype.getFrameRate = function() { return this.frameRate(); }; /** * Specifies the number of frames to be displayed every second. For example, * the function call frameRate(30) will attempt to refresh 30 times a second. * If the processor is not fast enough to maintain the specified rate, the * frame rate will not be achieved. Setting the frame rate within setup() is * recommended. The default rate is 60 frames per second. * * Calling frameRate() with no arguments returns the current framerate. * * @param {Number} [fps] number of frames to be displayed every second */ p5.prototype.setFrameRate = function(fps) { return this.frameRate(fps); }; /** * Hides the cursor from view. * * @method noCursor * @example *
* function setup() { * noCursor(); * } * * function draw() { * background(200); * ellipse(mouseX, mouseY, 10, 10); * } *
* * * @alt * cursor becomes 10x 10 white ellipse the moves with mouse x and y. * */ p5.prototype.noCursor = function() { this._curElement.elt.style.cursor = 'none'; }; /** * System variable that stores the width of the entire screen display. This * is used to run a full-screen program on any display size. * * @property {Number} displayWidth * @readOnly * @example *
* createCanvas(displayWidth, displayHeight); *
* * @alt * cursor becomes 10x 10 white ellipse the moves with mouse x and y. * */ p5.prototype.displayWidth = screen.width; /** * System variable that stores the height of the entire screen display. This * is used to run a full-screen program on any display size. * * @property {Number} displayHeight * @readOnly * @example *
* createCanvas(displayWidth, displayHeight); *
* * @alt * no display. * */ p5.prototype.displayHeight = screen.height; /** * System variable that stores the width of the inner window, it maps to * window.innerWidth. * * @property {Number} windowWidth * @readOnly * @example *
* createCanvas(windowWidth, windowHeight); *
* * @alt * no display. * */ p5.prototype.windowWidth = getWindowWidth(); /** * System variable that stores the height of the inner window, it maps to * window.innerHeight. * * @property {Number} windowHeight * @readOnly * @example *
* createCanvas(windowWidth, windowHeight); *
*@alt * no display. * */ p5.prototype.windowHeight = getWindowHeight(); /** * The windowResized() function is called once every time the browser window * is resized. This is a good place to resize the canvas or do any other * adjustments to accommodate the new window size. * * @method windowResized * @example *
* function setup() { * createCanvas(windowWidth, windowHeight); * } * * function draw() { * background(0, 100, 200); * } * * function windowResized() { * resizeCanvas(windowWidth, windowHeight); * } *
* @alt * no display. */ p5.prototype._onresize = function(e){ this._setProperty('windowWidth', getWindowWidth()); this._setProperty('windowHeight', getWindowHeight()); var context = this._isGlobal ? window : this; var executeDefault; if (typeof context.windowResized === 'function') { executeDefault = context.windowResized(e); if (executeDefault !== undefined && !executeDefault) { e.preventDefault(); } } }; function getWindowWidth() { return window.innerWidth || document.documentElement && document.documentElement.clientWidth || document.body && document.body.clientWidth || 0; } function getWindowHeight() { return window.innerHeight || document.documentElement && document.documentElement.clientHeight || document.body && document.body.clientHeight || 0; } /** * System variable that stores the width of the drawing canvas. This value * is set by the first parameter of the createCanvas() function. * For example, the function call createCanvas(320, 240) sets the width * variable to the value 320. The value of width defaults to 100 if * createCanvas() is not used in a program. * * @property {Number} width * @readOnly */ p5.prototype.width = 0; /** * System variable that stores the height of the drawing canvas. This value * is set by the second parameter of the createCanvas() function. For * example, the function call createCanvas(320, 240) sets the height * variable to the value 240. The value of height defaults to 100 if * createCanvas() is not used in a program. * * @property {Number} height * @readOnly */ p5.prototype.height = 0; /** * If argument is given, sets the sketch to fullscreen or not based on the * value of the argument. If no argument is given, returns the current * fullscreen state. Note that due to browser restrictions this can only * be called on user input, for example, on mouse press like the example * below. * * @method fullscreen * @param {Boolean} [val] whether the sketch should be in fullscreen mode * or not * @return {Boolean} current fullscreen state * @example *
* * // Clicking in the box toggles fullscreen on and off. * function setup() { * background(200); * } * function mousePressed() { * if (mouseX > 0 && mouseX < 100 && mouseY > 0 && mouseY < 100) { * var fs = fullscreen(); * fullscreen(!fs); * } * } * *
* * @alt * no display. * */ p5.prototype.fullscreen = function(val) { // no arguments, return fullscreen or not if (typeof val === 'undefined') { return document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement; } else { // otherwise set to fullscreen or not if (val) { launchFullscreen(document.documentElement); } else { exitFullscreen(); } } }; /** * Sets the pixel scaling for high pixel density displays. By default * pixel density is set to match display density, call pixelDensity(1) * to turn this off. Calling pixelDensity() with no arguments returns * the current pixel density of the sketch. * * * @method pixelDensity * @param {Number} [val] whether or how much the sketch should scale * @returns {Number} current pixel density of the sketch * @example *
* * function setup() { * pixelDensity(1); * createCanvas(100, 100); * background(200); * ellipse(width/2, height/2, 50, 50); * } * *
*
* * function setup() { * pixelDensity(3.0); * createCanvas(100, 100); * background(200); * ellipse(width/2, height/2, 50, 50); * } * *
* * @alt * fuzzy 50x50 white ellipse with black outline in center of canvas. * sharp 50x50 white ellipse with black outline in center of canvas. */ p5.prototype.pixelDensity = function(val) { if (typeof val === 'number') { this._pixelDensity = val; } else { return this._pixelDensity; } this.resizeCanvas(this.width, this.height, true); }; /** * Returns the pixel density of the current display the sketch is running on. * * @method displayDensity * @returns {Number} current pixel density of the display * @example *
* * function setup() { * var density = displayDensity(); * pixelDensity(density); * createCanvas(100, 100); * background(200); * ellipse(width/2, height/2, 50, 50); * } * *
* * @alt * 50x50 white ellipse with black outline in center of canvas. */ p5.prototype.displayDensity = function() { return window.devicePixelRatio; }; function launchFullscreen(element) { var enabled = document.fullscreenEnabled || document.webkitFullscreenEnabled || document.mozFullScreenEnabled || document.msFullscreenEnabled; if (!enabled) { throw new Error('Fullscreen not enabled in this browser.'); } if(element.requestFullscreen) { element.requestFullscreen(); } else if(element.mozRequestFullScreen) { element.mozRequestFullScreen(); } else if(element.webkitRequestFullscreen) { element.webkitRequestFullscreen(); } else if(element.msRequestFullscreen) { element.msRequestFullscreen(); } } function exitFullscreen() { if(document.exitFullscreen) { document.exitFullscreen(); } else if(document.mozCancelFullScreen) { document.mozCancelFullScreen(); } else if(document.webkitExitFullscreen) { document.webkitExitFullscreen(); } else if (document.msExitFullscreen) { document.msExitFullscreen(); } } /** * Gets the current URL. * @method getURL * @return {String} url * @example *
* * var url; * var x = 100; * * function setup() { * fill(0); * noStroke(); * url = getURL(); * } * * function draw() { * background(200); * text(url, x, height/2); * x--; * } * *
* * @alt * current url (http://p5js.org/reference/#/p5/getURL) moves right to left. * */ p5.prototype.getURL = function() { return location.href; }; /** * Gets the current URL path as an array. * @method getURLPath * @return {String[]} path components * @example *
* function setup() { * var urlPath = getURLPath(); * for (var i=0; i<urlPath.length; i++) { * text(urlPath[i], 10, i*20+20); * } * } *
* * @alt *no display * */ p5.prototype.getURLPath = function() { return location.pathname.split('/').filter(function(v){return v!=='';}); }; /** * Gets the current URL params as an Object. * @method getURLParams * @return {Object} URL params * @example *
* * // Example: http://p5js.org?year=2014&month=May&day=15 * * function setup() { * var params = getURLParams(); * text(params.day, 10, 20); * text(params.month, 10, 40); * text(params.year, 10, 60); * } * *
* @alt * no display. * */ p5.prototype.getURLParams = function() { var re = /[?&]([^&=]+)(?:[&=])([^&=]+)/gim; var m; var v={}; while ((m = re.exec(location.search)) != null) { if (m.index === re.lastIndex) { re.lastIndex++; } v[m[1]]=m[2]; } return v; }; module.exports = p5; },{"./constants":4,"./core":5}],8:[function(_dereq_,module,exports){ /** * @for p5 * @requires core */ 'use strict'; var p5 = _dereq_('./core'); var doFriendlyWelcome = false; // TEMP until we get it all working LM // -- Borrowed from jQuery 1.11.3 -- var class2type = {}; var toString = class2type.toString; var names = ['Boolean', 'Number', 'String', 'Function', 'Array', 'Date', 'RegExp', 'Object', 'Error']; for (var n=0; n p5.js says: '+message+'%c'+ // '[https://github.com/processing/p5.js/wiki/Local-server]', // 'background-color:' + color + ';color:#FFF;', // 'background-color:transparent;color:' + color +';', // 'background-color:' + color + ';color:#FFF;', // 'background-color:transparent;color:' + color +';' // ); // } // else{ // console.log( // '%c> p5.js says: '+message+'%c [http://p5js.org/reference/#p5/'+func+ // ']', 'background-color:' + color + ';color:#FFF;', // 'background-color:transparent;color:' + color +';' // ); // } } var errorCases = { '0': { fileType: 'image', method: 'loadImage', message: ' hosting the image online,' }, '1': { fileType: 'XML file', method: 'loadXML' }, '2': { fileType: 'table file', method: 'loadTable' }, '3': { fileType: 'text file', method: 'loadStrings' }, '4': { fileType: 'font', method: 'loadFont', message: ' hosting the font online,' }, }; p5._friendlyFileLoadError = function (errorType, filePath) { var errorInfo = errorCases[ errorType ]; var message = 'It looks like there was a problem' + ' loading your ' + errorInfo.fileType + '.' + ' Try checking if the file path%c [' + filePath + '] %cis correct,' + (errorInfo.message || '') + ' or running a local server.'; report(message, errorInfo.method, FILE_LOAD); }; function friendlyWelcome() { // p5.js brand - magenta: #ED225D var astrixBgColor = 'transparent'; var astrixTxtColor = '#ED225D'; var welcomeBgColor = '#ED225D'; var welcomeTextColor = 'white'; console.log( '%c _ \n'+ ' /\\| |/\\ \n'+ ' \\ ` \' / \n'+ ' / , . \\ \n'+ ' \\/|_|\\/ '+ '\n\n%c> p5.js says: Welcome! '+ 'This is your friendly debugger. ' + 'To turn me off switch to using “p5.min.js”.', 'background-color:'+astrixBgColor+';color:' + astrixTxtColor +';', 'background-color:'+welcomeBgColor+';color:' + welcomeTextColor +';' ); } /** * Prints out all the colors in the color pallete with white text. * For color blindness testing. */ /* function testColors() { var str = 'A box of biscuits, a box of mixed biscuits and a biscuit mixer'; report(str, 'print', '#ED225D'); // p5.js magenta report(str, 'print', '#2D7BB6'); // p5.js blue report(str, 'print', '#EE9900'); // p5.js orange report(str, 'print', '#A67F59'); // p5.js light brown report(str, 'print', '#704F21'); // p5.js gold report(str, 'print', '#1CC581'); // auto cyan report(str, 'print', '#FF6625'); // auto orange report(str, 'print', '#79EB22'); // auto green report(str, 'print', '#B40033'); // p5.js darkened magenta report(str, 'print', '#084B7F'); // p5.js darkened blue report(str, 'print', '#945F00'); // p5.js darkened orange report(str, 'print', '#6B441D'); // p5.js darkened brown report(str, 'print', '#2E1B00'); // p5.js darkened gold report(str, 'print', '#008851'); // auto dark cyan report(str, 'print', '#C83C00'); // auto dark orange report(str, 'print', '#4DB200'); // auto dark green } */ // This is a lazily-defined list of p5 symbols that may be // misused by beginners at top-level code, outside of setup/draw. We'd like // to detect these errors and help the user by suggesting they move them // into setup/draw. // // For more details, see https://github.com/processing/p5.js/issues/1121. var misusedAtTopLevelCode = null; var FAQ_URL = 'https://github.com/processing/p5.js/wiki/' + 'Frequently-Asked-Questions' + '#why-cant-i-assign-variables-using-p5-functions-and-' + 'variables-before-setup'; function defineMisusedAtTopLevelCode() { var uniqueNamesFound = {}; var getSymbols = function(obj) { return Object.getOwnPropertyNames(obj).filter(function(name) { if (name[0] === '_') { return false; } if (name in uniqueNamesFound) { return false; } uniqueNamesFound[name] = true; return true; }).map(function(name) { var type; if (typeof(obj[name]) === 'function') { type = 'function'; } else if (name === name.toUpperCase()) { type = 'constant'; } else { type = 'variable'; } return {name: name, type: type}; }); }; misusedAtTopLevelCode = [].concat( getSymbols(p5.prototype), // At present, p5 only adds its constants to p5.prototype during // construction, which may not have happened at the time a // ReferenceError is thrown, so we'll manually add them to our list. getSymbols(_dereq_('./constants')) ); // This will ultimately ensure that we report the most specific error // possible to the user, e.g. advising them about HALF_PI instead of PI // when their code misuses the former. misusedAtTopLevelCode.sort(function(a, b) { return b.name.length - a.name.length; }); } function helpForMisusedAtTopLevelCode(e, log) { if (!log) { log = console.log.bind(console); } if (!misusedAtTopLevelCode) { defineMisusedAtTopLevelCode(); } // If we find that we're logging lots of false positives, we can // uncomment the following code to avoid displaying anything if the // user's code isn't likely to be using p5's global mode. (Note that // setup/draw are more likely to be defined due to JS function hoisting.) // //if (!('setup' in window || 'draw' in window)) { // return; //} misusedAtTopLevelCode.some(function(symbol) { // Note that while just checking for the occurrence of the // symbol name in the error message could result in false positives, // a more rigorous test is difficult because different browsers // log different messages, and the format of those messages may // change over time. // // For example, if the user uses 'PI' in their code, it may result // in any one of the following messages: // // * 'PI' is undefined (Microsoft Edge) // * ReferenceError: PI is undefined (Firefox) // * Uncaught ReferenceError: PI is not defined (Chrome) if (e.message && e.message.match('\\W?'+symbol.name+'\\W') !== null) { log('%cDid you just try to use p5.js\'s ' + symbol.name + (symbol.type === 'function' ? '() ' : ' ') + symbol.type + '? If so, you may want to ' + 'move it into your sketch\'s setup() function.\n\n' + 'For more details, see: ' + FAQ_URL, 'color: #B40033' /* Dark magenta */); return true; } }); } // Exposing this primarily for unit testing. p5.prototype._helpForMisusedAtTopLevelCode = helpForMisusedAtTopLevelCode; if (document.readyState !== 'complete') { window.addEventListener('error', helpForMisusedAtTopLevelCode, false); // Our job is only to catch ReferenceErrors that are thrown when // global (non-instance mode) p5 APIs are used at the top-level // scope of a file, so we'll unbind our error listener now to make // sure we don't log false positives later. window.addEventListener('load', function() { window.removeEventListener('error', helpForMisusedAtTopLevelCode, false); }); } module.exports = p5; },{"./constants":4,"./core":5}],9:[function(_dereq_,module,exports){ var p5 = _dereq_('../core/core'); /** * _globalInit * * TODO: ??? * if sketch is on window * assume "global" mode * and instantiate p5 automatically * otherwise do nothing * * @return {Undefined} */ var _globalInit = function() { if (!window.PHANTOMJS && !window.mocha) { // If there is a setup or draw function on the window // then instantiate p5 in "global" mode if(((window.setup && typeof window.setup === 'function') || (window.draw && typeof window.draw === 'function')) && !p5.instance) { new p5(); } } }; // TODO: ??? if (document.readyState === 'complete') { _globalInit(); } else { window.addEventListener('load', _globalInit , false); } },{"../core/core":5}],10:[function(_dereq_,module,exports){ /** * @module DOM * @submodule DOM * @for p5.Element */ var p5 = _dereq_('./core'); /** * Base class for all elements added to a sketch, including canvas, * graphics buffers, and other HTML elements. Methods in blue are * included in the core functionality, methods in brown are added * with the p5.dom * library. * It is not called directly, but p5.Element * objects are created by calling createCanvas, createGraphics, * or in the p5.dom library, createDiv, createImg, createInput, etc. * * @class p5.Element * @constructor * @param {String} elt DOM node that is wrapped * @param {p5} [pInst] pointer to p5 instance */ p5.Element = function(elt, pInst) { /** * Underlying HTML element. All normal HTML methods can be called on this. * * @property elt * @readOnly */ this.elt = elt; this._pInst = pInst; this._events = {}; this.width = this.elt.offsetWidth; this.height = this.elt.offsetHeight; }; /** * * Attaches the element to the parent specified. A way of setting * the container for the element. Accepts either a string ID, DOM * node, or p5.Element. If no arguments given, parent node is returned. * For more ways to position the canvas, see the * * positioning the canvas wiki page. * * @method parent * @param {String|p5.Element|Object} parent the ID, DOM node, or p5.Element * of desired parent element * @chainable */ /** * @method parent * @return {p5.Element} * * @example *
* // in the html file: * <div id="myContainer"></div> * // in the js file: * var cnv = createCanvas(100, 100); * cnv.parent("myContainer"); *
*
* var div0 = createDiv('this is the parent'); * var div1 = createDiv('this is the child'); * div1.parent(div0); // use p5.Element *
*
* var div0 = createDiv('this is the parent'); * div0.id('apples'); * var div1 = createDiv('this is the child'); * div1.parent('apples'); // use id *
*
* var elt = document.getElementById('myParentDiv'); * var div1 = createDiv('this is the child'); * div1.parent(elt); // use element from page *
* * @alt * no display. * */ p5.Element.prototype.parent = function(p) { if (arguments.length === 0){ return this.elt.parentNode; } else { if (typeof p === 'string') { if (p[0] === '#') { p = p.substring(1); } p = document.getElementById(p); } else if (p instanceof p5.Element) { p = p.elt; } p.appendChild(this.elt); return this; } }; /** * * Sets the ID of the element. If no ID argument is passed in, it instead * returns the current ID of the element. * * @method id * @param {String} id ID of the element * @chainable */ /** * @method id * @return {String} the id of the element * * @example *
* function setup() { * var cnv = createCanvas(100, 100); * // Assigns a CSS selector ID to * // the canvas element. * cnv.id("mycanvas"); * } *
* * @alt * no display. * */ p5.Element.prototype.id = function(id) { if (arguments.length === 0) { return this.elt.id; } else { this.elt.id = id; this.width = this.elt.offsetWidth; this.height = this.elt.offsetHeight; return this; } }; /** * * Adds given class to the element. If no class argument is passed in, it * instead returns a string containing the current class(es) of the element. * * @method class * @param {String} class class to add * @chainable */ /** * @method class * @return {String} the class of the element */ p5.Element.prototype.class = function(c) { if (arguments.length === 0) { return this.elt.className; } else { this.elt.className = c; return this; } }; /** * The .mousePressed() function is called once after every time a * mouse button is pressed over the element. This can be used to * attach element specific event listeners. * * @method mousePressed * @param {function} fxn function to be fired when mouse is * pressed over the element. * @chainable * @example *
* var cnv; * var d; * var g; * function setup() { * cnv = createCanvas(100, 100); * cnv.mousePressed(changeGray); // attach listener for * // canvas click only * d = 10; * g = 100; * } * * function draw() { * background(g); * ellipse(width/2, height/2, d, d); * } * * // this function fires with any click anywhere * function mousePressed() { * d = d + 10; * } * * // this function fires only when cnv is clicked * function changeGray() { * g = random(0, 255); * } *
* * @alt * no display. * */ p5.Element.prototype.mousePressed = function (fxn) { attachListener('mousedown', fxn, this); attachListener('touchstart', fxn, this); return this; }; /** * The .doubleClicked() function is called once after every time a * mouse button is pressed twice over the element. This can be used to * attach element and action specific event listeners. * * @method doubleClicked * @param {Function} fxn function to be fired when mouse is * pressed over the element. * @return {p5.Element} * @example *
* var cnv; * var d; * var g; * function setup() { * cnv = createCanvas(100, 100); * cnv.doubleClicked(changeGray); // attach listener for * // canvas click only * d = 10; * g = 100; * } * * function draw() { * background(g); * ellipse(width/2, height/2, d, d); * } * * // this function fires with any double click anywhere * function doubleClicked() { * d = d + 10; * } * * // this function fires only when cnv is clicked * function changeGray() { * g = random(0, 255); * } *
* * @alt * no display. * */ p5.Element.prototype.doubleClicked = function (fxn) { attachListener('doubleClicked', fxn, this); return this; }; /** * The .mouseWheel() function is called once after every time a * mouse wheel is scrolled over the element. This can be used to * attach element specific event listeners. *

* The function accepts a callback function as argument which will be executed * when the `wheel` event is triggered on the element, the callback function is * passed one argument `event`. The `event.deltaY` property returns negative * values if the mouse wheel is rotated up or away from the user and positive * in the other direction. The `event.deltaX` does the same as `event.deltaY` * except it reads the horizontal wheel scroll of the mouse wheel. *

* On OS X with "natural" scrolling enabled, the `event.deltaY` values are * reversed. * * @method mouseWheel * @param {function} fxn function to be fired when mouse wheel is * scrolled over the element. * @chainable * @example *
* var cnv; * var d; * var g; * function setup() { * cnv = createCanvas(100, 100); * cnv.mouseWheel(changeSize); // attach listener for * // activity on canvas only * d = 10; * g = 100; * } * * function draw() { * background(g); * ellipse(width/2, height/2, d, d); * } * * // this function fires with mousewheel movement * // anywhere on screen * function mouseWheel() { * g = g + 10; * } * * // this function fires with mousewheel movement * // over canvas only * function changeSize(event) { * if (event.deltaY > 0) { * d = d + 10; * } else { * d = d - 10; * } * } *
* * * @alt * no display. * */ p5.Element.prototype.mouseWheel = function (fxn) { attachListener('wheel', fxn, this); return this; }; /** * The .mouseReleased() function is called once after every time a * mouse button is released over the element. This can be used to * attach element specific event listeners. * * @method mouseReleased * @param {function} fxn function to be fired when mouse is * released over the element. * @chainable * @example *
* var cnv; * var d; * var g; * function setup() { * cnv = createCanvas(100, 100); * cnv.mouseReleased(changeGray); // attach listener for * // activity on canvas only * d = 10; * g = 100; * } * * function draw() { * background(g); * ellipse(width/2, height/2, d, d); * } * * // this function fires after the mouse has been * // released * function mouseReleased() { * d = d + 10; * } * * // this function fires after the mouse has been * // released while on canvas * function changeGray() { * g = random(0, 255); * } *
* * * @alt * no display. * */ p5.Element.prototype.mouseReleased = function (fxn) { attachListener('mouseup', fxn, this); attachListener('touchend', fxn, this); return this; }; /** * The .mouseClicked() function is called once after a mouse button is * pressed and released over the element. This can be used to * attach element specific event listeners. * * @method mouseClicked * @param {function} fxn function to be fired when mouse is * clicked over the element. * @chainable * @example *
* * var cnv; * var d; * var g; * * function setup() { * cnv = createCanvas(100, 100); * cnv.mouseClicked(changeGray); // attach listener for * // activity on canvas only * d = 10; * g = 100; * } * * function draw() { * background(g); * ellipse(width/2, height/2, d, d); * } * * // this function fires after the mouse has been * // clicked anywhere * function mouseClicked() { * d = d + 10; * } * * // this function fires after the mouse has been * // clicked on canvas * function changeGray() { * g = random(0, 255); * } * *
* * @alt * no display. * */ p5.Element.prototype.mouseClicked = function (fxn) { attachListener('click', fxn, this); return this; }; /** * The .mouseMoved() function is called once every time a * mouse moves over the element. This can be used to attach an * element specific event listener. * * @method mouseMoved * @param {function} fxn function to be fired when mouse is * moved over the element. * @chainable * @example *
* var cnv; * var d = 30; * var g; * function setup() { * cnv = createCanvas(100, 100); * cnv.mouseMoved(changeSize); // attach listener for * // activity on canvas only * d = 10; * g = 100; * } * * function draw() { * background(g); * fill(200); * ellipse(width/2, height/2, d, d); * } * * // this function fires when mouse moves anywhere on * // page * function mouseMoved() { * g = g + 5; * if (g > 255) { * g = 0; * } * } * * // this function fires when mouse moves over canvas * function changeSize() { * d = d + 2; * if (d > 100) { * d = 0; * } * } *
* * * @alt * no display. * */ p5.Element.prototype.mouseMoved = function (fxn) { attachListener('mousemove', fxn, this); attachListener('touchmove', fxn, this); return this; }; /** * The .mouseOver() function is called once after every time a * mouse moves onto the element. This can be used to attach an * element specific event listener. * * @method mouseOver * @param {function} fxn function to be fired when mouse is * moved over the element. * @chainable * @example *
* var cnv; * var d; * var g; * function setup() { * cnv = createCanvas(100, 100); * cnv.mouseOver(changeGray); * d = 10; * } * * function draw() { * ellipse(width/2, height/2, d, d); * } * * function changeGray() { * d = d + 10; * if (d > 100) { * d = 0; * } * } *
* * * @alt * no display. * */ p5.Element.prototype.mouseOver = function (fxn) { attachListener('mouseover', fxn, this); return this; }; /** * The .changed() function is called when the value of an * element is changed. * This can be used to attach an element specific event listener. * * @method changed * @param {function} fxn function to be fired when the value of an * element changes. * @chainable * @example *
* var sel; * * function setup() { * textAlign(CENTER); * background(200); * sel = createSelect(); * sel.position(10, 10); * sel.option('pear'); * sel.option('kiwi'); * sel.option('grape'); * sel.changed(mySelectEvent); * } * * function mySelectEvent() { * var item = sel.value(); * background(200); * text("it's a "+item+"!", 50, 50); * } *
*
* var checkbox; * var cnv; * * function setup() { * checkbox = createCheckbox(" fill"); * checkbox.changed(changeFill); * cnv = createCanvas(100, 100); * cnv.position(0, 30); * noFill(); * } * * function draw() { * background(200); * ellipse(50, 50, 50, 50); * } * * function changeFill() { * if (checkbox.checked()) { * fill(0); * } else { * noFill(); * } * } *
* * @alt * dropdown: pear, kiwi, grape. When selected text "its a" + selection shown. * */ p5.Element.prototype.changed = function (fxn) { attachListener('change', fxn, this); return this; }; /** * The .input() function is called when any user input is * detected with an element. The input event is often used * to detect keystrokes in a input element, or changes on a * slider element. This can be used to attach an element specific * event listener. * * @method input * @param {function} fxn function to be fired on user input. * @chainable * @example *
* // Open your console to see the output * function setup() { * var inp = createInput(''); * inp.input(myInputEvent); * } * * function myInputEvent() { * console.log('you are typing: ', this.value()); * } *
* * @alt * no display. * */ p5.Element.prototype.input = function (fxn) { attachListener('input', fxn, this); return this; }; /** * The .mouseOut() function is called once after every time a * mouse moves off the element. This can be used to attach an * element specific event listener. * * @method mouseOut * @param {function} fxn function to be fired when mouse is * moved off the element. * @chainable * @example *
* var cnv; * var d; * var g; * function setup() { * cnv = createCanvas(100, 100); * cnv.mouseOut(changeGray); * d = 10; * } * * function draw() { * ellipse(width/2, height/2, d, d); * } * * function changeGray() { * d = d + 10; * if (d > 100) { * d = 0; * } * } *
* * @alt * no display. * */ p5.Element.prototype.mouseOut = function (fxn) { attachListener('mouseout', fxn, this); return this; }; /** * The .touchStarted() function is called once after every time a touch is * registered. This can be used to attach element specific event listeners. * * @method touchStarted * @param {function} fxn function to be fired when touch is * started over the element. * @chainable * @example *
* var cnv; * var d; * var g; * function setup() { * cnv = createCanvas(100, 100); * cnv.touchStarted(changeGray); // attach listener for * // canvas click only * d = 10; * g = 100; * } * * function draw() { * background(g); * ellipse(width/2, height/2, d, d); * } * * // this function fires with any touch anywhere * function touchStarted() { * d = d + 10; * } * * // this function fires only when cnv is clicked * function changeGray() { * g = random(0, 255); * } *
* * @alt * no display. * */ p5.Element.prototype.touchStarted = function (fxn) { attachListener('touchstart', fxn, this); attachListener('mousedown', fxn, this); return this; }; /** * The .touchMoved() function is called once after every time a touch move is * registered. This can be used to attach element specific event listeners. * * @method touchMoved * @param {function} fxn function to be fired when touch is moved * over the element. * @chainable * @example *
* var cnv; * var g; * function setup() { * cnv = createCanvas(100, 100); * cnv.touchMoved(changeGray); // attach listener for * // canvas click only * g = 100; * } * * function draw() { * background(g); * } * * // this function fires only when cnv is clicked * function changeGray() { * g = random(0, 255); * } *
* * @alt * no display. * */ p5.Element.prototype.touchMoved = function (fxn) { attachListener('touchmove', fxn, this); attachListener('mousemove', fxn, this); return this; }; /** * The .touchEnded() function is called once after every time a touch is * registered. This can be used to attach element specific event listeners. * * @method touchEnded * @param {function} fxn function to be fired when touch is * ended over the element. * @chainable * @example *
* var cnv; * var d; * var g; * function setup() { * cnv = createCanvas(100, 100); * cnv.touchEnded(changeGray); // attach listener for * // canvas click only * d = 10; * g = 100; * } * * function draw() { * background(g); * ellipse(width/2, height/2, d, d); * } * * // this function fires with any touch anywhere * function touchEnded() { * d = d + 10; * } * * // this function fires only when cnv is clicked * function changeGray() { * g = random(0, 255); * } *
* * * @alt * no display. * */ p5.Element.prototype.touchEnded = function (fxn) { attachListener('touchend', fxn, this); attachListener('mouseup', fxn, this); return this; }; /** * The .dragOver() function is called once after every time a * file is dragged over the element. This can be used to attach an * element specific event listener. * * @method dragOver * @param {function} fxn function to be fired when mouse is * dragged over the element. * @chainable * @example *
* // To test this sketch, simply drag a * // file over the canvas * function setup() { * var c = createCanvas(100, 100); * background(200); * textAlign(CENTER); * text('Drag file', width/2, height/2); * c.dragOver(dragOverCallback); * } * * // This function will be called whenever * // a file is dragged over the canvas * function dragOverCallback() { * background(240); * text('Dragged over', width/2, height/2); * } *
* @alt * nothing displayed */ p5.Element.prototype.dragOver = function (fxn) { attachListener('dragover', fxn, this); return this; }; /** * The .dragLeave() function is called once after every time a * dragged file leaves the element area. This can be used to attach an * element specific event listener. * * @method dragLeave * @param {function} fxn function to be fired when mouse is * dragged over the element. * @chainable * @example *
* // To test this sketch, simply drag a file * // over and then out of the canvas area * function setup() { * var c = createCanvas(100, 100); * background(200); * textAlign(CENTER); * text('Drag file', width/2, height/2); * c.dragLeave(dragLeaveCallback); * } * * // This function will be called whenever * // a file is dragged out of the canvas * function dragLeaveCallback() { * background(240); * text('Dragged off', width/2, height/2); * } *
* @alt * nothing displayed */ p5.Element.prototype.dragLeave = function (fxn) { attachListener('dragleave', fxn, this); return this; }; /** * The .drop() function is called for each file dropped on the element. * It requires a callback that is passed a p5.File object. You can * optionally pass two callbacks, the first one (required) is triggered * for each file dropped when the file is loaded. The second (optional) * is triggered just once when a file (or files) are dropped. * * @method drop * @param {function} callback callback triggered when files are dropped. * @param {function} fxn callback to receive loaded file. * @chainable * @example *
* function setup() { * var c = createCanvas(100, 100); * background(200); * textAlign(CENTER); * text('drop image', width/2, height/2); * c.drop(gotFile); * } * * function gotFile(file) { * var img = createImg(file.data).hide(); * // Draw the image onto the canvas * image(img, 0, 0, width, height); * } *
* * @alt * Canvas turns into whatever image is dragged/dropped onto it. * */ p5.Element.prototype.drop = function (callback, fxn) { // Make a file loader callback and trigger user's callback function makeLoader(theFile) { // Making a p5.File object var p5file = new p5.File(theFile); return function(e) { p5file.data = e.target.result; callback(p5file); }; } // Is the file stuff supported? if (window.File && window.FileReader && window.FileList && window.Blob) { // If you want to be able to drop you've got to turn off // a lot of default behavior attachListener('dragover',function(evt) { evt.stopPropagation(); evt.preventDefault(); },this); // If this is a drag area we need to turn off the default behavior attachListener('dragleave',function(evt) { evt.stopPropagation(); evt.preventDefault(); },this); // If just one argument it's the callback for the files if (arguments.length > 1) { attachListener('drop', fxn, this); } // Deal with the files attachListener('drop', function(evt) { evt.stopPropagation(); evt.preventDefault(); // A FileList var files = evt.dataTransfer.files; // Load each one and trigger the callback for (var i = 0; i < files.length; i++) { var f = files[i]; var reader = new FileReader(); reader.onload = makeLoader(f); // Text or data? // This should likely be improved if (f.type.indexOf('text') > -1) { reader.readAsText(f); } else { reader.readAsDataURL(f); } } }, this); } else { console.log('The File APIs are not fully supported in this browser.'); } return this; }; function attachListener(ev, fxn, ctx) { // LM removing, not sure why we had this? // var _this = ctx; // var f = function (e) { fxn(e, _this); }; var f = fxn.bind(ctx); ctx.elt.addEventListener(ev, f, false); ctx._events[ev] = f; } /** * Helper fxn for sharing pixel methods * */ p5.Element.prototype._setProperty = function (prop, value) { this[prop] = value; }; module.exports = p5.Element; },{"./core":5}],11:[function(_dereq_,module,exports){ /** * @module Rendering * @submodule Rendering * @for p5 */ var p5 = _dereq_('./core'); var constants = _dereq_('./constants'); /** * Thin wrapper around a renderer, to be used for creating a * graphics buffer object. Use this class if you need * to draw into an off-screen graphics buffer. The two parameters define the * width and height in pixels. The fields and methods for this class are * extensive, but mirror the normal drawing API for p5. * * @class p5.Graphics * @constructor * @extends p5.Element * @param {Number} w width * @param {Number} h height * @param {Constant} renderer the renderer to use, either P2D or WEBGL * @param {p5} [pInst] pointer to p5 instance */ p5.Graphics = function(w, h, renderer, pInst) { var r = renderer || constants.P2D; this.canvas = document.createElement('canvas'); var node = this._userNode || document.body; node.appendChild(this.canvas); p5.Element.call(this, this.canvas, pInst, false); this._styles = []; this.width = w; this.height = h; this._pixelDensity = pInst._pixelDensity; if (r === constants.WEBGL) { this._renderer = new p5.RendererGL(this.canvas, this, false); } else { this._renderer = new p5.Renderer2D(this.canvas, this, false); } this._renderer.resize(w, h); this._renderer._applyDefaults(); pInst._elements.push(this); // bind methods and props of p5 to the new object for (var p in p5.prototype) { if (!this[p]) { if (typeof p5.prototype[p] === 'function') { this[p] = p5.prototype[p].bind(this); } else { this[p] = p5.prototype[p]; } } } return this; }; p5.Graphics.prototype = Object.create(p5.Element.prototype); p5.Graphics.prototype.remove = function() { if (this.elt.parentNode) { this.elt.parentNode.removeChild(this.elt); } for (var elt_ev in this._events) { this.elt.removeEventListener(elt_ev, this._events[elt_ev]); } }; module.exports = p5.Graphics; },{"./constants":4,"./core":5}],12:[function(_dereq_,module,exports){ /** * @module Rendering * @submodule Rendering * @for p5 */ var p5 = _dereq_('./core'); var constants = _dereq_('../core/constants'); /** * Main graphics and rendering context, as well as the base API * implementation for p5.js "core". To be used as the superclass for * Renderer2D and Renderer3D classes, respecitvely. * * @class p5.Renderer * @constructor * @extends p5.Element * @param {String} elt DOM node that is wrapped * @param {p5} [pInst] pointer to p5 instance * @param {Boolean} [isMainCanvas] whether we're using it as main canvas */ p5.Renderer = function(elt, pInst, isMainCanvas) { p5.Element.call(this, elt, pInst); this.canvas = elt; this._pInst = pInst; if (isMainCanvas) { this._isMainCanvas = true; // for pixel method sharing with pimage this._pInst._setProperty('_curElement', this); this._pInst._setProperty('canvas', this.canvas); this._pInst._setProperty('width', this.width); this._pInst._setProperty('height', this.height); } else { // hide if offscreen buffer by default this.canvas.style.display = 'none'; this._styles = []; // non-main elt styles stored in p5.Renderer } this._textSize = 12; this._textLeading = 15; this._textFont = 'sans-serif'; this._textStyle = constants.NORMAL; this._textAscent = null; this._textDescent = null; this._rectMode = constants.CORNER; this._ellipseMode = constants.CENTER; this._curveTightness = 0; this._imageMode = constants.CORNER; this._tint = null; this._doStroke = true; this._doFill = true; this._strokeSet = false; this._fillSet = false; this._colorMode = constants.RGB; this._colorMaxes = { rgb: [255, 255, 255, 255], hsb: [360, 100, 100, 1], hsl: [360, 100, 100, 1] }; }; p5.Renderer.prototype = Object.create(p5.Element.prototype); /** * Resize our canvas element. */ p5.Renderer.prototype.resize = function(w, h) { this.width = w; this.height = h; this.elt.width = w * this._pInst._pixelDensity; this.elt.height = h * this._pInst._pixelDensity; this.elt.style.width = w +'px'; this.elt.style.height = h + 'px'; if (this._isMainCanvas) { this._pInst._setProperty('width', this.width); this._pInst._setProperty('height', this.height); } }; p5.Renderer.prototype.textLeading = function(l) { if (arguments.length && arguments[0]) { this._setProperty('_textLeading', l); return this; } return this._textLeading; }; p5.Renderer.prototype.textSize = function(s) { if (arguments.length && arguments[0]) { this._setProperty('_textSize', s); this._setProperty('_textLeading', s * constants._DEFAULT_LEADMULT); return this._applyTextProperties(); } return this._textSize; }; p5.Renderer.prototype.textStyle = function(s) { if (arguments.length && arguments[0]) { if (s === constants.NORMAL || s === constants.ITALIC || s === constants.BOLD) { this._setProperty('_textStyle', s); } return this._applyTextProperties(); } return this._textStyle; }; p5.Renderer.prototype.textAscent = function() { if (this._textAscent === null) { this._updateTextMetrics(); } return this._textAscent; }; p5.Renderer.prototype.textDescent = function() { if (this._textDescent === null) { this._updateTextMetrics(); } return this._textDescent; }; p5.Renderer.prototype._applyDefaults = function(){ return this; }; /** * Helper fxn to check font type (system or otf) */ p5.Renderer.prototype._isOpenType = function(f) { f = f || this._textFont; return (typeof f === 'object' && f.font && f.font.supported); }; p5.Renderer.prototype._updateTextMetrics = function() { if (this._isOpenType()) { this._setProperty('_textAscent', this._textFont._textAscent()); this._setProperty('_textDescent', this._textFont._textDescent()); return this; } // Adapted from http://stackoverflow.com/a/25355178 var text = document.createElement('span'); text.style.fontFamily = this._textFont; text.style.fontSize = this._textSize + 'px'; text.innerHTML = 'ABCjgq|'; var block = document.createElement('div'); block.style.display = 'inline-block'; block.style.width = '1px'; block.style.height = '0px'; var container = document.createElement('div'); container.appendChild(text); container.appendChild(block); container.style.height = '0px'; container.style.overflow = 'hidden'; document.body.appendChild(container); block.style.verticalAlign = 'baseline'; var blockOffset = calculateOffset(block); var textOffset = calculateOffset(text); var ascent = blockOffset[1] - textOffset[1]; block.style.verticalAlign = 'bottom'; blockOffset = calculateOffset(block); textOffset = calculateOffset(text); var height = blockOffset[1] - textOffset[1]; var descent = height - ascent; document.body.removeChild(container); this._setProperty('_textAscent', ascent); this._setProperty('_textDescent', descent); return this; }; /** * Helper fxn to measure ascent and descent. * Adapted from http://stackoverflow.com/a/25355178 */ function calculateOffset(object) { var currentLeft = 0, currentTop = 0; if (object.offsetParent) { do { currentLeft += object.offsetLeft; currentTop += object.offsetTop; } while (object = object.offsetParent); } else { currentLeft += object.offsetLeft; currentTop += object.offsetTop; } return [currentLeft, currentTop]; } module.exports = p5.Renderer; },{"../core/constants":4,"./core":5}],13:[function(_dereq_,module,exports){ var p5 = _dereq_('./core'); var canvas = _dereq_('./canvas'); var constants = _dereq_('./constants'); var filters = _dereq_('../image/filters'); _dereq_('./p5.Renderer'); /** * p5.Renderer2D * The 2D graphics canvas renderer class. * extends p5.Renderer */ var styleEmpty = 'rgba(0,0,0,0)'; // var alphaThreshold = 0.00125; // minimum visible p5.Renderer2D = function(elt, pInst, isMainCanvas){ p5.Renderer.call(this, elt, pInst, isMainCanvas); this.drawingContext = this.canvas.getContext('2d'); this._pInst._setProperty('drawingContext', this.drawingContext); return this; }; p5.Renderer2D.prototype = Object.create(p5.Renderer.prototype); p5.Renderer2D.prototype._applyDefaults = function() { this._setFill(constants._DEFAULT_FILL); this._setStroke(constants._DEFAULT_STROKE); this.drawingContext.lineCap = constants.ROUND; this.drawingContext.font = 'normal 12px sans-serif'; }; p5.Renderer2D.prototype.resize = function(w,h) { p5.Renderer.prototype.resize.call(this, w,h); this.drawingContext.scale(this._pInst._pixelDensity, this._pInst._pixelDensity); }; ////////////////////////////////////////////// // COLOR | Setting ////////////////////////////////////////////// p5.Renderer2D.prototype.background = function() { this.drawingContext.save(); this.drawingContext.setTransform(1, 0, 0, 1, 0, 0); this.drawingContext.scale(this._pInst._pixelDensity, this._pInst._pixelDensity); if (arguments[0] instanceof p5.Image) { this._pInst.image(arguments[0], 0, 0, this.width, this.height); } else { var curFill = this._getFill(); // create background rect var color = this._pInst.color.apply(this, arguments); var newFill = color.toString(); this._setFill(newFill); this.drawingContext.fillRect(0, 0, this.width, this.height); // reset fill this._setFill(curFill); } this.drawingContext.restore(); }; p5.Renderer2D.prototype.clear = function() { this.drawingContext.clearRect(0, 0, this.width, this.height); }; p5.Renderer2D.prototype.fill = function() { var color = this._pInst.color.apply(this, arguments); this._setFill(color.toString()); }; p5.Renderer2D.prototype.stroke = function() { var color = this._pInst.color.apply(this, arguments); this._setStroke(color.toString()); }; ////////////////////////////////////////////// // IMAGE | Loading & Displaying ////////////////////////////////////////////// p5.Renderer2D.prototype.image = function (img, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight) { var cnv; try { if (this._tint) { if (p5.MediaElement && img instanceof p5.MediaElement) { img.loadPixels(); } if (img.canvas) { cnv = this._getTintedImageCanvas(img); } } if (!cnv) { cnv = img.canvas || img.elt; } this.drawingContext.drawImage(cnv, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight); } catch (e) { if (e.name !== 'NS_ERROR_NOT_AVAILABLE') { throw e; } } }; p5.Renderer2D.prototype._getTintedImageCanvas = function (img) { if (!img.canvas) { return img; } var pixels = filters._toPixels(img.canvas); var tmpCanvas = document.createElement('canvas'); tmpCanvas.width = img.canvas.width; tmpCanvas.height = img.canvas.height; var tmpCtx = tmpCanvas.getContext('2d'); var id = tmpCtx.createImageData(img.canvas.width, img.canvas.height); var newPixels = id.data; for (var i = 0; i < pixels.length; i += 4) { var r = pixels[i]; var g = pixels[i + 1]; var b = pixels[i + 2]; var a = pixels[i + 3]; newPixels[i] = r * this._tint[0] / 255; newPixels[i + 1] = g * this._tint[1] / 255; newPixels[i + 2] = b * this._tint[2] / 255; newPixels[i + 3] = a * this._tint[3] / 255; } tmpCtx.putImageData(id, 0, 0); return tmpCanvas; }; ////////////////////////////////////////////// // IMAGE | Pixels ////////////////////////////////////////////// p5.Renderer2D.prototype.blendMode = function(mode) { this.drawingContext.globalCompositeOperation = mode; }; p5.Renderer2D.prototype.blend = function() { var currBlend = this.drawingContext.globalCompositeOperation; var blendMode = arguments[arguments.length - 1]; var copyArgs = Array.prototype.slice.call( arguments, 0, arguments.length - 1 ); this.drawingContext.globalCompositeOperation = blendMode; if (this._pInst) { this._pInst.copy.apply(this._pInst, copyArgs); } else { this.copy.apply(this, copyArgs); } this.drawingContext.globalCompositeOperation = currBlend; }; p5.Renderer2D.prototype.copy = function () { var srcImage, sx, sy, sw, sh, dx, dy, dw, dh; if (arguments.length === 9) { srcImage = arguments[0]; sx = arguments[1]; sy = arguments[2]; sw = arguments[3]; sh = arguments[4]; dx = arguments[5]; dy = arguments[6]; dw = arguments[7]; dh = arguments[8]; } else if (arguments.length === 8) { srcImage = this._pInst; sx = arguments[0]; sy = arguments[1]; sw = arguments[2]; sh = arguments[3]; dx = arguments[4]; dy = arguments[5]; dw = arguments[6]; dh = arguments[7]; } else { throw new Error('Signature not supported'); } p5.Renderer2D._copyHelper(srcImage, sx, sy, sw, sh, dx, dy, dw, dh); }; p5.Renderer2D._copyHelper = function (srcImage, sx, sy, sw, sh, dx, dy, dw, dh) { srcImage.loadPixels(); var s = srcImage.canvas.width / srcImage.width; this.drawingContext.drawImage(srcImage.canvas, s * sx, s * sy, s * sw, s * sh, dx, dy, dw, dh); }; p5.Renderer2D.prototype.get = function(x, y, w, h) { if (x === undefined && y === undefined && w === undefined && h === undefined){ x = 0; y = 0; w = this.width; h = this.height; } else if (w === undefined && h === undefined) { w = 1; h = 1; } // if the section does not overlap the canvas if(x + w < 0 || y + h < 0 || x > this.width || y > this.height){ return [0, 0, 0, 255]; } var ctx = this._pInst || this; ctx.loadPixels(); var pd = ctx._pixelDensity; // round down to get integer numbers x = Math.floor(x); y = Math.floor(y); w = Math.floor(w); h = Math.floor(h); var sx = x * pd; var sy = y * pd; if (w === 1 && h === 1){ var imageData = this.drawingContext.getImageData(sx, sy, 1, 1).data; //imageData = [0,0,0,0]; return [ imageData[0], imageData[1], imageData[2], imageData[3] ]; } else { //auto constrain the width and height to //dimensions of the source image var dw = Math.min(w, ctx.width); var dh = Math.min(h, ctx.height); var sw = dw * pd; var sh = dh * pd; var region = new p5.Image(dw, dh); region.canvas.getContext('2d').drawImage(this.canvas, sx, sy, sw, sh, 0, 0, dw, dh); return region; } }; p5.Renderer2D.prototype.loadPixels = function () { var pd = this._pixelDensity || this._pInst._pixelDensity; var w = this.width * pd; var h = this.height * pd; var imageData = this.drawingContext.getImageData(0, 0, w, h); // @todo this should actually set pixels per object, so diff buffers can // have diff pixel arrays. if (this._pInst) { this._pInst._setProperty('imageData', imageData); this._pInst._setProperty('pixels', imageData.data); } else { // if called by p5.Image this._setProperty('imageData', imageData); this._setProperty('pixels', imageData.data); } }; p5.Renderer2D.prototype.set = function (x, y, imgOrCol) { // round down to get integer numbers x = Math.floor(x); y = Math.floor(y); if (imgOrCol instanceof p5.Image) { this.drawingContext.save(); this.drawingContext.setTransform(1, 0, 0, 1, 0, 0); this.drawingContext.scale(this._pInst._pixelDensity, this._pInst._pixelDensity); this.drawingContext.drawImage(imgOrCol.canvas, x, y); this.loadPixels.call(this._pInst); this.drawingContext.restore(); } else { var ctx = this._pInst || this; var r = 0, g = 0, b = 0, a = 0; var idx = 4*((y * ctx._pixelDensity) * (this.width * ctx._pixelDensity) + (x * ctx._pixelDensity)); if (!ctx.imageData) { ctx.loadPixels.call(ctx); } if (typeof imgOrCol === 'number') { if (idx < ctx.pixels.length) { r = imgOrCol; g = imgOrCol; b = imgOrCol; a = 255; //this.updatePixels.call(this); } } else if (imgOrCol instanceof Array) { if (imgOrCol.length < 4) { throw new Error('pixel array must be of the form [R, G, B, A]'); } if (idx < ctx.pixels.length) { r = imgOrCol[0]; g = imgOrCol[1]; b = imgOrCol[2]; a = imgOrCol[3]; //this.updatePixels.call(this); } } else if (imgOrCol instanceof p5.Color) { if (idx < ctx.pixels.length) { r = imgOrCol.levels[0]; g = imgOrCol.levels[1]; b = imgOrCol.levels[2]; a = imgOrCol.levels[3]; //this.updatePixels.call(this); } } // loop over pixelDensity * pixelDensity for (var i = 0; i < ctx._pixelDensity; i++) { for (var j = 0; j < ctx._pixelDensity; j++) { // loop over idx = 4*((y * ctx._pixelDensity + j) * this.width * ctx._pixelDensity + (x * ctx._pixelDensity + i)); ctx.pixels[idx] = r; ctx.pixels[idx+1] = g; ctx.pixels[idx+2] = b; ctx.pixels[idx+3] = a; } } } }; p5.Renderer2D.prototype.updatePixels = function (x, y, w, h) { var pd = this._pixelDensity || this._pInst._pixelDensity; if (x === undefined && y === undefined && w === undefined && h === undefined) { x = 0; y = 0; w = this.width; h = this.height; } w *= pd; h *= pd; if (this._pInst) { this.drawingContext.putImageData(this._pInst.imageData, x, y, 0, 0, w, h); } else { this.drawingContext.putImageData(this.imageData, x, y, 0, 0, w, h); } }; ////////////////////////////////////////////// // SHAPE | 2D Primitives ////////////////////////////////////////////// /** * Generate a cubic Bezier representing an arc on the unit circle of total * angle `size` radians, beginning `start` radians above the x-axis. Up to * four of these curves are combined to make a full arc. * * See www.joecridge.me/bezier.pdf for an explanation of the method. */ p5.Renderer2D.prototype._acuteArcToBezier = function _acuteArcToBezier(start, size) { // Evauate constants. var alpha = size / 2.0, cos_alpha = Math.cos(alpha), sin_alpha = Math.sin(alpha), cot_alpha = 1.0 / Math.tan(alpha), phi = start + alpha, // This is how far the arc needs to be rotated. cos_phi = Math.cos(phi), sin_phi = Math.sin(phi), lambda = (4.0 - cos_alpha) / 3.0, mu = sin_alpha + (cos_alpha - lambda) * cot_alpha; // Return rotated waypoints. return { ax: Math.cos(start), ay: Math.sin(start), bx: lambda * cos_phi + mu * sin_phi, by: lambda * sin_phi - mu * cos_phi, cx: lambda * cos_phi - mu * sin_phi, cy: lambda * sin_phi + mu * cos_phi, dx: Math.cos(start + size), dy: Math.sin(start + size) }; }; p5.Renderer2D.prototype.arc = function(x, y, w, h, start, stop, mode) { var ctx = this.drawingContext; var vals = canvas.arcModeAdjust(x, y, w, h, this._ellipseMode); var rx = vals.w / 2.0; var ry = vals.h / 2.0; var epsilon = 0.00001; // Smallest visible angle on displays up to 4K. var arcToDraw = 0; var curves = []; // Create curves while(stop - start > epsilon) { arcToDraw = Math.min(stop - start, constants.HALF_PI); curves.push(this._acuteArcToBezier(start, arcToDraw)); start += arcToDraw; } // Fill curves if (this._doFill) { ctx.beginPath(); curves.forEach(function (curve, index) { if (index === 0) { ctx.moveTo(vals.x + curve.ax * rx, vals.y + curve.ay * ry); } ctx.bezierCurveTo(vals.x + curve.bx * rx, vals.y + curve.by * ry, vals.x + curve.cx * rx, vals.y + curve.cy * ry, vals.x + curve.dx * rx, vals.y + curve.dy * ry); }); if (mode === constants.PIE || mode == null) { ctx.lineTo(vals.x, vals.y); } ctx.closePath(); ctx.fill(); } // Stroke curves if (this._doStroke) { ctx.beginPath(); curves.forEach(function (curve, index) { if (index === 0) { ctx.moveTo(vals.x + curve.ax * rx, vals.y + curve.ay * ry); } ctx.bezierCurveTo(vals.x + curve.bx * rx, vals.y + curve.by * ry, vals.x + curve.cx * rx, vals.y + curve.cy * ry, vals.x + curve.dx * rx, vals.y + curve.dy * ry); }); if (mode === constants.PIE) { ctx.lineTo(vals.x, vals.y); ctx.closePath(); } else if (mode === constants.CHORD) { ctx.closePath(); } ctx.stroke(); } return this; }; p5.Renderer2D.prototype.ellipse = function(args) { var ctx = this.drawingContext; var doFill = this._doFill, doStroke = this._doStroke; var x = args[0], y = args[1], w = args[2], h = args[3]; if (doFill && !doStroke) { if(this._getFill() === styleEmpty) { return this; } } else if (!doFill && doStroke) { if(this._getStroke() === styleEmpty) { return this; } } var kappa = 0.5522847498, ox = (w / 2) * kappa, // control point offset horizontal oy = (h / 2) * kappa, // control point offset vertical xe = x + w, // x-end ye = y + h, // y-end xm = x + w / 2, // x-middle ym = y + h / 2; // y-middle ctx.beginPath(); ctx.moveTo(x, ym); ctx.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); ctx.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); ctx.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); ctx.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); ctx.closePath(); if (doFill) { ctx.fill(); } if (doStroke) { ctx.stroke(); } }; p5.Renderer2D.prototype.line = function(x1, y1, x2, y2) { var ctx = this.drawingContext; if (!this._doStroke) { return this; } else if(this._getStroke() === styleEmpty){ return this; } // Translate the line by (0.5, 0.5) to draw it crisp if (ctx.lineWidth % 2 === 1) { ctx.translate(0.5, 0.5); } ctx.beginPath(); ctx.moveTo(x1, y1); ctx.lineTo(x2, y2); ctx.stroke(); if (ctx.lineWidth % 2 === 1) { ctx.translate(-0.5, -0.5); } return this; }; p5.Renderer2D.prototype.point = function(x, y) { var ctx = this.drawingContext; if (!this._doStroke) { return this; } else if(this._getStroke() === styleEmpty){ return this; } x = Math.round(x); y = Math.round(y); if (ctx.lineWidth > 1) { ctx.beginPath(); ctx.arc( x, y, ctx.lineWidth / 2, 0, constants.TWO_PI, false ); ctx.fill(); } else { ctx.fillRect(x, y, 1, 1); } }; p5.Renderer2D.prototype.quad = function(x1, y1, x2, y2, x3, y3, x4, y4) { var ctx = this.drawingContext; var doFill = this._doFill, doStroke = this._doStroke; if (doFill && !doStroke) { if(this._getFill() === styleEmpty) { return this; } } else if (!doFill && doStroke) { if(this._getStroke() === styleEmpty) { return this; } } ctx.beginPath(); ctx.moveTo(x1, y1); ctx.lineTo(x2, y2); ctx.lineTo(x3, y3); ctx.lineTo(x4, y4); ctx.closePath(); if (doFill) { ctx.fill(); } if (doStroke) { ctx.stroke(); } return this; }; p5.Renderer2D.prototype.rect = function(args) { var x = args[0], y = args[1], w = args[2], h = args[3], tl = args[4], tr = args[5], br = args[6], bl = args[7]; var ctx = this.drawingContext; var doFill = this._doFill, doStroke = this._doStroke; if (doFill && !doStroke) { if(this._getFill() === styleEmpty) { return this; } } else if (!doFill && doStroke) { if(this._getStroke() === styleEmpty) { return this; } } // Translate the line by (0.5, 0.5) to draw a crisp rectangle border if (this._doStroke && ctx.lineWidth % 2 === 1) { ctx.translate(0.5, 0.5); } ctx.beginPath(); if (typeof tl === 'undefined') { // No rounded corners ctx.rect(x, y, w, h); } else { // At least one rounded corner // Set defaults when not specified if (typeof tr === 'undefined') { tr = tl; } if (typeof br === 'undefined') { br = tr; } if (typeof bl === 'undefined') { bl = br; } var hw = w / 2; var hh = h / 2; // Clip radii if (w < 2 * tl) { tl = hw; } if (h < 2 * tl) { tl = hh; } if (w < 2 * tr) { tr = hw; } if (h < 2 * tr) { tr = hh; } if (w < 2 * br) { br = hw; } if (h < 2 * br) { br = hh; } if (w < 2 * bl) { bl = hw; } if (h < 2 * bl) { bl = hh; } // Draw shape ctx.beginPath(); ctx.moveTo(x + tl, y); ctx.arcTo(x + w, y, x + w, y + h, tr); ctx.arcTo(x + w, y + h, x, y + h, br); ctx.arcTo(x, y + h, x, y, bl); ctx.arcTo(x, y, x + w, y, tl); ctx.closePath(); } if (this._doFill) { ctx.fill(); } if (this._doStroke) { ctx.stroke(); } if (this._doStroke && ctx.lineWidth % 2 === 1) { ctx.translate(-0.5, -0.5); } return this; }; p5.Renderer2D.prototype.triangle = function(args) { var ctx = this.drawingContext; var doFill = this._doFill, doStroke = this._doStroke; var x1=args[0], y1=args[1]; var x2=args[2], y2=args[3]; var x3=args[4], y3=args[5]; if (doFill && !doStroke) { if(this._getFill() === styleEmpty) { return this; } } else if (!doFill && doStroke) { if(this._getStroke() === styleEmpty) { return this; } } ctx.beginPath(); ctx.moveTo(x1, y1); ctx.lineTo(x2, y2); ctx.lineTo(x3, y3); ctx.closePath(); if (doFill) { ctx.fill(); } if (doStroke) { ctx.stroke(); } }; p5.Renderer2D.prototype.endShape = function (mode, vertices, isCurve, isBezier, isQuadratic, isContour, shapeKind) { if (vertices.length === 0) { return this; } if (!this._doStroke && !this._doFill) { return this; } var closeShape = mode === constants.CLOSE; var v; if (closeShape && !isContour) { vertices.push(vertices[0]); } var i, j; var numVerts = vertices.length; if (isCurve && (shapeKind === constants.POLYGON || shapeKind === null)) { if (numVerts > 3) { var b = [], s = 1 - this._curveTightness; this.drawingContext.beginPath(); this.drawingContext.moveTo(vertices[1][0], vertices[1][1]); for (i = 1; i + 2 < numVerts; i++) { v = vertices[i]; b[0] = [ v[0], v[1] ]; b[1] = [ v[0] + (s * vertices[i + 1][0] - s * vertices[i - 1][0]) / 6, v[1] + (s * vertices[i + 1][1] - s * vertices[i - 1][1]) / 6 ]; b[2] = [ vertices[i + 1][0] + (s * vertices[i][0]-s * vertices[i + 2][0]) / 6, vertices[i + 1][1]+(s * vertices[i][1] - s*vertices[i + 2][1]) / 6 ]; b[3] = [ vertices[i + 1][0], vertices[i + 1][1] ]; this.drawingContext.bezierCurveTo(b[1][0],b[1][1], b[2][0],b[2][1],b[3][0],b[3][1]); } if (closeShape) { this.drawingContext.lineTo(vertices[i + 1][0], vertices[i + 1][1]); } this._doFillStrokeClose(); } } else if (isBezier&&(shapeKind===constants.POLYGON ||shapeKind === null)) { this.drawingContext.beginPath(); for (i = 0; i < numVerts; i++) { if (vertices[i].isVert) { if (vertices[i].moveTo) { this.drawingContext.moveTo(vertices[i][0], vertices[i][1]); } else { this.drawingContext.lineTo(vertices[i][0], vertices[i][1]); } } else { this.drawingContext.bezierCurveTo(vertices[i][0], vertices[i][1], vertices[i][2], vertices[i][3], vertices[i][4], vertices[i][5]); } } this._doFillStrokeClose(); } else if (isQuadratic && (shapeKind === constants.POLYGON || shapeKind === null)) { this.drawingContext.beginPath(); for (i = 0; i < numVerts; i++) { if (vertices[i].isVert) { if (vertices[i].moveTo) { this.drawingContext.moveTo([0], vertices[i][1]); } else { this.drawingContext.lineTo(vertices[i][0], vertices[i][1]); } } else { this.drawingContext.quadraticCurveTo(vertices[i][0], vertices[i][1], vertices[i][2], vertices[i][3]); } } this._doFillStrokeClose(); } else { if (shapeKind === constants.POINTS) { for (i = 0; i < numVerts; i++) { v = vertices[i]; if (this._doStroke) { this._pInst.stroke(v[6]); } this._pInst.point(v[0], v[1]); } } else if (shapeKind === constants.LINES) { for (i = 0; i + 1 < numVerts; i += 2) { v = vertices[i]; if (this._doStroke) { this._pInst.stroke(vertices[i + 1][6]); } this._pInst.line(v[0], v[1], vertices[i + 1][0], vertices[i + 1][1]); } } else if (shapeKind === constants.TRIANGLES) { for (i = 0; i + 2 < numVerts; i += 3) { v = vertices[i]; this.drawingContext.beginPath(); this.drawingContext.moveTo(v[0], v[1]); this.drawingContext.lineTo(vertices[i + 1][0], vertices[i + 1][1]); this.drawingContext.lineTo(vertices[i + 2][0], vertices[i + 2][1]); this.drawingContext.lineTo(v[0], v[1]); if (this._doFill) { this._pInst.fill(vertices[i + 2][5]); this.drawingContext.fill(); } if (this._doStroke) { this._pInst.stroke(vertices[i + 2][6]); this.drawingContext.stroke(); } this.drawingContext.closePath(); } } else if (shapeKind === constants.TRIANGLE_STRIP) { for (i = 0; i + 1 < numVerts; i++) { v = vertices[i]; this.drawingContext.beginPath(); this.drawingContext.moveTo(vertices[i + 1][0], vertices[i + 1][1]); this.drawingContext.lineTo(v[0], v[1]); if (this._doStroke) { this._pInst.stroke(vertices[i + 1][6]); } if (this._doFill) { this._pInst.fill(vertices[i + 1][5]); } if (i + 2 < numVerts) { this.drawingContext.lineTo(vertices[i + 2][0], vertices[i + 2][1]); if (this._doStroke) { this._pInst.stroke(vertices[i + 2][6]); } if (this._doFill) { this._pInst.fill(vertices[i + 2][5]); } } this._doFillStrokeClose(); } } else if (shapeKind === constants.TRIANGLE_FAN) { if (numVerts > 2) { // For performance reasons, try to batch as many of the // fill and stroke calls as possible. this.drawingContext.beginPath(); for (i = 2; i < numVerts; i++) { v = vertices[i]; this.drawingContext.moveTo(vertices[0][0], vertices[0][1]); this.drawingContext.lineTo(vertices[i - 1][0], vertices[i - 1][1]); this.drawingContext.lineTo(v[0], v[1]); this.drawingContext.lineTo(vertices[0][0], vertices[0][1]); // If the next colour is going to be different, stroke / fill now if (i < numVerts - 1) { if ( (this._doFill && v[5] !== vertices[i + 1][5]) || (this._doStroke && v[6] !== vertices[i + 1][6])) { if (this._doFill) { this._pInst.fill(v[5]); this.drawingContext.fill(); this._pInst.fill(vertices[i + 1][5]); } if (this._doStroke) { this._pInst.stroke(v[6]); this.drawingContext.stroke(); this._pInst.stroke(vertices[i + 1][6]); } this.drawingContext.closePath(); this.drawingContext.beginPath(); // Begin the next one } } } this._doFillStrokeClose(); } } else if (shapeKind === constants.QUADS) { for (i = 0; i + 3 < numVerts; i += 4) { v = vertices[i]; this.drawingContext.beginPath(); this.drawingContext.moveTo(v[0], v[1]); for (j = 1; j < 4; j++) { this.drawingContext.lineTo(vertices[i + j][0], vertices[i + j][1]); } this.drawingContext.lineTo(v[0], v[1]); if (this._doFill) { this._pInst.fill(vertices[i + 3][5]); } if (this._doStroke) { this._pInst.stroke(vertices[i + 3][6]); } this._doFillStrokeClose(); } } else if (shapeKind === constants.QUAD_STRIP) { if (numVerts > 3) { for (i = 0; i + 1 < numVerts; i += 2) { v = vertices[i]; this.drawingContext.beginPath(); if (i + 3 < numVerts) { this.drawingContext.moveTo(vertices[i + 2][0], vertices[i+2][1]); this.drawingContext.lineTo(v[0], v[1]); this.drawingContext.lineTo(vertices[i + 1][0], vertices[i+1][1]); this.drawingContext.lineTo(vertices[i + 3][0], vertices[i+3][1]); if (this._doFill) { this._pInst.fill(vertices[i + 3][5]); } if (this._doStroke) { this._pInst.stroke(vertices[i + 3][6]); } } else { this.drawingContext.moveTo(v[0], v[1]); this.drawingContext.lineTo(vertices[i + 1][0], vertices[i+1][1]); } this._doFillStrokeClose(); } } } else { this.drawingContext.beginPath(); this.drawingContext.moveTo(vertices[0][0], vertices[0][1]); for (i = 1; i < numVerts; i++) { v = vertices[i]; if (v.isVert) { if (v.moveTo) { this.drawingContext.moveTo(v[0], v[1]); } else { this.drawingContext.lineTo(v[0], v[1]); } } } this._doFillStrokeClose(); } } isCurve = false; isBezier = false; isQuadratic = false; isContour = false; if (closeShape) { vertices.pop(); } return this; }; ////////////////////////////////////////////// // SHAPE | Attributes ////////////////////////////////////////////// p5.Renderer2D.prototype.noSmooth = function() { if ('imageSmoothingEnabled' in this.drawingContext) { this.drawingContext.imageSmoothingEnabled = false; } else if ('mozImageSmoothingEnabled' in this.drawingContext) { this.drawingContext.mozImageSmoothingEnabled = false; } else if ('webkitImageSmoothingEnabled' in this.drawingContext) { this.drawingContext.webkitImageSmoothingEnabled = false; } else if ('msImageSmoothingEnabled' in this.drawingContext) { this.drawingContext.msImageSmoothingEnabled = false; } return this; }; p5.Renderer2D.prototype.smooth = function() { if ('imageSmoothingEnabled' in this.drawingContext) { this.drawingContext.imageSmoothingEnabled = true; } else if ('mozImageSmoothingEnabled' in this.drawingContext) { this.drawingContext.mozImageSmoothingEnabled = true; } else if ('webkitImageSmoothingEnabled' in this.drawingContext) { this.drawingContext.webkitImageSmoothingEnabled = true; } else if ('msImageSmoothingEnabled' in this.drawingContext) { this.drawingContext.msImageSmoothingEnabled = true; } return this; }; p5.Renderer2D.prototype.strokeCap = function(cap) { if (cap === constants.ROUND || cap === constants.SQUARE || cap === constants.PROJECT) { this.drawingContext.lineCap = cap; } return this; }; p5.Renderer2D.prototype.strokeJoin = function(join) { if (join === constants.ROUND || join === constants.BEVEL || join === constants.MITER) { this.drawingContext.lineJoin = join; } return this; }; p5.Renderer2D.prototype.strokeWeight = function(w) { if (typeof w === 'undefined' || w === 0) { // hack because lineWidth 0 doesn't work this.drawingContext.lineWidth = 0.0001; } else { this.drawingContext.lineWidth = w; } return this; }; p5.Renderer2D.prototype._getFill = function(){ return this._cachedFillStyle; }; p5.Renderer2D.prototype._setFill = function(fillStyle){ if (fillStyle !== this._cachedFillStyle) { this.drawingContext.fillStyle = fillStyle; this._cachedFillStyle = fillStyle; } }; p5.Renderer2D.prototype._getStroke = function(){ return this._cachedStrokeStyle; }; p5.Renderer2D.prototype._setStroke = function(strokeStyle){ if (strokeStyle !== this._cachedStrokeStyle) { this.drawingContext.strokeStyle = strokeStyle; this._cachedStrokeStyle = strokeStyle; } }; ////////////////////////////////////////////// // SHAPE | Curves ////////////////////////////////////////////// p5.Renderer2D.prototype.bezier = function (x1, y1, x2, y2, x3, y3, x4, y4) { this._pInst.beginShape(); this._pInst.vertex(x1, y1); this._pInst.bezierVertex(x2, y2, x3, y3, x4, y4); this._pInst.endShape(); return this; }; p5.Renderer2D.prototype.curve = function (x1, y1, x2, y2, x3, y3, x4, y4) { this._pInst.beginShape(); this._pInst.curveVertex(x1, y1); this._pInst.curveVertex(x2, y2); this._pInst.curveVertex(x3, y3); this._pInst.curveVertex(x4, y4); this._pInst.endShape(); return this; }; ////////////////////////////////////////////// // SHAPE | Vertex ////////////////////////////////////////////// p5.Renderer2D.prototype._doFillStrokeClose = function () { if (this._doFill) { this.drawingContext.fill(); } if (this._doStroke) { this.drawingContext.stroke(); } this.drawingContext.closePath(); }; ////////////////////////////////////////////// // TRANSFORM ////////////////////////////////////////////// p5.Renderer2D.prototype.applyMatrix = function(n00, n01, n02, n10, n11, n12) { this.drawingContext.transform(n00, n01, n02, n10, n11, n12); }; p5.Renderer2D.prototype.resetMatrix = function() { this.drawingContext.setTransform(1, 0, 0, 1, 0, 0); this.drawingContext.scale(this._pInst._pixelDensity, this._pInst._pixelDensity); return this; }; p5.Renderer2D.prototype.rotate = function(r) { this.drawingContext.rotate(r); }; p5.Renderer2D.prototype.scale = function(x,y) { this.drawingContext.scale(x, y); return this; }; p5.Renderer2D.prototype.shearX = function(angle) { if (this._pInst._angleMode === constants.DEGREES) { // undoing here, because it gets redone in tan() angle = this._pInst.degrees(angle); } this.drawingContext.transform(1, 0, this._pInst.tan(angle), 1, 0, 0); return this; }; p5.Renderer2D.prototype.shearY = function(angle) { if (this._pInst._angleMode === constants.DEGREES) { // undoing here, because it gets redone in tan() angle = this._pInst.degrees(angle); } this.drawingContext.transform(1, this._pInst.tan(angle), 0, 1, 0, 0); return this; }; p5.Renderer2D.prototype.translate = function(x, y) { this.drawingContext.translate(x, y); return this; }; ////////////////////////////////////////////// // TYPOGRAPHY // ////////////////////////////////////////////// p5.Renderer2D.prototype.text = function (str, x, y, maxWidth, maxHeight) { var p = this._pInst, cars, n, ii, jj, line, testLine, testWidth, words, totalHeight, baselineHacked, finalMaxHeight = Number.MAX_VALUE; // baselineHacked: (HACK) // A temporary fix to conform to Processing's implementation // of BASELINE vertical alignment in a bounding box if (!(this._doFill || this._doStroke)) { return; } if (typeof str !== 'string') { str = str.toString(); } str = str.replace(/(\t)/g, ' '); cars = str.split('\n'); if (typeof maxWidth !== 'undefined') { totalHeight = 0; for (ii = 0; ii < cars.length; ii++) { line = ''; words = cars[ii].split(' '); for (n = 0; n < words.length; n++) { testLine = line + words[n] + ' '; testWidth = this.textWidth(testLine); if (testWidth > maxWidth) { line = words[n] + ' '; totalHeight += p.textLeading(); } else { line = testLine; } } } if (this._rectMode === constants.CENTER) { x -= maxWidth / 2; y -= maxHeight / 2; } switch (this.drawingContext.textAlign) { case constants.CENTER: x += maxWidth / 2; break; case constants.RIGHT: x += maxWidth; break; } if (typeof maxHeight !== 'undefined') { switch (this.drawingContext.textBaseline) { case constants.BOTTOM: y += (maxHeight - totalHeight); break; case constants._CTX_MIDDLE: // CENTER? y += (maxHeight - totalHeight) / 2; break; case constants.BASELINE: baselineHacked = true; this.drawingContext.textBaseline = constants.TOP; break; } // remember the max-allowed y-position for any line (fix to #928) finalMaxHeight = (y + maxHeight) - p.textAscent(); } for (ii = 0; ii < cars.length; ii++) { line = ''; words = cars[ii].split(' '); for (n = 0; n < words.length; n++) { testLine = line + words[n] + ' '; testWidth = this.textWidth(testLine); if (testWidth > maxWidth && line.length > 0) { this._renderText(p, line, x, y, finalMaxHeight); line = words[n] + ' '; y += p.textLeading(); } else { line = testLine; } } this._renderText(p, line, x, y, finalMaxHeight); y += p.textLeading(); } } else { // Offset to account for vertically centering multiple lines of text - no // need to adjust anything for vertical align top or baseline var offset = 0, vAlign = p.textAlign().vertical; if (vAlign === constants.CENTER) { offset = ((cars.length - 1) * p.textLeading()) / 2; } else if (vAlign === constants.BOTTOM) { offset = (cars.length - 1) * p.textLeading(); } for (jj = 0; jj < cars.length; jj++) { this._renderText(p, cars[jj], x, y-offset, finalMaxHeight); y += p.textLeading(); } } if (baselineHacked) { this.drawingContext.textBaseline = constants.BASELINE; } return p; }; p5.Renderer2D.prototype._renderText = function(p, line, x, y, maxY) { if (y >= maxY) { return; // don't render lines beyond our maxY position } p.push(); // fix to #803 if (!this._isOpenType()) { // a system/browser font // no stroke unless specified by user if (this._doStroke && this._strokeSet) { this.drawingContext.strokeText(line, x, y); } if (this._doFill) { // if fill hasn't been set by user, use default text fill if (! this._fillSet) { this._setFill(constants._DEFAULT_TEXT_FILL); } this.drawingContext.fillText(line, x, y); } } else { // an opentype font, let it handle the rendering this._textFont._renderPath(line, x, y, { renderer: this }); } p.pop(); return p; }; p5.Renderer2D.prototype.textWidth = function(s) { if (this._isOpenType()) { return this._textFont._textWidth(s, this._textSize); } return this.drawingContext.measureText(s).width; }; p5.Renderer2D.prototype.textAlign = function(h, v) { if (arguments.length) { if (h === constants.LEFT || h === constants.RIGHT || h === constants.CENTER) { this.drawingContext.textAlign = h; } if (v === constants.TOP || v === constants.BOTTOM || v === constants.CENTER || v === constants.BASELINE) { if (v === constants.CENTER) { this.drawingContext.textBaseline = constants._CTX_MIDDLE; } else { this.drawingContext.textBaseline = v; } } return this._pInst; } else { var valign = this.drawingContext.textBaseline; if (valign === constants._CTX_MIDDLE) { valign = constants.CENTER; } return { horizontal: this.drawingContext.textAlign, vertical: valign }; } }; p5.Renderer2D.prototype._applyTextProperties = function() { var font, p = this._pInst; this._setProperty('_textAscent', null); this._setProperty('_textDescent', null); font = this._textFont; if (this._isOpenType()) { font = this._textFont.font.familyName; this._setProperty('_textStyle', this._textFont.font.styleName); } this.drawingContext.font = this._textStyle + ' ' + this._textSize + 'px ' + font; return p; }; ////////////////////////////////////////////// // STRUCTURE ////////////////////////////////////////////// p5.Renderer2D.prototype.push = function() { this.drawingContext.save(); }; p5.Renderer2D.prototype.pop = function() { this.drawingContext.restore(); // Re-cache the fill / stroke state this._cachedFillStyle = this.drawingContext.fillStyle; this._cachedStrokeStyle = this.drawingContext.strokeStyle; }; module.exports = p5.Renderer2D; },{"../image/filters":19,"./canvas":3,"./constants":4,"./core":5,"./p5.Renderer":12}],14:[function(_dereq_,module,exports){ /** * @module Rendering * @submodule Rendering * @for p5 */ var p5 = _dereq_('./core'); var constants = _dereq_('./constants'); _dereq_('./p5.Graphics'); _dereq_('./p5.Renderer2D'); _dereq_('../webgl/p5.RendererGL'); var defaultId = 'defaultCanvas0'; // this gets set again in createCanvas /** * Creates a canvas element in the document, and sets the dimensions of it * in pixels. This method should be called only once at the start of setup. * Calling createCanvas more than once in a sketch will result in very * unpredicable behavior. If you want more than one drawing canvas * you could use createGraphics (hidden by default but it can be shown). *

* The system variables width and height are set by the parameters passed * to this function. If createCanvas() is not used, the window will be * given a default size of 100x100 pixels. *

* For more ways to position the canvas, see the * * positioning the canvas wiki page. * * @method createCanvas * @param {Number} w width of the canvas * @param {Number} h height of the canvas * @param {Constant} [renderer] either P2D or WEBGL * @return {HTMLCanvasElement} canvas generated * @example *
* * function setup() { * createCanvas(100, 50); * background(153); * line(0, 0, width, height); * } * *
* * @alt * Black line extending from top-left of canvas to bottom right. * */ p5.prototype.createCanvas = function(w, h, renderer) { //optional: renderer, otherwise defaults to p2d var r = renderer || constants.P2D; var isDefault, c; //4th arg (isDefault) used when called onLoad, //otherwise hidden to the public api if(arguments[3]){ isDefault = (typeof arguments[3] === 'boolean') ? arguments[3] : false; } if(r === constants.WEBGL){ c = document.getElementById(defaultId); if(c){ //if defaultCanvas already exists c.parentNode.removeChild(c); //replace the existing defaultCanvas } c = document.createElement('canvas'); c.id = defaultId; } else { if (isDefault) { c = document.createElement('canvas'); var i = 0; while (document.getElementById('defaultCanvas'+i)) { i++; } defaultId = 'defaultCanvas'+i; c.id = defaultId; } else { // resize the default canvas if new one is created c = this.canvas; } } // set to invisible if still in setup (to prevent flashing with manipulate) if (!this._setupDone) { c.dataset.hidden = true; // tag to show later c.style.visibility='hidden'; } if (this._userNode) { // user input node case this._userNode.appendChild(c); } else { document.body.appendChild(c); } // Init our graphics renderer //webgl mode if (r === constants.WEBGL) { this._setProperty('_renderer', new p5.RendererGL(c, this, true)); this._isdefaultGraphics = true; } //P2D mode else { if (!this._isdefaultGraphics) { this._setProperty('_renderer', new p5.Renderer2D(c, this, true)); this._isdefaultGraphics = true; } } this._renderer.resize(w, h); this._renderer._applyDefaults(); if (isDefault) { // only push once this._elements.push(this._renderer); } return this._renderer; }; /** * Resizes the canvas to given width and height. The canvas will be cleared * and draw will be called immediately, allowing the sketch to re-render itself * in the resized canvas. * @method resizeCanvas * @param {Number} w width of the canvas * @param {Number} h height of the canvas * @param {Boolean} noRedraw don't redraw the canvas immediately * @example *
* function setup() { * createCanvas(windowWidth, windowHeight); * } * * function draw() { * background(0, 100, 200); * } * * function windowResized() { * resizeCanvas(windowWidth, windowHeight); * } *
* * @alt * No image displayed. * */ p5.prototype.resizeCanvas = function (w, h, noRedraw) { if (this._renderer) { // save canvas properties var props = {}; for (var key in this.drawingContext) { var val = this.drawingContext[key]; if (typeof val !== 'object' && typeof val !== 'function') { props[key] = val; } } this._renderer.resize(w, h); // reset canvas properties for (var savedKey in props) { this.drawingContext[savedKey] = props[savedKey]; } if (!noRedraw) { this.redraw(); } } }; /** * Removes the default canvas for a p5 sketch that doesn't * require a canvas * @method noCanvas * @example *
* * function setup() { * noCanvas(); * } * *
* * @alt * no image displayed * */ p5.prototype.noCanvas = function() { if (this.canvas) { this.canvas.parentNode.removeChild(this.canvas); } }; /** * Creates and returns a new p5.Renderer object. Use this class if you need * to draw into an off-screen graphics buffer. The two parameters define the * width and height in pixels. * * @method createGraphics * @param {Number} w width of the offscreen graphics buffer * @param {Number} h height of the offscreen graphics buffer * @param {Constant} [renderer] either P2D or WEBGL * undefined defaults to p2d * @return {p5.Graphics} offscreen graphics buffer * @example *
* * var pg; * function setup() { * createCanvas(100, 100); * pg = createGraphics(100, 100); * } * function draw() { * background(200); * pg.background(100); * pg.noStroke(); * pg.ellipse(pg.width/2, pg.height/2, 50, 50); * image(pg, 50, 50); * image(pg, 0, 0, 50, 50); * } * *
* * @alt * 4 grey squares alternating light and dark grey. White quarter circle mid-left. * */ p5.prototype.createGraphics = function(w, h, renderer){ return new p5.Graphics(w, h, renderer, this); }; /** * Blends the pixels in the display window according to the defined mode. * There is a choice of the following modes to blend the source pixels (A) * with the ones of pixels already in the display window (B): *
    *
  • BLEND - linear interpolation of colours: C = * A*factor + B. This is the default blending mode.
  • *
  • ADD - sum of A and B
  • *
  • DARKEST - only the darkest colour succeeds: C = * min(A*factor, B).
  • *
  • LIGHTEST - only the lightest colour succeeds: C = * max(A*factor, B).
  • *
  • DIFFERENCE - subtract colors from underlying image.
  • *
  • EXCLUSION - similar to DIFFERENCE, but less * extreme.
  • *
  • MULTIPLY - multiply the colors, result will always be * darker.
  • *
  • SCREEN - opposite multiply, uses inverse values of the * colors.
  • *
  • REPLACE - the pixels entirely replace the others and * don't utilize alpha (transparency) values.
  • *
  • OVERLAY - mix of MULTIPLY and SCREEN * . Multiplies dark values, and screens light values.
  • *
  • HARD_LIGHT - SCREEN when greater than 50% * gray, MULTIPLY when lower.
  • *
  • SOFT_LIGHT - mix of DARKEST and * LIGHTEST. Works like OVERLAY, but not as harsh. *
  • *
  • DODGE - lightens light tones and increases contrast, * ignores darks.
  • *
  • BURN - darker areas are applied, increasing contrast, * ignores lights.
  • *
* * @method blendMode * @param {Constant} mode blend mode to set for canvas. * either BLEND, DARKEST, LIGHTEST, DIFFERENCE, MULTIPLY, * EXCLUSION, SCREEN, REPLACE, OVERLAY, HARD_LIGHT, * SOFT_LIGHT, DODGE, BURN, ADD or NORMAL * @example *
* * blendMode(LIGHTEST); * strokeWeight(30); * stroke(80, 150, 255); * line(25, 25, 75, 75); * stroke(255, 50, 50); * line(75, 25, 25, 75); * *
*
* * blendMode(MULTIPLY); * strokeWeight(30); * stroke(80, 150, 255); * line(25, 25, 75, 75); * stroke(255, 50, 50); * line(75, 25, 25, 75); * *
* @alt * translucent image thick red & blue diagonal rounded lines intersecting center * Thick red & blue diagonal rounded lines intersecting center. dark at overlap * */ p5.prototype.blendMode = function(mode) { if (mode === constants.BLEND || mode === constants.DARKEST || mode === constants.LIGHTEST || mode === constants.DIFFERENCE || mode === constants.MULTIPLY || mode === constants.EXCLUSION || mode === constants.SCREEN || mode === constants.REPLACE || mode === constants.OVERLAY || mode === constants.HARD_LIGHT || mode === constants.SOFT_LIGHT || mode === constants.DODGE || mode === constants.BURN || mode === constants.ADD || mode === constants.NORMAL) { this._renderer.blendMode(mode); } else { throw new Error('Mode '+mode+' not recognized.'); } }; module.exports = p5; },{"../webgl/p5.RendererGL":30,"./constants":4,"./core":5,"./p5.Graphics":11,"./p5.Renderer2D":13}],15:[function(_dereq_,module,exports){ // requestAnim shim layer by Paul Irish window.requestAnimationFrame = (function(){ return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function(callback, element){ // should '60' here be framerate? window.setTimeout(callback, 1000 / 60); }; })(); // use window.performance() to get max fast and accurate time in milliseconds window.performance = window.performance || {}; window.performance.now = (function(){ var load_date = Date.now(); return window.performance.now || window.performance.mozNow || window.performance.msNow || window.performance.oNow || window.performance.webkitNow || function () { return Date.now() - load_date; }; })(); /* // http://paulirish.com/2011/requestanimationframe-for-smart-animating/ // http://my.opera.com/emoller/blog/2011/12/20/ // requestanimationframe-for-smart-er-animating // requestAnimationFrame polyfill by Erik Möller // fixes from Paul Irish and Tino Zijdel (function() { var lastTime = 0; var vendors = ['ms', 'moz', 'webkit', 'o']; for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) { window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame']; window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame'] || window[vendors[x]+'CancelRequestAnimationFrame']; } if (!window.requestAnimationFrame) { window.requestAnimationFrame = function(callback, element) { var currTime = new Date().getTime(); var timeToCall = Math.max(0, 16 - (currTime - lastTime)); var id = window.setTimeout(function() { callback(currTime + timeToCall); }, timeToCall); lastTime = currTime + timeToCall; return id; }; } if (!window.cancelAnimationFrame) { window.cancelAnimationFrame = function(id) { clearTimeout(id); }; } }()); */ /** * shim for Uint8ClampedArray.slice * (allows arrayCopy to work with pixels[]) * with thanks to http://halfpapstudios.com/blog/tag/html5-canvas/ * Enumerable set to false to protect for...in from * Uint8ClampedArray.prototype pollution. */ (function () { 'use strict'; if (typeof Uint8ClampedArray !== 'undefined' && !Uint8ClampedArray.prototype.slice) { Object.defineProperty(Uint8ClampedArray.prototype, 'slice', { value: Array.prototype.slice, writable: true, configurable: true, enumerable: false }); } }()); },{}],16:[function(_dereq_,module,exports){ /** * @module Structure * @submodule Structure * @for p5 * @requires core */ 'use strict'; var p5 = _dereq_('./core'); p5.prototype.exit = function() { throw 'exit() not implemented, see remove()'; }; /** * Stops p5.js from continuously executing the code within draw(). * If loop() is called, the code in draw() begins to run continuously again. * If using noLoop() in setup(), it should be the last line inside the block. *

* When noLoop() is used, it's not possible to manipulate or access the * screen inside event handling functions such as mousePressed() or * keyPressed(). Instead, use those functions to call redraw() or loop(), * which will run draw(), which can update the screen properly. This means * that when noLoop() has been called, no drawing can happen, and functions * like saveFrame() or loadPixels() may not be used. *

* Note that if the sketch is resized, redraw() will be called to update * the sketch, even after noLoop() has been specified. Otherwise, the sketch * would enter an odd state until loop() was called. * * @method noLoop * @example *
* function setup() { * createCanvas(100, 100); * background(200); * noLoop(); * } * function draw() { * line(10, 10, 90, 90); * } *
* *
* var x = 0; * function setup() { * createCanvas(100, 100); * } * * function draw() { * background(204); * x = x + 0.1; * if (x > width) { * x = 0; * } * line(x, 0, x, height); * } * * function mousePressed() { * noLoop(); * } * * function mouseReleased() { * loop(); * } *
* * @alt * 113 pixel long line extending from top-left to bottom right of canvas. * horizontal line moves slowly from left. Loops but stops on mouse press. * */ p5.prototype.noLoop = function() { this._loop = false; }; /** * By default, p5.js loops through draw() continuously, executing the code * within it. However, the draw() loop may be stopped by calling noLoop(). * In that case, the draw() loop can be resumed with loop(). * * @method loop * @example *
* var x = 0; * function setup() { * createCanvas(100, 100); * noLoop(); * } * * function draw() { * background(204); * x = x + 0.1; * if (x > width) { * x = 0; * } * line(x, 0, x, height); * } * * function mousePressed() { * loop(); * } * * function mouseReleased() { * noLoop(); * } *
* * @alt * horizontal line moves slowly from left. Loops but stops on mouse press. * */ p5.prototype.loop = function() { this._loop = true; this._draw(); }; /** * The push() function saves the current drawing style settings and * transformations, while pop() restores these settings. Note that these * functions are always used together. They allow you to change the style * and transformation settings and later return to what you had. When a new * state is started with push(), it builds on the current style and transform * information. The push() and pop() functions can be embedded to provide * more control. (See the second example for a demonstration.) *

* push() stores information related to the current transformation state * and style settings controlled by the following functions: fill(), * stroke(), tint(), strokeWeight(), strokeCap(), strokeJoin(), * imageMode(), rectMode(), ellipseMode(), colorMode(), textAlign(), * textFont(), textMode(), textSize(), textLeading(). * * @method push * @example *
* * ellipse(0, 50, 33, 33); // Left circle * * push(); // Start a new drawing state * strokeWeight(10); * fill(204, 153, 0); * translate(50, 0); * ellipse(0, 50, 33, 33); // Middle circle * pop(); // Restore original state * * ellipse(100, 50, 33, 33); // Right circle * *
*
* * ellipse(0, 50, 33, 33); // Left circle * * push(); // Start a new drawing state * strokeWeight(10); * fill(204, 153, 0); * ellipse(33, 50, 33, 33); // Left-middle circle * * push(); // Start another new drawing state * stroke(0, 102, 153); * ellipse(66, 50, 33, 33); // Right-middle circle * pop(); // Restore previous state * * pop(); // Restore original state * * ellipse(100, 50, 33, 33); // Right circle * *
* * @alt * Gold ellipse + thick black outline @center 2 white ellipses on left and right. * 2 Gold ellipses left black right blue stroke. 2 white ellipses on left+right. * */ p5.prototype.push = function () { this._renderer.push(); this._styles.push({ _doStroke: this._renderer._doStroke, _strokeSet: this._renderer._strokeSet, _doFill: this._renderer._doFill, _fillSet: this._renderer._fillSet, _tint: this._renderer._tint, _imageMode: this._renderer._imageMode, _rectMode: this._renderer._rectMode, _ellipseMode: this._renderer._ellipseMode, _colorMode: this._renderer._colorMode, _textFont: this._renderer._textFont, _textLeading: this._renderer._textLeading, _textSize: this._renderer._textSize, _textStyle: this._renderer._textStyle }); }; /** * The push() function saves the current drawing style settings and * transformations, while pop() restores these settings. Note that these * functions are always used together. They allow you to change the style * and transformation settings and later return to what you had. When a new * state is started with push(), it builds on the current style and transform * information. The push() and pop() functions can be embedded to provide * more control. (See the second example for a demonstration.) *

* push() stores information related to the current transformation state * and style settings controlled by the following functions: fill(), * stroke(), tint(), strokeWeight(), strokeCap(), strokeJoin(), * imageMode(), rectMode(), ellipseMode(), colorMode(), textAlign(), * textFont(), textMode(), textSize(), textLeading(). * * @method pop * @example *
* * ellipse(0, 50, 33, 33); // Left circle * * push(); // Start a new drawing state * translate(50, 0); * strokeWeight(10); * fill(204, 153, 0); * ellipse(0, 50, 33, 33); // Middle circle * pop(); // Restore original state * * ellipse(100, 50, 33, 33); // Right circle * *
*
* * ellipse(0, 50, 33, 33); // Left circle * * push(); // Start a new drawing state * strokeWeight(10); * fill(204, 153, 0); * ellipse(33, 50, 33, 33); // Left-middle circle * * push(); // Start another new drawing state * stroke(0, 102, 153); * ellipse(66, 50, 33, 33); // Right-middle circle * pop(); // Restore previous state * * pop(); // Restore original state * * ellipse(100, 50, 33, 33); // Right circle * *
* * @alt * Gold ellipse + thick black outline @center 2 white ellipses on left and right. * 2 Gold ellipses left black right blue stroke. 2 white ellipses on left+right. * */ p5.prototype.pop = function () { this._renderer.pop(); var lastS = this._styles.pop(); for(var prop in lastS){ this._renderer[prop] = lastS[prop]; } }; p5.prototype.pushStyle = function() { throw new Error('pushStyle() not used, see push()'); }; p5.prototype.popStyle = function() { throw new Error('popStyle() not used, see pop()'); }; /** * * Executes the code within draw() one time. This functions allows the * program to update the display window only when necessary, for example * when an event registered by mousePressed() or keyPressed() occurs. *

* In structuring a program, it only makes sense to call redraw() within * events such as mousePressed(). This is because redraw() does not run * draw() immediately (it only sets a flag that indicates an update is * needed). *

* The redraw() function does not work properly when called inside draw(). * To enable/disable animations, use loop() and noLoop(). *

* In addition you can set the number of redraws per method call. Just * add an integer as single parameter for the number of redraws. * * @method redraw * @param {Integer} [n] Redraw for n-times. The default value is 1. * @example *
* var x = 0; * * function setup() { * createCanvas(100, 100); * noLoop(); * } * * function draw() { * background(204); * line(x, 0, x, height); * } * * function mousePressed() { * x += 1; * redraw(); * } *
* *
* var x = 0; * * function setup() { * createCanvas(100, 100); * noLoop(); * } * * function draw() { * background(204); * x += 1; * line(x, 0, x, height); * } * * function mousePressed() { * redraw(5); * } *
* * @alt * black line on far left of canvas * black line on far left of canvas * */ p5.prototype.redraw = function () { this.resetMatrix(); if(this._renderer.isP3D){ this._renderer._update(); } var numberOfRedraws = 1; if (arguments.length === 1) { try { if (parseInt(arguments[0]) > 1) { numberOfRedraws = parseInt(arguments[0]); } } catch (error) { // Do nothing, because the default value didn't be changed. } } var userSetup = this.setup || window.setup; var userDraw = this.draw || window.draw; if (typeof userDraw === 'function') { if (typeof userSetup === 'undefined') { this.scale(this._pixelDensity, this._pixelDensity); } var self = this; var callMethod = function (f) { f.call(self); }; for (var idxRedraw = 0; idxRedraw < numberOfRedraws; idxRedraw++) { this._registeredMethods.pre.forEach(callMethod); userDraw(); this._registeredMethods.post.forEach(callMethod); } } }; p5.prototype.size = function() { var s = 'size() is not a valid p5 function, to set the size of the '; s += 'drawing canvas, please use createCanvas() instead'; throw s; }; module.exports = p5; },{"./core":5}],17:[function(_dereq_,module,exports){ /** * @module Transform * @submodule Transform * @for p5 * @requires core * @requires constants */ 'use strict'; var p5 = _dereq_('./core'); var constants = _dereq_('./constants'); /** * Multiplies the current matrix by the one specified through the parameters. * This is very slow because it will try to calculate the inverse of the * transform, so avoid it whenever possible. * * @method applyMatrix * @param {Number} n00 numbers which define the 3x2 matrix to be multiplied * @param {Number} n01 numbers which define the 3x2 matrix to be multiplied * @param {Number} n02 numbers which define the 3x2 matrix to be multiplied * @param {Number} n10 numbers which define the 3x2 matrix to be multiplied * @param {Number} n11 numbers which define the 3x2 matrix to be multiplied * @param {Number} n12 numbers which define the 3x2 matrix to be multiplied * @chainable * @example *
* * // Example in the works. * *
* * @alt * no image diplayed * */ p5.prototype.applyMatrix = function(n00, n01, n02, n10, n11, n12) { this._renderer.applyMatrix(n00, n01, n02, n10, n11, n12); return this; }; p5.prototype.popMatrix = function() { throw new Error('popMatrix() not used, see pop()'); }; p5.prototype.printMatrix = function() { throw new Error('printMatrix() not implemented'); }; p5.prototype.pushMatrix = function() { throw new Error('pushMatrix() not used, see push()'); }; /** * Replaces the current matrix with the identity matrix. * * @method resetMatrix * @chainable * @example *
* * // Example in the works. * *
* * @alt * no image diplayed * */ p5.prototype.resetMatrix = function() { this._renderer.resetMatrix(); return this; }; /** * Rotates a shape the amount specified by the angle parameter. This * function accounts for angleMode, so angles can be entered in either * RADIANS or DEGREES. *

* Objects are always rotated around their relative position to the * origin and positive numbers rotate objects in a clockwise direction. * Transformations apply to everything that happens after and subsequent * calls to the function accumulates the effect. For example, calling * rotate(HALF_PI) and then rotate(HALF_PI) is the same as rotate(PI). * All tranformations are reset when draw() begins again. *

* Technically, rotate() multiplies the current transformation matrix * by a rotation matrix. This function can be further controlled by * the push() and pop(). * * @method rotate * @param {Number} angle the angle of rotation, specified in radians * or degrees, depending on current angleMode * @param {p5.Vector|Array} [axis] (in 3d) the axis to rotate around * @chainable * @example *
* * translate(width/2, height/2); * rotate(PI/3.0); * rect(-26, -26, 52, 52); * *
* * @alt * white 52x52 rect with black outline at center rotated counter 45 degrees * */ p5.prototype.rotate = function(angle, axis) { var args = new Array(arguments.length); var r; for (var i = 0; i < args.length; ++i) { args[i] = arguments[i]; } if (this._angleMode === constants.DEGREES) { r = this.radians(args[0]); } else if (this._angleMode === constants.RADIANS){ r = args[0]; } //in webgl mode if(args.length > 1){ this._renderer.rotate(r, args[1]); } else { this._renderer.rotate(r); } return this; }; /** * Rotates around X axis. * @method rotateX * @param {Number} rad angles in radians * @chainable */ p5.prototype.rotateX = function(rad) { if (this._renderer.isP3D) { this._renderer.rotateX(rad); } else { throw 'not supported in p2d. Please use webgl mode'; } return this; }; /** * Rotates around Y axis. * @method rotateY * @param {Number} rad angles in radians * @chainable */ p5.prototype.rotateY = function(rad) { if (this._renderer.isP3D) { this._renderer.rotateY(rad); } else { throw 'not supported in p2d. Please use webgl mode'; } return this; }; /** * Rotates around Z axis. Webgl mode only. * @method rotateZ * @param {Number} rad angles in radians * @chainable */ p5.prototype.rotateZ = function(rad) { if (this._renderer.isP3D) { this._renderer.rotateZ(rad); } else { throw 'not supported in p2d. Please use webgl mode'; } return this; }; /** * Increases or decreases the size of a shape by expanding and contracting * vertices. Objects always scale from their relative origin to the * coordinate system. Scale values are specified as decimal percentages. * For example, the function call scale(2.0) increases the dimension of a * shape by 200%. *

* Transformations apply to everything that happens after and subsequent * calls to the function multiply the effect. For example, calling scale(2.0) * and then scale(1.5) is the same as scale(3.0). If scale() is called * within draw(), the transformation is reset when the loop begins again. *

* Using this function with the z parameter is only available in WEBGL mode. * This function can be further controlled with push() and pop(). * * @method scale * @param {Number|p5.Vector|Array} s * percent to scale the object, or percentage to * scale the object in the x-axis if multiple arguments * are given * @param {Number} [y] percent to scale the object in the y-axis * @param {Number} [z] percent to scale the object in the z-axis (webgl only) * @chainable * @example *
* * translate(width/2, height/2); * rotate(PI/3.0); * rect(-26, -26, 52, 52); * *
* *
* * rect(30, 20, 50, 50); * scale(0.5, 1.3); * rect(30, 20, 50, 50); * *
* * @alt * white 52x52 rect with black outline at center rotated counter 45 degrees * 2 white rects with black outline- 1 50x50 at center. other 25x65 bottom left * */ p5.prototype.scale = function() { var x,y,z; var args = new Array(arguments.length); for(var i = 0; i < args.length; i++) { args[i] = arguments[i]; } // Only check for Vector argument type if Vector is available if (typeof p5.Vector !== 'undefined') { if(args[0] instanceof p5.Vector){ x = args[0].x; y = args[0].y; z = args[0].z; } } else if(args[0] instanceof Array){ x = args[0][0]; y = args[0][1]; z = args[0][2] || 1; } else { if(args.length === 1){ x = y = z = args[0]; } else { x = args[0]; y = args[1]; z = args[2] || 1; } } if(this._renderer.isP3D){ this._renderer.scale.call(this._renderer, x,y,z); } else { this._renderer.scale.call(this._renderer, x,y); } return this; }; /** * Shears a shape around the x-axis the amount specified by the angle * parameter. Angles should be specified in the current angleMode. * Objects are always sheared around their relative position to the origin * and positive numbers shear objects in a clockwise direction. *

* Transformations apply to everything that happens after and subsequent * calls to the function accumulates the effect. For example, calling * shearX(PI/2) and then shearX(PI/2) is the same as shearX(PI). * If shearX() is called within the draw(), the transformation is reset when * the loop begins again. *

* Technically, shearX() multiplies the current transformation matrix by a * rotation matrix. This function can be further controlled by the * push() and pop() functions. * * @method shearX * @param {Number} angle angle of shear specified in radians or degrees, * depending on current angleMode * @chainable * @example *
* * translate(width/4, height/4); * shearX(PI/4.0); * rect(0, 0, 30, 30); * *
* * @alt * white irregular quadrilateral with black outline at top middle. * */ p5.prototype.shearX = function(angle) { if (this._angleMode === constants.DEGREES) { angle = this.radians(angle); } this._renderer.shearX(angle); return this; }; /** * Shears a shape around the y-axis the amount specified by the angle * parameter. Angles should be specified in the current angleMode. Objects * are always sheared around their relative position to the origin and * positive numbers shear objects in a clockwise direction. *

* Transformations apply to everything that happens after and subsequent * calls to the function accumulates the effect. For example, calling * shearY(PI/2) and then shearY(PI/2) is the same as shearY(PI). If * shearY() is called within the draw(), the transformation is reset when * the loop begins again. *

* Technically, shearY() multiplies the current transformation matrix by a * rotation matrix. This function can be further controlled by the * push() and pop() functions. * * @method shearY * @param {Number} angle angle of shear specified in radians or degrees, * depending on current angleMode * @chainable * @example *
* * translate(width/4, height/4); * shearY(PI/4.0); * rect(0, 0, 30, 30); * *
* * @alt * white irregular quadrilateral with black outline at middle bottom. * */ p5.prototype.shearY = function(angle) { if (this._angleMode === constants.DEGREES) { angle = this.radians(angle); } this._renderer.shearY(angle); return this; }; /** * Specifies an amount to displace objects within the display window. * The x parameter specifies left/right translation, the y parameter * specifies up/down translation. *

* Transformations are cumulative and apply to everything that happens after * and subsequent calls to the function accumulates the effect. For example, * calling translate(50, 0) and then translate(20, 0) is the same as * translate(70, 0). If translate() is called within draw(), the * transformation is reset when the loop begins again. This function can be * further controlled by using push() and pop(). * * @method translate * @param {Number} x left/right translation * @param {Number} y up/down translation * @param {Number} [z] forward/backward translation (webgl only) * @chainable * @example *
* * translate(30, 20); * rect(0, 0, 55, 55); * *
* *
* * rect(0, 0, 55, 55); // Draw rect at original 0,0 * translate(30, 20); * rect(0, 0, 55, 55); // Draw rect at new 0,0 * translate(14, 14); * rect(0, 0, 55, 55); // Draw rect at new 0,0 * *
* * @alt * white 55x55 rect with black outline at center right. * 3 white 55x55 rects with black outlines at top-l, center-r and bottom-r. * */ p5.prototype.translate = function(x, y, z) { if (this._renderer.isP3D) { this._renderer.translate(x, y, z); } else { this._renderer.translate(x, y); } return this; }; module.exports = p5; },{"./constants":4,"./core":5}],18:[function(_dereq_,module,exports){ /** * @module Shape * @submodule Vertex * @for p5 * @requires core * @requires constants */ 'use strict'; var p5 = _dereq_('./core'); var constants = _dereq_('./constants'); var shapeKind = null; var vertices = []; var contourVertices = []; var isBezier = false; var isCurve = false; var isQuadratic = false; var isContour = false; var isFirstContour = true; /** * Use the beginContour() and endContour() functions to create negative * shapes within shapes such as the center of the letter 'O'. beginContour() * begins recording vertices for the shape and endContour() stops recording. * The vertices that define a negative shape must "wind" in the opposite * direction from the exterior shape. First draw vertices for the exterior * clockwise order, then for internal shapes, draw vertices * shape in counter-clockwise. *

* These functions can only be used within a beginShape()/endShape() pair and * transformations such as translate(), rotate(), and scale() do not work * within a beginContour()/endContour() pair. It is also not possible to use * other shapes, such as ellipse() or rect() within. * * @method beginContour * @chainable * @example *
* * translate(50, 50); * stroke(255, 0, 0); * beginShape(); * // Exterior part of shape, clockwise winding * vertex(-40, -40); * vertex(40, -40); * vertex(40, 40); * vertex(-40, 40); * // Interior part of shape, counter-clockwise winding * beginContour(); * vertex(-20, -20); * vertex(-20, 20); * vertex(20, 20); * vertex(20, -20); * endContour(); * endShape(CLOSE); * *
* * @alt * white rect and smaller grey rect with red outlines in center of canvas. * */ p5.prototype.beginContour = function() { contourVertices = []; isContour = true; return this; }; /** * Using the beginShape() and endShape() functions allow creating more * complex forms. beginShape() begins recording vertices for a shape and * endShape() stops recording. The value of the kind parameter tells it which * types of shapes to create from the provided vertices. With no mode * specified, the shape can be any irregular polygon. *

* The parameters available for beginShape() are POINTS, LINES, TRIANGLES, * TRIANGLE_FAN, TRIANGLE_STRIP, QUADS, and QUAD_STRIP. After calling the * beginShape() function, a series of vertex() commands must follow. To stop * drawing the shape, call endShape(). Each shape will be outlined with the * current stroke color and filled with the fill color. *

* Transformations such as translate(), rotate(), and scale() do not work * within beginShape(). It is also not possible to use other shapes, such as * ellipse() or rect() within beginShape(). * * @method beginShape * @param {Constant} [kind] either POINTS, LINES, TRIANGLES, TRIANGLE_FAN * TRIANGLE_STRIP, QUADS, or QUAD_STRIP * @chainable * @example *
* * beginShape(); * vertex(30, 20); * vertex(85, 20); * vertex(85, 75); * vertex(30, 75); * endShape(CLOSE); * *
* *
* * // currently not working * beginShape(POINTS); * vertex(30, 20); * vertex(85, 20); * vertex(85, 75); * vertex(30, 75); * endShape(); * *
* *
* * beginShape(LINES); * vertex(30, 20); * vertex(85, 20); * vertex(85, 75); * vertex(30, 75); * endShape(); * *
* *
* * noFill(); * beginShape(); * vertex(30, 20); * vertex(85, 20); * vertex(85, 75); * vertex(30, 75); * endShape(); * *
* *
* * noFill(); * beginShape(); * vertex(30, 20); * vertex(85, 20); * vertex(85, 75); * vertex(30, 75); * endShape(CLOSE); * *
* *
* * beginShape(TRIANGLES); * vertex(30, 75); * vertex(40, 20); * vertex(50, 75); * vertex(60, 20); * vertex(70, 75); * vertex(80, 20); * endShape(); * *
* *
* * beginShape(TRIANGLE_STRIP); * vertex(30, 75); * vertex(40, 20); * vertex(50, 75); * vertex(60, 20); * vertex(70, 75); * vertex(80, 20); * vertex(90, 75); * endShape(); * *
* *
* * beginShape(TRIANGLE_FAN); * vertex(57.5, 50); * vertex(57.5, 15); * vertex(92, 50); * vertex(57.5, 85); * vertex(22, 50); * vertex(57.5, 15); * endShape(); * *
* *
* * beginShape(QUADS); * vertex(30, 20); * vertex(30, 75); * vertex(50, 75); * vertex(50, 20); * vertex(65, 20); * vertex(65, 75); * vertex(85, 75); * vertex(85, 20); * endShape(); * *
* *
* * beginShape(QUAD_STRIP); * vertex(30, 20); * vertex(30, 75); * vertex(50, 20); * vertex(50, 75); * vertex(65, 20); * vertex(65, 75); * vertex(85, 20); * vertex(85, 75); * endShape(); * *
* *
* * beginShape(); * vertex(20, 20); * vertex(40, 20); * vertex(40, 40); * vertex(60, 40); * vertex(60, 60); * vertex(20, 60); * endShape(CLOSE); * *
* @alt * white square-shape with black outline in middle-right of canvas. * 4 black points in a square shape in middle-right of canvas. * 2 horizontal black lines. In the top-right and bottom-right of canvas. * 3 line shape with horizontal on top, vertical in middle and horizontal bottom. * square line shape in middle-right of canvas. * 2 white triangle shapes mid-right canvas. left one pointing up and right down. * 5 horizontal interlocking and alternating white triangles in mid-right canvas. * 4 interlocking white triangles in 45 degree rotated square-shape. * 2 white rectangle shapes in mid-right canvas. Both 20x55. * 3 side-by-side white rectangles center rect is smaller in mid-right canvas. * Thick white l-shape with black outline mid-top-left of canvas. * */ p5.prototype.beginShape = function(kind) { if (kind === constants.POINTS || kind === constants.LINES || kind === constants.TRIANGLES || kind === constants.TRIANGLE_FAN || kind === constants.TRIANGLE_STRIP || kind === constants.QUADS || kind === constants.QUAD_STRIP) { shapeKind = kind; } else { shapeKind = null; } if(this._renderer.isP3D){ this._renderer.beginShape(kind); } else { vertices = []; contourVertices = []; } return this; }; /** * Specifies vertex coordinates for Bezier curves. Each call to * bezierVertex() defines the position of two control points and * one anchor point of a Bezier curve, adding a new segment to a * line or shape. *

* The first time bezierVertex() is used within a * beginShape() call, it must be prefaced with a call to vertex() * to set the first anchor point. This function must be used between * beginShape() and endShape() and only when there is no MODE * parameter specified to beginShape(). * * @method bezierVertex * @param {Number} x2 x-coordinate for the first control point * @param {Number} y2 y-coordinate for the first control point * @param {Number} x3 x-coordinate for the second control point * @param {Number} y3 y-coordinate for the second control point * @param {Number} x4 x-coordinate for the anchor point * @param {Number} y4 y-coordinate for the anchor point * @chainable * @example *
* * noFill(); * beginShape(); * vertex(30, 20); * bezierVertex(80, 0, 80, 75, 30, 75); * endShape(); * *
* *
* * beginShape(); * vertex(30, 20); * bezierVertex(80, 0, 80, 75, 30, 75); * bezierVertex(50, 80, 60, 25, 30, 20); * endShape(); * *
* * @alt * crescent-shaped line in middle of canvas. Points facing left. * white crescent shape in middle of canvas. Points facing left. * */ p5.prototype.bezierVertex = function(x2, y2, x3, y3, x4, y4) { if (vertices.length === 0) { throw 'vertex() must be used once before calling bezierVertex()'; } else { isBezier = true; var vert = []; for (var i = 0; i < arguments.length; i++) { vert[i] = arguments[i]; } vert.isVert = false; if (isContour) { contourVertices.push(vert); } else { vertices.push(vert); } } return this; }; /** * Specifies vertex coordinates for curves. This function may only * be used between beginShape() and endShape() and only when there * is no MODE parameter specified to beginShape(). *

* The first and last points in a series of curveVertex() lines will be used to * guide the beginning and end of a the curve. A minimum of four * points is required to draw a tiny curve between the second and * third points. Adding a fifth point with curveVertex() will draw * the curve between the second, third, and fourth points. The * curveVertex() function is an implementation of Catmull-Rom * splines. * * @method curveVertex * @param {Number} x x-coordinate of the vertex * @param {Number} y y-coordinate of the vertex * @chainable * @example *
* * noFill(); * beginShape(); * curveVertex(84, 91); * curveVertex(84, 91); * curveVertex(68, 19); * curveVertex(21, 17); * curveVertex(32, 100); * curveVertex(32, 100); * endShape(); * *
* * @alt * Upside-down u-shape line, mid canvas. left point extends beyond canvas view. * */ p5.prototype.curveVertex = function(x,y) { isCurve = true; this.vertex(x, y); return this; }; /** * Use the beginContour() and endContour() functions to create negative * shapes within shapes such as the center of the letter 'O'. beginContour() * begins recording vertices for the shape and endContour() stops recording. * The vertices that define a negative shape must "wind" in the opposite * direction from the exterior shape. First draw vertices for the exterior * clockwise order, then for internal shapes, draw vertices * shape in counter-clockwise. *

* These functions can only be used within a beginShape()/endShape() pair and * transformations such as translate(), rotate(), and scale() do not work * within a beginContour()/endContour() pair. It is also not possible to use * other shapes, such as ellipse() or rect() within. * * @method endContour * @chainable * @example *
* * translate(50, 50); * stroke(255, 0, 0); * beginShape(); * // Exterior part of shape, clockwise winding * vertex(-40, -40); * vertex(40, -40); * vertex(40, 40); * vertex(-40, 40); * // Interior part of shape, counter-clockwise winding * beginContour(); * vertex(-20, -20); * vertex(-20, 20); * vertex(20, 20); * vertex(20, -20); * endContour(); * endShape(CLOSE); * *
* * @alt * white rect and smaller grey rect with red outlines in center of canvas. * */ p5.prototype.endContour = function() { var vert = contourVertices[0].slice(); // copy all data vert.isVert = contourVertices[0].isVert; vert.moveTo = false; contourVertices.push(vert); // prevent stray lines with multiple contours if (isFirstContour) { vertices.push(vertices[0]); isFirstContour = false; } for (var i = 0; i < contourVertices.length; i++) { vertices.push(contourVertices[i]); } return this; }; /** * The endShape() function is the companion to beginShape() and may only be * called after beginShape(). When endshape() is called, all of image data * defined since the previous call to beginShape() is written into the image * buffer. The constant CLOSE as the value for the MODE parameter to close * the shape (to connect the beginning and the end). * * @method endShape * @param {Constant} [mode] use CLOSE to close the shape * @chainable * @example *
* * noFill(); * * beginShape(); * vertex(20, 20); * vertex(45, 20); * vertex(45, 80); * endShape(CLOSE); * * beginShape(); * vertex(50, 20); * vertex(75, 20); * vertex(75, 80); * endShape(); * *
* * @alt * Triangle line shape with smallest interior angle on bottom and upside-down L. * */ p5.prototype.endShape = function(mode) { if(this._renderer.isP3D){ this._renderer.endShape(mode, isCurve, isBezier, isQuadratic, isContour, shapeKind); }else{ if (vertices.length === 0) { return this; } if (!this._renderer._doStroke && !this._renderer._doFill) { return this; } var closeShape = mode === constants.CLOSE; // if the shape is closed, the first element is also the last element if (closeShape && !isContour) { vertices.push(vertices[0]); } this._renderer.endShape(mode, vertices, isCurve, isBezier, isQuadratic, isContour, shapeKind); // Reset some settings isCurve = false; isBezier = false; isQuadratic = false; isContour = false; isFirstContour = true; // If the shape is closed, the first element was added as last element. // We must remove it again to prevent the list of vertices from growing // over successive calls to endShape(CLOSE) if (closeShape) { vertices.pop(); } } return this; }; /** * Specifies vertex coordinates for quadratic Bezier curves. Each call to * quadraticVertex() defines the position of one control points and one * anchor point of a Bezier curve, adding a new segment to a line or shape. * The first time quadraticVertex() is used within a beginShape() call, it * must be prefaced with a call to vertex() to set the first anchor point. * This function must be used between beginShape() and endShape() and only * when there is no MODE parameter specified to beginShape(). * * @method quadraticVertex * @param {Number} cx x-coordinate for the control point * @param {Number} cy y-coordinate for the control point * @param {Number} x3 x-coordinate for the anchor point * @param {Number} y3 y-coordinate for the anchor point * @chainable * @example *
* * noFill(); * strokeWeight(4); * beginShape(); * vertex(20, 20); * quadraticVertex(80, 20, 50, 50); * endShape(); * *
* *
* * noFill(); * strokeWeight(4); * beginShape(); * vertex(20, 20); * quadraticVertex(80, 20, 50, 50); * quadraticVertex(20, 80, 80, 80); * vertex(80, 60); * endShape(); * *
* * @alt * arched-shaped black line with 4 pixel thick stroke weight. * backwards s-shaped black line with 4 pixel thick stroke weight. * */ p5.prototype.quadraticVertex = function(cx, cy, x3, y3) { //if we're drawing a contour, put the points into an // array for inside drawing if(this._contourInited) { var pt = {}; pt.x = cx; pt.y = cy; pt.x3 = x3; pt.y3 = y3; pt.type = constants.QUADRATIC; this._contourVertices.push(pt); return this; } if (vertices.length > 0) { isQuadratic = true; var vert = []; for (var i = 0; i < arguments.length; i++) { vert[i] = arguments[i]; } vert.isVert = false; if (isContour) { contourVertices.push(vert); } else { vertices.push(vert); } } else { throw 'vertex() must be used once before calling quadraticVertex()'; } return this; }; /** * All shapes are constructed by connecting a series of vertices. vertex() * is used to specify the vertex coordinates for points, lines, triangles, * quads, and polygons. It is used exclusively within the beginShape() and * endShape() functions. * * @method vertex * @param {Number} x x-coordinate of the vertex * @param {Number} y y-coordinate of the vertex * @param {Number|Boolean} [z] z-coordinate of the vertex * @chainable * @example *
* * beginShape(POINTS); * vertex(30, 20); * vertex(85, 20); * vertex(85, 75); * vertex(30, 75); * endShape(); * *
* * @alt * 4 black points in a square shape in middle-right of canvas. * */ p5.prototype.vertex = function(x, y, moveTo) { if(this._renderer.isP3D){ this._renderer.vertex(x, y, moveTo); }else{ var vert = []; vert.isVert = true; vert[0] = x; vert[1] = y; vert[2] = 0; vert[3] = 0; vert[4] = 0; vert[5] = this._renderer._getFill(); vert[6] = this._renderer._getStroke(); if (moveTo) { vert.moveTo = moveTo; } if (isContour) { if (contourVertices.length === 0) { vert.moveTo = true; } contourVertices.push(vert); } else { vertices.push(vert); } } return this; }; module.exports = p5; },{"./constants":4,"./core":5}],19:[function(_dereq_,module,exports){ /*global ImageData:false */ /** * This module defines the filters for use with image buffers. * * This module is basically a collection of functions stored in an object * as opposed to modules. The functions are destructive, modifying * the passed in canvas rather than creating a copy. * * Generally speaking users of this module will use the Filters.apply method * on a canvas to create an effect. * * A number of functions are borrowed/adapted from * http://www.html5rocks.com/en/tutorials/canvas/imagefilters/ * or the java processing implementation. */ 'use strict'; var Filters = {}; /* * Helper functions */ /** * Returns the pixel buffer for a canvas * * @private * * @param {Canvas|ImageData} canvas the canvas to get pixels from * @return {Uint8ClampedArray} a one-dimensional array containing * the data in thc RGBA order, with integer * values between 0 and 255 */ Filters._toPixels = function (canvas) { if (canvas instanceof ImageData) { return canvas.data; } else { return canvas.getContext('2d').getImageData( 0, 0, canvas.width, canvas.height ).data; } }; /** * Returns a 32 bit number containing ARGB data at ith pixel in the * 1D array containing pixels data. * * @private * * @param {Uint8ClampedArray} data array returned by _toPixels() * @param {Integer} i index of a 1D Image Array * @return {Integer} 32 bit integer value representing * ARGB value. */ Filters._getARGB = function (data, i) { var offset = i * 4; return (data[offset+3] << 24) & 0xff000000 | (data[offset] << 16) & 0x00ff0000 | (data[offset+1] << 8) & 0x0000ff00 | data[offset+2] & 0x000000ff; }; /** * Modifies pixels RGBA values to values contained in the data object. * * @private * * @param {Uint8ClampedArray} pixels array returned by _toPixels() * @param {Int32Array} data source 1D array where each value * represents ARGB values */ Filters._setPixels = function (pixels, data) { var offset = 0; for( var i = 0, al = pixels.length; i < al; i++) { offset = i*4; pixels[offset + 0] = (data[i] & 0x00ff0000)>>>16; pixels[offset + 1] = (data[i] & 0x0000ff00)>>>8; pixels[offset + 2] = (data[i] & 0x000000ff); pixels[offset + 3] = (data[i] & 0xff000000)>>>24; } }; /** * Returns the ImageData object for a canvas * https://developer.mozilla.org/en-US/docs/Web/API/ImageData * * @private * * @param {Canvas|ImageData} canvas canvas to get image data from * @return {ImageData} Holder of pixel data (and width and * height) for a canvas */ Filters._toImageData = function (canvas) { if (canvas instanceof ImageData) { return canvas; } else { return canvas.getContext('2d').getImageData( 0, 0, canvas.width, canvas.height ); } }; /** * Returns a blank ImageData object. * * @private * * @param {Integer} width * @param {Integer} height * @return {ImageData} */ Filters._createImageData = function (width, height) { Filters._tmpCanvas = document.createElement('canvas'); Filters._tmpCtx = Filters._tmpCanvas.getContext('2d'); return this._tmpCtx.createImageData(width, height); }; /** * Applys a filter function to a canvas. * * The difference between this and the actual filter functions defined below * is that the filter functions generally modify the pixel buffer but do * not actually put that data back to the canvas (where it would actually * update what is visible). By contrast this method does make the changes * actually visible in the canvas. * * The apply method is the method that callers of this module would generally * use. It has been separated from the actual filters to support an advanced * use case of creating a filter chain that executes without actually updating * the canvas in between everystep. * * @param {HTMLCanvasElement} canvas [description] * @param {function(ImageData,Object)} func [description] * @param {Object} filterParam [description] */ Filters.apply = function (canvas, func, filterParam) { var ctx = canvas.getContext('2d'); var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); //Filters can either return a new ImageData object, or just modify //the one they received. var newImageData = func(imageData, filterParam); if (newImageData instanceof ImageData) { ctx.putImageData(newImageData, 0, 0, 0, 0, canvas.width, canvas.height); } else { ctx.putImageData(imageData, 0, 0, 0, 0, canvas.width, canvas.height); } }; /* * Filters */ /** * Converts the image to black and white pixels depending if they are above or * below the threshold defined by the level parameter. The parameter must be * between 0.0 (black) and 1.0 (white). If no level is specified, 0.5 is used. * * Borrowed from http://www.html5rocks.com/en/tutorials/canvas/imagefilters/ * * @param {Canvas} canvas * @param {Float} level */ Filters.threshold = function (canvas, level) { var pixels = Filters._toPixels(canvas); if (level === undefined) { level = 0.5; } var thresh = Math.floor(level * 255); for (var i = 0; i < pixels.length; i += 4) { var r = pixels[i]; var g = pixels[i + 1]; var b = pixels[i + 2]; var gray = (0.2126 * r + 0.7152 * g + 0.0722 * b); var val; if (gray >= thresh) { val = 255; } else { val = 0; } pixels[i] = pixels[i + 1] = pixels[i + 2] = val; } }; /** * Converts any colors in the image to grayscale equivalents. * No parameter is used. * * Borrowed from http://www.html5rocks.com/en/tutorials/canvas/imagefilters/ * * @param {Canvas} canvas */ Filters.gray = function (canvas) { var pixels = Filters._toPixels(canvas); for (var i = 0; i < pixels.length; i += 4) { var r = pixels[i]; var g = pixels[i + 1]; var b = pixels[i + 2]; // CIE luminance for RGB var gray = (0.2126 * r + 0.7152 * g + 0.0722 * b); pixels[i] = pixels[i + 1] = pixels[i + 2] = gray; } }; /** * Sets the alpha channel to entirely opaque. No parameter is used. * * @param {Canvas} canvas */ Filters.opaque = function (canvas) { var pixels = Filters._toPixels(canvas); for (var i = 0; i < pixels.length; i += 4) { pixels[i + 3] = 255; } return pixels; }; /** * Sets each pixel to its inverse value. No parameter is used. * @param {Canvas} canvas */ Filters.invert = function (canvas) { var pixels = Filters._toPixels(canvas); for (var i = 0; i < pixels.length; i += 4) { pixels[i] = 255 - pixels[i]; pixels[i + 1] = 255 - pixels[i + 1]; pixels[i + 2] = 255 - pixels[i + 2]; } }; /** * Limits each channel of the image to the number of colors specified as * the parameter. The parameter can be set to values between 2 and 255, but * results are most noticeable in the lower ranges. * * Adapted from java based processing implementation * * @param {Canvas} canvas * @param {Integer} level */ Filters.posterize = function (canvas, level) { var pixels = Filters._toPixels(canvas); if ((level < 2) || (level > 255)) { throw new Error( 'Level must be greater than 2 and less than 255 for posterize' ); } var levels1 = level - 1; for (var i = 0; i < pixels.length; i+=4) { var rlevel = pixels[i]; var glevel = pixels[i + 1]; var blevel = pixels[i + 2]; pixels[i] = (((rlevel * level) >> 8) * 255) / levels1; pixels[i + 1] = (((glevel * level) >> 8) * 255) / levels1; pixels[i + 2] = (((blevel * level) >> 8) * 255) / levels1; } }; /** * reduces the bright areas in an image * @param {Canvas} canvas * */ Filters.dilate = function (canvas) { var pixels = Filters._toPixels(canvas); var currIdx = 0; var maxIdx = pixels.length ? pixels.length/4 : 0; var out = new Int32Array(maxIdx); var currRowIdx, maxRowIdx, colOrig, colOut, currLum; var idxRight, idxLeft, idxUp, idxDown, colRight, colLeft, colUp, colDown, lumRight, lumLeft, lumUp, lumDown; while(currIdx < maxIdx) { currRowIdx = currIdx; maxRowIdx = currIdx + canvas.width; while (currIdx < maxRowIdx) { colOrig = colOut = Filters._getARGB(pixels, currIdx); idxLeft = currIdx - 1; idxRight = currIdx + 1; idxUp = currIdx - canvas.width; idxDown = currIdx + canvas.width; if (idxLeft < currRowIdx) { idxLeft = currIdx; } if (idxRight >= maxRowIdx) { idxRight = currIdx; } if (idxUp < 0){ idxUp = 0; } if (idxDown >= maxIdx) { idxDown = currIdx; } colUp = Filters._getARGB(pixels, idxUp); colLeft = Filters._getARGB(pixels, idxLeft); colDown = Filters._getARGB(pixels, idxDown); colRight = Filters._getARGB(pixels, idxRight); //compute luminance currLum = 77*(colOrig>>16&0xff) + 151*(colOrig>>8&0xff) + 28*(colOrig&0xff); lumLeft = 77*(colLeft>>16&0xff) + 151*(colLeft>>8&0xff) + 28*(colLeft&0xff); lumRight = 77*(colRight>>16&0xff) + 151*(colRight>>8&0xff) + 28*(colRight&0xff); lumUp = 77*(colUp>>16&0xff) + 151*(colUp>>8&0xff) + 28*(colUp&0xff); lumDown = 77*(colDown>>16&0xff) + 151*(colDown>>8&0xff) + 28*(colDown&0xff); if (lumLeft > currLum) { colOut = colLeft; currLum = lumLeft; } if (lumRight > currLum) { colOut = colRight; currLum = lumRight; } if (lumUp > currLum) { colOut = colUp; currLum = lumUp; } if (lumDown > currLum) { colOut = colDown; currLum = lumDown; } out[currIdx++]=colOut; } } Filters._setPixels(pixels, out); }; /** * increases the bright areas in an image * @param {Canvas} canvas * */ Filters.erode = function(canvas) { var pixels = Filters._toPixels(canvas); var currIdx = 0; var maxIdx = pixels.length ? pixels.length/4 : 0; var out = new Int32Array(maxIdx); var currRowIdx, maxRowIdx, colOrig, colOut, currLum; var idxRight, idxLeft, idxUp, idxDown, colRight, colLeft, colUp, colDown, lumRight, lumLeft, lumUp, lumDown; while(currIdx < maxIdx) { currRowIdx = currIdx; maxRowIdx = currIdx + canvas.width; while (currIdx < maxRowIdx) { colOrig = colOut = Filters._getARGB(pixels, currIdx); idxLeft = currIdx - 1; idxRight = currIdx + 1; idxUp = currIdx - canvas.width; idxDown = currIdx + canvas.width; if (idxLeft < currRowIdx) { idxLeft = currIdx; } if (idxRight >= maxRowIdx) { idxRight = currIdx; } if (idxUp < 0) { idxUp = 0; } if (idxDown >= maxIdx) { idxDown = currIdx; } colUp = Filters._getARGB(pixels, idxUp); colLeft = Filters._getARGB(pixels, idxLeft); colDown = Filters._getARGB(pixels, idxDown); colRight = Filters._getARGB(pixels, idxRight); //compute luminance currLum = 77*(colOrig>>16&0xff) + 151*(colOrig>>8&0xff) + 28*(colOrig&0xff); lumLeft = 77*(colLeft>>16&0xff) + 151*(colLeft>>8&0xff) + 28*(colLeft&0xff); lumRight = 77*(colRight>>16&0xff) + 151*(colRight>>8&0xff) + 28*(colRight&0xff); lumUp = 77*(colUp>>16&0xff) + 151*(colUp>>8&0xff) + 28*(colUp&0xff); lumDown = 77*(colDown>>16&0xff) + 151*(colDown>>8&0xff) + 28*(colDown&0xff); if (lumLeft < currLum) { colOut = colLeft; currLum = lumLeft; } if (lumRight < currLum) { colOut = colRight; currLum = lumRight; } if (lumUp < currLum) { colOut = colUp; currLum = lumUp; } if (lumDown < currLum) { colOut = colDown; currLum = lumDown; } out[currIdx++]=colOut; } } Filters._setPixels(pixels, out); }; // BLUR // internal kernel stuff for the gaussian blur filter var blurRadius; var blurKernelSize; var blurKernel; var blurMult; /* * Port of https://github.com/processing/processing/blob/ * master/core/src/processing/core/PImage.java#L1250 * * Optimized code for building the blur kernel. * further optimized blur code (approx. 15% for radius=20) * bigger speed gains for larger radii (~30%) * added support for various image types (ALPHA, RGB, ARGB) * [toxi 050728] */ function buildBlurKernel(r) { var radius = (r * 3.5)|0; radius = (radius < 1) ? 1 : ((radius < 248) ? radius : 248); if (blurRadius !== radius) { blurRadius = radius; blurKernelSize = 1 + blurRadius<<1; blurKernel = new Int32Array(blurKernelSize); blurMult = new Array(blurKernelSize); for(var l = 0; l < blurKernelSize; l++){ blurMult[l] = new Int32Array(256); } var bk,bki; var bm,bmi; for (var i = 1, radiusi = radius - 1; i < radius; i++) { blurKernel[radius+i] = blurKernel[radiusi] = bki = radiusi * radiusi; bm = blurMult[radius+i]; bmi = blurMult[radiusi--]; for (var j = 0; j < 256; j++){ bm[j] = bmi[j] = bki * j; } } bk = blurKernel[radius] = radius * radius; bm = blurMult[radius]; for (var k = 0; k < 256; k++){ bm[k] = bk * k; } } } // Port of https://github.com/processing/processing/blob/ // master/core/src/processing/core/PImage.java#L1433 function blurARGB(canvas, radius) { var pixels = Filters._toPixels(canvas); var width = canvas.width; var height = canvas.height; var numPackedPixels = width * height; var argb = new Int32Array(numPackedPixels); for (var j = 0; j < numPackedPixels; j++) { argb[j] = Filters._getARGB(pixels, j); } var sum, cr, cg, cb, ca; var read, ri, ym, ymi, bk0; var a2 = new Int32Array(numPackedPixels); var r2 = new Int32Array(numPackedPixels); var g2 = new Int32Array(numPackedPixels); var b2 = new Int32Array(numPackedPixels); var yi = 0; buildBlurKernel(radius); var x, y, i; var bm; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { cb = cg = cr = ca = sum = 0; read = x - blurRadius; if (read < 0) { bk0 = -read; read = 0; } else { if (read >= width) { break; } bk0 = 0; } for (i = bk0; i < blurKernelSize; i++) { if (read >= width) { break; } var c = argb[read + yi]; bm = blurMult[i]; ca += bm[(c & -16777216) >>> 24]; cr += bm[(c & 16711680) >> 16]; cg += bm[(c & 65280) >> 8]; cb += bm[c & 255]; sum += blurKernel[i]; read++; } ri = yi + x; a2[ri] = ca / sum; r2[ri] = cr / sum; g2[ri] = cg / sum; b2[ri] = cb / sum; } yi += width; } yi = 0; ym = -blurRadius; ymi = ym * width; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { cb = cg = cr = ca = sum = 0; if (ym < 0) { bk0 = ri = -ym; read = x; } else { if (ym >= height) { break; } bk0 = 0; ri = ym; read = x + ymi; } for (i = bk0; i < blurKernelSize; i++) { if (ri >= height) { break; } bm = blurMult[i]; ca += bm[a2[read]]; cr += bm[r2[read]]; cg += bm[g2[read]]; cb += bm[b2[read]]; sum += blurKernel[i]; ri++; read += width; } argb[x + yi] = (ca/sum)<<24 | (cr/sum)<<16 | (cg/sum)<<8 | (cb/sum); } yi += width; ymi += width; ym++; } Filters._setPixels(pixels, argb); } Filters.blur = function(canvas, radius){ blurARGB(canvas, radius); }; module.exports = Filters; },{}],20:[function(_dereq_,module,exports){ module.exports = { degreesToRadians: function(x) { return 2 * Math.PI * x / 360; }, radiansToDegrees: function(x) { return 360 * x / (2 * Math.PI); } }; },{}],21:[function(_dereq_,module,exports){ /** * @module Lights, Camera * @submodule Camera * @for p5 * @requires core */ 'use strict'; var p5 = _dereq_('../core/core'); /** * Sets camera position * @method camera * @param {Number} x camera position value on x axis * @param {Number} y camera position value on y axis * @param {Number} z camera position value on z axis * @return {p5} the p5 object * @example *
* * function setup(){ * createCanvas(100, 100, WEBGL); * } * function draw(){ * //move the camera away from the plane by a sin wave * camera(0, 0, sin(frameCount * 0.01) * 100); * plane(120, 120); * } * *
* * @alt * blue square shrinks in size grows to fill canvas. disappears then loops. * */ p5.prototype.camera = function(x, y, z){ //what it manipulates is the model view matrix this._renderer.translate(-x, -y, -z); }; /** * Sets perspective camera * @method perspective * @param {Number} fovy camera frustum vertical field of view, * from bottom to top of view, in degrees * @param {Number} aspect camera frustum aspect ratio * @param {Number} near frustum near plane length * @param {Number} far frustum far plane length * @return {p5} the p5 object * @example *
* * //drag mouse to toggle the world! * //you will see there's a vanish point * function setup(){ * createCanvas(100, 100, WEBGL); * var fov = 60 / 180 * PI; * var cameraZ = (height/2.0) / tan(fov/2.0); * perspective(60 / 180 * PI, width/height, cameraZ * 0.1, cameraZ * 10); * } * function draw(){ * background(200); * orbitControl(); * for(var i = -1; i < 2; i++){ * for(var j = -2; j < 3; j++){ * push(); * translate(i*160, 0, j*160); * box(40, 40, 40); * pop(); * } * } * } * *
* * @alt * colored 3d boxes toggleable with mouse position * */ p5.prototype.perspective = function(fovy,aspect,near,far) { fovy = fovy || (60 / 180 * this.PI); aspect = aspect || (this.width/this.height); near = near || ((this.height/2.0) / this.tan(fovy/2.0) * 0.1); far = far || ((this.height/2.0) / this.tan(fovy/2.0) * 10); this._renderer.uPMatrix = p5.Matrix.identity(); this._renderer.uPMatrix.perspective(fovy,aspect,near,far); this._renderer._curCamera = 'custom'; }; /** * Setup ortho camera * @method ortho * @param {Number} left camera frustum left plane * @param {Number} right camera frustum right plane * @param {Number} bottom camera frustum bottom plane * @param {Number} top camera frustum top plane * @param {Number} near camera frustum near plane * @param {Number} far camera frustum far plane * @return {p5} the p5 object * @example *
* * //drag mouse to toggle the world! * //there's no vanish point * function setup(){ * createCanvas(100, 100, WEBGL); * ortho(-width/2, width/2, height/2, -height/2, 0, 500); * } * function draw(){ * background(200); * orbitControl(); * for(var i = -1; i < 2; i++){ * for(var j = -2; j < 3; j++){ * push(); * translate(i*160, 0, j*160); * box(40, 40, 40); * pop(); * } * } * } * *
* * @alt * 3 3d boxes, reveal several more boxes on 3d plane when mouse used to toggle * */ p5.prototype.ortho = function(left,right,bottom,top,near,far) { left = left || (-this.width/2); right = right || (this.width/2); bottom = bottom || (-this.height/2); top = top || (this.height/2); near = near || 0; far = far || Math.max(this.width, this.height); this._renderer.uPMatrix = p5.Matrix.identity(); this._renderer.uPMatrix.ortho(left,right,bottom,top,near,far); this._renderer._curCamera = 'custom'; }; module.exports = p5; },{"../core/core":5}],22:[function(_dereq_,module,exports){ 'use strict'; var p5 = _dereq_('../core/core'); //@TODO: implement full orbit controls including //pan, zoom, quaternion rotation, etc. p5.prototype.orbitControl = function(){ if(this.mouseIsPressed){ this.rotateY((this.mouseX - this.width / 2) / (this.width / 2)); this.rotateX((this.mouseY - this.height / 2) / (this.width / 2)); } return this; }; module.exports = p5; },{"../core/core":5}],23:[function(_dereq_,module,exports){ /** * @module Lights, Camera * @submodule Lights * @for p5 * @requires core */ 'use strict'; var p5 = _dereq_('../core/core'); /** * Creates an ambient light with a color * * @method ambientLight * @param {Number} v1 red or hue value relative to * the current color range * @param {Number} v2 green or saturation value * relative to the current color range * @param {Number} v3 blue or brightness value * relative to the current color range * @param {Number} [alpha] * @chainable */ /** * @method ambientLight * @param {String} value a color string * @param {Number} [alpha] * @chainable */ /** * @method ambientLight * @param {Number[]} values an array containing the red,green,blue & * and alpha components of the color * @chainable */ /** * @method ambientLight * @param {p5.Color} color the ambient light color * @param {Number} [alpha] * @chainable * * @example *
* * function setup(){ * createCanvas(100, 100, WEBGL); * } * function draw(){ * background(0); * ambientLight(150); * ambientMaterial(250); * sphere(50); * } * *
* * @alt * nothing displayed * */ p5.prototype.ambientLight = function(v1, v2, v3, a){ var gl = this._renderer.GL; var shaderProgram = this._renderer._getShader( 'lightVert', 'lightTextureFrag'); gl.useProgram(shaderProgram); shaderProgram.uAmbientColor = gl.getUniformLocation( shaderProgram, 'uAmbientColor[' + this._renderer.ambientLightCount + ']'); var color = this._renderer._pInst.color.apply( this._renderer._pInst, arguments); var colors = color._array; gl.uniform3f( shaderProgram.uAmbientColor, colors[0], colors[1], colors[2]); //in case there's no material color for the geometry shaderProgram.uMaterialColor = gl.getUniformLocation( shaderProgram, 'uMaterialColor' ); gl.uniform4f( shaderProgram.uMaterialColor, 1, 1, 1, 1); this._renderer.ambientLightCount ++; shaderProgram.uAmbientLightCount = gl.getUniformLocation(shaderProgram, 'uAmbientLightCount'); gl.uniform1i(shaderProgram.uAmbientLightCount, this._renderer.ambientLightCount); return this; }; /** * Creates a directional light with a color and a direction * @method directionalLight * @param {Number|Array|String|p5.Color} v1 gray value, * red or hue value (depending on the current color mode), * or color Array, or CSS color string * @param {Number} [v2] optional: green or saturation value * @param {Number} [v3] optional: blue or brightness value * @param {Number} [a] optional: opacity * @param {Number|p5.Vector} x x axis direction or a p5.Vector * @param {Number} [y] optional: y axis direction * @param {Number} [z] optional: z axis direction * @chainable * @example *
* * function setup(){ * createCanvas(100, 100, WEBGL); * } * function draw(){ * background(0); * //move your mouse to change light direction * var dirX = (mouseX / width - 0.5) *2; * var dirY = (mouseY / height - 0.5) *(-2); * directionalLight(250, 250, 250, dirX, dirY, 0.25); * ambientMaterial(250); * sphere(50); * } * *
* * @alt * light source on canvas changeable with mouse position * */ p5.prototype.directionalLight = function(v1, v2, v3, a, x, y, z) { var gl = this._renderer.GL; var shaderProgram = this._renderer._getShader( 'lightVert', 'lightTextureFrag'); gl.useProgram(shaderProgram); shaderProgram.uDirectionalColor = gl.getUniformLocation( shaderProgram, 'uDirectionalColor[' + this._renderer.directionalLightCount + ']'); //@TODO: check parameters number var color = this._renderer._pInst.color.apply( this._renderer._pInst, [v1, v2, v3]); var colors = color._array; gl.uniform3f( shaderProgram.uDirectionalColor, colors[0], colors[1], colors[2]); var _x, _y, _z; var args = new Array(arguments.length); for (var i = 0; i < args.length; ++i) { args[i] = arguments[i]; } if(typeof args[args.length-1] === 'number'){ _x = args[args.length-3]; _y = args[args.length-2]; _z = args[args.length-1]; }else{ try{ _x = args[args.length-1].x; _y = args[args.length-1].y; _z = args[args.length-1].z; } catch(error){ throw error; } } shaderProgram.uLightingDirection = gl.getUniformLocation( shaderProgram, 'uLightingDirection[' + this._renderer.directionalLightCount + ']'); gl.uniform3f( shaderProgram.uLightingDirection, _x, _y, _z); //in case there's no material color for the geometry shaderProgram.uMaterialColor = gl.getUniformLocation( shaderProgram, 'uMaterialColor' ); gl.uniform4f( shaderProgram.uMaterialColor, 1, 1, 1, 1); this._renderer.directionalLightCount ++; shaderProgram.uDirectionalLightCount = gl.getUniformLocation(shaderProgram, 'uDirectionalLightCount'); gl.uniform1i(shaderProgram.uDirectionalLightCount, this._renderer.directionalLightCount); return this; }; /** * Creates a point light with a color and a light position * @method pointLight * @param {Number|Array|String|p5.Color} v1 gray value, * red or hue value (depending on the current color mode), * or color Array, or CSS color string * @param {Number} [v2] optional: green or saturation value * @param {Number} [v3] optional: blue or brightness value * @param {Number} [a] optional: opacity * @param {Number|p5.Vector} x x axis position or a p5.Vector * @param {Number} [y] optional: y axis position * @param {Number} [z] optional: z axis position * @chainable * @example *
* * function setup(){ * createCanvas(100, 100, WEBGL); * } * function draw(){ * background(0); * //move your mouse to change light position * var locY = (mouseY / height - 0.5) *(-2); * var locX = (mouseX / width - 0.5) *2; * //to set the light position, * //think of the world's coordinate as: * // -1,1 -------- 1,1 * // | | * // | | * // | | * // -1,-1---------1,-1 * pointLight(250, 250, 250, locX, locY, 0); * ambientMaterial(250); * sphere(50); * } * *
* * @alt * spot light on canvas changes position with mouse * */ p5.prototype.pointLight = function(v1, v2, v3, a, x, y, z) { var gl = this._renderer.GL; var shaderProgram = this._renderer._getShader( 'lightVert', 'lightTextureFrag'); gl.useProgram(shaderProgram); shaderProgram.uPointLightColor = gl.getUniformLocation( shaderProgram, 'uPointLightColor[' + this._renderer.pointLightCount + ']'); //@TODO: check parameters number var color = this._renderer._pInst.color.apply( this._renderer._pInst, [v1, v2, v3]); var colors = color._array; gl.uniform3f( shaderProgram.uPointLightColor, colors[0], colors[1], colors[2]); var _x, _y, _z; var args = new Array(arguments.length); for (var i = 0; i < args.length; ++i) { args[i] = arguments[i]; } if(typeof args[args.length-1] === 'number'){ _x = args[args.length-3]; _y = args[args.length-2]; _z = args[args.length-1]; }else{ try{ _x = args[args.length-1].x; _y = args[args.length-1].y; _z = args[args.length-1].z; } catch(error){ throw error; } } shaderProgram.uPointLightLocation = gl.getUniformLocation( shaderProgram, 'uPointLightLocation[' + this._renderer.pointLightCount + ']'); gl.uniform3f( shaderProgram.uPointLightLocation, _x, _y, _z); //in case there's no material color for the geometry shaderProgram.uMaterialColor = gl.getUniformLocation( shaderProgram, 'uMaterialColor' ); gl.uniform4f( shaderProgram.uMaterialColor, 1, 1, 1, 1); this._renderer.pointLightCount ++; shaderProgram.uPointLightCount = gl.getUniformLocation(shaderProgram, 'uPointLightCount'); gl.uniform1i(shaderProgram.uPointLightCount, this._renderer.pointLightCount); return this; }; module.exports = p5; },{"../core/core":5}],24:[function(_dereq_,module,exports){ /** * @module Shape * @submodule 3D Models * @for p5 * @requires core * @requires p5.Geometry */ 'use strict'; var p5 = _dereq_('../core/core'); _dereq_('./p5.Geometry'); /** * Load a 3d model from an OBJ file. *

* One of the limitations of the OBJ format is that it doesn't have a built-in * sense of scale. This means that models exported from different programs might * be very different sizes. If your model isn't displaying, try calling * loadModel() with the normalized parameter set to true. This will resize the * model to a scale appropriate for p5. You can also make additional changes to * the final size of your model with the scale() function. * * @method loadModel * @param {String} path Path of the model to be loaded * @param {Boolean} normalize If true, scale the model to a * standardized size when loading * @param {function(p5.Geometry)} [successCallback] Function to be called * once the model is loaded. Will be passed * the 3D model object. * @param {function(Event)} [failureCallback] called with event error if * the image fails to load. * @return {p5.Geometry} the p5.Geometry object */ /** * @method loadModel * @param {String} path * @param {function(p5.Geometry)} [successCallback] * @param {function(Event)} [failureCallback] * @return {p5.Geometry} the p5.Geometry object * @example *
* * //draw a spinning teapot * var teapot; * * function setup(){ * createCanvas(100, 100, WEBGL); * * teapot = loadModel('assets/teapot.obj'); * } * * function draw(){ * background(200); * rotateX(frameCount * 0.01); * rotateY(frameCount * 0.01); * model(teapot); * } * *
* * @alt * Vertically rotating 3-d teapot with red, green and blue gradient. * */ p5.prototype.loadModel = function () { var path = arguments[0]; var normalize; var successCallback; var failureCallback; if(typeof arguments[1] === 'boolean') { normalize = arguments[1]; successCallback = arguments[2]; failureCallback = arguments[3]; } else { normalize = false; successCallback = arguments[1]; failureCallback = arguments[2]; } var model = new p5.Geometry(); model.gid = path + '|' + normalize; this.loadStrings(path, function(strings) { parseObj(model, strings); if (normalize) { model.normalize(); } if (typeof successCallback === 'function') { successCallback(model); } }.bind(this), failureCallback); return model; }; /** * Parse OBJ lines into model. For reference, this is what a simple model of a * square might look like: * * v -0.5 -0.5 0.5 * v -0.5 -0.5 -0.5 * v -0.5 0.5 -0.5 * v -0.5 0.5 0.5 * * f 4 3 2 1 */ function parseObj( model, lines ) { // OBJ allows a face to specify an index for a vertex (in the above example), // but it also allows you to specify a custom combination of vertex, UV // coordinate, and vertex normal. So, "3/4/3" would mean, "use vertex 3 with // UV coordinate 4 and vertex normal 3". In WebGL, every vertex with different // parameters must be a different vertex, so loadedVerts is used to // temporarily store the parsed vertices, normals, etc., and indexedVerts is // used to map a specific combination (keyed on, for example, the string // "3/4/3"), to the actual index of the newly created vertex in the final // object. var loadedVerts = {'v' : [], 'vt' : [], 'vn' : []}; var indexedVerts = {}; for (var line = 0; line < lines.length; ++line) { // Each line is a separate object (vertex, face, vertex normal, etc) // For each line, split it into tokens on whitespace. The first token // describes the type. var tokens = lines[line].trim().split(/\b\s+/); if (tokens.length > 0) { if (tokens[0] === 'v' || tokens[0] === 'vn') { // Check if this line describes a vertex or vertex normal. // It will have three numeric parameters. var vertex = new p5.Vector(parseFloat(tokens[1]), parseFloat(tokens[2]), parseFloat(tokens[3])); loadedVerts[tokens[0]].push(vertex); } else if (tokens[0] === 'vt') { // Check if this line describes a texture coordinate. // It will have two numeric parameters. var texVertex = [parseFloat(tokens[1]), parseFloat(tokens[2])]; loadedVerts[tokens[0]].push(texVertex); } else if (tokens[0] === 'f') { // Check if this line describes a face. // OBJ faces can have more than three points. Triangulate points. for (var tri = 3; tri < tokens.length; ++tri) { var face = []; var vertexTokens = [1, tri - 1, tri]; for (var tokenInd = 0; tokenInd < vertexTokens.length; ++tokenInd) { // Now, convert the given token into an index var vertString = tokens[vertexTokens[tokenInd]]; var vertIndex = 0; // TODO: Faces can technically use negative numbers to refer to the // previous nth vertex. I haven't seen this used in practice, but // it might be good to implement this in the future. if (indexedVerts[vertString] !== undefined) { vertIndex = indexedVerts[vertString]; } else { var vertParts = vertString.split('/'); for (var i = 0; i < vertParts.length; i++) { vertParts[i] = parseInt(vertParts[i]) - 1; } vertIndex = indexedVerts[vertString] = model.vertices.length; model.vertices.push(loadedVerts.v[vertParts[0]].copy()); if (loadedVerts.vt[vertParts[1]]) { model.uvs.push(loadedVerts.vt[vertParts[1]].slice()); } else { model.uvs.push([0, 0]); } if (loadedVerts.vn[vertParts[2]]) { model.vertexNormals.push(loadedVerts.vn[vertParts[2]].copy()); } } face.push(vertIndex); } model.faces.push(face); } } } } // If the model doesn't have normals, compute the normals if(model.vertexNormals.length === 0) { model.computeNormals(); } return model; } /** * Render a 3d model to the screen. * * @method model * @param {p5.Geometry} model Loaded 3d model to be rendered * @example *
* * //draw a spinning teapot * var teapot; * * function setup(){ * createCanvas(100, 100, WEBGL); * * teapot = loadModel('assets/teapot.obj'); * } * * function draw(){ * background(200); * rotateX(frameCount * 0.01); * rotateY(frameCount * 0.01); * model(teapot); * } * *
* * @alt * Vertically rotating 3-d teapot with red, green and blue gradient. * */ p5.prototype.model = function ( model ) { if (model.vertices.length > 0) { if (!this._renderer.geometryInHash(model.gid)) { this._renderer.createBuffers(model.gid, model); } this._renderer.drawBuffers(model.gid); } }; module.exports = p5; },{"../core/core":5,"./p5.Geometry":26}],25:[function(_dereq_,module,exports){ /** * @module Lights, Camera * @submodule Material * @for p5 * @requires core */ 'use strict'; var p5 = _dereq_('../core/core'); //require('./p5.Texture'); /** * Normal material for geometry. You can view all * possible materials in this * example. * @method normalMaterial * @chainable * @example *
* * function setup(){ * createCanvas(100, 100, WEBGL); * } * * function draw(){ * background(200); * normalMaterial(); * sphere(50); * } * *
* * @alt * Red, green and blue gradient. * */ p5.prototype.normalMaterial = function(){ this._renderer._getShader('normalVert', 'normalFrag'); return this; }; /** * Texture for geometry. You can view other possible materials in this * example. * @method texture * @param {p5.Image | p5.MediaElement | p5.Graphics} tex 2-dimensional graphics * to render as texture * @chainable * @example *
* * var img; * function setup(){ * createCanvas(100, 100, WEBGL); * img = loadImage("assets/laDefense.jpg"); * } * * function draw(){ * background(0); * rotateZ(frameCount * 0.01); * rotateX(frameCount * 0.01); * rotateY(frameCount * 0.01); * //pass image as texture * texture(img); * box(200, 200, 200); * } * *
* *
* * var pg; * function setup(){ * createCanvas(100, 100, WEBGL); * pg = createGraphics(200, 200); * pg.textSize(100); * } * * function draw(){ * background(0); * pg.background(255); * pg.text('hello!', 0, 100); * //pass image as texture * texture(pg); * plane(200); * } * *
* *
* * var vid; * function preload(){ * vid = createVideo("assets/fingers.mov"); * vid.hide(); * vid.loop(); * } * function setup(){ * createCanvas(100, 100, WEBGL); * } * * function draw(){ * background(0); * //pass video frame as texture * texture(vid); * plane(200); * } * *
* * @alt * Rotating view of many images umbrella and grid roof on a 3d plane * black canvas * black canvas * */ p5.prototype.texture = function(){ var args = new Array(arguments.length); for (var i = 0; i < args.length; ++i) { args[i] = arguments[i]; } var gl = this._renderer.GL; gl.enable(gl.BLEND); gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); this._renderer.drawMode = 'texture'; var shaderProgram = this._renderer._getShader('lightVert', 'lightTextureFrag'); gl.useProgram(shaderProgram); var textureData; //if argument is not already a texture //create a new one if(!args[0].isTexture){ if (args[0] instanceof p5.Image) { textureData = args[0].canvas; } //if param is a video else if (typeof p5.MediaElement !== 'undefined' && args[0] instanceof p5.MediaElement){ if(!args[0].loadedmetadata) {return;} textureData = args[0].elt; } //used with offscreen 2d graphics renderer else if(args[0] instanceof p5.Graphics){ textureData = args[0].elt; } var tex = gl.createTexture(); args[0]._setProperty('tex', tex); args[0]._setProperty('isTexture', true); this._renderer._bind.call(this, tex, textureData); } else { if(args[0] instanceof p5.Graphics || (typeof p5.MediaElement !== 'undefined' && args[0] instanceof p5.MediaElement)){ textureData = args[0].elt; } else if(args[0] instanceof p5.Image){ textureData = args[0].canvas; } this._renderer._bind.call(this, args[0].tex, textureData); } //this is where we'd activate multi textures //eg. gl.activeTexture(gl.TEXTURE0 + (unit || 0)); //but for now we just have a single texture. //@TODO need to extend this functionality gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, args[0].tex); gl.uniform1i(gl.getUniformLocation(shaderProgram, 'isTexture'), true); gl.uniform1i(gl.getUniformLocation(shaderProgram, 'uSampler'), 0); return this; }; /** * Texture Util functions */ p5.RendererGL.prototype._bind = function(tex, data){ var gl = this._renderer.GL; gl.bindTexture(gl.TEXTURE_2D, tex); gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, data); gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); gl.bindTexture(gl.TEXTURE_2D, null); }; /** * Checks whether val is a pot * more info on power of 2 here: * https://www.opengl.org/wiki/NPOT_Texture * @param {Number} value * @return {Boolean} */ // function _isPowerOf2 (value){ // return (value & (value - 1)) === 0; // } /** * returns the next highest power of 2 value * @param {Number} value [description] * @return {Number} [description] */ // function _nextHighestPOT (value){ // --value; // for (var i = 1; i < 32; i <<= 1) { // value = value | value >> i; // } // return value + 1; /** * Ambient material for geometry with a given color. You can view all * possible materials in this * example. * @method ambientMaterial * @param {Number|Array|String|p5.Color} v1 gray value, * red or hue value (depending on the current color mode), * or color Array, or CSS color string * @param {Number} [v2] optional: green or saturation value * @param {Number} [v3] optional: blue or brightness value * @param {Number} [a] optional: opacity * @chainable * @example *
* * function setup(){ * createCanvas(100, 100, WEBGL); * } * function draw(){ * background(0); * ambientLight(100); * pointLight(250, 250, 250, 100, 100, 0); * ambientMaterial(250); * sphere(50); * } * *
* * @alt * radiating light source from top right of canvas * */ p5.prototype.ambientMaterial = function(v1, v2, v3, a) { var gl = this._renderer.GL; var shaderProgram = this._renderer._getShader('lightVert', 'lightTextureFrag'); gl.useProgram(shaderProgram); shaderProgram.uMaterialColor = gl.getUniformLocation( shaderProgram, 'uMaterialColor' ); var colors = this._renderer._applyColorBlend.apply(this._renderer, arguments); gl.uniform4f(shaderProgram.uMaterialColor, colors[0], colors[1], colors[2], colors[3]); shaderProgram.uSpecular = gl.getUniformLocation( shaderProgram, 'uSpecular' ); gl.uniform1i(shaderProgram.uSpecular, false); gl.uniform1i(gl.getUniformLocation(shaderProgram, 'isTexture'), false); return this; }; /** * Specular material for geometry with a given color. You can view all * possible materials in this * example. * @method specularMaterial * @param {Number|Array|String|p5.Color} v1 gray value, * red or hue value (depending on the current color mode), * or color Array, or CSS color string * @param {Number} [v2] optional: green or saturation value * @param {Number} [v3] optional: blue or brightness value * @param {Number} [a] optional: opacity * @chainable * @example *
* * function setup(){ * createCanvas(100, 100, WEBGL); * } * function draw(){ * background(0); * ambientLight(100); * pointLight(250, 250, 250, 100, 100, 0); * specularMaterial(250); * sphere(50); * } * *
* * @alt * diffused radiating light source from top right of canvas * */ p5.prototype.specularMaterial = function(v1, v2, v3, a) { var gl = this._renderer.GL; var shaderProgram = this._renderer._getShader('lightVert', 'lightTextureFrag'); gl.useProgram(shaderProgram); gl.uniform1i(gl.getUniformLocation(shaderProgram, 'isTexture'), false); shaderProgram.uMaterialColor = gl.getUniformLocation( shaderProgram, 'uMaterialColor' ); var colors = this._renderer._applyColorBlend.apply(this._renderer, arguments); gl.uniform4f(shaderProgram.uMaterialColor, colors[0], colors[1], colors[2], colors[3]); shaderProgram.uSpecular = gl.getUniformLocation( shaderProgram, 'uSpecular' ); gl.uniform1i(shaderProgram.uSpecular, true); return this; }; /** * @private blends colors according to color components. * If alpha value is less than 1, we need to enable blending * on our gl context. Otherwise opaque objects need to a depthMask. * @param {Number} v1 [description] * @param {Number} v2 [description] * @param {Number} v3 [description] * @param {Number} a [description] * @return {[Number]} Normalized numbers array */ p5.RendererGL.prototype._applyColorBlend = function(v1,v2,v3,a){ var gl = this.GL; var color = this._pInst.color.apply( this._pInst, arguments); var colors = color._array; if(colors[colors.length-1] < 1.0){ gl.depthMask(false); gl.enable(gl.BLEND); gl.blendEquation( gl.FUNC_ADD ); gl.blendFunc( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA ); } else { gl.depthMask(true); gl.disable(gl.BLEND); } return colors; }; module.exports = p5; },{"../core/core":5}],26:[function(_dereq_,module,exports){ //some of the functions are adjusted from Three.js(http://threejs.org) 'use strict'; var p5 = _dereq_('../core/core'); /** * p5 Geometry class * @class p5.Geometry * @constructor * @param {function | Object} vertData callback function or Object * containing routine(s) for vertex data generation * @param {Number} [detailX] number of vertices on horizontal surface * @param {Number} [detailY] number of vertices on horizontal surface * @param {function} [callback] function to call upon object instantiation. * */ p5.Geometry = function (detailX, detailY, callback){ //an array containing every vertex //@type [p5.Vector] this.vertices = []; //an array containing 1 normal per vertex //@type [p5.Vector] //[p5.Vector, p5.Vector, p5.Vector,p5.Vector, p5.Vector, p5.Vector,...] this.vertexNormals = []; //an array containing each three vertex indices that form a face //[[0, 1, 2], [2, 1, 3], ...] this.faces = []; //a 2D array containing uvs for every vertex //[[0.0,0.0],[1.0,0.0], ...] this.uvs = []; this.detailX = (detailX !== undefined) ? detailX: 1; this.detailY = (detailY !== undefined) ? detailY: 1; if(callback instanceof Function){ callback.call(this); } return this; // TODO: is this a constructor? }; p5.Geometry.prototype.computeFaces = function(){ var sliceCount = this.detailX + 1; var a, b, c, d; for (var i = 0; i < this.detailY; i++){ for (var j = 0; j < this.detailX; j++){ a = i * sliceCount + j;// + offset; b = i * sliceCount + j + 1;// + offset; c = (i + 1)* sliceCount + j + 1;// + offset; d = (i + 1)* sliceCount + j;// + offset; this.faces.push([a, b, d]); this.faces.push([d, b, c]); } } return this; }; p5.Geometry.prototype._getFaceNormal = function(faceId,vertId){ //This assumes that vA->vB->vC is a counter-clockwise ordering var face = this.faces[faceId]; var vA = this.vertices[face[vertId%3]]; var vB = this.vertices[face[(vertId+1)%3]]; var vC = this.vertices[face[(vertId+2)%3]]; var n = p5.Vector.cross( p5.Vector.sub(vB,vA), p5.Vector.sub(vC,vA)); var sinAlpha = p5.Vector.mag(n) / (p5.Vector.mag(p5.Vector.sub(vB,vA))* p5.Vector.mag(p5.Vector.sub(vC,vA))); n = n.normalize(); return n.mult(Math.asin(sinAlpha)); }; /** * computes smooth normals per vertex as an average of each * face. * @chainable */ p5.Geometry.prototype.computeNormals = function (){ for(var v=0; v < this.vertices.length; v++){ var normal = new p5.Vector(); for(var i=0; i < this.faces.length; i++){ //if our face contains a given vertex //calculate an average of the normals //of the triangles adjacent to that vertex if(this.faces[i][0] === v || this.faces[i][1] === v || this.faces[i][2] === v) { normal = normal.add(this._getFaceNormal(i, v)); } } normal = normal.normalize(); this.vertexNormals.push(normal); } return this; }; /** * Averages the vertex normals. Used in curved * surfaces * @chainable */ p5.Geometry.prototype.averageNormals = function() { for(var i = 0; i <= this.detailY; i++){ var offset = this.detailX + 1; var temp = p5.Vector .add(this.vertexNormals[i*offset], this.vertexNormals[i*offset + this.detailX]); temp = p5.Vector.div(temp, 2); this.vertexNormals[i*offset] = temp; this.vertexNormals[i*offset + this.detailX] = temp; } return this; }; /** * Averages pole normals. Used in spherical primitives * @chainable */ p5.Geometry.prototype.averagePoleNormals = function() { //average the north pole var sum = new p5.Vector(0, 0, 0); for(var i = 0; i < this.detailX; i++){ sum.add(this.vertexNormals[i]); } sum = p5.Vector.div(sum, this.detailX); for(i = 0; i < this.detailX; i++){ this.vertexNormals[i] = sum; } //average the south pole sum = new p5.Vector(0, 0, 0); for(i = this.vertices.length - 1; i > this.vertices.length - 1 - this.detailX; i--){ sum.add(this.vertexNormals[i]); } sum = p5.Vector.div(sum, this.detailX); for(i = this.vertices.length - 1; i > this.vertices.length - 1 - this.detailX; i--){ this.vertexNormals[i] = sum; } return this; }; /** * Modifies all vertices to be centered within the range -100 to 100. * @chainable */ p5.Geometry.prototype.normalize = function() { if(this.vertices.length > 0) { // Find the corners of our bounding box var maxPosition = this.vertices[0].copy(); var minPosition = this.vertices[0].copy(); for(var i = 0; i < this.vertices.length; i++) { maxPosition.x = Math.max(maxPosition.x, this.vertices[i].x); minPosition.x = Math.min(minPosition.x, this.vertices[i].x); maxPosition.y = Math.max(maxPosition.y, this.vertices[i].y); minPosition.y = Math.min(minPosition.y, this.vertices[i].y); maxPosition.z = Math.max(maxPosition.z, this.vertices[i].z); minPosition.z = Math.min(minPosition.z, this.vertices[i].z); } var center = p5.Vector.lerp(maxPosition, minPosition, 0.5); var dist = p5.Vector.sub(maxPosition, minPosition); var longestDist = Math.max(Math.max(dist.x, dist.y), dist.z); var scale = 200 / longestDist; for(i = 0; i < this.vertices.length; i++) { this.vertices[i].sub(center); this.vertices[i].mult(scale); } } return this; }; module.exports = p5.Geometry; },{"../core/core":5}],27:[function(_dereq_,module,exports){ /** * @requires constants * @todo see methods below needing further implementation. * future consideration: implement SIMD optimizations * when browser compatibility becomes available * https://developer.mozilla.org/en-US/docs/Web/JavaScript/ * Reference/Global_Objects/SIMD */ 'use strict'; var p5 = _dereq_('../core/core'); var polarGeometry = _dereq_('../math/polargeometry'); var constants = _dereq_('../core/constants'); var GLMAT_ARRAY_TYPE = ( typeof Float32Array !== 'undefined') ? Float32Array : Array; /** * A class to describe a 4x4 matrix * for model and view matrix manipulation in the p5js webgl renderer. * class p5.Matrix * @constructor * @param {Array} [mat4] array literal of our 4x4 matrix */ p5.Matrix = function() { var args = new Array(arguments.length); for (var i = 0; i < args.length; ++i) { args[i] = arguments[i]; } // This is default behavior when object // instantiated using createMatrix() // @todo implement createMatrix() in core/math.js if(args[0] instanceof p5) { // save reference to p5 if passed in this.p5 = args[0]; if(args[1] === 'mat3'){ this.mat3 = args[2] || new GLMAT_ARRAY_TYPE([ 1, 0, 0, 0, 1, 0, 0, 0, 1 ]); } else { this.mat4 = args[1] || new GLMAT_ARRAY_TYPE([ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ]); } // default behavior when object // instantiated using new p5.Matrix() } else { if(args[0] === 'mat3'){ this.mat3 = args[1] || new GLMAT_ARRAY_TYPE([ 1, 0, 0, 0, 1, 0, 0, 0, 1 ]); } else { this.mat4 = args[0] || new GLMAT_ARRAY_TYPE([ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ]); } } return this; }; /** * Sets the x, y, and z component of the vector using two or three separate * variables, the data from a p5.Matrix, or the values from a float array. * * @param {p5.Matrix|Float32Array|Array} [inMatrix] the input p5.Matrix or * an Array of length 16 * @chainable */ p5.Matrix.prototype.set = function (inMatrix) { if (inMatrix instanceof p5.Matrix) { this.mat4 = inMatrix.mat4; return this; } else if (inMatrix instanceof GLMAT_ARRAY_TYPE) { this.mat4 = inMatrix; return this; } return this; }; /** * Gets a copy of the vector, returns a p5.Matrix object. * * @return {p5.Matrix} the copy of the p5.Matrix object */ p5.Matrix.prototype.get = function () { return new p5.Matrix(this.mat4); }; /** * return a copy of a matrix * @return {p5.Matrix} the result matrix */ p5.Matrix.prototype.copy = function(){ var copied = new p5.Matrix(); copied.mat4[0] = this.mat4[0]; copied.mat4[1] = this.mat4[1]; copied.mat4[2] = this.mat4[2]; copied.mat4[3] = this.mat4[3]; copied.mat4[4] = this.mat4[4]; copied.mat4[5] = this.mat4[5]; copied.mat4[6] = this.mat4[6]; copied.mat4[7] = this.mat4[7]; copied.mat4[8] = this.mat4[8]; copied.mat4[9] = this.mat4[9]; copied.mat4[10] = this.mat4[10]; copied.mat4[11] = this.mat4[11]; copied.mat4[12] = this.mat4[12]; copied.mat4[13] = this.mat4[13]; copied.mat4[14] = this.mat4[14]; copied.mat4[15] = this.mat4[15]; return copied; }; /** * return an identity matrix * @return {p5.Matrix} the result matrix */ p5.Matrix.identity = function(){ return new p5.Matrix(); }; /** * transpose according to a given matrix * @param {p5.Matrix|Float32Array|Array} a the matrix to be based on to transpose * @chainable */ p5.Matrix.prototype.transpose = function(a){ var a01, a02, a03, a12, a13, a23; if(a instanceof p5.Matrix){ a01 = a.mat4[1]; a02 = a.mat4[2]; a03 = a.mat4[3]; a12 = a.mat4[6]; a13 = a.mat4[7]; a23 = a.mat4[11]; this.mat4[0] = a.mat4[0]; this.mat4[1] = a.mat4[4]; this.mat4[2] = a.mat4[8]; this.mat4[3] = a.mat4[12]; this.mat4[4] = a01; this.mat4[5] = a.mat4[5]; this.mat4[6] = a.mat4[9]; this.mat4[7] = a.mat4[13]; this.mat4[8] = a02; this.mat4[9] = a12; this.mat4[10] = a.mat4[10]; this.mat4[11] = a.mat4[14]; this.mat4[12] = a03; this.mat4[13] = a13; this.mat4[14] = a23; this.mat4[15] = a.mat4[15]; }else if(a instanceof GLMAT_ARRAY_TYPE){ a01 = a[1]; a02 = a[2]; a03 = a[3]; a12 = a[6]; a13 = a[7]; a23 = a[11]; this.mat4[0] = a[0]; this.mat4[1] = a[4]; this.mat4[2] = a[8]; this.mat4[3] = a[12]; this.mat4[4] = a01; this.mat4[5] = a[5]; this.mat4[6] = a[9]; this.mat4[7] = a[13]; this.mat4[8] = a02; this.mat4[9] = a12; this.mat4[10] = a[10]; this.mat4[11] = a[14]; this.mat4[12] = a03; this.mat4[13] = a13; this.mat4[14] = a23; this.mat4[15] = a[15]; } return this; }; /** * invert matrix according to a give matrix * @param {p5.Matrix|Float32Array|Array} a the matrix to be based on to invert * @chainable */ p5.Matrix.prototype.invert = function(a){ var a00, a01, a02, a03, a10, a11, a12, a13, a20, a21, a22, a23, a30, a31, a32, a33; if(a instanceof p5.Matrix){ a00 = a.mat4[0]; a01 = a.mat4[1]; a02 = a.mat4[2]; a03 = a.mat4[3]; a10 = a.mat4[4]; a11 = a.mat4[5]; a12 = a.mat4[6]; a13 = a.mat4[7]; a20 = a.mat4[8]; a21 = a.mat4[9]; a22 = a.mat4[10]; a23 = a.mat4[11]; a30 = a.mat4[12]; a31 = a.mat4[13]; a32 = a.mat4[14]; a33 = a.mat4[15]; }else if(a instanceof GLMAT_ARRAY_TYPE){ a00 = a[0]; a01 = a[1]; a02 = a[2]; a03 = a[3]; a10 = a[4]; a11 = a[5]; a12 = a[6]; a13 = a[7]; a20 = a[8]; a21 = a[9]; a22 = a[10]; a23 = a[11]; a30 = a[12]; a31 = a[13]; a32 = a[14]; a33 = a[15]; } var b00 = a00 * a11 - a01 * a10, b01 = a00 * a12 - a02 * a10, b02 = a00 * a13 - a03 * a10, b03 = a01 * a12 - a02 * a11, b04 = a01 * a13 - a03 * a11, b05 = a02 * a13 - a03 * a12, b06 = a20 * a31 - a21 * a30, b07 = a20 * a32 - a22 * a30, b08 = a20 * a33 - a23 * a30, b09 = a21 * a32 - a22 * a31, b10 = a21 * a33 - a23 * a31, b11 = a22 * a33 - a23 * a32, // Calculate the determinant det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; if (!det) { return null; } det = 1.0 / det; this.mat4[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det; this.mat4[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det; this.mat4[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det; this.mat4[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det; this.mat4[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det; this.mat4[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det; this.mat4[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det; this.mat4[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det; this.mat4[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det; this.mat4[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det; this.mat4[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det; this.mat4[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det; this.mat4[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det; this.mat4[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det; this.mat4[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det; this.mat4[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det; return this; }; /** * Inverts a 3x3 matrix * @chainable */ p5.Matrix.prototype.invert3x3 = function (){ var a00 = this.mat3[0], a01 = this.mat3[1], a02 = this.mat3[2], a10 = this.mat3[3], a11 = this.mat3[4], a12 = this.mat3[5], a20 = this.mat3[6], a21 = this.mat3[7], a22 = this.mat3[8], b01 = a22 * a11 - a12 * a21, b11 = -a22 * a10 + a12 * a20, b21 = a21 * a10 - a11 * a20, // Calculate the determinant det = a00 * b01 + a01 * b11 + a02 * b21; if (!det) { return null; } det = 1.0 / det; this.mat3[0] = b01 * det; this.mat3[1] = (-a22 * a01 + a02 * a21) * det; this.mat3[2] = (a12 * a01 - a02 * a11) * det; this.mat3[3] = b11 * det; this.mat3[4] = (a22 * a00 - a02 * a20) * det; this.mat3[5] = (-a12 * a00 + a02 * a10) * det; this.mat3[6] = b21 * det; this.mat3[7] = (-a21 * a00 + a01 * a20) * det; this.mat3[8] = (a11 * a00 - a01 * a10) * det; return this; }; /** * transposes a 3x3 p5.Matrix by a mat3 * @param {[Number]} mat3 1-dimensional array * @chainable */ p5.Matrix.prototype.transpose3x3 = function (mat3){ var a01 = mat3[1], a02 = mat3[2], a12 = mat3[5]; this.mat3[1] = mat3[3]; this.mat3[2] = mat3[6]; this.mat3[3] = a01; this.mat3[5] = mat3[7]; this.mat3[6] = a02; this.mat3[7] = a12; return this; }; /** * converts a 4x4 matrix to its 3x3 inverse tranform * commonly used in MVMatrix to NMatrix conversions. * @param {p5.Matrix} mat4 the matrix to be based on to invert * @chainable * @todo finish implementation */ p5.Matrix.prototype.inverseTranspose = function (matrix){ if(this.mat3 === undefined){ console.error('sorry, this function only works with mat3'); } else { //convert mat4 -> mat3 this.mat3[0] = matrix.mat4[0]; this.mat3[1] = matrix.mat4[1]; this.mat3[2] = matrix.mat4[2]; this.mat3[3] = matrix.mat4[4]; this.mat3[4] = matrix.mat4[5]; this.mat3[5] = matrix.mat4[6]; this.mat3[6] = matrix.mat4[8]; this.mat3[7] = matrix.mat4[9]; this.mat3[8] = matrix.mat4[10]; } this.invert3x3().transpose3x3(this.mat3); return this; }; /** * inspired by Toji's mat4 determinant * @return {Number} Determinant of our 4x4 matrix */ p5.Matrix.prototype.determinant = function(){ var d00 = (this.mat4[0] * this.mat4[5]) - (this.mat4[1] * this.mat4[4]), d01 = (this.mat4[0] * this.mat4[6]) - (this.mat4[2] * this.mat4[4]), d02 = (this.mat4[0] * this.mat4[7]) - (this.mat4[3] * this.mat4[4]), d03 = (this.mat4[1] * this.mat4[6]) - (this.mat4[2] * this.mat4[5]), d04 = (this.mat4[1] * this.mat4[7]) - (this.mat4[3] * this.mat4[5]), d05 = (this.mat4[2] * this.mat4[7]) - (this.mat4[3] * this.mat4[6]), d06 = (this.mat4[8] * this.mat4[13]) - (this.mat4[9] * this.mat4[12]), d07 = (this.mat4[8] * this.mat4[14]) - (this.mat4[10] * this.mat4[12]), d08 = (this.mat4[8] * this.mat4[15]) - (this.mat4[11] * this.mat4[12]), d09 = (this.mat4[9] * this.mat4[14]) - (this.mat4[10] * this.mat4[13]), d10 = (this.mat4[9] * this.mat4[15]) - (this.mat4[11] * this.mat4[13]), d11 = (this.mat4[10] * this.mat4[15]) - (this.mat4[11] * this.mat4[14]); // Calculate the determinant return d00 * d11 - d01 * d10 + d02 * d09 + d03 * d08 - d04 * d07 + d05 * d06; }; /** * multiply two mat4s * @param {p5.Matrix|Float32Array|Array} multMatrix The matrix * we want to multiply by * @chainable */ p5.Matrix.prototype.mult = function(multMatrix){ var _dest = new GLMAT_ARRAY_TYPE(16); var _src = new GLMAT_ARRAY_TYPE(16); if(multMatrix instanceof p5.Matrix) { _src = multMatrix.mat4; } else if(multMatrix instanceof GLMAT_ARRAY_TYPE){ _src = multMatrix; } // each row is used for the multiplier var b0 = this.mat4[0], b1 = this.mat4[1], b2 = this.mat4[2], b3 = this.mat4[3]; _dest[0] = b0*_src[0] + b1*_src[4] + b2*_src[8] + b3*_src[12]; _dest[1] = b0*_src[1] + b1*_src[5] + b2*_src[9] + b3*_src[13]; _dest[2] = b0*_src[2] + b1*_src[6] + b2*_src[10] + b3*_src[14]; _dest[3] = b0*_src[3] + b1*_src[7] + b2*_src[11] + b3*_src[15]; b0 = this.mat4[4]; b1 = this.mat4[5]; b2 = this.mat4[6]; b3 = this.mat4[7]; _dest[4] = b0*_src[0] + b1*_src[4] + b2*_src[8] + b3*_src[12]; _dest[5] = b0*_src[1] + b1*_src[5] + b2*_src[9] + b3*_src[13]; _dest[6] = b0*_src[2] + b1*_src[6] + b2*_src[10] + b3*_src[14]; _dest[7] = b0*_src[3] + b1*_src[7] + b2*_src[11] + b3*_src[15]; b0 = this.mat4[8]; b1 = this.mat4[9]; b2 = this.mat4[10]; b3 = this.mat4[11]; _dest[8] = b0*_src[0] + b1*_src[4] + b2*_src[8] + b3*_src[12]; _dest[9] = b0*_src[1] + b1*_src[5] + b2*_src[9] + b3*_src[13]; _dest[10] = b0*_src[2] + b1*_src[6] + b2*_src[10] + b3*_src[14]; _dest[11] = b0*_src[3] + b1*_src[7] + b2*_src[11] + b3*_src[15]; b0 = this.mat4[12]; b1 = this.mat4[13]; b2 = this.mat4[14]; b3 = this.mat4[15]; _dest[12] = b0*_src[0] + b1*_src[4] + b2*_src[8] + b3*_src[12]; _dest[13] = b0*_src[1] + b1*_src[5] + b2*_src[9] + b3*_src[13]; _dest[14] = b0*_src[2] + b1*_src[6] + b2*_src[10] + b3*_src[14]; _dest[15] = b0*_src[3] + b1*_src[7] + b2*_src[11] + b3*_src[15]; this.mat4 = _dest; return this; }; /** * scales a p5.Matrix by scalars or a vector * @param {p5.Vector|Float32Array|Array} s vector to scale by * @chainable */ p5.Matrix.prototype.scale = function() { var x,y,z; var args = new Array(arguments.length); for (var i = 0; i < args.length; ++i) { args[i] = arguments[i]; } //if our 1st arg is a type p5.Vector if (args[0] instanceof p5.Vector){ x = args[0].x; y = args[0].y; z = args[0].z; } //otherwise if it's an array else if (args[0] instanceof Array){ x = args[0][0]; y = args[0][1]; z = args[0][2]; } var _dest = new GLMAT_ARRAY_TYPE(16); _dest[0] = this.mat4[0] * x; _dest[1] = this.mat4[1] * x; _dest[2] = this.mat4[2] * x; _dest[3] = this.mat4[3] * x; _dest[4] = this.mat4[4] * y; _dest[5] = this.mat4[5] * y; _dest[6] = this.mat4[6] * y; _dest[7] = this.mat4[7] * y; _dest[8] = this.mat4[8] * z; _dest[9] = this.mat4[9] * z; _dest[10] = this.mat4[10] * z; _dest[11] = this.mat4[11] * z; _dest[12] = this.mat4[12]; _dest[13] = this.mat4[13]; _dest[14] = this.mat4[14]; _dest[15] = this.mat4[15]; this.mat4 = _dest; return this; }; /** * rotate our Matrix around an axis by the given angle. * @param {Number} a The angle of rotation in radians * @param {p5.Vector|Array} axis the axis(es) to rotate around * @chainable * inspired by Toji's gl-matrix lib, mat4 rotation */ p5.Matrix.prototype.rotate = function(a, axis){ var x, y, z, _a, len; if (this.p5) { if (this.p5._angleMode === constants.DEGREES) { _a = polarGeometry.degreesToRadians(a); } } else { _a = a; } if (axis instanceof p5.Vector) { x = axis.x; y = axis.y; z = axis.z; } else if (axis instanceof Array) { x = axis[0]; y = axis[1]; z = axis[2]; } len = Math.sqrt(x * x + y * y + z * z); x *= (1/len); y *= (1/len); z *= (1/len); var a00 = this.mat4[0]; var a01 = this.mat4[1]; var a02 = this.mat4[2]; var a03 = this.mat4[3]; var a10 = this.mat4[4]; var a11 = this.mat4[5]; var a12 = this.mat4[6]; var a13 = this.mat4[7]; var a20 = this.mat4[8]; var a21 = this.mat4[9]; var a22 = this.mat4[10]; var a23 = this.mat4[11]; //sin,cos, and tan of respective angle var sA = Math.sin(_a); var cA = Math.cos(_a); var tA = 1 - cA; // Construct the elements of the rotation matrix var b00 = x * x * tA + cA; var b01 = y * x * tA + z * sA; var b02 = z * x * tA - y * sA; var b10 = x * y * tA - z * sA; var b11 = y * y * tA + cA; var b12 = z * y * tA + x * sA; var b20 = x * z * tA + y * sA; var b21 = y * z * tA - x * sA; var b22 = z * z * tA + cA; // rotation-specific matrix multiplication this.mat4[0] = a00 * b00 + a10 * b01 + a20 * b02; this.mat4[1] = a01 * b00 + a11 * b01 + a21 * b02; this.mat4[2] = a02 * b00 + a12 * b01 + a22 * b02; this.mat4[3] = a03 * b00 + a13 * b01 + a23 * b02; this.mat4[4] = a00 * b10 + a10 * b11 + a20 * b12; this.mat4[5] = a01 * b10 + a11 * b11 + a21 * b12; this.mat4[6] = a02 * b10 + a12 * b11 + a22 * b12; this.mat4[7] = a03 * b10 + a13 * b11 + a23 * b12; this.mat4[8] = a00 * b20 + a10 * b21 + a20 * b22; this.mat4[9] = a01 * b20 + a11 * b21 + a21 * b22; this.mat4[10] = a02 * b20 + a12 * b21 + a22 * b22; this.mat4[11] = a03 * b20 + a13 * b21 + a23 * b22; return this; }; /** * @todo finish implementing this method! * translates * @param {Number[]} v vector to translate by * @chainable */ p5.Matrix.prototype.translate = function(v){ var x = v[0], y = v[1], z = v[2] || 0; this.mat4[12] = this.mat4[0] * x +this.mat4[4] * y +this.mat4[8] * z +this.mat4[12]; this.mat4[13] = this.mat4[1] * x +this.mat4[5] * y +this.mat4[9] * z +this.mat4[13]; this.mat4[14] = this.mat4[2] * x +this.mat4[6] * y +this.mat4[10] * z +this.mat4[14]; this.mat4[15] = this.mat4[3] * x +this.mat4[7] * y +this.mat4[11] * z +this.mat4[15]; }; p5.Matrix.prototype.rotateX = function(a){ this.rotate(a, [1,0,0]); }; p5.Matrix.prototype.rotateY = function(a){ this.rotate(a, [0,1,0]); }; p5.Matrix.prototype.rotateZ = function(a){ this.rotate(a, [0,0,1]); }; /** * sets the perspective matrix * @param {Number} fovy [description] * @param {Number} aspect [description] * @param {Number} near near clipping plane * @param {Number} far far clipping plane * @chainable */ p5.Matrix.prototype.perspective = function(fovy,aspect,near,far){ var f = 1.0 / Math.tan(fovy / 2), nf = 1 / (near - far); this.mat4[0] = f / aspect; this.mat4[1] = 0; this.mat4[2] = 0; this.mat4[3] = 0; this.mat4[4] = 0; this.mat4[5] = f; this.mat4[6] = 0; this.mat4[7] = 0; this.mat4[8] = 0; this.mat4[9] = 0; this.mat4[10] = (far + near) * nf; this.mat4[11] = -1; this.mat4[12] = 0; this.mat4[13] = 0; this.mat4[14] = (2 * far * near) * nf; this.mat4[15] = 0; return this; }; /** * sets the ortho matrix * @param {Number} left [description] * @param {Number} right [description] * @param {Number} bottom [description] * @param {Number} top [description] * @param {Number} near near clipping plane * @param {Number} far far clipping plane * @chainable */ p5.Matrix.prototype.ortho = function(left,right,bottom,top,near,far){ var lr = 1 / (left - right), bt = 1 / (bottom - top), nf = 1 / (near - far); this.mat4[0] = -2 * lr; this.mat4[1] = 0; this.mat4[2] = 0; this.mat4[3] = 0; this.mat4[4] = 0; this.mat4[5] = -2 * bt; this.mat4[6] = 0; this.mat4[7] = 0; this.mat4[8] = 0; this.mat4[9] = 0; this.mat4[10] = 2 * nf; this.mat4[11] = 0; this.mat4[12] = (left + right) * lr; this.mat4[13] = (top + bottom) * bt; this.mat4[14] = (far + near) * nf; this.mat4[15] = 1; return this; }; /** * PRIVATE */ // matrix methods adapted from: // https://developer.mozilla.org/en-US/docs/Web/WebGL/ // gluPerspective // // function _makePerspective(fovy, aspect, znear, zfar){ // var ymax = znear * Math.tan(fovy * Math.PI / 360.0); // var ymin = -ymax; // var xmin = ymin * aspect; // var xmax = ymax * aspect; // return _makeFrustum(xmin, xmax, ymin, ymax, znear, zfar); // } //// //// glFrustum //// //function _makeFrustum(left, right, bottom, top, znear, zfar){ // var X = 2*znear/(right-left); // var Y = 2*znear/(top-bottom); // var A = (right+left)/(right-left); // var B = (top+bottom)/(top-bottom); // var C = -(zfar+znear)/(zfar-znear); // var D = -2*zfar*znear/(zfar-znear); // var frustrumMatrix =[ // X, 0, A, 0, // 0, Y, B, 0, // 0, 0, C, D, // 0, 0, -1, 0 //]; //return frustrumMatrix; // } // function _setMVPMatrices(){ ////an identity matrix ////@TODO use the p5.Matrix class to abstract away our MV matrices and ///other math //var _mvMatrix = //[ // 1.0,0.0,0.0,0.0, // 0.0,1.0,0.0,0.0, // 0.0,0.0,1.0,0.0, // 0.0,0.0,0.0,1.0 //]; module.exports = p5.Matrix; },{"../core/constants":4,"../core/core":5,"../math/polargeometry":20}],28:[function(_dereq_,module,exports){ /** * Welcome to RendererGL Immediate Mode. * Immediate mode is used for drawing custom shapes * from a set of vertices. Immediate Mode is activated * when you call beginShape() & de-activated when you call endShape(). * Immediate mode is a style of programming borrowed * from OpenGL's (now-deprecated) immediate mode. * It differs from p5.js' default, Retained Mode, which caches * geometries and buffers on the CPU to reduce the number of webgl * draw calls. Retained mode is more efficient & performative, * however, Immediate Mode is useful for sketching quick * geometric ideas. */ 'use strict'; var p5 = _dereq_('../core/core'); var constants = _dereq_('../core/constants'); /** * Begin shape drawing. This is a helpful way of generating * custom shapes quickly. However in WEBGL mode, application * performance will likely drop as a result of too many calls to * beginShape() / endShape(). As a high performance alternative, * please use p5.js geometry primitives. * @param {Number} mode webgl primitives mode. beginShape supports the * following modes: * POINTS,LINES,LINE_STRIP,LINE_LOOP,TRIANGLES, * TRIANGLE_STRIP,and TRIANGLE_FAN. * @chainable */ p5.RendererGL.prototype.beginShape = function(mode){ //default shape mode is line_strip this.immediateMode.shapeMode = (mode !== undefined ) ? mode : constants.LINE_STRIP; //if we haven't yet initialized our //immediateMode vertices & buffers, create them now! if(this.immediateMode.vertexPositions === undefined){ this.immediateMode.vertexPositions = []; this.immediateMode.vertexColors = []; this.immediateMode.vertexBuffer = this.GL.createBuffer(); this.immediateMode.colorBuffer = this.GL.createBuffer(); } else { this.immediateMode.vertexPositions.length = 0; this.immediateMode.vertexColors.length = 0; } this.isImmediateDrawing = true; return this; }; /** * adds a vertex to be drawn in a custom Shape. * @param {Number} x x-coordinate of vertex * @param {Number} y y-coordinate of vertex * @param {Number} z z-coordinate of vertex * @chainable * @TODO implement handling of p5.Vector args */ p5.RendererGL.prototype.vertex = function(x, y, z){ this.immediateMode.vertexPositions.push(x, y, z); var vertexColor = this.curFillColor || [0.5, 0.5, 0.5, 1.0]; this.immediateMode.vertexColors.push( vertexColor[0], vertexColor[1], vertexColor[2], vertexColor[3]); return this; }; /** * End shape drawing and render vertices to screen. * @chainable */ p5.RendererGL.prototype.endShape = function(mode, isCurve, isBezier,isQuadratic, isContour, shapeKind){ var gl = this.GL; this._bindImmediateBuffers( this.immediateMode.vertexPositions, this.immediateMode.vertexColors); if(mode){ if(this.drawMode === 'fill' || this.drawMode ==='texture'){ switch(this.immediateMode.shapeMode){ case constants.LINE_STRIP: this.immediateMode.shapeMode = constants.TRIANGLE_FAN; break; case constants.LINES: this.immediateMode.shapeMode = constants.TRIANGLE_FAN; break; case constants.TRIANGLES: this.immediateMode.shapeMode = constants.TRIANGLE_FAN; break; } } else { switch(this.immediateMode.shapeMode){ case constants.LINE_STRIP: this.immediateMode.shapeMode = constants.LINE_LOOP; break; case constants.LINES: this.immediateMode.shapeMode = constants.LINE_LOOP; break; } } } //QUADS & QUAD_STRIP are not supported primitives modes //in webgl. if(this.immediateMode.shapeMode === constants.QUADS || this.immediateMode.shapeMode === constants.QUAD_STRIP){ throw new Error('sorry, ' + this.immediateMode.shapeMode+ ' not yet implemented in webgl mode.'); } else { gl.enable(gl.BLEND); gl.drawArrays(this.immediateMode.shapeMode, 0, this.immediateMode.vertexPositions.length / 3); } //clear out our vertexPositions & colors arrays //after rendering this.immediateMode.vertexPositions.length = 0; this.immediateMode.vertexColors.length = 0; this.isImmediateDrawing = false; return this; }; /** * Bind immediateMode buffers to data, * then draw gl arrays * @param {Number[]} vertices Numbers array representing * vertex positions * @chainable */ p5.RendererGL.prototype._bindImmediateBuffers = function(vertices, colors){ this._setDefaultCamera(); var gl = this.GL; var shaderKey = this._getCurShaderId(); var shaderProgram = this.mHash[shaderKey]; //vertex position Attribute shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, 'aPosition'); gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); gl.bindBuffer(gl.ARRAY_BUFFER, this.immediateMode.vertexBuffer); gl.bufferData( gl.ARRAY_BUFFER, new Float32Array(vertices), gl.DYNAMIC_DRAW); gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, 3, gl.FLOAT, false, 0, 0); shaderProgram.vertexColorAttribute = gl.getAttribLocation(shaderProgram, 'aVertexColor'); gl.enableVertexAttribArray(shaderProgram.vertexColorAttribute); gl.bindBuffer(gl.ARRAY_BUFFER, this.immediateMode.colorBuffer); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors),gl.DYNAMIC_DRAW); gl.vertexAttribPointer(shaderProgram.vertexColorAttribute, 4, gl.FLOAT, false, 0, 0); //matrix this._setMatrixUniforms(shaderKey); //@todo implement in all shaders (not just immediateVert) //set our default point size // this._setUniform1f(shaderKey, // 'uPointSize', // this.pointSize); return this; }; ////////////////////////////////////////////// // COLOR ////////////////////////////////////////////// p5.RendererGL.prototype._getColorVertexShader = function(){ var gl = this.GL; var mId = 'immediateVert|vertexColorFrag'; var shaderProgram; if(!this.materialInHash(mId)){ shaderProgram = this._initShaders('immediateVert', 'vertexColorFrag', true); this.mHash[mId] = shaderProgram; shaderProgram.vertexColorAttribute = gl.getAttribLocation(shaderProgram, 'aVertexColor'); gl.enableVertexAttribArray(shaderProgram.vertexColorAttribute); }else{ shaderProgram = this.mHash[mId]; } return shaderProgram; }; module.exports = p5.RendererGL; },{"../core/constants":4,"../core/core":5}],29:[function(_dereq_,module,exports){ //Retained Mode. The default mode for rendering 3D primitives //in WEBGL. 'use strict'; var p5 = _dereq_('../core/core'); var hashCount = 0; /** * _initBufferDefaults * @description initializes buffer defaults. runs each time a new geometry is * registered * @param {String} gId key of the geometry object */ p5.RendererGL.prototype._initBufferDefaults = function(gId) { //@TODO remove this limit on hashes in gHash hashCount ++; if(hashCount > 1000){ var key = Object.keys(this.gHash)[0]; delete this.gHash[key]; hashCount --; } var gl = this.GL; //create a new entry in our gHash this.gHash[gId] = {}; this.gHash[gId].vertexBuffer = gl.createBuffer(); this.gHash[gId].normalBuffer = gl.createBuffer(); this.gHash[gId].uvBuffer = gl.createBuffer(); this.gHash[gId].indexBuffer = gl.createBuffer(); }; /** * createBuffers description * @param {String} gId key of the geometry object * @param {p5.Geometry} obj contains geometry data */ p5.RendererGL.prototype.createBuffers = function(gId, obj) { var gl = this.GL; this._setDefaultCamera(); //initialize the gl buffers for our geom groups this._initBufferDefaults(gId); //return the current shaderProgram from our material hash var shaderProgram = this.mHash[this._getCurShaderId()]; //@todo rename "numberOfItems" property to something more descriptive //we mult the num geom faces by 3 this.gHash[gId].numberOfItems = obj.faces.length * 3; gl.bindBuffer(gl.ARRAY_BUFFER, this.gHash[gId].vertexBuffer); gl.bufferData( gl.ARRAY_BUFFER, new Float32Array( vToNArray(obj.vertices) ), gl.STATIC_DRAW); //vertex position shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, 'aPosition'); gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); gl.vertexAttribPointer( shaderProgram.vertexPositionAttribute, 3, gl.FLOAT, false, 0, 0); gl.bindBuffer(gl.ARRAY_BUFFER, this.gHash[gId].normalBuffer); gl.bufferData( gl.ARRAY_BUFFER, new Float32Array( vToNArray(obj.vertexNormals) ), gl.STATIC_DRAW); //vertex normal shaderProgram.vertexNormalAttribute = gl.getAttribLocation(shaderProgram, 'aNormal'); gl.enableVertexAttribArray(shaderProgram.vertexNormalAttribute); gl.vertexAttribPointer( shaderProgram.vertexNormalAttribute, 3, gl.FLOAT, false, 0, 0); gl.bindBuffer(gl.ARRAY_BUFFER, this.gHash[gId].uvBuffer); gl.bufferData( gl.ARRAY_BUFFER, new Float32Array( flatten(obj.uvs) ), gl.STATIC_DRAW); //texture coordinate Attribute shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, 'aTexCoord'); gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); gl.vertexAttribPointer( shaderProgram.textureCoordAttribute, 2, gl.FLOAT, false, 0, 0); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.gHash[gId].indexBuffer); gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, new Uint16Array( flatten(obj.faces) ), gl.STATIC_DRAW); }; /** * Draws buffers given a geometry key ID * @param {String} gId ID in our geom hash * @chainable */ p5.RendererGL.prototype.drawBuffers = function(gId) { this._setDefaultCamera(); var gl = this.GL; var shaderKey = this._getCurShaderId(); var shaderProgram = this.mHash[shaderKey]; //vertex position buffer gl.bindBuffer(gl.ARRAY_BUFFER, this.gHash[gId].vertexBuffer); gl.vertexAttribPointer( shaderProgram.vertexPositionAttribute, 3, gl.FLOAT, false, 0, 0); //normal buffer gl.bindBuffer(gl.ARRAY_BUFFER, this.gHash[gId].normalBuffer); gl.vertexAttribPointer( shaderProgram.vertexNormalAttribute, 3, gl.FLOAT, false, 0, 0); // uv buffer gl.bindBuffer(gl.ARRAY_BUFFER, this.gHash[gId].uvBuffer); gl.vertexAttribPointer( shaderProgram.textureCoordAttribute, 2, gl.FLOAT, false, 0, 0); //vertex index buffer gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.gHash[gId].indexBuffer); this._setMatrixUniforms(shaderKey); gl.drawElements( gl.TRIANGLES, this.gHash[gId].numberOfItems, gl.UNSIGNED_SHORT, 0); return this; }; /////////////////////////////// //// UTILITY FUNCTIONS ////////////////////////////// /** * turn a two dimensional array into one dimensional array * @param {Array} arr 2-dimensional array * @return {Array} 1-dimensional array * [[1, 2, 3],[4, 5, 6]] -> [1, 2, 3, 4, 5, 6] */ function flatten(arr){ if (arr.length>0){ return arr.reduce(function(a, b){ return a.concat(b); }); } else { return []; } } /** * turn a p5.Vector Array into a one dimensional number array * @param {p5.Vector[]} arr an array of p5.Vector * @return {Number[]} a one dimensional array of numbers * [p5.Vector(1, 2, 3), p5.Vector(4, 5, 6)] -> * [1, 2, 3, 4, 5, 6] */ function vToNArray(arr){ return flatten(arr.map(function(item){ return [item.x, item.y, item.z]; })); } module.exports = p5.RendererGL; },{"../core/core":5}],30:[function(_dereq_,module,exports){ 'use strict'; var p5 = _dereq_('../core/core'); var shader = _dereq_('./shader'); _dereq_('../core/p5.Renderer'); _dereq_('./p5.Matrix'); var uMVMatrixStack = []; //@TODO should implement public method //to override these attributes var attributes = { alpha: true, depth: true, stencil: true, antialias: false, premultipliedAlpha: false, preserveDrawingBuffer: false }; /** * 3D graphics class * @class p5.RendererGL * @constructor * @extends p5.Renderer * @todo extend class to include public method for offscreen * rendering (FBO). * */ p5.RendererGL = function(elt, pInst, isMainCanvas) { p5.Renderer.call(this, elt, pInst, isMainCanvas); this._initContext(); this.isP3D = true; //lets us know we're in 3d mode this.GL = this.drawingContext; //lights this.ambientLightCount = 0; this.directionalLightCount = 0; this.pointLightCount = 0; //camera this._curCamera = null; /** * model view, projection, & normal * matrices */ this.uMVMatrix = new p5.Matrix(); this.uPMatrix = new p5.Matrix(); this.uNMatrix = new p5.Matrix('mat3'); //Geometry & Material hashes this.gHash = {}; this.mHash = {}; //Imediate Mode //default drawing is done in Retained Mode this.isImmediateDrawing = false; this.immediateMode = {}; this.curFillColor = [0.5,0.5,0.5,1.0]; this.curStrokeColor = [0.5,0.5,0.5,1.0]; this.pointSize = 5.0;//default point/stroke return this; }; p5.RendererGL.prototype = Object.create(p5.Renderer.prototype); ////////////////////////////////////////////// // Setting ////////////////////////////////////////////// p5.RendererGL.prototype._initContext = function() { try { this.drawingContext = this.canvas.getContext('webgl', attributes) || this.canvas.getContext('experimental-webgl', attributes); if (this.drawingContext === null) { throw new Error('Error creating webgl context'); } else { console.log('p5.RendererGL: enabled webgl context'); var gl = this.drawingContext; gl.enable(gl.DEPTH_TEST); gl.depthFunc(gl.LEQUAL); gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight); } } catch (er) { throw new Error(er); } }; //detect if user didn't set the camera //then call this function below p5.RendererGL.prototype._setDefaultCamera = function(){ if(this._curCamera === null){ var _w = this.width; var _h = this.height; this.uPMatrix = p5.Matrix.identity(); var cameraZ = (this.height / 2) / Math.tan(Math.PI * 30 / 180); this.uPMatrix.perspective(60 / 180 * Math.PI, _w / _h, cameraZ * 0.1, cameraZ * 10); this._curCamera = 'default'; } }; p5.RendererGL.prototype._update = function() { this.uMVMatrix = p5.Matrix.identity(); this.translate(0, 0, -(this.height / 2) / Math.tan(Math.PI * 30 / 180)); this.ambientLightCount = 0; this.directionalLightCount = 0; this.pointLightCount = 0; }; /** * [background description] */ p5.RendererGL.prototype.background = function() { var gl = this.GL; var _col = this._pInst.color.apply(this._pInst, arguments); var _r = (_col.levels[0]) / 255; var _g = (_col.levels[1]) / 255; var _b = (_col.levels[2]) / 255; var _a = (_col.levels[3]) / 255; gl.clearColor(_r, _g, _b, _a); gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); }; //@TODO implement this // p5.RendererGL.prototype.clear = function() { //@TODO // }; ////////////////////////////////////////////// // SHADER ////////////////////////////////////////////// /** * [_initShaders description] * @param {string} vertId [description] * @param {string} fragId [description] * @return {Object} the shader program */ p5.RendererGL.prototype._initShaders = function(vertId, fragId, isImmediateMode) { var gl = this.GL; //set up our default shaders by: // 1. create the shader, // 2. load the shader source, // 3. compile the shader var _vertShader = gl.createShader(gl.VERTEX_SHADER); //load in our default vertex shader gl.shaderSource(_vertShader, shader[vertId]); gl.compileShader(_vertShader); // if our vertex shader failed compilation? if (!gl.getShaderParameter(_vertShader, gl.COMPILE_STATUS)) { alert('Yikes! An error occurred compiling the shaders:' + gl.getShaderInfoLog(_vertShader)); return null; } var _fragShader = gl.createShader(gl.FRAGMENT_SHADER); //load in our material frag shader gl.shaderSource(_fragShader, shader[fragId]); gl.compileShader(_fragShader); // if our frag shader failed compilation? if (!gl.getShaderParameter(_fragShader, gl.COMPILE_STATUS)) { alert('Darn! An error occurred compiling the shaders:' + gl.getShaderInfoLog(_fragShader)); return null; } var shaderProgram = gl.createProgram(); gl.attachShader(shaderProgram, _vertShader); gl.attachShader(shaderProgram, _fragShader); gl.linkProgram(shaderProgram); if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { alert('Snap! Error linking shader program'); } //END SHADERS SETUP this._getLocation(shaderProgram, isImmediateMode); return shaderProgram; }; p5.RendererGL.prototype._getLocation = function(shaderProgram, isImmediateMode) { var gl = this.GL; gl.useProgram(shaderProgram); //projection Matrix uniform shaderProgram.uPMatrixUniform = gl.getUniformLocation(shaderProgram, 'uProjectionMatrix'); //model view Matrix uniform shaderProgram.uMVMatrixUniform = gl.getUniformLocation(shaderProgram, 'uModelViewMatrix'); //@TODO: figure out a better way instead of if statement if(isImmediateMode === undefined){ //normal Matrix uniform shaderProgram.uNMatrixUniform = gl.getUniformLocation(shaderProgram, 'uNormalMatrix'); shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, 'uSampler'); } }; /** * Sets a shader uniform given a shaderProgram and uniform string * @param {String} shaderKey key to material Hash. * @param {String} uniform location in shader. * @param {Number} data data to bind uniform. Float data type. * @chainable * @todo currently this function sets uniform1f data. * Should generalize function to accept any uniform * data type. */ p5.RendererGL.prototype._setUniform1f = function(shaderKey,uniform,data) { var gl = this.GL; var shaderProgram = this.mHash[shaderKey]; gl.useProgram(shaderProgram); shaderProgram[uniform] = gl.getUniformLocation(shaderProgram, uniform); gl.uniform1f(shaderProgram[uniform], data); return this; }; p5.RendererGL.prototype._setMatrixUniforms = function(shaderKey) { var gl = this.GL; var shaderProgram = this.mHash[shaderKey]; gl.useProgram(shaderProgram); gl.uniformMatrix4fv( shaderProgram.uPMatrixUniform, false, this.uPMatrix.mat4); gl.uniformMatrix4fv( shaderProgram.uMVMatrixUniform, false, this.uMVMatrix.mat4); this.uNMatrix.inverseTranspose(this.uMVMatrix); gl.uniformMatrix3fv( shaderProgram.uNMatrixUniform, false, this.uNMatrix.mat3); }; ////////////////////////////////////////////// // GET CURRENT | for shader and color ////////////////////////////////////////////// p5.RendererGL.prototype._getShader = function(vertId, fragId, isImmediateMode) { var mId = vertId + '|' + fragId; //create it and put it into hashTable if(!this.materialInHash(mId)){ var shaderProgram = this._initShaders(vertId, fragId, isImmediateMode); this.mHash[mId] = shaderProgram; } this.curShaderId = mId; return this.mHash[this.curShaderId]; }; p5.RendererGL.prototype._getCurShaderId = function(){ //if the shader ID is not yet defined if(this.drawMode !== 'fill' && this.curShaderId === undefined){ //default shader: normalMaterial() var mId = 'normalVert|normalFrag'; var shaderProgram = this._initShaders('normalVert', 'normalFrag'); this.mHash[mId] = shaderProgram; this.curShaderId = mId; } else if(this.isImmediateDrawing && this.drawMode === 'fill'){ // note that this._getShader will check if the shader already exists // by looking up the shader id (composed of vertexShaderId|fragmentShaderId) // in the material hash. If the material isn't found in the hash, it // creates a new one using this._initShaders--however, we'd like // use the cached version as often as possible, so we defer to this._getShader // here instead of calling this._initShaders directly. this._getShader('immediateVert', 'vertexColorFrag', true); } return this.curShaderId; }; ////////////////////////////////////////////// // COLOR ////////////////////////////////////////////// /** * Basic fill material for geometry with a given color * @method fill * @param {Number|Array|String|p5.Color} v1 gray value, * red or hue value (depending on the current color mode), * or color Array, or CSS color string * @param {Number} [v2] optional: green or saturation value * @param {Number} [v3] optional: blue or brightness value * @param {Number} [a] optional: opacity * @chainable * @example *
* * function setup(){ * createCanvas(100, 100, WEBGL); * } * * function draw(){ * background(0); * fill(250, 0, 0); * rotateX(frameCount * 0.01); * rotateY(frameCount * 0.01); * rotateZ(frameCount * 0.01); * box(200, 200, 200); * } * *
* * @alt * red canvas * */ p5.RendererGL.prototype.fill = function(v1, v2, v3, a) { var gl = this.GL; var shaderProgram; //see material.js for more info on color blending in webgl var colors = this._applyColorBlend.apply(this, arguments); this.curFillColor = colors; this.drawMode = 'fill'; if(this.isImmediateDrawing){ shaderProgram = this._getShader('immediateVert','vertexColorFrag'); gl.useProgram(shaderProgram); } else { shaderProgram = this._getShader('normalVert', 'basicFrag'); gl.useProgram(shaderProgram); //RetainedMode uses a webgl uniform to pass color vals //in ImmediateMode, we want access to each vertex so therefore //we cannot use a uniform. shaderProgram.uMaterialColor = gl.getUniformLocation( shaderProgram, 'uMaterialColor' ); gl.uniform4f( shaderProgram.uMaterialColor, colors[0], colors[1], colors[2], colors[3]); } return this; }; p5.RendererGL.prototype.stroke = function(r, g, b, a) { var color = this._pInst.color.apply(this._pInst, arguments); var colorNormalized = color._array; this.curStrokeColor = colorNormalized; this.drawMode = 'stroke'; return this; }; //@TODO p5.RendererGL.prototype._strokeCheck = function(){ if(this.drawMode === 'stroke'){ throw new Error( 'stroke for shapes in 3D not yet implemented, use fill for now :(' ); } }; /** * [strokeWeight description] * @param {Number} pointSize stroke point size * @chainable * @todo strokeWeight currently works on points only. * implement on all wireframes and strokes. */ p5.RendererGL.prototype.strokeWeight = function(pointSize) { this.pointSize = pointSize; return this; }; ////////////////////////////////////////////// // HASH | for material and geometry ////////////////////////////////////////////// p5.RendererGL.prototype.geometryInHash = function(gId){ return this.gHash[gId] !== undefined; }; p5.RendererGL.prototype.materialInHash = function(mId){ return this.mHash[mId] !== undefined; }; /** * [resize description] * @param {Number} w [description] * @param {Number} h [description] */ p5.RendererGL.prototype.resize = function(w,h) { var gl = this.GL; p5.Renderer.prototype.resize.call(this, w, h); gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight); // If we're using the default camera, update the aspect ratio if(this._curCamera === 'default') { this._curCamera = null; this._setDefaultCamera(); } }; /** * clears color and depth buffers * with r,g,b,a * @param {Number} r normalized red val. * @param {Number} g normalized green val. * @param {Number} b normalized blue val. * @param {Number} a normalized alpha val. */ p5.RendererGL.prototype.clear = function() { var gl = this.GL; gl.clearColor(arguments[0], arguments[1], arguments[2], arguments[3]); gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); }; /** * [translate description] * @param {Number} x [description] * @param {Number} y [description] * @param {Number} z [description] * @chainable * @todo implement handle for components or vector as args */ p5.RendererGL.prototype.translate = function(x, y, z) { this.uMVMatrix.translate([x,-y,z]); return this; }; /** * Scales the Model View Matrix by a vector * @param {Number | p5.Vector | Array} x [description] * @param {Number} [y] y-axis scalar * @param {Number} [z] z-axis scalar * @chainable */ p5.RendererGL.prototype.scale = function(x,y,z) { this.uMVMatrix.scale([x,y,z]); return this; }; p5.RendererGL.prototype.rotate = function(rad, axis){ this.uMVMatrix.rotate(rad, axis); return this; }; p5.RendererGL.prototype.rotateX = function(rad) { this.rotate(rad, [1,0,0]); return this; }; p5.RendererGL.prototype.rotateY = function(rad) { this.rotate(rad, [0,1,0]); return this; }; p5.RendererGL.prototype.rotateZ = function(rad) { this.rotate(rad, [0,0,1]); return this; }; /** * pushes a copy of the model view matrix onto the * MV Matrix stack. */ p5.RendererGL.prototype.push = function() { uMVMatrixStack.push(this.uMVMatrix.copy()); }; /** * [pop description] */ p5.RendererGL.prototype.pop = function() { if (uMVMatrixStack.length === 0) { throw new Error('Invalid popMatrix!'); } this.uMVMatrix = uMVMatrixStack.pop(); }; p5.RendererGL.prototype.resetMatrix = function() { this.uMVMatrix = p5.Matrix.identity(); this.translate(0, 0, -800); return this; }; // Text/Typography // @TODO: p5.RendererGL.prototype._applyTextProperties = function() { //@TODO finish implementation console.error('text commands not yet implemented in webgl'); }; module.exports = p5.RendererGL; },{"../core/core":5,"../core/p5.Renderer":12,"./p5.Matrix":27,"./shader":32}],31:[function(_dereq_,module,exports){ /** * @module Shape * @submodule 3D Primitives * @for p5 * @requires core * @requires p5.Geometry */ 'use strict'; var p5 = _dereq_('../core/core'); _dereq_('./p5.Geometry'); /** * Draw a plane with given a width and height * @method plane * @param {Number} width width of the plane * @param {Number} height height of the plane * @param {Number} [detailX] Optional number of triangle * subdivisions in x-dimension * @param {Number} [detailY] Optional number of triangle * subdivisions in y-dimension * @return {p5} the p5 object * @example *
* * //draw a plane with width 200 and height 200 * function setup(){ * createCanvas(100, 100, WEBGL); * } * * function draw(){ * background(200); * plane(50, 50); * } * *
* * @alt * Nothing displayed on canvas * Rotating interior view of a box with sides that change color. * 3d red and green gradient. * Rotating interior view of a cylinder with sides that change color. * Rotating view of a cylinder with sides that change color. * 3d red and green gradient. * rotating view of a multi-colored cylinder with concave sides. */ p5.prototype.plane = function(){ var args = new Array(arguments.length); for (var i = 0; i < args.length; ++i) { args[i] = arguments[i]; } var width = args[0] || 50; var height = args[1] || width; var detailX = typeof args[2] === 'number' ? args[2] : 1; var detailY = typeof args[3] === 'number' ? args[3] : 1; var gId = 'plane|'+width+'|'+height+'|'+detailX+'|'+detailY; if(!this._renderer.geometryInHash(gId)){ var _plane = function(){ var u,v,p; for (var i = 0; i <= this.detailY; i++){ v = i / this.detailY; for (var j = 0; j <= this.detailX; j++){ u = j / this.detailX; p = new p5.Vector(width * u - width/2, height * v - height/2, 0); this.vertices.push(p); this.uvs.push([u,v]); } } }; var planeGeom = new p5.Geometry(detailX, detailY, _plane); planeGeom .computeFaces() .computeNormals(); this._renderer.createBuffers(gId, planeGeom); } this._renderer.drawBuffers(gId); }; /** * Draw a box with given width, height and depth * @method box * @param {Number} width width of the box * @param {Number} [Height] height of the box * @param {Number} [depth] depth of the box * @param {Number} [detailX] Optional number of triangle * subdivisions in x-dimension * @param {Number} [detailY] Optional number of triangle * subdivisions in y-dimension * @chainable * @example *
* * //draw a spinning box with width, height and depth 200 * function setup(){ * createCanvas(100, 100, WEBGL); * } * * function draw(){ * background(200); * rotateX(frameCount * 0.01); * rotateY(frameCount * 0.01); * box(200, 200, 200); * } * *
*/ p5.prototype.box = function(){ var args = new Array(arguments.length); for (var i = 0; i < args.length; ++i) { args[i] = arguments[i]; } var width = args[0] || 50; var height = args[1] || width; var depth = args[2] || width; var detailX = typeof args[3] === 'number' ? args[3] : 4; var detailY = typeof args[4] === 'number' ? args[4] : 4; var gId = 'box|'+width+'|'+height+'|'+depth+'|'+detailX+'|'+detailY; if(!this._renderer.geometryInHash(gId)){ var _box = function(){ var cubeIndices = [ [0, 4, 2, 6],// -1, 0, 0],// -x [1, 3, 5, 7],// +1, 0, 0],// +x [0, 1, 4, 5],// 0, -1, 0],// -y [2, 6, 3, 7],// 0, +1, 0],// +y [0, 2, 1, 3],// 0, 0, -1],// -z [4, 5, 6, 7]// 0, 0, +1] // +z ]; var id=0; for (var i = 0; i < cubeIndices.length; i++) { var cubeIndex = cubeIndices[i]; var v = i * 4; for (var j = 0; j < 4; j++) { var d = cubeIndex[j]; //inspired by lightgl: //https://github.com/evanw/lightgl.js //octants:https://en.wikipedia.org/wiki/Octant_(solid_geometry) var octant = new p5.Vector( ((d & 1) * 2 - 1)*width/2, ((d & 2) - 1) *height/2, ((d & 4) / 2 - 1) * depth/2); this.vertices.push( octant ); this.uvs.push([j & 1, (j & 2) / 2]); id++; } this.faces.push([v, v + 1, v + 2]); this.faces.push([v + 2, v + 1, v + 3]); } }; var boxGeom = new p5.Geometry(detailX,detailY, _box); boxGeom.computeNormals(); //initialize our geometry buffer with //the key val pair: //geometry Id, Geom object this._renderer.createBuffers(gId, boxGeom); } this._renderer.drawBuffers(gId); return this; }; /** * Draw a sphere with given radius * @method sphere * @param {Number} radius radius of circle * @param {Number} [detailX] optional: number of segments, * the more segments the smoother geometry * default is 24 * @param {Number} [detailY] optional: number of segments, * the more segments the smoother geometry * default is 16 * @chainable * @example *
* * // draw a sphere with radius 200 * function setup(){ * createCanvas(100, 100, WEBGL); * } * * function draw(){ * background(200); * sphere(50); * } * *
*/ p5.prototype.sphere = function(){ var args = new Array(arguments.length); for (var i = 0; i < args.length; ++i) { args[i] = arguments[i]; } var radius = args[0] || 50; var detailX = typeof args[1] === 'number' ? args[1] : 24; var detailY = typeof args[2] === 'number' ? args[2] : 16; var gId = 'sphere|'+radius+'|'+detailX+'|'+detailY; if(!this._renderer.geometryInHash(gId)){ var _sphere = function(){ var u,v,p; for (var i = 0; i <= this.detailY; i++){ v = i / this.detailY; for (var j = 0; j <= this.detailX; j++){ u = j / this.detailX; var theta = 2 * Math.PI * u; var phi = Math.PI * v - Math.PI / 2; p = new p5.Vector(radius * Math.cos(phi) * Math.sin(theta), radius * Math.sin(phi), radius * Math.cos(phi) * Math.cos(theta)); this.vertices.push(p); this.uvs.push([u,v]); } } }; var sphereGeom = new p5.Geometry(detailX, detailY, _sphere); sphereGeom .computeFaces() .computeNormals() .averageNormals() .averagePoleNormals(); this._renderer.createBuffers(gId, sphereGeom); } this._renderer.drawBuffers(gId); return this; }; /** * @private * helper function for creating both cones and cyllinders */ var _truncatedCone = function( bottomRadius, topRadius, height, detailX, detailY, topCap, bottomCap) { detailX = (detailX < 3) ? 3 : detailX; detailY = (detailY < 1) ? 1 : detailY; topCap = (topCap === undefined) ? true : topCap; bottomCap = (bottomCap === undefined) ? true : bottomCap; var extra = (topCap ? 2 : 0) + (bottomCap ? 2 : 0); var vertsAroundEdge = detailX + 1; // ensure constant slant var slant = Math.atan2(bottomRadius - topRadius, height); var start = topCap ? -2 : 0; var end = detailY + (bottomCap ? 2 : 0); var yy, ii; for (yy = start; yy <= end; ++yy) { var v = yy / detailY; var y = height * v; var ringRadius; if (yy < 0) { y = 0; v = 1; ringRadius = bottomRadius; } else if (yy > detailY) { y = height; v = 1; ringRadius = topRadius; } else { ringRadius = bottomRadius + (topRadius - bottomRadius) * (yy / detailY); } if (yy === -2 || yy === detailY + 2) { ringRadius = 0; v = 0; } y -= height / 2; for (ii = 0; ii < vertsAroundEdge; ++ii) { //VERTICES this.vertices.push( new p5.Vector( Math.sin(ii*Math.PI * 2 /detailX) * ringRadius, y, Math.cos(ii*Math.PI * 2 /detailX) * ringRadius) ); //VERTEX NORMALS this.vertexNormals.push( new p5.Vector( (yy < 0 || yy > detailY) ? 0 : (Math.sin(ii * Math.PI * 2 / detailX) * Math.cos(slant)), (yy < 0) ? -1 : (yy > detailY ? 1 : Math.sin(slant)), (yy < 0 || yy > detailY) ? 0 : (Math.cos(ii * Math.PI * 2 / detailX) * Math.cos(slant))) ); //UVs this.uvs.push([(ii / detailX), v]); } } for (yy = 0; yy < detailY + extra; ++yy) { for (ii = 0; ii < detailX; ++ii) { this.faces.push([vertsAroundEdge * (yy + 0) + 0 + ii, vertsAroundEdge * (yy + 0) + 1 + ii, vertsAroundEdge * (yy + 1) + 1 + ii]); this.faces.push([vertsAroundEdge * (yy + 0) + 0 + ii, vertsAroundEdge * (yy + 1) + 1 + ii, vertsAroundEdge * (yy + 1) + 0 + ii]); } } }; /** * Draw a cylinder with given radius and height * @method cylinder * @param {Number} radius radius of the surface * @param {Number} height height of the cylinder * @param {Number} [detailX] optional: number of segments, * the more segments the smoother geometry * default is 24 * @param {Number} [detailY] optional: number of segments in y-dimension, * the more segments the smoother geometry * default is 16 * @chainable * @example *
* * //draw a spinning cylinder with radius 200 and height 200 * function setup(){ * createCanvas(100, 100, WEBGL); * } * * function draw(){ * background(200); * rotateX(frameCount * 0.01); * rotateZ(frameCount * 0.01); * cylinder(200, 200); * } * *
*/ p5.prototype.cylinder = function(){ var args = new Array(arguments.length); for (var i = 0; i < args.length; ++i) { args[i] = arguments[i]; } var radius = args[0] || 50; var height = args[1] || radius; var detailX = typeof args[2] === 'number' ? args[2] : 24; var detailY = typeof args[3] === 'number' ? args[3] : 16; var gId = 'cylinder|'+radius+'|'+height+'|'+detailX+'|'+detailY; if(!this._renderer.geometryInHash(gId)){ var cylinderGeom = new p5.Geometry(detailX, detailY); _truncatedCone.call( cylinderGeom, radius, radius, height, detailX, detailY, true,true); cylinderGeom.computeNormals(); this._renderer.createBuffers(gId, cylinderGeom); } this._renderer.drawBuffers(gId); return this; }; /** * Draw a cone with given radius and height * @method cone * @param {Number} radius radius of the bottom surface * @param {Number} height height of the cone * @param {Number} [detailX] optional: number of segments, * the more segments the smoother geometry * default is 24 * @param {Number} [detailY] optional: number of segments, * the more segments the smoother geometry * default is 16 * @chainable * @example *
* * //draw a spinning cone with radius 200 and height 200 * function setup(){ * createCanvas(100, 100, WEBGL); * } * * function draw(){ * background(200); * rotateX(frameCount * 0.01); * rotateZ(frameCount * 0.01); * cone(200, 200); * } * *
*/ p5.prototype.cone = function(){ var args = new Array(arguments.length); for (var i = 0; i < args.length; ++i) { args[i] = arguments[i]; } var baseRadius = args[0] || 50; var height = args[1] || baseRadius; var detailX = typeof args[2] === 'number' ? args[2] : 24; var detailY = typeof args[3] === 'number' ? args[3] : 16; var gId = 'cone|'+baseRadius+'|'+height+'|'+detailX+'|'+detailY; if(!this._renderer.geometryInHash(gId)){ var coneGeom = new p5.Geometry(detailX, detailY); _truncatedCone.call(coneGeom, baseRadius, 0,//top radius 0 height, detailX, detailY, true, true); //for cones we need to average Normals coneGeom .computeNormals(); this._renderer.createBuffers(gId, coneGeom); } this._renderer.drawBuffers(gId); return this; }; /** * Draw an ellipsoid with given radius * @method ellipsoid * @param {Number} radiusx xradius of circle * @param {Number} radiusy yradius of circle * @param {Number} radiusz zradius of circle * @param {Number} [detailX] optional: number of segments, * the more segments the smoother geometry * default is 24. Avoid detail number above * 150, it may crash the browser. * @param {Number} [detailY] optional: number of segments, * the more segments the smoother geometry * default is 16. Avoid detail number above * 150, it may crash the browser. * @chainable * @example *
* * // draw an ellipsoid with radius 20, 30 and 40. * function setup(){ * createCanvas(100, 100, WEBGL); * } * * function draw(){ * background(200); * ellipsoid(20, 30, 40); * } * *
*/ p5.prototype.ellipsoid = function(){ var args = new Array(arguments.length); for (var i = 0; i < args.length; ++i) { args[i] = arguments[i]; } var detailX = typeof args[3] === 'number' ? args[3] : 24; var detailY = typeof args[4] === 'number' ? args[4] : 24; var radiusX = args[0] || 50; var radiusY = args[1] || radiusX; var radiusZ = args[2] || radiusX; var gId = 'ellipsoid|'+radiusX+'|'+radiusY+ '|'+radiusZ+'|'+detailX+'|'+detailY; if(!this._renderer.geometryInHash(gId)){ var _ellipsoid = function(){ var u,v,p; for (var i = 0; i <= this.detailY; i++){ v = i / this.detailY; for (var j = 0; j <= this.detailX; j++){ u = j / this.detailX; var theta = 2 * Math.PI * u; var phi = Math.PI * v - Math.PI / 2; p = new p5.Vector(radiusX * Math.cos(phi) * Math.sin(theta), radiusY * Math.sin(phi), radiusZ * Math.cos(phi) * Math.cos(theta)); this.vertices.push(p); this.uvs.push([u,v]); } } }; var ellipsoidGeom = new p5.Geometry(detailX, detailY,_ellipsoid); ellipsoidGeom .computeFaces() .computeNormals(); this._renderer.createBuffers(gId, ellipsoidGeom); } this._renderer.drawBuffers(gId); return this; }; /** * Draw a torus with given radius and tube radius * @method torus * @param {Number} radius radius of the whole ring * @param {Number} tubeRadius radius of the tube * @param {Number} [detailX] optional: number of segments in x-dimension, * the more segments the smoother geometry * default is 24 * @param {Number} [detailY] optional: number of segments in y-dimension, * the more segments the smoother geometry * default is 16 * @chainable * @example *
* * //draw a spinning torus with radius 200 and tube radius 60 * function setup(){ * createCanvas(100, 100, WEBGL); * } * * function draw(){ * background(200); * rotateX(frameCount * 0.01); * rotateY(frameCount * 0.01); * torus(200, 60); * } * *
*/ p5.prototype.torus = function(){ var args = new Array(arguments.length); for (var i = 0; i < args.length; ++i) { args[i] = arguments[i]; } var detailX = typeof args[2] === 'number' ? args[2] : 24; var detailY = typeof args[3] === 'number' ? args[3] : 16; var radius = args[0] || 50; var tubeRadius = args[1] || 10; var gId = 'torus|'+radius+'|'+tubeRadius+'|'+detailX+'|'+detailY; if(!this._renderer.geometryInHash(gId)){ var _torus = function(){ var u,v,p; for (var i = 0; i <= this.detailY; i++){ v = i / this.detailY; for (var j = 0; j <= this.detailX; j++){ u = j / this.detailX; var theta = 2 * Math.PI * u; var phi = 2 * Math.PI * v; p = new p5.Vector( (radius + tubeRadius * Math.cos(phi)) * Math.cos(theta), (radius + tubeRadius * Math.cos(phi)) * Math.sin(theta), tubeRadius * Math.sin(phi)); this.vertices.push(p); this.uvs.push([u,v]); } } }; var torusGeom = new p5.Geometry(detailX, detailY, _torus); torusGeom .computeFaces() .computeNormals() .averageNormals(); this._renderer.createBuffers(gId, torusGeom); } this._renderer.drawBuffers(gId); return this; }; /////////////////////// /// 2D primitives ///////////////////////// //@TODO p5.RendererGL.prototype.point = function(x, y, z){ console.log('point not yet implemented in webgl'); return this; }; p5.RendererGL.prototype.triangle = function (args){ var x1=args[0], y1=args[1]; var x2=args[2], y2=args[3]; var x3=args[4], y3=args[5]; var gId = 'tri|'+x1+'|'+y1+'|'+ x2+'|'+y2+'|'+ x3+'|'+y3; if(!this.geometryInHash(gId)){ var _triangle = function(){ var vertices = []; vertices.push(new p5.Vector(x1,y1,0)); vertices.push(new p5.Vector(x2,y2,0)); vertices.push(new p5.Vector(x3,y3,0)); this.vertices = vertices; this.faces = [[0,1,2]]; this.uvs = [[0,0],[0,1],[1,1]]; }; var triGeom = new p5.Geometry(1,1,_triangle); triGeom.computeNormals(); this.createBuffers(gId, triGeom); } this.drawBuffers(gId); return this; }; p5.RendererGL.prototype.ellipse = function (args){ var x = args[0]; var y = args[1]; var width = args[2]; var height = args[3]; //detailX and Y are optional 6th & 7th //arguments var detailX = args[4] || 24; var detailY = args[5] || 16; var gId = 'ellipse|'+args[0]+'|'+args[1]+'|'+args[2]+'|'+ args[3]; if(!this.geometryInHash(gId)){ var _ellipse = function(){ var u,v,p; var centerX = x+width*0.5; var centerY = y+height*0.5; for (var i = 0; i <= this.detailY; i++){ v = i / this.detailY; for (var j = 0; j <= this.detailX; j++){ u = j / this.detailX; var theta = 2 * Math.PI * u; if(v === 0){ p = new p5.Vector(centerX, centerY, 0); } else{ var _x = centerX + width*0.5 * Math.cos(theta); var _y = centerY + height*0.5 * Math.sin(theta); p = new p5.Vector(_x, _y, 0); } this.vertices.push(p); this.uvs.push([u,v]); } } }; var ellipseGeom = new p5.Geometry(detailX,detailY,_ellipse); ellipseGeom .computeFaces() .computeNormals(); this.createBuffers(gId, ellipseGeom); } this.drawBuffers(gId); return this; }; p5.RendererGL.prototype.rect = function (args){ var gId = 'rect|'+args[0]+'|'+args[1]+'|'+args[2]+'|'+ args[3]; var x = args[0]; var y = args[1]; var width = args[2]; var height = args[3]; var detailX = args[4] || 24; var detailY = args[5] || 16; if(!this.geometryInHash(gId)){ var _rect = function(){ var u,v,p; for (var i = 0; i <= this.detailY; i++){ v = i / this.detailY; for (var j = 0; j <= this.detailX; j++){ u = j / this.detailX; // var _x = x-width/2; // var _y = y-height/2; p = new p5.Vector( x + (width*u), y + (height*v), 0 ); this.vertices.push(p); this.uvs.push([u,v]); } } }; var rectGeom = new p5.Geometry(detailX,detailY,_rect); rectGeom .computeFaces() .computeNormals(); this.createBuffers(gId, rectGeom); } this.drawBuffers(gId); return this; }; p5.RendererGL.prototype.quad = function(){ var args = new Array(arguments.length); for (var i = 0; i < args.length; ++i) { args[i] = arguments[i]; } var x1 = args[0], y1 = args[1], x2 = args[2], y2 = args[3], x3 = args[4], y3 = args[5], x4 = args[6], y4 = args[7]; var gId = 'quad|'+x1+'|'+y1+'|'+ x2+'|'+y2+'|'+ x3+'|'+y3+'|'+ x4+'|'+y4; if(!this.geometryInHash(gId)){ var _quad = function(){ this.vertices.push(new p5.Vector(x1,y1,0)); this.vertices.push(new p5.Vector(x2,y2,0)); this.vertices.push(new p5.Vector(x3,y3,0)); this.vertices.push(new p5.Vector(x4,y4,0)); this.uvs.push([0, 0], [1, 0], [1, 1], [0, 1]); }; var quadGeom = new p5.Geometry(2,2,_quad); quadGeom.computeNormals(); quadGeom.faces = [[0,1,2],[2,3,0]]; this.createBuffers(gId, quadGeom); } this.drawBuffers(gId); return this; }; //this implementation of bezier curve //is based on Bernstein polynomial p5.RendererGL.prototype.bezier = function (args){ var bezierDetail=args[12] || 20;//value of Bezier detail this.beginShape(); var coeff=[0,0,0,0];// Bernstein polynomial coeffecients var vertex=[0,0,0]; //(x,y,z) coordinates of points in bezier curve for(var i=0; i<=bezierDetail; i++){ coeff[0]=Math.pow(1-(i/bezierDetail),3); coeff[1]=(3*(i/bezierDetail)) * (Math.pow(1-(i/bezierDetail),2)); coeff[2]=(3*Math.pow(i/bezierDetail,2)) * (1-(i/bezierDetail)); coeff[3]=Math.pow(i/bezierDetail,3); vertex[0]=args[0]*coeff[0] + args[3]*coeff[1] + args[6]*coeff[2] + args[9]*coeff[3]; vertex[1]=args[1]*coeff[0] + args[4]*coeff[1] + args[7]*coeff[2] + args[10]*coeff[3]; vertex[2]=args[2]*coeff[0] + args[5]*coeff[1] + args[8]*coeff[2] + args[11]*coeff[3]; this.vertex(vertex[0],vertex[1],vertex[2]); } this.endShape(); return this; }; p5.RendererGL.prototype.curve=function (args){ var curveDetail=args[12]; this.beginShape(); var coeff=[0,0,0,0];//coeffecients of the equation var vertex=[0,0,0]; //(x,y,z) coordinates of points in bezier curve for(var i=0; i<=curveDetail; i++){ coeff[0]=Math.pow((i/curveDetail),3) * 0.5; coeff[1]=Math.pow((i/curveDetail),2) * 0.5; coeff[2]=(i/curveDetail) * 0.5; coeff[3]=0.5; vertex[0]=coeff[0]*(-args[0] + (3*args[3]) - (3*args[6]) +args[9]) + coeff[1]*((2*args[0]) - (5*args[3]) + (4*args[6]) - args[9]) + coeff[2]*(-args[0] + args[6]) + coeff[3]*(2*args[3]); vertex[1]=coeff[0]*(-args[1] + (3*args[4]) - (3*args[7]) +args[10]) + coeff[1]*((2*args[1]) - (5*args[4]) + (4*args[7]) - args[10]) + coeff[2]*(-args[1] + args[7]) + coeff[3]*(2*args[4]); vertex[2]=coeff[0]*(-args[2] + (3*args[5]) - (3*args[8]) +args[11]) + coeff[1]*((2*args[2]) - (5*args[5]) + (4*args[8]) - args[11]) + coeff[2]*(-args[2] + args[8]) + coeff[3]*(2*args[5]); this.vertex(vertex[0],vertex[1],vertex[2]); } this.endShape(); return this; }; /** * Draw a line given two points * @param {Number} x0 x-coordinate of first vertex * @param {Number} y0 y-coordinate of first vertex * @param {Number} z0 z-coordinate of first vertex * @param {Number} x1 x-coordinate of second vertex * @param {Number} y1 y-coordinate of second vertex * @param {Number} z1 z-coordinate of second vertex * @chainable * @example *
* * //draw a line * function setup(){ * createCanvas(100, 100, WEBGL); * } * * function draw(){ * background(200); * rotateX(frameCount * 0.01); * rotateY(frameCount * 0.01); * // Use fill instead of stroke to change the color of shape. * fill(255, 0, 0); * line(10, 10, 0, 60, 60, 20); * } * *
*/ p5.RendererGL.prototype.line = function(x0,y0,z0,x1,y1,z1) { if (typeof x0 !== 'undefined' || typeof y0 !== 'undefined' || typeof z0 !== 'undefined' || typeof x1 !== 'undefined' || typeof y1 !== 'undefined' || typeof z1 !== 'undefined') { this.beginShape(); this.vertex(x0, y0, z0); this.vertex(x1, y1, z1); this.endShape(); } return this; }; module.exports = p5; },{"../core/core":5,"./p5.Geometry":26}],32:[function(_dereq_,module,exports){ module.exports = { immediateVert: "attribute vec3 aPosition;\nattribute vec4 aVertexColor;\n\nuniform mat4 uModelViewMatrix;\nuniform mat4 uProjectionMatrix;\nuniform float uResolution;\nuniform float uPointSize;\n\nvarying vec4 vColor;\nvoid main(void) {\n vec4 positionVec4 = vec4(aPosition * vec3(1.0, -1.0, 1.0), 1.0);\n gl_Position = uProjectionMatrix * uModelViewMatrix * positionVec4;\n vColor = aVertexColor;\n gl_PointSize = uPointSize;\n}\n", vertexColorVert: "attribute vec3 aPosition;\nattribute vec4 aVertexColor;\n\nuniform mat4 uModelViewMatrix;\nuniform mat4 uProjectionMatrix;\n\nvarying vec4 vColor;\n\nvoid main(void) {\n vec4 positionVec4 = vec4(aPosition * vec3(1.0, -1.0, 1.0), 1.0);\n gl_Position = uProjectionMatrix * uModelViewMatrix * positionVec4;\n vColor = aVertexColor;\n}\n", vertexColorFrag: "precision mediump float;\nvarying vec4 vColor;\nvoid main(void) {\n gl_FragColor = vColor;\n}", normalVert: "attribute vec3 aPosition;\nattribute vec3 aNormal;\nattribute vec2 aTexCoord;\n\nuniform mat4 uModelViewMatrix;\nuniform mat4 uProjectionMatrix;\nuniform mat3 uNormalMatrix;\n\nvarying vec3 vVertexNormal;\nvarying highp vec2 vVertTexCoord;\n\nvoid main(void) {\n vec4 positionVec4 = vec4(aPosition * vec3(1.0, -1.0, 1.0), 1.0);\n gl_Position = uProjectionMatrix * uModelViewMatrix * positionVec4;\n vVertexNormal = vec3( uNormalMatrix * aNormal );\n vVertTexCoord = aTexCoord;\n}\n", normalFrag: "precision mediump float;\nvarying vec3 vVertexNormal;\nvoid main(void) {\n gl_FragColor = vec4(vVertexNormal, 1.0);\n}", basicFrag: "precision mediump float;\nvarying vec3 vVertexNormal;\nuniform vec4 uMaterialColor;\nvoid main(void) {\n gl_FragColor = uMaterialColor;\n}", lightVert: "attribute vec3 aPosition;\nattribute vec3 aNormal;\nattribute vec2 aTexCoord;\n\nuniform mat4 uModelViewMatrix;\nuniform mat4 uProjectionMatrix;\nuniform mat3 uNormalMatrix;\nuniform int uAmbientLightCount;\nuniform int uDirectionalLightCount;\nuniform int uPointLightCount;\n\nuniform vec3 uAmbientColor[8];\nuniform vec3 uLightingDirection[8];\nuniform vec3 uDirectionalColor[8];\nuniform vec3 uPointLightLocation[8];\nuniform vec3 uPointLightColor[8];\nuniform bool uSpecular;\n\nvarying vec3 vVertexNormal;\nvarying vec2 vVertTexCoord;\nvarying vec3 vLightWeighting;\n\nvec3 ambientLightFactor = vec3(0.0, 0.0, 0.0);\nvec3 directionalLightFactor = vec3(0.0, 0.0, 0.0);\nvec3 pointLightFactor = vec3(0.0, 0.0, 0.0);\nvec3 pointLightFactor2 = vec3(0.0, 0.0, 0.0);\n\nvoid main(void){\n\n vec4 positionVec4 = vec4(aPosition, 1.0);\n gl_Position = uProjectionMatrix * uModelViewMatrix * positionVec4;\n\n vec3 vertexNormal = vec3( uNormalMatrix * aNormal );\n vVertexNormal = vertexNormal;\n vVertTexCoord = aTexCoord;\n\n vec4 mvPosition = uModelViewMatrix * vec4(aPosition, 1.0);\n vec3 eyeDirection = normalize(-mvPosition.xyz);\n\n float shininess = 32.0;\n float specularFactor = 2.0;\n float diffuseFactor = 0.3;\n\n for(int i = 0; i < 8; i++){\n if(uAmbientLightCount == i) break;\n ambientLightFactor += uAmbientColor[i];\n }\n\n for(int j = 0; j < 8; j++){\n if(uDirectionalLightCount == j) break;\n vec3 dir = uLightingDirection[j];\n float directionalLightWeighting = max(dot(vertexNormal, dir), 0.0);\n directionalLightFactor += uDirectionalColor[j] * directionalLightWeighting;\n }\n\n for(int k = 0; k < 8; k++){\n if(uPointLightCount == k) break;\n vec3 loc = uPointLightLocation[k];\n vec3 lightDirection = normalize(loc - mvPosition.xyz);\n\n float directionalLightWeighting = max(dot(vertexNormal, lightDirection), 0.0);\n pointLightFactor += uPointLightColor[k] * directionalLightWeighting;\n\n //factor2 for specular\n vec3 reflectionDirection = reflect(-lightDirection, vertexNormal);\n float specularLightWeighting = pow(max(dot(reflectionDirection, eyeDirection), 0.0), shininess);\n\n pointLightFactor2 += uPointLightColor[k] * (specularFactor * specularLightWeighting\n + directionalLightWeighting * diffuseFactor);\n }\n\n if(!uSpecular){\n vLightWeighting = ambientLightFactor + directionalLightFactor + pointLightFactor;\n }else{\n vLightWeighting = ambientLightFactor + directionalLightFactor + pointLightFactor2;\n }\n\n}\n", lightTextureFrag: "precision mediump float;\n\nuniform vec4 uMaterialColor;\nuniform sampler2D uSampler;\nuniform bool isTexture;\n\nvarying vec3 vLightWeighting;\nvarying highp vec2 vVertTexCoord;\n\nvoid main(void) {\n if(!isTexture){\n gl_FragColor = vec4(vec3(uMaterialColor.rgb * vLightWeighting), uMaterialColor.a);\n }else{\n vec4 textureColor = texture2D(uSampler, vVertTexCoord);\n if(vLightWeighting == vec3(0., 0., 0.)){\n gl_FragColor = textureColor;\n }else{\n gl_FragColor = vec4(vec3(textureColor.rgb * vLightWeighting), textureColor.a);\n }\n }\n}" }; },{}]},{},[5,10,11,13,4,7,14,1,2,6,18,16,17,30,26,29,28,31,24,27,25,23,32,21,22,9])(32) });