1) {
+ message += ' ' + func + ' takes different numbers of parameters ' +
+ 'depending on what you want to do. ' +
+ 'Click this link to learn more:';
+ }
+ report(message, func, WRONG_TYPE);
+ }
+ }
+ }
+};
+/*
+ * NOTE THIS FUNCTION IS TEMPORARILY DISABLED UNTIL FURTHER WORK
+ * AND UPDATES ARE IMPLEMENTED. -LMCCART
+ */
+p5.prototype._validateParameters = function() {
+ return true;
+};
+
+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.indexOf(symbol.name) !== -1) {
+ 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":36,"./core":37}],41:[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 {Object} [pInst] pointer to p5 instance
+ */
+p5.Element = function(elt, pInst) {
+ /**
+ * Underlying HTML element. All normal HTML methods can be called on this.
+ *
+ * @property elt
+ */
+ 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|Object} parent the ID, DOM node, or p5.Element
+ * of desired parent element
+ * @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
+ * @return {p5.Element|String}
+ * @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
+ * @return {p5.Element|String}
+ */
+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.
+ * @return {p5.Element}
+ * @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 .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 callabck 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.
+ * @return {p5.Element}
+ * @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.
+ * @return {p5.Element}
+ * @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.
+ * @return {p5.Element}
+ * @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.
+ * @return {p5.Element}
+ * @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.
+ * @return {p5.Element}
+ * @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.
+ * @return {p5.Element}
+ * @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.
+ * @return {p5.Element}
+ * @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.
+ * @return {p5.Element}
+ * @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.
+ * @return {p5.Element}
+ * @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.
+ * @return {p5.Element}
+ * @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.
+ * @return {p5.Element}
+ * @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.
+ * @return {p5.Element}
+ */
+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.
+ * @return {p5.Element}
+ */
+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 triggered when files are dropped.
+ * @param {Function} callback to receive loaded file.
+ * @return {p5.Element}
+ * @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":37}],42:[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 {String} elt DOM node that is wrapped
+ * @param {Object} [pInst] pointer to p5 instance
+ * @param {Boolean} whether we're using it as main canvas
+ */
+p5.Graphics = function(w, h, renderer, pInst) {
+
+ var r = renderer || constants.P2D;
+
+ var c = document.createElement('canvas');
+ var node = this._userNode || document.body;
+ node.appendChild(c);
+
+ p5.Element.call(this, c, pInst, false);
+ this._styles = [];
+ this.width = w;
+ this.height = h;
+ this._pixelDensity = pInst._pixelDensity;
+
+ if (r === constants.WEBGL) {
+ this._renderer = new p5.RendererGL(c, this, false);
+ } else {
+ this._renderer = new p5.Renderer2D(c, 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);
+
+module.exports = p5.Graphics;
+
+},{"./constants":36,"./core":37}],43:[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 {Object} [pInst] pointer to p5 instance
+ * @param {Boolean} 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":36,"./core":37}],44:[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.drawingContext.fillStyle = constants._DEFAULT_FILL;
+ this.drawingContext.strokeStyle = 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.drawingContext.fillStyle;
+ // create background rect
+ var color = this._pInst.color.apply(this, arguments);
+ var newFill = color.toString();
+ this.drawingContext.fillStyle = newFill;
+ this.drawingContext.fillRect(0, 0, this.width, this.height);
+ // reset fill
+ this.drawingContext.fillStyle = curFill;
+ }
+ this.drawingContext.restore();
+};
+
+p5.Renderer2D.prototype.clear = function() {
+ this.drawingContext.clearRect(0, 0, this.width, this.height);
+};
+
+p5.Renderer2D.prototype.fill = function() {
+
+ var ctx = this.drawingContext;
+ var color = this._pInst.color.apply(this, arguments);
+ ctx.fillStyle = color.toString();
+};
+
+p5.Renderer2D.prototype.stroke = function() {
+ var ctx = this.drawingContext;
+ var color = this._pInst.color.apply(this, arguments);
+ ctx.strokeStyle = 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) {
+ if (!srcImage.canvas) {
+ 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;
+
+ var pd = ctx._pixelDensity;
+
+ // round down to get integer numbers
+ x = Math.floor(x);
+ y = Math.floor(y);
+
+ 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(ctx.fillStyle === styleEmpty) {
+ return this;
+ }
+ } else if (!doFill && doStroke) {
+ if(ctx.strokeStyle === 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(ctx.strokeStyle === 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;
+ var s = ctx.strokeStyle;
+ var f = ctx.fillStyle;
+ if (!this._doStroke) {
+ return this;
+ } else if(ctx.strokeStyle === styleEmpty){
+ return this;
+ }
+ x = Math.round(x);
+ y = Math.round(y);
+ ctx.fillStyle = s;
+ 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);
+ }
+ ctx.fillStyle = f;
+};
+
+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(ctx.fillStyle === styleEmpty) {
+ return this;
+ }
+ } else if (!doFill && doStroke) {
+ if(ctx.strokeStyle === 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(ctx.fillStyle === styleEmpty) {
+ return this;
+ }
+ } else if (!doFill && doStroke) {
+ if(ctx.strokeStyle === 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(ctx.fillStyle === styleEmpty) {
+ return this;
+ }
+ } else if (!doFill && doStroke) {
+ if(ctx.strokeStyle === 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) {
+ this.drawingContext.beginPath();
+ this.drawingContext.moveTo(vertices[0][0], vertices[0][1]);
+ this.drawingContext.lineTo(vertices[1][0], vertices[1][1]);
+ this.drawingContext.lineTo(vertices[2][0], vertices[2][1]);
+ if (this._doFill) {
+ this._pInst.fill(vertices[2][5]);
+ }
+ if (this._doStroke) {
+ this._pInst.stroke(vertices[2][6]);
+ }
+ this._doFillStrokeClose();
+ for (i = 3; i < numVerts; i++) {
+ v = vertices[i];
+ this.drawingContext.beginPath();
+ 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]);
+ if (this._doFill) {
+ this._pInst.fill(v[5]);
+ }
+ if (this._doStroke) {
+ this._pInst.stroke(v[6]);
+ }
+ 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.drawingContext.fillStyle;
+};
+
+p5.Renderer2D.prototype._getStroke = function(){
+ return this.drawingContext.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
+ this.drawingContext.fillStyle = this._fillSet ?
+ this.drawingContext.fillStyle : 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();
+};
+
+module.exports = p5.Renderer2D;
+
+},{"../image/filters":54,"./canvas":35,"./constants":36,"./core":37,"./p5.Renderer":43}],45:[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] P2D or WEBGL
+ * @return {Object} 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
+ * @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] P2D or WEBGL
+ * undefined defaults to p2d
+ * @return {Object} 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
+ * @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":86,"./constants":36,"./core":37,"./p5.Graphics":42,"./p5.Renderer2D":44}],46:[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
+ });
+ }
+}());
+
+},{}],47:[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":37}],48:[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
+ * @return {p5} the p5 object
+ * @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
+ * @return {p5} the p5 object
+ * @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
+ * @return {p5} the p5 object
+ * @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
+ *
+ */
+/**
+ * @method rotate
+ * @param {Number} rad angle in radians
+ * @param {p5.Vector | Array} axis axis to rotate around
+ * @return {p5.RendererGL} [description]
+ */
+p5.prototype.rotate = function() {
+ 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
+ * @return {[type]} [description]
+ */
+p5.prototype.rotateX = function(rad) {
+ var args = new Array(arguments.length);
+ for (var i = 0; i < args.length; ++i) {
+ args[i] = arguments[i];
+ }
+ if (this._renderer.isP3D) {
+ this._validateParameters(
+ 'rotateX',
+ args,
+ [
+ ['Number']
+ ]
+ );
+ 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
+ * @return {[type]} [description]
+ */
+p5.prototype.rotateY = function(rad) {
+ if (this._renderer.isP3D) {
+ var args = new Array(arguments.length);
+ for (var i = 0; i < args.length; ++i) {
+ args[i] = arguments[i];
+ }
+ this._validateParameters(
+ 'rotateY',
+ args,
+ [
+ ['Number']
+ ]
+ );
+ 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
+ * @return {[type]} [description]
+ */
+p5.prototype.rotateZ = function(rad) {
+ if (this._renderer.isP3D) {
+ var args = new Array(arguments.length);
+ for (var i = 0; i < args.length; ++i) {
+ args[i] = arguments[i];
+ }
+ this._validateParameters(
+ 'rotateZ',
+ args,
+ [
+ ['Number']
+ ]
+ );
+ 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)
+ * @return {p5} the p5 object
+ * @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];
+ }
+ 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
+ * @return {p5} the p5 object
+ * @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
+ * @return {p5} the p5 object
+ * @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)
+ * @return {p5} the p5 object
+ * @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) {
+ var args = new Array(arguments.length);
+ for (var i = 0; i < args.length; ++i) {
+ args[i] = arguments[i];
+ }
+
+ if (this._renderer.isP3D) {
+ this._validateParameters(
+ 'translate',
+ args,
+ [
+ //p3d
+ ['Number', 'Number', 'Number']
+ ]
+ );
+ this._renderer.translate(x, y, z);
+ } else {
+ this._validateParameters(
+ 'translate',
+ args,
+ [
+ //p2d
+ ['Number', 'Number']
+ ]
+ );
+ this._renderer.translate(x, y);
+ }
+ return this;
+};
+
+module.exports = p5;
+
+},{"./constants":36,"./core":37}],49:[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
+ * @return {Object} the p5 object
+ * @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
+ * @return {Object} the p5 object
+ * @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
+ * @return {Object} the p5 object
+ * @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
+ * @return {Object} the p5 object
+ * @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
+ * @return {Object} the p5 object
+ * @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
+ * @return {Object} the p5 object
+ * @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
+ * @return {Object} the p5 object
+ * @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
+ * @return {Object} the p5 object
+ * @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) {
+ var args = new Array(arguments.length);
+ for (var i = 0; i < args.length; ++i) {
+ args[i] = arguments[i];
+ }
+ if(this._renderer.isP3D){
+ this._validateParameters(
+ 'vertex',
+ args,
+ [
+ ['Number', 'Number', 'Number']
+ ]
+ );
+ this._renderer.vertex
+ (arguments[0], arguments[1], arguments[2]);
+ }else{
+ this._validateParameters(
+ 'vertex',
+ args,
+ [
+ ['Number', 'Number'],
+ ['Number', 'Number', 'Number']
+ ]
+ );
+ 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":36,"./core":37}],50:[function(_dereq_,module,exports){
+/**
+ * @module Events
+ * @submodule Acceleration
+ * @for p5
+ * @requires core
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+
+/**
+ * The system variable deviceOrientation always contains the orientation of
+ * the device. The value of this variable will either be set 'landscape'
+ * or 'portrait'. If no data is available it will be set to 'undefined'.
+ *
+ * @property deviceOrientation
+ */
+p5.prototype.deviceOrientation = undefined;
+
+/**
+ * The system variable accelerationX always contains the acceleration of the
+ * device along the x axis. Value is represented as meters per second squared.
+ *
+ * @property accelerationX
+ */
+p5.prototype.accelerationX = 0;
+
+/**
+ * The system variable accelerationY always contains the acceleration of the
+ * device along the y axis. Value is represented as meters per second squared.
+ *
+ * @property accelerationY
+ */
+p5.prototype.accelerationY = 0;
+
+/**
+ * The system variable accelerationZ always contains the acceleration of the
+ * device along the z axis. Value is represented as meters per second squared.
+ *
+ * @property accelerationZ
+ */
+p5.prototype.accelerationZ = 0;
+
+/**
+ * The system variable pAccelerationX always contains the acceleration of the
+ * device along the x axis in the frame previous to the current frame. Value
+ * is represented as meters per second squared.
+ *
+ * @property pAccelerationX
+ */
+p5.prototype.pAccelerationX = 0;
+
+/**
+ * The system variable pAccelerationY always contains the acceleration of the
+ * device along the y axis in the frame previous to the current frame. Value
+ * is represented as meters per second squared.
+ *
+ * @property pAccelerationY
+ */
+p5.prototype.pAccelerationY = 0;
+
+/**
+ * The system variable pAccelerationZ always contains the acceleration of the
+ * device along the z axis in the frame previous to the current frame. Value
+ * is represented as meters per second squared.
+ *
+ * @property pAccelerationZ
+ */
+p5.prototype.pAccelerationZ = 0;
+
+/**
+ * _updatePAccelerations updates the pAcceleration values
+ *
+ * @private
+ */
+p5.prototype._updatePAccelerations = function(){
+ this._setProperty('pAccelerationX', this.accelerationX);
+ this._setProperty('pAccelerationY', this.accelerationY);
+ this._setProperty('pAccelerationZ', this.accelerationZ);
+};
+
+/**
+ * The system variable rotationX always contains the rotation of the
+ * device along the x axis. Value is represented as 0 to +/-180 degrees.
+ *
+ * Note: The order the rotations are called is important, ie. if used
+ * together, it must be called in the order Z-X-Y or there might be
+ * unexpected behaviour.
+ *
+ * @example
+ *
+ *
+ * function setup(){
+ * createCanvas(100, 100, WEBGL);
+ * }
+ *
+ * function draw(){
+ * background(200);
+ * //rotateZ(radians(rotationZ));
+ * rotateX(radians(rotationX));
+ * //rotateY(radians(rotationY));
+ * box(200, 200, 200);
+ * }
+ *
+ *
+ *
+ * @property rotationX
+ *
+ * @alt
+ * red horizontal line right, green vertical line bottom. black background.
+ *
+ */
+p5.prototype.rotationX = 0;
+
+/**
+ * The system variable rotationY always contains the rotation of the
+ * device along the y axis. Value is represented as 0 to +/-90 degrees.
+ *
+ * Note: The order the rotations are called is important, ie. if used
+ * together, it must be called in the order Z-X-Y or there might be
+ * unexpected behaviour.
+ *
+ * @example
+ *
+ *
+ * function setup(){
+ * createCanvas(100, 100, WEBGL);
+ * }
+ *
+ * function draw(){
+ * background(200);
+ * //rotateZ(radians(rotationZ));
+ * //rotateX(radians(rotationX));
+ * rotateY(radians(rotationY));
+ * box(200, 200, 200);
+ * }
+ *
+ *
+ *
+ * @property rotationY
+ *
+ * @alt
+ * red horizontal line right, green vertical line bottom. black background.
+ */
+p5.prototype.rotationY = 0;
+
+/**
+ * The system variable rotationZ always contains the rotation of the
+ * device along the z axis. Value is represented as 0 to 359 degrees.
+ *
+ * Unlike rotationX and rotationY, this variable is available for devices
+ * with a built-in compass only.
+ *
+ * Note: The order the rotations are called is important, ie. if used
+ * together, it must be called in the order Z-X-Y or there might be
+ * unexpected behaviour.
+ *
+ * @example
+ *
+ *
+ * function setup(){
+ * createCanvas(100, 100, WEBGL);
+ * }
+ *
+ * function draw(){
+ * background(200);
+ * rotateZ(radians(rotationZ));
+ * //rotateX(radians(rotationX));
+ * //rotateY(radians(rotationY));
+ * box(200, 200, 200);
+ * }
+ *
+ *
+ *
+ * @property rotationZ
+ *
+ * @alt
+ * red horizontal line right, green vertical line bottom. black background.
+ */
+p5.prototype.rotationZ = 0;
+
+/**
+ * The system variable pRotationX always contains the rotation of the
+ * device along the x axis in the frame previous to the current frame. Value
+ * is represented as 0 to +/-180 degrees.
+ *
+ * pRotationX can also be used with rotationX to determine the rotate
+ * direction of the device along the X-axis.
+ * @example
+ *
+ *
+ * // A simple if statement looking at whether
+ * // rotationX - pRotationX < 0 is true or not will be
+ * // sufficient for determining the rotate direction
+ * // in most cases.
+ *
+ * // Some extra logic is needed to account for cases where
+ * // the angles wrap around.
+ * var rotateDirection = 'clockwise';
+ *
+ * // Simple range conversion to make things simpler.
+ * // This is not absolutely neccessary but the logic
+ * // will be different in that case.
+ *
+ * var rX = rotationX + 180;
+ * var pRX = pRotationX + 180;
+ *
+ * if ((rX - pRX > 0 && rX - pRX < 270)|| rX - pRX < -270){
+ * rotateDirection = 'clockwise';
+ * } else if (rX - pRX < 0 || rX - pRX > 270){
+ * rotateDirection = 'counter-clockwise';
+ * }
+ *
+ *
+ *
+ * @alt
+ * no image to display.
+ *
+ *
+ * @property pRotationX
+ */
+p5.prototype.pRotationX = 0;
+
+/**
+ * The system variable pRotationY always contains the rotation of the
+ * device along the y axis in the frame previous to the current frame. Value
+ * is represented as 0 to +/-90 degrees.
+ *
+ * pRotationY can also be used with rotationY to determine the rotate
+ * direction of the device along the Y-axis.
+ * @example
+ *
+ *
+ * // A simple if statement looking at whether
+ * // rotationY - pRotationY < 0 is true or not will be
+ * // sufficient for determining the rotate direction
+ * // in most cases.
+ *
+ * // Some extra logic is needed to account for cases where
+ * // the angles wrap around.
+ * var rotateDirection = 'clockwise';
+ *
+ * // Simple range conversion to make things simpler.
+ * // This is not absolutely neccessary but the logic
+ * // will be different in that case.
+ *
+ * var rY = rotationY + 180;
+ * var pRY = pRotationY + 180;
+ *
+ * if ((rY - pRY > 0 && rY - pRY < 270)|| rY - pRY < -270){
+ * rotateDirection = 'clockwise';
+ * } else if (rY - pRY < 0 || rY - pRY > 270){
+ * rotateDirection = 'counter-clockwise';
+ * }
+ *
+ *
+ *
+ * @alt
+ * no image to display.
+ *
+ *
+ * @property pRotationY
+ */
+p5.prototype.pRotationY = 0;
+
+/**
+ * The system variable pRotationZ always contains the rotation of the
+ * device along the z axis in the frame previous to the current frame. Value
+ * is represented as 0 to 359 degrees.
+ *
+ * pRotationZ can also be used with rotationZ to determine the rotate
+ * direction of the device along the Z-axis.
+ * @example
+ *
+ *
+ * // A simple if statement looking at whether
+ * // rotationZ - pRotationZ < 0 is true or not will be
+ * // sufficient for determining the rotate direction
+ * // in most cases.
+ *
+ * // Some extra logic is needed to account for cases where
+ * // the angles wrap around.
+ * var rotateDirection = 'clockwise';
+ *
+ * if ((rotationZ - pRotationZ > 0 &&
+ * rotationZ - pRotationZ < 270)||
+ * rotationZ - pRotationZ < -270){
+ *
+ * rotateDirection = 'clockwise';
+ *
+ * } else if (rotationZ - pRotationZ < 0 ||
+ * rotationZ - pRotationZ > 270){
+ *
+ * rotateDirection = 'counter-clockwise';
+ *
+ * }
+ *
+ *
+ *
+ * @alt
+ * no image to display.
+ *
+ *
+ * @property pRotationZ
+ */
+p5.prototype.pRotationZ = 0;
+
+var startAngleX = 0;
+var startAngleY = 0;
+var startAngleZ = 0;
+
+var rotateDirectionX = 'clockwise';
+var rotateDirectionY = 'clockwise';
+var rotateDirectionZ = 'clockwise';
+
+var pRotateDirectionX;
+var pRotateDirectionY;
+var pRotateDirectionZ;
+
+p5.prototype._updatePRotations = function(){
+ this._setProperty('pRotationX', this.rotationX);
+ this._setProperty('pRotationY', this.rotationY);
+ this._setProperty('pRotationZ', this.rotationZ);
+};
+
+p5.prototype.turnAxis = undefined;
+
+var move_threshold = 0.5;
+var shake_threshold = 30;
+
+/**
+ * The setMoveThreshold() function is used to set the movement threshold for
+ * the deviceMoved() function. The default threshold is set to 0.5.
+ *
+ * @method setMoveThreshold
+ * @param {number} value The threshold value
+ */
+p5.prototype.setMoveThreshold = function(val){
+ if(typeof val === 'number'){
+ move_threshold = val;
+ }
+};
+
+/**
+ * The setShakeThreshold() function is used to set the movement threshold for
+ * the deviceShaken() function. The default threshold is set to 30.
+ *
+ * @method setShakeThreshold
+ * @param {number} value The threshold value
+ */
+p5.prototype.setShakeThreshold = function(val){
+ if(typeof val === 'number'){
+ shake_threshold = val;
+ }
+};
+
+/**
+ * The deviceMoved() function is called when the device is moved by more than
+ * the threshold value along X, Y or Z axis. The default threshold is set to
+ * 0.5.
+ * @method deviceMoved
+ * @example
+ *
+ *
+ * // Run this example on a mobile device
+ * // Move the device around
+ * // to change the value.
+ *
+ * var value = 0;
+ * function draw() {
+ * fill(value);
+ * rect(25, 25, 50, 50);
+ * }
+ * function deviceMoved() {
+ * value = value + 5;
+ * if (value > 255) {
+ * value = 0;
+ * }
+ * }
+ *
+ *
+ *
+ * @alt
+ * 50x50 black rect in center of canvas. turns white on mobile when device moves
+ *
+ */
+
+/**
+ * The deviceTurned() function is called when the device rotates by
+ * more than 90 degrees continuously.
+ *
+ * The axis that triggers the deviceTurned() method is stored in the turnAxis
+ * variable. The deviceTurned() method can be locked to trigger on any axis:
+ * X, Y or Z by comparing the turnAxis variable to 'X', 'Y' or 'Z'.
+ *
+ * @method deviceTurned
+ * @example
+ *
+ *
+ * // Run this example on a mobile device
+ * // Rotate the device by 90 degrees
+ * // to change the value.
+ *
+ * var value = 0;
+ * function draw() {
+ * fill(value);
+ * rect(25, 25, 50, 50);
+ * }
+ * function deviceTurned() {
+ * if (value == 0){
+ * value = 255
+ * } else if (value == 255) {
+ * value = 0;
+ * }
+ * }
+ *
+ *
+ *
+ *
+ * // Run this example on a mobile device
+ * // Rotate the device by 90 degrees in the
+ * // X-axis to change the value.
+ *
+ * var value = 0;
+ * function draw() {
+ * fill(value);
+ * rect(25, 25, 50, 50);
+ * }
+ * function deviceTurned() {
+ * if (turnAxis == 'X'){
+ * if (value == 0){
+ * value = 255
+ * } else if (value == 255) {
+ * value = 0;
+ * }
+ * }
+ * }
+ *
+ *
+ *
+ * @alt
+ * 50x50 black rect in center of canvas. turns white on mobile when device turns
+ * 50x50 black rect in center of canvas. turns white on mobile when x-axis turns
+ *
+ */
+
+/**
+ * The deviceShaken() function is called when the device total acceleration
+ * changes of accelerationX and accelerationY values is more than
+ * the threshold value. The default threshold is set to 30.
+ * @method deviceShaken
+ * @example
+ *
+ *
+ * // Run this example on a mobile device
+ * // Shake the device to change the value.
+ *
+ * var value = 0;
+ * function draw() {
+ * fill(value);
+ * rect(25, 25, 50, 50);
+ * }
+ * function deviceShaken() {
+ * value = value + 5;
+ * if (value > 255) {
+ * value = 0;
+ * }
+ * }
+ *
+ *
+ *
+ * @alt
+ * 50x50 black rect in center of canvas. turns white on mobile when device shakes
+ *
+ */
+
+p5.prototype._ondeviceorientation = function (e) {
+ this._updatePRotations();
+ this._setProperty('rotationX', e.beta);
+ this._setProperty('rotationY', e.gamma);
+ this._setProperty('rotationZ', e.alpha);
+ this._handleMotion();
+};
+p5.prototype._ondevicemotion = function (e) {
+ this._updatePAccelerations();
+ this._setProperty('accelerationX', e.acceleration.x * 2);
+ this._setProperty('accelerationY', e.acceleration.y * 2);
+ this._setProperty('accelerationZ', e.acceleration.z * 2);
+ this._handleMotion();
+};
+p5.prototype._handleMotion = function() {
+ if (window.orientation === 90 || window.orientation === -90) {
+ this._setProperty('deviceOrientation', 'landscape');
+ } else if (window.orientation === 0) {
+ this._setProperty('deviceOrientation', 'portrait');
+ } else if (window.orientation === undefined) {
+ this._setProperty('deviceOrientation', 'undefined');
+ }
+ var deviceMoved = this.deviceMoved || window.deviceMoved;
+ if (typeof deviceMoved === 'function') {
+ if (Math.abs(this.accelerationX - this.pAccelerationX) > move_threshold ||
+ Math.abs(this.accelerationY - this.pAccelerationY) > move_threshold ||
+ Math.abs(this.accelerationZ - this.pAccelerationZ) > move_threshold) {
+ deviceMoved();
+ }
+ }
+ var deviceTurned = this.deviceTurned || window.deviceTurned;
+ if (typeof deviceTurned === 'function') {
+ // The angles given by rotationX etc is from range -180 to 180.
+ // The following will convert them to 0 to 360 for ease of calculation
+ // of cases when the angles wrapped around.
+ // _startAngleX will be converted back at the end and updated.
+ var wRX = this.rotationX + 180;
+ var wPRX = this.pRotationX + 180;
+ var wSAX = startAngleX + 180;
+ if ((wRX - wPRX > 0 && wRX - wPRX < 270)|| wRX - wPRX < -270){
+ rotateDirectionX = 'clockwise';
+ } else if (wRX - wPRX < 0 || wRX - wPRX > 270){
+ rotateDirectionX = 'counter-clockwise';
+ }
+ if (rotateDirectionX !== pRotateDirectionX){
+ wSAX = wRX;
+ }
+ if (Math.abs(wRX - wSAX) > 90 && Math.abs(wRX - wSAX) < 270){
+ wSAX = wRX;
+ this._setProperty('turnAxis', 'X');
+ deviceTurned();
+ }
+ pRotateDirectionX = rotateDirectionX;
+ startAngleX = wSAX - 180;
+
+ // Y-axis is identical to X-axis except for changing some names.
+ var wRY = this.rotationY + 180;
+ var wPRY = this.pRotationY + 180;
+ var wSAY = startAngleY + 180;
+ if ((wRY - wPRY > 0 && wRY - wPRY < 270)|| wRY - wPRY < -270){
+ rotateDirectionY = 'clockwise';
+ } else if (wRY - wPRY < 0 || wRY - this.pRotationY > 270){
+ rotateDirectionY = 'counter-clockwise';
+ }
+ if (rotateDirectionY !== pRotateDirectionY){
+ wSAY = wRY;
+ }
+ if (Math.abs(wRY - wSAY) > 90 && Math.abs(wRY - wSAY) < 270){
+ wSAY = wRY;
+ this._setProperty('turnAxis', 'Y');
+ deviceTurned();
+ }
+ pRotateDirectionY = rotateDirectionY;
+ startAngleY = wSAY - 180;
+
+ // Z-axis is already in the range 0 to 360
+ // so no conversion is needed.
+ if ((this.rotationZ - this.pRotationZ > 0 &&
+ this.rotationZ - this.pRotationZ < 270)||
+ this.rotationZ - this.pRotationZ < -270){
+ rotateDirectionZ = 'clockwise';
+ } else if (this.rotationZ - this.pRotationZ < 0 ||
+ this.rotationZ - this.pRotationZ > 270){
+ rotateDirectionZ = 'counter-clockwise';
+ }
+ if (rotateDirectionZ !== pRotateDirectionZ){
+ startAngleZ = this.rotationZ;
+ }
+ if (Math.abs(this.rotationZ - startAngleZ) > 90 &&
+ Math.abs(this.rotationZ - startAngleZ) < 270){
+ startAngleZ = this.rotationZ;
+ this._setProperty('turnAxis', 'Z');
+ deviceTurned();
+ }
+ pRotateDirectionZ = rotateDirectionZ;
+ this._setProperty('turnAxis', undefined);
+ }
+ var deviceShaken = this.deviceShaken || window.deviceShaken;
+ if (typeof deviceShaken === 'function') {
+ var accelerationChangeX;
+ var accelerationChangeY;
+ // Add accelerationChangeZ if acceleration change on Z is needed
+ if (this.pAccelerationX !== null) {
+ accelerationChangeX = Math.abs(this.accelerationX - this.pAccelerationX);
+ accelerationChangeY = Math.abs(this.accelerationY - this.pAccelerationY);
+ }
+ if (accelerationChangeX + accelerationChangeY > shake_threshold) {
+ deviceShaken();
+ }
+ }
+};
+
+
+module.exports = p5;
+
+},{"../core/core":37}],51:[function(_dereq_,module,exports){
+/**
+ * @module Events
+ * @submodule Keyboard
+ * @for p5
+ * @requires core
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+
+/**
+ * Holds the key codes of currently pressed keys.
+ * @private
+ */
+var downKeys = {};
+
+/**
+ * The boolean system variable keyIsPressed is true if any key is pressed
+ * and false if no keys are pressed.
+ *
+ * @property keyIsPressed
+ * @example
+ *
+ *
+ * var value = 0;
+ * function draw() {
+ * if (keyIsPressed === true) {
+ * fill(0);
+ * } else {
+ * fill(255);
+ * }
+ * rect(25, 25, 50, 50);
+ * }
+ *
+ *
+ *
+ * @alt
+ * 50x50 white rect that turns black on keypress.
+ *
+ */
+p5.prototype.isKeyPressed = false;
+p5.prototype.keyIsPressed = false; // khan
+
+/**
+ * The system variable key always contains the value of the most recent
+ * key on the keyboard that was typed. To get the proper capitalization, it
+ * is best to use it within keyTyped(). For non-ASCII keys, use the keyCode
+ * variable.
+ *
+ * @property key
+ * @example
+ *
+ * // Click any key to display it!
+ * // (Not Guaranteed to be Case Sensitive)
+ * function setup() {
+ * fill(245, 123, 158);
+ * textSize(50);
+ * }
+ *
+ * function draw() {
+ * background(200);
+ * text(key, 33,65); // Display last key pressed.
+ * }
+ *
+ *
+ * @alt
+ * canvas displays any key value that is pressed in pink font.
+ *
+ */
+p5.prototype.key = '';
+
+/**
+ * The variable keyCode is used to detect special keys such as BACKSPACE,
+ * DELETE, ENTER, RETURN, TAB, ESCAPE, SHIFT, CONTROL, OPTION, ALT, UP_ARROW,
+ * DOWN_ARROW, LEFT_ARROW, RIGHT_ARROW.
+ *
+ * @property keyCode
+ * @example
+ *
+ * var fillVal = 126;
+ * function draw() {
+ * fill(fillVal);
+ * rect(25, 25, 50, 50);
+ * }
+ *
+ * function keyPressed() {
+ * if (keyCode == UP_ARROW) {
+ * fillVal = 255;
+ * } else if (keyCode == DOWN_ARROW) {
+ * fillVal = 0;
+ * }
+ * return false; // prevent default
+ * }
+ *
+ *
+ * @alt
+ * Grey rect center. turns white when up arrow pressed and black when down
+ *
+ */
+p5.prototype.keyCode = 0;
+
+/**
+ * The keyPressed() function is called once every time a key is pressed. The
+ * keyCode for the key that was pressed is stored in the keyCode variable.
+ *
+ * For non-ASCII keys, use the keyCode variable. You can check if the keyCode
+ * equals BACKSPACE, DELETE, ENTER, RETURN, TAB, ESCAPE, SHIFT, CONTROL,
+ * OPTION, ALT, UP_ARROW, DOWN_ARROW, LEFT_ARROW, RIGHT_ARROW.
+ *
+ * For ASCII keys that was pressed is stored in the key variable. However, it
+ * does not distinguish between uppercase and lowercase. For this reason, it
+ * is recommended to use keyTyped() to read the key variable, in which the
+ * case of the variable will be distinguished.
+ *
+ * Because of how operating systems handle key repeats, holding down a key
+ * may cause multiple calls to keyTyped() (and keyReleased() as well). The
+ * rate of repeat is set by the operating system and how each computer is
+ * configured.
+ * Browsers may have different default
+ * behaviors attached to various key events. To prevent any default
+ * behavior for this event, add "return false" to the end of the method.
+ *
+ * @method keyPressed
+ * @example
+ *
+ *
+ * var value = 0;
+ * function draw() {
+ * fill(value);
+ * rect(25, 25, 50, 50);
+ * }
+ * function keyPressed() {
+ * if (value === 0) {
+ * value = 255;
+ * } else {
+ * value = 0;
+ * }
+ * }
+ *
+ *
+ *
+ *
+ * var value = 0;
+ * function draw() {
+ * fill(value);
+ * rect(25, 25, 50, 50);
+ * }
+ * function keyPressed() {
+ * if (keyCode === LEFT_ARROW) {
+ * value = 255;
+ * } else if (keyCode === RIGHT_ARROW) {
+ * value = 0;
+ * }
+ * }
+ *
+ *
+ *
+ *
+ * function keyPressed(){
+ * // Do something
+ * return false; // prevent any default behaviour
+ * }
+ *
+ *
+ * @alt
+ * black rect center. turns white when key pressed and black when released
+ * black rect center. turns white when left arrow pressed and black when right.
+ *
+ *
+ */
+p5.prototype._onkeydown = function (e) {
+ if (downKeys[e.which]) { // prevent multiple firings
+ return;
+ }
+ this._setProperty('isKeyPressed', true);
+ this._setProperty('keyIsPressed', true);
+ this._setProperty('keyCode', e.which);
+ downKeys[e.which] = true;
+ var key = String.fromCharCode(e.which);
+ if (!key) {
+ key = e.which;
+ }
+ this._setProperty('key', key);
+ var keyPressed = this.keyPressed || window.keyPressed;
+ if (typeof keyPressed === 'function' && !e.charCode) {
+ var executeDefault = keyPressed(e);
+ if(executeDefault === false) {
+ e.preventDefault();
+ }
+ }
+};
+/**
+ * The keyReleased() function is called once every time a key is released.
+ * See key and keyCode for more information.
+ * Browsers may have different default
+ * behaviors attached to various key events. To prevent any default
+ * behavior for this event, add "return false" to the end of the method.
+ *
+ * @method keyReleased
+ * @example
+ *
+ *
+ * var value = 0;
+ * function draw() {
+ * fill(value);
+ * rect(25, 25, 50, 50);
+ * }
+ * function keyReleased() {
+ * if (value === 0) {
+ * value = 255;
+ * } else {
+ * value = 0;
+ * }
+ * return false; // prevent any default behavior
+ * }
+ *
+ *
+ *
+ * @alt
+ * black rect center. turns white when key pressed and black when pressed again
+ *
+ */
+p5.prototype._onkeyup = function (e) {
+ var keyReleased = this.keyReleased || window.keyReleased;
+ this._setProperty('isKeyPressed', false);
+ this._setProperty('keyIsPressed', false);
+ this._setProperty('_lastKeyCodeTyped', null);
+ downKeys[e.which] = false;
+ //delete this._downKeys[e.which];
+ var key = String.fromCharCode(e.which);
+ if (!key) {
+ key = e.which;
+ }
+ this._setProperty('key', key);
+ this._setProperty('keyCode', e.which);
+ if (typeof keyReleased === 'function') {
+ var executeDefault = keyReleased(e);
+ if(executeDefault === false) {
+ e.preventDefault();
+ }
+ }
+};
+
+/**
+ * The keyTyped() function is called once every time a key is pressed, but
+ * action keys such as Ctrl, Shift, and Alt are ignored. The most recent
+ * key pressed will be stored in the key variable.
+ *
+ * Because of how operating systems handle key repeats, holding down a key
+ * will cause multiple calls to keyTyped() (and keyReleased() as well). The
+ * rate of repeat is set by the operating system and how each computer is
+ * configured.
+ * Browsers may have different default behaviors attached to various key
+ * events. To prevent any default behavior for this event, add "return false"
+ * to the end of the method.
+ *
+ * @method keyTyped
+ * @example
+ *
+ *
+ * var value = 0;
+ * function draw() {
+ * fill(value);
+ * rect(25, 25, 50, 50);
+ * }
+ * function keyTyped() {
+ * if (key === 'a') {
+ * value = 255;
+ * } else if (key === 'b') {
+ * value = 0;
+ * }
+ * // uncomment to prevent any default behavior
+ * // return false;
+ * }
+ *
+ *
+ *
+ * @alt
+ * black rect center. turns white when 'a' key typed and black when 'b' pressed
+ *
+ */
+p5.prototype._onkeypress = function (e) {
+ if (e.which === this._lastKeyCodeTyped) { // prevent multiple firings
+ return;
+ }
+ this._setProperty('keyCode', e.which);
+ this._setProperty('_lastKeyCodeTyped', e.which); // track last keyCode
+ this._setProperty('key', String.fromCharCode(e.which));
+ var keyTyped = this.keyTyped || window.keyTyped;
+ if (typeof keyTyped === 'function') {
+ var executeDefault = keyTyped(e);
+ if(executeDefault === false) {
+ e.preventDefault();
+ }
+ }
+};
+/**
+ * The onblur function is called when the user is no longer focused
+ * on the p5 element. Because the keyup events will not fire if the user is
+ * not focused on the element we must assume all keys currently down have
+ * been released.
+ */
+p5.prototype._onblur = function (e) {
+ downKeys = {};
+};
+
+/**
+ * The keyIsDown() function checks if the key is currently down, i.e. pressed.
+ * It can be used if you have an object that moves, and you want several keys
+ * to be able to affect its behaviour simultaneously, such as moving a
+ * sprite diagonally. You can put in any number representing the keyCode of
+ * the key, or use any of the variable keyCode names listed
+ * here.
+ *
+ * @method keyIsDown
+ * @param {Number} code The key to check for.
+ * @return {Boolean} whether key is down or not
+ * @example
+ *
+ * var x = 100;
+ * var y = 100;
+ *
+ * function setup() {
+ * createCanvas(512, 512);
+ * }
+ *
+ * function draw() {
+ * if (keyIsDown(LEFT_ARROW))
+ * x-=5;
+ *
+ * if (keyIsDown(RIGHT_ARROW))
+ * x+=5;
+ *
+ * if (keyIsDown(UP_ARROW))
+ * y-=5;
+ *
+ * if (keyIsDown(DOWN_ARROW))
+ * y+=5;
+ *
+ * clear();
+ * fill(255, 0, 0);
+ * ellipse(x, y, 50, 50);
+ * }
+ *
+ *
+ * @alt
+ * 50x50 red ellipse moves left, right, up and down with arrow presses.
+ *
+ */
+p5.prototype.keyIsDown = function(code) {
+ return downKeys[code];
+};
+
+module.exports = p5;
+
+},{"../core/core":37}],52:[function(_dereq_,module,exports){
+/**
+ * @module Events
+ * @submodule Mouse
+ * @for p5
+ * @requires core
+ * @requires constants
+ */
+
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+var constants = _dereq_('../core/constants');
+
+/*
+ * This is a flag which is false until the first time
+ * we receive a mouse event. The pmouseX and pmouseY
+ * values will match the mouseX and mouseY values until
+ * this interaction takes place.
+ */
+p5.prototype._hasMouseInteracted = false;
+
+/**
+ * The system variable mouseX always contains the current horizontal
+ * position of the mouse, relative to (0, 0) of the canvas.
+ *
+ * @property mouseX
+ *
+ * @example
+ *
+ *
+ * // Move the mouse across the canvas
+ * function draw() {
+ * background(244, 248, 252);
+ * line(mouseX, 0, mouseX, 100);
+ * }
+ *
+ *
+ *
+ * @alt
+ * horizontal black line moves left and right with mouse x-position
+ *
+ */
+p5.prototype.mouseX = 0;
+
+/**
+ * The system variable mouseY always contains the current vertical position
+ * of the mouse, relative to (0, 0) of the canvas.
+ *
+ * @property mouseY
+ *
+ * @example
+ *
+ *
+ * // Move the mouse across the canvas
+ * function draw() {
+ * background(244, 248, 252);
+ * line(0, mouseY, 100, mouseY);
+ *}
+ *
+ *
+ *
+ * @alt
+ * vertical black line moves up and down with mouse y-position
+ *
+ */
+p5.prototype.mouseY = 0;
+
+/**
+ * The system variable pmouseX always contains the horizontal position of
+ * the mouse in the frame previous to the current frame, relative to (0, 0)
+ * of the canvas.
+ *
+ * @property pmouseX
+ *
+ * @example
+ *
+ *
+ * // Move the mouse across the canvas to leave a trail
+ * function setup() {
+ * //slow down the frameRate to make it more visible
+ * frameRate(10);
+ * }
+ *
+ * function draw() {
+ * background(244, 248, 252);
+ * line(mouseX, mouseY, pmouseX, pmouseY);
+ * print(pmouseX + " -> " + mouseX);
+ * }
+ *
+ *
+ *
+ *
+ * @alt
+ * line trail is created from cursor movements. faster movement make longer line.
+ *
+ */
+p5.prototype.pmouseX = 0;
+
+/**
+ * The system variable pmouseY always contains the vertical position of the
+ * mouse in the frame previous to the current frame, relative to (0, 0) of
+ * the canvas.
+ *
+ * @property pmouseY
+ *
+ * @example
+ *
+ *
+ * function draw() {
+ * background(237, 34, 93);
+ * fill(0);
+ * //draw a square only if the mouse is not moving
+ * if(mouseY == pmouseY && mouseX == pmouseX)
+ * rect(20,20,60,60);
+ *
+ * print(pmouseY + " -> " + mouseY);
+ * }
+ *
+ *
+ *
+ *
+ * @alt
+ * 60x60 black rect center, fuschia background. rect flickers on mouse movement
+ *
+ */
+p5.prototype.pmouseY = 0;
+
+/**
+ * The system variable winMouseX always contains the current horizontal
+ * position of the mouse, relative to (0, 0) of the window.
+ *
+ * @property winMouseX
+ *
+ * @example
+ *
+ *
+ * var myCanvas;
+ *
+ * function setup() {
+ * //use a variable to store a pointer to the canvas
+ * myCanvas = createCanvas(100, 100);
+ * }
+ *
+ * function draw() {
+ * background(237, 34, 93);
+ * fill(0);
+ *
+ * //move the canvas to the horizontal mouse position
+ * //relative to the window
+ * myCanvas.position(winMouseX+1, windowHeight/2);
+ *
+ * //the y of the square is relative to the canvas
+ * rect(20,mouseY,60,60);
+ * }
+ *
+ *
+ *
+ *
+ * @alt
+ * 60x60 black rect y moves with mouse y and fuschia canvas moves with mouse x
+ *
+ */
+p5.prototype.winMouseX = 0;
+
+/**
+ * The system variable winMouseY always contains the current vertical
+ * position of the mouse, relative to (0, 0) of the window.
+ *
+ * @property winMouseY
+ *
+ * @example
+ *
+ *
+ *var myCanvas;
+ *
+ * function setup() {
+ * //use a variable to store a pointer to the canvas
+ * myCanvas = createCanvas(100, 100);
+ * }
+ *
+ * function draw() {
+ * background(237, 34, 93);
+ * fill(0);
+ *
+ * //move the canvas to the vertical mouse position
+ * //relative to the window
+ * myCanvas.position(windowWidth/2, winMouseY+1);
+ *
+ * //the x of the square is relative to the canvas
+ * rect(mouseX,20,60,60);
+ * }
+ *
+ *
+ *
+ *
+ * @alt
+ * 60x60 black rect x moves with mouse x and fuschia canvas y moves with mouse y
+ *
+ */
+p5.prototype.winMouseY = 0;
+
+/**
+ * The system variable pwinMouseX always contains the horizontal position
+ * of the mouse in the frame previous to the current frame, relative to
+ * (0, 0) of the window.
+ *
+ * @property pwinMouseX
+ *
+ * @example
+ *
+ *
+ *
+ * var myCanvas;
+ *
+ * function setup() {
+ * //use a variable to store a pointer to the canvas
+ * myCanvas = createCanvas(100, 100);
+ * noStroke();
+ * fill(237, 34, 93);
+ * }
+ *
+ * function draw() {
+ * clear();
+ * //the difference between previous and
+ * //current x position is the horizontal mouse speed
+ * var speed = abs(winMouseX-pwinMouseX);
+ * //change the size of the circle
+ * //according to the horizontal speed
+ * ellipse(50, 50, 10+speed*5, 10+speed*5);
+ * //move the canvas to the mouse position
+ * myCanvas.position( winMouseX+1, winMouseY+1);
+ * }
+ *
+ *
+ *
+ *
+ * @alt
+ * fuschia ellipse moves with mouse x and y. Grows and shrinks with mouse speed
+ *
+ */
+p5.prototype.pwinMouseX = 0;
+
+/**
+ * The system variable pwinMouseY always contains the vertical position of
+ * the mouse in the frame previous to the current frame, relative to (0, 0)
+ * of the window.
+ *
+ * @property pwinMouseY
+ *
+ *
+ * @example
+ *
+ *
+ *
+ * var myCanvas;
+ *
+ * function setup() {
+ * //use a variable to store a pointer to the canvas
+ * myCanvas = createCanvas(100, 100);
+ * noStroke();
+ * fill(237, 34, 93);
+ * }
+ *
+ * function draw() {
+ * clear();
+ * //the difference between previous and
+ * //current y position is the vertical mouse speed
+ * var speed = abs(winMouseY-pwinMouseY);
+ * //change the size of the circle
+ * //according to the vertical speed
+ * ellipse(50, 50, 10+speed*5, 10+speed*5);
+ * //move the canvas to the mouse position
+ * myCanvas.position( winMouseX+1, winMouseY+1);
+ * }
+ *
+ *
+ *
+ *
+ * @alt
+ * fuschia ellipse moves with mouse x and y. Grows and shrinks with mouse speed
+ *
+ */
+p5.prototype.pwinMouseY = 0;
+
+/**
+ * Processing automatically tracks if the mouse button is pressed and which
+ * button is pressed. The value of the system variable mouseButton is either
+ * LEFT, RIGHT, or CENTER depending on which button was pressed last.
+ * Warning: different browsers may track mouseButton differently.
+ *
+ * @property mouseButton
+ *
+ * @example
+ *
+ *
+ * function draw() {
+ * background(237, 34, 93);
+ * fill(0);
+ *
+ * if (mouseIsPressed) {
+ * if (mouseButton == LEFT)
+ * ellipse(50, 50, 50, 50);
+ * if (mouseButton == RIGHT)
+ * rect(25, 25, 50, 50);
+ * if (mouseButton == CENTER)
+ * triangle(23, 75, 50, 20, 78, 75);
+ * }
+ *
+ * print(mouseButton);
+ * }
+ *
+ *
+ *
+ * @alt
+ * 50x50 black ellipse appears on center of fuschia canvas on mouse click/press.
+ *
+ */
+p5.prototype.mouseButton = 0;
+
+/**
+ * The boolean system variable mouseIsPressed is true if the mouse is pressed
+ * and false if not.
+ *
+ * @property mouseIsPressed
+ *
+ * @example
+ *
+ *
+ * function draw() {
+ * background(237, 34, 93);
+ * fill(0);
+ *
+ * if (mouseIsPressed)
+ * ellipse(50, 50, 50, 50);
+ * else
+ * rect(25, 25, 50, 50);
+ *
+ * print(mouseIsPressed);
+ * }
+ *
+ *
+ *
+ * @alt
+ * black 50x50 rect becomes ellipse with mouse click/press. fuschia background.
+ *
+ */
+p5.prototype.mouseIsPressed = false;
+p5.prototype.isMousePressed = false; // both are supported
+
+p5.prototype._updateNextMouseCoords = function(e) {
+ var x = this.mouseX;
+ var y = this.mouseY;
+ var winX = this.winMouseX;
+ var winY = this.winMouseY;
+ if(e.type === 'touchstart' ||
+ e.type === 'touchmove' ||
+ e.type === 'touchend' || e.touches) {
+ x = this.touchX;
+ y = this.touchY;
+ winX = this.winTouchX;
+ winY = this.winTouchY;
+ } else if(this._curElement !== null) {
+ var mousePos = getMousePos(this._curElement.elt, e);
+ x = mousePos.x;
+ y = mousePos.y;
+ winX = mousePos.winX;
+ winY = mousePos.winY;
+ }
+ this._setProperty('mouseX', x);
+ this._setProperty('mouseY', y);
+ this._setProperty('winMouseX', winX);
+ this._setProperty('winMouseY', winY);
+ if (!this._hasMouseInteracted) {
+ // For first draw, make previous and next equal
+ this._updateMouseCoords();
+ this._setProperty('_hasMouseInteracted', true);
+ }
+};
+
+p5.prototype._updateMouseCoords = function() {
+ this._setProperty('pmouseX', this.mouseX);
+ this._setProperty('pmouseY', this.mouseY);
+ this._setProperty('pwinMouseX', this.winMouseX);
+ this._setProperty('pwinMouseY', this.winMouseY);
+};
+
+function getMousePos(canvas, evt) {
+ var rect = canvas.getBoundingClientRect();
+ return {
+ x: evt.clientX - rect.left,
+ y: evt.clientY - rect.top,
+ winX: evt.clientX,
+ winY: evt.clientY
+ };
+}
+
+p5.prototype._setMouseButton = function(e) {
+ if (e.button === 1) {
+ this._setProperty('mouseButton', constants.CENTER);
+ } else if (e.button === 2) {
+ this._setProperty('mouseButton', constants.RIGHT);
+ } else {
+ this._setProperty('mouseButton', constants.LEFT);
+ }
+};
+
+/**
+ * The mouseMoved() function is called every time the mouse moves and a mouse
+ * button is not pressed.
+ * Browsers may have different default
+ * behaviors attached to various mouse events. To prevent any default
+ * behavior for this event, add "return false" to the end of the method.
+ *
+ * @method mouseMoved
+ * @example
+ *
+ *
+ * // Move the mouse across the page
+ * // to change its value
+ *
+ * var value = 0;
+ * function draw() {
+ * fill(value);
+ * rect(25, 25, 50, 50);
+ * }
+ * function mouseMoved() {
+ * value = value + 5;
+ * if (value > 255) {
+ * value = 0;
+ * }
+ * }
+ *
+ *
+ *
+ *
+ *
+ * function mouseMoved() {
+ * ellipse(mouseX, mouseY, 5, 5);
+ * // prevent default
+ * return false;
+ * }
+ *
+ *
+ *
+ * @alt
+ * black 50x50 rect becomes lighter with mouse movements until white then resets
+ * no image displayed
+ *
+ */
+
+/**
+ * The mouseDragged() function is called once every time the mouse moves and
+ * a mouse button is pressed. If no mouseDragged() function is defined, the
+ * touchMoved() function will be called instead if it is defined.
+ * Browsers may have different default
+ * behaviors attached to various mouse events. To prevent any default
+ * behavior for this event, add "return false" to the end of the method.
+ *
+ * @method mouseDragged
+ * @example
+ *
+ *
+ * // Drag the mouse across the page
+ * // to change its value
+ *
+ * var value = 0;
+ * function draw() {
+ * fill(value);
+ * rect(25, 25, 50, 50);
+ * }
+ * function mouseDragged() {
+ * value = value + 5;
+ * if (value > 255) {
+ * value = 0;
+ * }
+ * }
+ *
+ *
+ *
+ *
+ *
+ * function mouseDragged() {
+ * ellipse(mouseX, mouseY, 5, 5);
+ * // prevent default
+ * return false;
+ * }
+ *
+ *
+ *
+ * @alt
+ * black 50x50 rect turns lighter with mouse click and drag until white, resets
+ * no image displayed
+ *
+ */
+p5.prototype._onmousemove = function(e){
+ var context = this._isGlobal ? window : this;
+ var executeDefault;
+ this._updateNextMouseCoords(e);
+ this._updateNextTouchCoords(e);
+ if (!this.isMousePressed) {
+ if (typeof context.mouseMoved === 'function') {
+ executeDefault = context.mouseMoved(e);
+ if(executeDefault === false) {
+ e.preventDefault();
+ }
+ }
+ }
+ else {
+ if (typeof context.mouseDragged === 'function') {
+ executeDefault = context.mouseDragged(e);
+ if(executeDefault === false) {
+ e.preventDefault();
+ }
+ } else if (typeof context.touchMoved === 'function') {
+ executeDefault = context.touchMoved(e);
+ if(executeDefault === false) {
+ e.preventDefault();
+ }
+ }
+ }
+};
+
+/**
+ * The mousePressed() function is called once after every time a mouse button
+ * is pressed. The mouseButton variable (see the related reference entry)
+ * can be used to determine which button has been pressed. If no
+ * mousePressed() function is defined, the touchStarted() function will be
+ * called instead if it is defined.
+ * Browsers may have different default
+ * behaviors attached to various mouse events. To prevent any default
+ * behavior for this event, add "return false" to the end of the method.
+ *
+ * @method mousePressed
+ * @example
+ *
+ *
+ * // Click within the image to change
+ * // the value of the rectangle
+ *
+ * var value = 0;
+ * function draw() {
+ * fill(value);
+ * rect(25, 25, 50, 50);
+ * }
+ * function mousePressed() {
+ * if (value == 0) {
+ * value = 255;
+ * } else {
+ * value = 0;
+ * }
+ * }
+ *
+ *
+ *
+ *
+ *
+ * function mousePressed() {
+ * ellipse(mouseX, mouseY, 5, 5);
+ * // prevent default
+ * return false;
+ * }
+ *
+ *
+ *
+ * @alt
+ * black 50x50 rect turns white with mouse click/press.
+ * no image displayed
+ *
+ */
+p5.prototype._onmousedown = function(e) {
+ var context = this._isGlobal ? window : this;
+ var executeDefault;
+ this._setProperty('isMousePressed', true);
+ this._setProperty('mouseIsPressed', true);
+ this._setMouseButton(e);
+ this._updateNextMouseCoords(e);
+ this._updateNextTouchCoords(e);
+ if (typeof context.mousePressed === 'function') {
+ executeDefault = context.mousePressed(e);
+ if(executeDefault === false) {
+ e.preventDefault();
+ }
+ } else if (typeof context.touchStarted === 'function') {
+ executeDefault = context.touchStarted(e);
+ if(executeDefault === false) {
+ e.preventDefault();
+ }
+ }
+};
+
+/**
+ * The mouseReleased() function is called every time a mouse button is
+ * released. If no mouseReleased() function is defined, the touchEnded()
+ * function will be called instead if it is defined.
+ * Browsers may have different default
+ * behaviors attached to various mouse events. To prevent any default
+ * behavior for this event, add "return false" to the end of the method.
+ *
+ *
+ * @method mouseReleased
+ * @example
+ *
+ *
+ * // Click within the image to change
+ * // the value of the rectangle
+ * // after the mouse has been clicked
+ *
+ * var value = 0;
+ * function draw() {
+ * fill(value);
+ * rect(25, 25, 50, 50);
+ * }
+ * function mouseReleased() {
+ * if (value == 0) {
+ * value = 255;
+ * } else {
+ * value = 0;
+ * }
+ * }
+ *
+ *
+ *
+ *
+ *
+ * function mouseReleased() {
+ * ellipse(mouseX, mouseY, 5, 5);
+ * // prevent default
+ * return false;
+ * }
+ *
+ *
+ *
+ * @alt
+ * black 50x50 rect turns white with mouse click/press.
+ * no image displayed
+ *
+ */
+p5.prototype._onmouseup = function(e) {
+ var context = this._isGlobal ? window : this;
+ var executeDefault;
+ this._setProperty('isMousePressed', false);
+ this._setProperty('mouseIsPressed', false);
+ if (typeof context.mouseReleased === 'function') {
+ executeDefault = context.mouseReleased(e);
+ if(executeDefault === false) {
+ e.preventDefault();
+ }
+ } else if (typeof context.touchEnded === 'function') {
+ executeDefault = context.touchEnded(e);
+ if(executeDefault === false) {
+ e.preventDefault();
+ }
+ }
+};
+
+p5.prototype._ondragend = p5.prototype._onmouseup;
+p5.prototype._ondragover = p5.prototype._onmousemove;
+
+/**
+ * The mouseClicked() function is called once after a mouse button has been
+ * pressed and then released.
+ * Browsers may have different default
+ * behaviors attached to various mouse events. To prevent any default
+ * behavior for this event, add "return false" to the end of the method.
+ *
+ * @method mouseClicked
+ * @example
+ *
+ *
+ * // Click within the image to change
+ * // the value of the rectangle
+ * // after the mouse has been clicked
+ *
+ * var value = 0;
+ * function draw() {
+ * fill(value);
+ * rect(25, 25, 50, 50);
+ * }
+ * function mouseClicked() {
+ * if (value == 0) {
+ * value = 255;
+ * } else {
+ * value = 0;
+ * }
+ * }
+ *
+ *
+ *
+ *
+ *
+ * function mouseClicked() {
+ * ellipse(mouseX, mouseY, 5, 5);
+ * // prevent default
+ * return false;
+ * }
+ *
+ *
+ *
+ * @alt
+ * black 50x50 rect turns white with mouse click/press.
+ * no image displayed
+ *
+ */
+p5.prototype._onclick = function(e) {
+ var context = this._isGlobal ? window : this;
+ if (typeof context.mouseClicked === 'function') {
+ var executeDefault = context.mouseClicked(e);
+ if(executeDefault === false) {
+ e.preventDefault();
+ }
+ }
+};
+
+/**
+ * The function mouseWheel() is executed every time a vertical mouse wheel
+ * event is detected either triggered by an actual mouse wheel or by a
+ * touchpad.
+ * The event.delta property returns the amount the mouse wheel
+ * have scrolled. The values can be positive or negative depending on the
+ * scroll direction (on OS X with "natural" scrolling enabled, the signs
+ * are inverted).
+ * Browsers may have different default behaviors attached to various
+ * mouse events. To prevent any default behavior for this event, add
+ * "return false" to the end of the method.
+ * Due to the current support of the "wheel" event on Safari, the function
+ * may only work as expected if "return false" is included while using Safari.
+ *
+ * @method mouseWheel
+ *
+ * @example
+ *
+ *
+ * var pos = 25;
+ *
+ * function draw() {
+ * background(237, 34, 93);
+ * fill(0);
+ * rect(25, pos, 50, 50);
+ * }
+ *
+ * function mouseWheel(event) {
+ * print(event.delta);
+ * //move the square according to the vertical scroll amount
+ * pos += event.delta;
+ * //uncomment to block page scrolling
+ * //return false;
+ * }
+ *
+ *
+ *
+ * @alt
+ * black 50x50 rect moves up and down with vertical scroll. fuschia background
+ *
+ */
+p5.prototype._onwheel = function(e) {
+ var context = this._isGlobal ? window : this;
+ if (typeof context.mouseWheel === 'function') {
+ e.delta = e.deltaY;
+ var executeDefault = context.mouseWheel(e);
+ if(executeDefault === false) {
+ e.preventDefault();
+ }
+ }
+};
+
+module.exports = p5;
+
+},{"../core/constants":36,"../core/core":37}],53:[function(_dereq_,module,exports){
+/**
+ * @module Events
+ * @submodule Touch
+ * @for p5
+ * @requires core
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+
+/*
+ * This is a flag which is false until the first time
+ * we receive a touch event. The ptouchX and ptouchY
+ * values will match the touchX and touchY values until
+ * this interaction takes place.
+ */
+p5.prototype._hasTouchInteracted = false;
+
+/**
+ * The system variable touchX always contains the horizontal position of
+ * one finger, relative to (0, 0) of the canvas. This is best used for
+ * single touch interactions. For multi-touch interactions, use the
+ * touches[] array.
+ *
+ * @property touchX
+ * @method touchX
+ * @example
+ *
+ *
+ * // Touch and move the finger in horizontally across the canvas
+ * function setup() {
+ * createCanvas(100, 100);
+ * }
+ *
+ * function draw() {
+ * background(51);
+ * stroke(255, 204, 0);
+ * strokeWeight(4);
+ * rect(touchX, 50, 10, 10);
+ * }
+ *
+ *
+ *
+ * @alt
+ * 10x10 white rect with thick gold outline moves left and right with touch x.
+ *
+ */
+p5.prototype.touchX = 0;
+
+/**
+ * The system variable touchY always contains the vertical position of
+ * one finger, relative to (0, 0) of the canvas. This is best used for
+ * single touch interactions. For multi-touch interactions, use the
+ * touches[] array.
+ *
+ * @property touchY
+ * @method touchY
+ * @example
+ *
+ *
+ * // Touch and move the finger vertically across the canvas
+ * function setup() {
+ * createCanvas(100, 100);
+ * }
+ *
+ * function draw() {
+ * background(51);
+ * stroke(255, 204, 0);
+ * strokeWeight(4);
+ * rect(50, touchY, 10, 10);
+ * }
+ *
+ *
+ *
+ * @alt
+ * 10x10 white rect with thick gold outline moves up and down with touch y.
+ *
+ */
+p5.prototype.touchY = 0;
+
+/**
+ * The system variable ptouchX always contains the horizontal position of
+ * one finger, relative to (0, 0) of the canvas, in the frame previous to the
+ * current frame.
+ *
+ * @property ptouchX
+ */
+p5.prototype.ptouchX = 0;
+
+/**
+ * The system variable ptouchY always contains the vertical position of
+ * one finger, relative to (0, 0) of the canvas, in the frame previous to the
+ * current frame.
+ *
+ * @property ptouchY
+ */
+p5.prototype.ptouchY = 0;
+
+/**
+ * The system variable winTouchX always contains the horizontal position of
+ * one finger, relative to (0, 0) of the window.
+ *
+ * @property winTouchX
+ */
+p5.prototype.winTouchX = 0;
+
+/**
+ * The system variable winTouchY always contains the vertical position of
+ * one finger, relative to (0, 0) of the window.
+ *
+ * @property winTouchY
+ */
+p5.prototype.winTouchY = 0;
+
+/**
+ * The system variable pwinTouchX always contains the horizontal position of
+ * one finger, relative to (0, 0) of the window, in the frame previous to the
+ * current frame.
+ *
+ * @property pwinTouchX
+ */
+p5.prototype.pwinTouchX = 0;
+
+/**
+ * The system variable pwinTouchY always contains the vertical position of
+ * one finger, relative to (0, 0) of the window, in the frame previous to the
+ * current frame.
+ *
+ * @property pwinTouchY
+ */
+p5.prototype.pwinTouchY = 0;
+
+/**
+ * The system variable touches[] contains an array of the positions of all
+ * current touch points, relative to (0, 0) of the canvas, and IDs identifying a
+ * unique touch as it moves. Each element in the array is an object with x, y,
+ * and id properties.
+ *
+ * @property touches[]
+ */
+p5.prototype.touches = [];
+
+/**
+ * The boolean system variable touchIsDown is true if the screen is
+ * touched and false if not.
+ *
+ * @property touchIsDown
+ */
+p5.prototype.touchIsDown = false;
+
+p5.prototype._updateNextTouchCoords = function(e) {
+ var x = this.touchX;
+ var y = this.touchY;
+ var winX = this.winTouchX;
+ var winY = this.winTouchY;
+ if(e.type === 'mousedown' ||
+ e.type === 'mousemove' ||
+ e.type === 'mouseup' || !e.touches) {
+ x = this.mouseX;
+ y = this.mouseY;
+ winX = this.winMouseX;
+ winY = this.winMouseY;
+ } else {
+ if(this._curElement !== null) {
+ var touchInfo = getTouchInfo(this._curElement.elt, e, 0);
+ x = touchInfo.x;
+ y = touchInfo.y;
+ winX = touchInfo.winX;
+ winY = touchInfo.winY;
+
+ var touches = [];
+ for(var i = 0; i < e.touches.length; i++){
+ touches[i] = getTouchInfo(this._curElement.elt, e, i);
+ }
+ this._setProperty('touches', touches);
+ }
+ }
+ this._setProperty('touchX', x);
+ this._setProperty('touchY', y);
+ this._setProperty('winTouchX', winX);
+ this._setProperty('winTouchY', winY);
+ if (!this._hasTouchInteracted) {
+ // For first draw, make previous and next equal
+ this._updateTouchCoords();
+ this._setProperty('_hasTouchInteracted', true);
+ }
+};
+
+p5.prototype._updateTouchCoords = function() {
+ this._setProperty('ptouchX', this.touchX);
+ this._setProperty('ptouchY', this.touchY);
+ this._setProperty('pwinTouchX', this.winTouchX);
+ this._setProperty('pwinTouchY', this.winTouchY);
+};
+
+function getTouchInfo(canvas, e, i) {
+ i = i || 0;
+ var rect = canvas.getBoundingClientRect();
+ var touch = e.touches[i] || e.changedTouches[i];
+ return {
+ x: touch.clientX - rect.left,
+ y: touch.clientY - rect.top,
+ winX: touch.clientX,
+ winY: touch.clientY,
+ id: touch.identifier
+ };
+}
+
+/**
+ * The touchStarted() function is called once after every time a touch is
+ * registered. If no touchStarted() function is defined, the mousePressed()
+ * function will be called instead if it is defined.
+ * Browsers may have different default behaviors attached to various touch
+ * events. To prevent any default behavior for this event, add "return false"
+ * to the end of the method.
+ *
+ * @method touchStarted
+ * @example
+ *
+ *
+ * // Touch within the image to change
+ * // the value of the rectangle
+ *
+ * var value = 0;
+ * function draw() {
+ * fill(value);
+ * rect(25, 25, 50, 50);
+ * }
+ * function touchStarted() {
+ * if (value == 0) {
+ * value = 255;
+ * } else {
+ * value = 0;
+ * }
+ * }
+ *
+ *
+ *
+ *
+ *
+ * function touchStarted() {
+ * ellipse(touchX, touchY, 5, 5);
+ * // prevent default
+ * return false;
+ * }
+ *
+ *
+ *
+ * @alt
+ * 50x50 black rect turns white with touch event.
+ * no image displayed
+ */
+p5.prototype._ontouchstart = function(e) {
+ var context = this._isGlobal ? window : this;
+ var executeDefault;
+ this._updateNextTouchCoords(e);
+ this._updateNextMouseCoords(e);
+ this._setProperty('touchIsDown', true);
+ if(typeof context.touchStarted === 'function') {
+ executeDefault = context.touchStarted(e);
+ if(executeDefault === false) {
+ e.preventDefault();
+ }
+ } else if (typeof context.mousePressed === 'function') {
+ executeDefault = context.mousePressed(e);
+ if(executeDefault === false) {
+ e.preventDefault();
+ }
+ //this._setMouseButton(e);
+ }
+};
+
+/**
+ * The touchMoved() function is called every time a touch move is registered.
+ * If no touchMoved() function is defined, the mouseDragged() function will
+ * be called instead if it is defined.
+ * Browsers may have different default behaviors attached to various touch
+ * events. To prevent any default behavior for this event, add "return false"
+ * to the end of the method.
+ *
+ * @method touchMoved
+ * @example
+ *
+ *
+ * // Move your finger across the page
+ * // to change its value
+ *
+ * var value = 0;
+ * function draw() {
+ * fill(value);
+ * rect(25, 25, 50, 50);
+ * }
+ * function touchMoved() {
+ * value = value + 5;
+ * if (value > 255) {
+ * value = 0;
+ * }
+ * }
+ *
+ *
+ *
+ *
+ *
+ * function touchMoved() {
+ * ellipse(touchX, touchY, 5, 5);
+ * // prevent default
+ * return false;
+ * }
+ *
+ *
+ *
+ * @alt
+ * 50x50 black rect turns lighter with touch until white. resets
+ * no image displayed
+ *
+ */
+p5.prototype._ontouchmove = function(e) {
+ var context = this._isGlobal ? window : this;
+ var executeDefault;
+ this._updateNextTouchCoords(e);
+ this._updateNextMouseCoords(e);
+ if (typeof context.touchMoved === 'function') {
+ executeDefault = context.touchMoved(e);
+ if(executeDefault === false) {
+ e.preventDefault();
+ }
+ } else if (typeof context.mouseDragged === 'function') {
+ executeDefault = context.mouseDragged(e);
+ if(executeDefault === false) {
+ e.preventDefault();
+ }
+ }
+};
+
+/**
+ * The touchEnded() function is called every time a touch ends. If no
+ * touchEnded() function is defined, the mouseReleased() function will be
+ * called instead if it is defined.
+ * Browsers may have different default behaviors attached to various touch
+ * events. To prevent any default behavior for this event, add "return false"
+ * to the end of the method.
+ *
+ * @method touchEnded
+ * @example
+ *
+ *
+ * // Release touch within the image to
+ * // change the value of the rectangle
+ *
+ * var value = 0;
+ * function draw() {
+ * fill(value);
+ * rect(25, 25, 50, 50);
+ * }
+ * function touchEnded() {
+ * if (value == 0) {
+ * value = 255;
+ * } else {
+ * value = 0;
+ * }
+ * }
+ *
+ *
+ *
+ *
+ *
+ * function touchEnded() {
+ * ellipse(touchX, touchY, 5, 5);
+ * // prevent default
+ * return false;
+ * }
+ *
+ *
+ *
+ * @alt
+ * 50x50 black rect turns white with touch.
+ * no image displayed
+ *
+ */
+p5.prototype._ontouchend = function(e) {
+ this._updateNextTouchCoords(e);
+ this._updateNextMouseCoords(e);
+ if (this.touches.length === 0) {
+ this._setProperty('touchIsDown', false);
+ }
+ var context = this._isGlobal ? window : this;
+ var executeDefault;
+ if (typeof context.touchEnded === 'function') {
+ executeDefault = context.touchEnded(e);
+ if(executeDefault === false) {
+ e.preventDefault();
+ }
+ } else if (typeof context.mouseReleased === 'function') {
+ executeDefault = context.mouseReleased(e);
+ if(executeDefault === false) {
+ e.preventDefault();
+ }
+ }
+};
+
+module.exports = p5;
+
+},{"../core/core":37}],54:[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 {[type]} func [description]
+ * @param {[type]} canvas [description]
+ * @param {[type]} level [description]
+ * @return {[type]} [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 {Invert}
+ */
+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;
+
+},{}],55:[function(_dereq_,module,exports){
+/**
+ * @module Image
+ * @submodule Image
+ * @for p5
+ * @requires core
+ */
+
+/**
+ * This module defines the p5 methods for the p5.Image class
+ * for drawing images to the main display canvas.
+ */
+'use strict';
+
+
+var p5 = _dereq_('../core/core');
+
+/* global frames:true */// This is not global, but JSHint is not aware that
+// this module is implicitly enclosed with Browserify: this overrides the
+// redefined-global error and permits using the name "frames" for the array
+// of saved animation frames.
+var frames = [];
+
+
+/**
+ * Creates a new p5.Image (the datatype for storing images). This provides a
+ * fresh buffer of pixels to play with. Set the size of the buffer with the
+ * width and height parameters.
+ *
+ * .pixels gives access to an array containing the values for all the pixels
+ * in the display window.
+ * These values are numbers. This array is the size (including an appropriate
+ * factor for the pixelDensity) of the display window x4,
+ * representing the R, G, B, A values in order for each pixel, moving from
+ * left to right across each row, then down each column. See .pixels for
+ * more info. It may also be simpler to use set() or get().
+ *
+ * Before accessing the pixels of an image, the data must loaded with the
+ * loadPixels() function. After the array data has been modified, the
+ * updatePixels() function must be run to update the changes.
+ *
+ * @method createImage
+ * @param {Integer} width width in pixels
+ * @param {Integer} height height in pixels
+ * @return {p5.Image} the p5.Image object
+ * @example
+ *
+ *
+ * img = createImage(66, 66);
+ * img.loadPixels();
+ * for (i = 0; i < img.width; i++) {
+ * for (j = 0; j < img.height; j++) {
+ * img.set(i, j, color(0, 90, 102));
+ * }
+ * }
+ * img.updatePixels();
+ * image(img, 17, 17);
+ *
+ *
+ *
+ *
+ *
+ * img = createImage(66, 66);
+ * img.loadPixels();
+ * for (i = 0; i < img.width; i++) {
+ * for (j = 0; j < img.height; j++) {
+ * img.set(i, j, color(0, 90, 102, i % img.width * 2));
+ * }
+ * }
+ * img.updatePixels();
+ * image(img, 17, 17);
+ * image(img, 34, 34);
+ *
+ *
+ *
+ *
+ *
+ * var pink = color(255, 102, 204);
+ * img = createImage(66, 66);
+ * img.loadPixels();
+ * var d = pixelDensity;
+ * var halfImage = 4 * (width * d) * (height/2 * d);
+ * for (var i = 0; i < halfImage; i+=4) {
+ * img.pixels[i] = red(pink);
+ * img.pixels[i+1] = green(pink);
+ * img.pixels[i+2] = blue(pink);
+ * img.pixels[i+3] = alpha(pink);
+ * }
+ * img.updatePixels();
+ * image(img, 17, 17);
+ *
+ *
+ *
+ * @alt
+ * 66x66 dark turquoise rect in center of canvas.
+ * 2 gradated dark turquoise rects fade left. 1 center 1 bottom right of canvas
+ * no image displayed
+ *
+ */
+p5.prototype.createImage = function(width, height) {
+ return new p5.Image(width, height);
+};
+
+/**
+ * Save the current canvas as an image. In Safari, this will open the
+ * image in the window and the user must provide their own
+ * filename on save-as. Other browsers will either save the
+ * file immediately, or prompt the user with a dialogue window.
+ *
+ * @method saveCanvas
+ * @param {[selectedCanvas]} canvas a variable representing a
+ * specific html5 canvas (optional)
+ * @param {[String]} filename
+ * @param {[String]} extension 'jpg' or 'png'
+ * @example
+ *
+ * function setup() {
+ * var c = createCanvas(100, 100);
+ * background(255, 0, 0);
+ * saveCanvas(c, 'myCanvas', 'jpg');
+ * }
+ *
+ *
+ * // note that this example has the same result as above
+ * // if no canvas is specified, defaults to main canvas
+ * function setup() {
+ * createCanvas(100, 100);
+ * background(255, 0, 0);
+ * saveCanvas('myCanvas', 'jpg');
+ * }
+ *
+ *
+ * // all of the following are valid
+ * saveCanvas(c, 'myCanvas', 'jpg');
+ * saveCanvas(c, 'myCanvas');
+ * saveCanvas(c);
+ * saveCanvas('myCanvas', 'png');
+ * saveCanvas('myCanvas');
+ * saveCanvas();
+ *
+ *
+ * @alt
+ * no image displayed
+ * no image displayed
+ * no image displayed
+ *
+ */
+p5.prototype.saveCanvas = function() {
+
+ var cnv, filename, extension;
+ if (arguments.length === 3) {
+ cnv = arguments[0];
+ filename = arguments[1];
+ extension = arguments[2];
+ } else if (arguments.length === 2) {
+ if (typeof arguments[0] === 'object') {
+ cnv = arguments[0];
+ filename = arguments[1];
+ } else {
+ filename = arguments[0];
+ extension = arguments[1];
+ }
+ } else if (arguments.length === 1) {
+ if (typeof arguments[0] === 'object') {
+ cnv = arguments[0];
+ } else {
+ filename = arguments[0];
+ }
+ }
+
+ if (cnv instanceof p5.Element) {
+ cnv = cnv.elt;
+ }
+ if (!(cnv instanceof HTMLCanvasElement)) {
+ cnv = null;
+ }
+
+ if (!extension) {
+ extension = p5.prototype._checkFileExtension(filename, extension)[1];
+ if (extension === '') {
+ extension = 'png';
+ }
+ }
+
+ if (!cnv) {
+ if (this._curElement && this._curElement.elt) {
+ cnv = this._curElement.elt;
+ }
+ }
+
+ if ( p5.prototype._isSafari() ) {
+ var aText = 'Hello, Safari user!\n';
+ aText += 'Now capturing a screenshot...\n';
+ aText += 'To save this image,\n';
+ aText += 'go to File --> Save As.\n';
+ alert(aText);
+ window.location.href = cnv.toDataURL();
+ } else {
+ var mimeType;
+ if (typeof(extension) === 'undefined') {
+ extension = 'png';
+ mimeType = 'image/png';
+ }
+ else {
+ switch(extension){
+ case 'png':
+ mimeType = 'image/png';
+ break;
+ case 'jpeg':
+ mimeType = 'image/jpeg';
+ break;
+ case 'jpg':
+ mimeType = 'image/jpeg';
+ break;
+ default:
+ mimeType = 'image/png';
+ break;
+ }
+ }
+ var downloadMime = 'image/octet-stream';
+ var imageData = cnv.toDataURL(mimeType);
+ imageData = imageData.replace(mimeType, downloadMime);
+
+ p5.prototype.downloadFile(imageData, filename, extension);
+ }
+};
+
+/**
+ * Capture a sequence of frames that can be used to create a movie.
+ * Accepts a callback. For example, you may wish to send the frames
+ * to a server where they can be stored or converted into a movie.
+ * If no callback is provided, the browser will pop up save dialogues in an
+ * attempt to download all of the images that have just been created. With the
+ * callback provided the image data isn't saved by default but instead passed
+ * as an argument to the callback function as an array of objects, with the
+ * size of array equal to the total number of frames.
+ *
+ * @method saveFrames
+ * @param {String} filename
+ * @param {String} extension 'jpg' or 'png'
+ * @param {Number} duration Duration in seconds to save the frames for.
+ * @param {Number} framerate Framerate to save the frames in.
+ * @param {Function} [callback] A callback function that will be executed
+ to handle the image data. This function
+ should accept an array as argument. The
+ array will contain the specified number of
+ frames of objects. Each object has three
+ properties: imageData - an
+ image/octet-stream, filename and extension.
+ * @example
+ *
+ * function draw() {
+ * background(mouseX);
+ * }
+ *
+ * function mousePressed() {
+ * saveFrames("out", "png", 1, 25, function(data){
+ * print(data);
+ * });
+ * }
+ *
+ *
+ * @alt
+ * canvas background goes from light to dark with mouse x.
+ *
+ */
+p5.prototype.saveFrames = function(fName, ext, _duration, _fps, callback) {
+ var duration = _duration || 3;
+ duration = p5.prototype.constrain(duration, 0, 15);
+ duration = duration * 1000;
+ var fps = _fps || 15;
+ fps = p5.prototype.constrain(fps, 0, 22);
+ var count = 0;
+
+ var makeFrame = p5.prototype._makeFrame;
+ var cnv = this._curElement.elt;
+ var frameFactory = setInterval(function(){
+ makeFrame(fName + count, ext, cnv);
+ count++;
+ },1000/fps);
+
+ setTimeout(function(){
+ clearInterval(frameFactory);
+ if (callback) {
+ callback(frames);
+ }
+ else {
+ for (var i = 0; i < frames.length; i++) {
+ var f = frames[i];
+ p5.prototype.downloadFile(f.imageData, f.filename, f.ext);
+ }
+ }
+ frames = []; // clear frames
+ }, duration + 0.01);
+};
+
+p5.prototype._makeFrame = function(filename, extension, _cnv) {
+ var cnv;
+ if (this) {
+ cnv = this._curElement.elt;
+ } else {
+ cnv = _cnv;
+ }
+ var mimeType;
+ if (!extension) {
+ extension = 'png';
+ mimeType = 'image/png';
+ }
+ else {
+ switch(extension.toLowerCase()){
+ case 'png':
+ mimeType = 'image/png';
+ break;
+ case 'jpeg':
+ mimeType = 'image/jpeg';
+ break;
+ case 'jpg':
+ mimeType = 'image/jpeg';
+ break;
+ default:
+ mimeType = 'image/png';
+ break;
+ }
+ }
+ var downloadMime = 'image/octet-stream';
+ var imageData = cnv.toDataURL(mimeType);
+ imageData = imageData.replace(mimeType, downloadMime);
+
+ var thisFrame = {};
+ thisFrame.imageData = imageData;
+ thisFrame.filename = filename;
+ thisFrame.ext = extension;
+ frames.push(thisFrame);
+};
+
+module.exports = p5;
+
+},{"../core/core":37}],56:[function(_dereq_,module,exports){
+/**
+ * @module Image
+ * @submodule Loading & Displaying
+ * @for p5
+ * @requires core
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+var Filters = _dereq_('./filters');
+var canvas = _dereq_('../core/canvas');
+var constants = _dereq_('../core/constants');
+
+_dereq_('../core/error_helpers');
+
+/**
+ * Loads an image from a path and creates a p5.Image from it.
+ *
+ * The image may not be immediately available for rendering
+ * If you want to ensure that the image is ready before doing
+ * anything with it, place the loadImage() call in preload().
+ * You may also supply a callback function to handle the image when it's ready.
+ *
+ * The path to the image should be relative to the HTML file
+ * that links in your sketch. Loading an from a URL or other
+ * remote location may be blocked due to your browser's built-in
+ * security.
+ *
+ * @method loadImage
+ * @param {String} path Path of the image to be loaded
+ * @param {Function(p5.Image)} [successCallback] Function to be called once
+ * the image is loaded. Will be passed the
+ * p5.Image.
+ * @param {Function(Event)} [failureCallback] called with event error if
+ * the image fails to load.
+ * @return {p5.Image} the p5.Image object
+ * @example
+ *
+ *
+ * var img;
+ * function preload() {
+ * img = loadImage("assets/laDefense.jpg");
+ * }
+ * function setup() {
+ * image(img, 0, 0);
+ * }
+ *
+ *
+ *
+ *
+ * function setup() {
+ * // here we use a callback to display the image after loading
+ * loadImage("assets/laDefense.jpg", function(img) {
+ * image(img, 0, 0);
+ * });
+ * }
+ *
+ *
+ *
+ * @alt
+ * image of the underside of a white umbrella and grided ceililng above
+ * image of the underside of a white umbrella and grided ceililng above
+ *
+ */
+p5.prototype.loadImage = function(path, successCallback, failureCallback) {
+ var img = new Image();
+ var pImg = new p5.Image(1, 1, this);
+ var decrementPreload = p5._getDecrementPreload.apply(this, arguments);
+
+ img.onload = function() {
+ pImg.width = pImg.canvas.width = img.width;
+ pImg.height = pImg.canvas.height = img.height;
+
+ // Draw the image into the backing canvas of the p5.Image
+ pImg.drawingContext.drawImage(img, 0, 0);
+
+ if (typeof successCallback === 'function') {
+ successCallback(pImg);
+ }
+ if (decrementPreload && (successCallback !== decrementPreload)) {
+ decrementPreload();
+ }
+ };
+ img.onerror = function(e) {
+ p5._friendlyFileLoadError(0,img.src);
+ // don't get failure callback mixed up with decrementPreload
+ if ((typeof failureCallback === 'function') &&
+ (failureCallback !== decrementPreload)) {
+ failureCallback(e);
+ }
+ };
+
+ //set crossOrigin in case image is served which CORS headers
+ //this will let us draw to canvas without tainting it.
+ //see https://developer.mozilla.org/en-US/docs/HTML/CORS_Enabled_Image
+ // When using data-uris the file will be loaded locally
+ // so we don't need to worry about crossOrigin with base64 file types
+ if(path.indexOf('data:image/') !== 0) {
+ img.crossOrigin = 'Anonymous';
+ }
+
+ //start loading the image
+ img.src = path;
+
+ return pImg;
+};
+
+/**
+ * Validates clipping params. Per drawImage spec sWidth and sHight cannot be
+ * negative or greater than image intrinsic width and height
+ * @private
+ * @param {Number} sVal
+ * @param {Number} iVal
+ * @returns {Number}
+ * @private
+ */
+function _sAssign(sVal, iVal) {
+ if (sVal > 0 && sVal < iVal) {
+ return sVal;
+ }
+ else {
+ return iVal;
+ }
+}
+
+/**
+ * Draw an image to the main canvas of the p5js sketch
+ *
+ * @method image
+ * @param {p5.Image} img the image to display
+ * @param {Number} [sx=0] The X coordinate of the top left corner of the
+ * sub-rectangle of the source image to draw into
+ * the destination canvas.
+ * @param {Number} [sy=0] The Y coordinate of the top left corner of the
+ * sub-rectangle of the source image to draw into
+ * the destination canvas.
+ * @param {Number} [sWidth=img.width] The width of the sub-rectangle of the
+ * source image to draw into the destination
+ * canvas.
+ * @param {Number} [sHeight=img.height] The height of the sub-rectangle of the
+ * source image to draw into the
+ * destination context.
+ * @param {Number} [dx=0] The X coordinate in the destination canvas at
+ * which to place the top-left corner of the
+ * source image.
+ * @param {Number} [dy=0] The Y coordinate in the destination canvas at
+ * which to place the top-left corner of the
+ * source image.
+ * @param {Number} [dWidth] The width to draw the image in the destination
+ * canvas. This allows scaling of the drawn image.
+ * @param {Number} [dHeight] The height to draw the image in the destination
+ * canvas. This allows scaling of the drawn image.
+ * @example
+ *
+ *
+ * var img;
+ * function preload() {
+ * img = loadImage("assets/laDefense.jpg");
+ * }
+ * function setup() {
+ * image(img, 0, 0);
+ * image(img, 0, 0, 100, 100);
+ * image(img, 0, 0, 100, 100, 0, 0, 100, 100);
+ * }
+ *
+ *
+ *
+ *
+ * function setup() {
+ * // here we use a callback to display the image after loading
+ * loadImage("assets/laDefense.jpg", function(img) {
+ * image(img, 0, 0);
+ * });
+ * }
+ *
+ *
+ *
+ * @alt
+ * image of the underside of a white umbrella and grided ceiling above
+ * image of the underside of a white umbrella and grided ceiling above
+ *
+ */
+p5.prototype.image =
+ function(img, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight) {
+ // Temporarily disabling until options for p5.Graphics are added.
+ // var args = new Array(arguments.length);
+ // for (var i = 0; i < args.length; ++i) {
+ // args[i] = arguments[i];
+ // }
+ // this._validateParameters(
+ // 'image',
+ // args,
+ // [
+ // ['p5.Image', 'Number', 'Number'],
+ // ['p5.Image', 'Number', 'Number', 'Number', 'Number']
+ // ]
+ // );
+
+ // set defaults per spec: https://goo.gl/3ykfOq
+ if (arguments.length <= 5) {
+ dx = sx || 0;
+ dy = sy || 0;
+ sx = 0;
+ sy = 0;
+ if (img.elt && img.elt.videoWidth && !img.canvas) { // video no canvas
+ var actualW = img.elt.videoWidth;
+ var actualH = img.elt.videoHeight;
+ dWidth = sWidth || img.elt.width;
+ dHeight = sHeight || img.elt.width*actualH/actualW;
+ sWidth = actualW;
+ sHeight = actualH;
+ } else {
+ dWidth = sWidth || img.width;
+ dHeight = sHeight || img.height;
+ sWidth = img.width;
+ sHeight = img.height;
+ }
+ } else if (arguments.length === 9) {
+ sx = sx || 0;
+ sy = sy || 0;
+ sWidth = _sAssign(sWidth, img.width);
+ sHeight = _sAssign(sHeight, img.height);
+
+ dx = dx || 0;
+ dy = dy || 0;
+ dWidth = dWidth || img.width;
+ dHeight = dHeight || img.height;
+ } else {
+ throw 'Wrong number of arguments to image()';
+ }
+
+ var vals = canvas.modeAdjust(dx, dy, dWidth, dHeight,
+ this._renderer._imageMode);
+
+ // tint the image if there is a tint
+ this._renderer.image(img, sx, sy, sWidth, sHeight, vals.x, vals.y, vals.w,
+ vals.h);
+};
+
+/**
+ * Sets the fill value for displaying images. Images can be tinted to
+ * specified colors or made transparent by including an alpha value.
+ *
+ * To apply transparency to an image without affecting its color, use
+ * white as the tint color and specify an alpha value. For instance,
+ * tint(255, 128) will make an image 50% transparent (assuming the default
+ * alpha range of 0-255, which can be changed with colorMode()).
+ *
+ * The value for the gray parameter must be less than or equal to the current
+ * maximum value as specified by colorMode(). The default maximum value is
+ * 255.
+ *
+ * @method tint
+ * @param {Number|Array} v1 gray value, red or hue value (depending on the
+ * current color mode), or color Array
+ * @param {Number|Array} [v2] green or saturation value (depending on the
+ * current color mode)
+ * @param {Number|Array} [v3] blue or brightness value (depending on the
+ * current color mode)
+ * @param {Number|Array} [a] opacity of the background
+ * @example
+ *
+ *
+ * var img;
+ * function preload() {
+ * img = loadImage("assets/laDefense.jpg");
+ * }
+ * function setup() {
+ * image(img, 0, 0);
+ * tint(0, 153, 204); // Tint blue
+ * image(img, 50, 0);
+ * }
+ *
+ *
+ *
+ *
+ *
+ * var img;
+ * function preload() {
+ * img = loadImage("assets/laDefense.jpg");
+ * }
+ * function setup() {
+ * image(img, 0, 0);
+ * tint(0, 153, 204, 126); // Tint blue and set transparency
+ * image(img, 50, 0);
+ * }
+ *
+ *
+ *
+ *
+ *
+ * var img;
+ * function preload() {
+ * img = loadImage("assets/laDefense.jpg");
+ * }
+ * function setup() {
+ * image(img, 0, 0);
+ * tint(255, 126); // Apply transparency without changing color
+ * image(img, 50, 0);
+ * }
+ *
+ *
+ *
+ * @alt
+ * 2 side by side images of umbrella and ceiling, one image with blue tint
+ * Images of umbrella and ceiling, one half of image with blue tint
+ * 2 side by side images of umbrella and ceiling, one image translucent
+ *
+ */
+p5.prototype.tint = function () {
+ var c = this.color.apply(this, arguments);
+ this._renderer._tint = c.levels;
+};
+
+/**
+ * Removes the current fill value for displaying images and reverts to
+ * displaying images with their original hues.
+ *
+ * @method noTint
+ * @example
+ *
+ *
+ * var img;
+ * function preload() {
+ * img = loadImage("assets/bricks.jpg");
+ * }
+ * function setup() {
+ * tint(0, 153, 204); // Tint blue
+ * image(img, 0, 0);
+ * noTint(); // Disable tint
+ * image(img, 50, 0);
+ * }
+ *
+ *
+ *
+ * @alt
+ * 2 side by side images of bricks, left image with blue tint
+ *
+ */
+p5.prototype.noTint = function() {
+ this._renderer._tint = null;
+};
+
+/**
+ * Apply the current tint color to the input image, return the resulting
+ * canvas.
+ *
+ * @param {p5.Image} The image to be tinted
+ * @return {canvas} The resulting tinted canvas
+ *
+ */
+p5.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._renderer._tint[0]/255;
+ newPixels[i+1] = g*this._renderer._tint[1]/255;
+ newPixels[i+2] = b*this._renderer._tint[2]/255;
+ newPixels[i+3] = a*this._renderer._tint[3]/255;
+ }
+
+ tmpCtx.putImageData(id, 0, 0);
+ return tmpCanvas;
+};
+
+/**
+ * Set image mode. Modifies the location from which images are drawn by
+ * changing the way in which parameters given to image() are interpreted.
+ * The default mode is imageMode(CORNER), which interprets the second and
+ * third parameters of image() as the upper-left corner of the image. If
+ * two additional parameters are specified, they are used to set the image's
+ * width and height.
+ *
+ * imageMode(CORNERS) interprets the second and third parameters of image()
+ * as the location of one corner, and the fourth and fifth parameters as the
+ * opposite corner.
+ *
+ * imageMode(CENTER) interprets the second and third parameters of image()
+ * as the image's center point. If two additional parameters are specified,
+ * they are used to set the image's width and height.
+ *
+ * @method imageMode
+ * @param {Constant} mode either CORNER, CORNERS, or CENTER
+ * @example
+ *
+ *
+ *
+ * var img;
+ * function preload() {
+ * img = loadImage("assets/bricks.jpg");
+ * }
+ * function setup() {
+ * imageMode(CORNER);
+ * image(img, 10, 10, 50, 50);
+ * }
+ *
+ *
+ *
+ *
+ *
+ * var img;
+ * function preload() {
+ * img = loadImage("assets/bricks.jpg");
+ * }
+ * function setup() {
+ * imageMode(CORNERS);
+ * image(img, 10, 10, 90, 40);
+ * }
+ *
+ *
+ *
+ *
+ *
+ * var img;
+ * function preload() {
+ * img = loadImage("assets/bricks.jpg");
+ * }
+ * function setup() {
+ * imageMode(CENTER);
+ * image(img, 50, 50, 80, 80);
+ * }
+ *
+ *
+ *
+ * @alt
+ * small square image of bricks
+ * horizontal rectangle image of bricks
+ * large square image of bricks
+ *
+ */
+p5.prototype.imageMode = function(m) {
+ if (m === constants.CORNER ||
+ m === constants.CORNERS ||
+ m === constants.CENTER) {
+ this._renderer._imageMode = m;
+ }
+};
+
+
+module.exports = p5;
+
+},{"../core/canvas":35,"../core/constants":36,"../core/core":37,"../core/error_helpers":40,"./filters":54}],57:[function(_dereq_,module,exports){
+/**
+ * @module Image
+ * @submodule Image
+ * @requires core
+ * @requires constants
+ * @requires filters
+ */
+
+/**
+ * This module defines the p5.Image class and P5 methods for
+ * drawing images to the main display canvas.
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+var Filters = _dereq_('./filters');
+
+/*
+ * Class methods
+ */
+
+/**
+ * Creates a new p5.Image. A p5.Image is a canvas backed representation of an
+ * image.
+ *
+ * p5 can display .gif, .jpg and .png images. Images may be displayed
+ * in 2D and 3D space. Before an image is used, it must be loaded with the
+ * loadImage() function. The p5.Image class contains fields for the width and
+ * height of the image, as well as an array called pixels[] that contains the
+ * values for every pixel in the image.
+ *
+ * The methods described below allow easy access to the image's pixels and
+ * alpha channel and simplify the process of compositing.
+ *
+ * Before using the pixels[] array, be sure to use the loadPixels() method on
+ * the image to make sure that the pixel data is properly loaded.
+ *
+ * @class p5.Image
+ * @constructor
+ * @param {Number} width
+ * @param {Number} height
+ * @param {Object} pInst An instance of a p5 sketch.
+ */
+p5.Image = function(width, height){
+ /**
+ * Image width.
+ * @property width
+ * @example
+ *
+ * var img;
+ * function preload() {
+ * img = loadImage("assets/rockies.jpg");
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ * image(img, 0, 0);
+ * for (var i=0; i < img.width; i++) {
+ * var c = img.get(i, img.height/2);
+ * stroke(c);
+ * line(i, height/2, i, height);
+ * }
+ * }
+ *
+ *
+ * @alt
+ * rocky mountains in top and horizontal lines in corresponding colors in bottom.
+ *
+ */
+ this.width = width;
+ /**
+ * Image height.
+ * @property height
+ * @example
+ *
+ * var img;
+ * function preload() {
+ * img = loadImage("assets/rockies.jpg");
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ * image(img, 0, 0);
+ * for (var i=0; i < img.height; i++) {
+ * var c = img.get(img.width/2, i);
+ * stroke(c);
+ * line(0, i, width/2, i);
+ * }
+ * }
+ *
+ *
+ * @alt
+ * rocky mountains on right and vertical lines in corresponding colors on left.
+ *
+ */
+ this.height = height;
+ this.canvas = document.createElement('canvas');
+ this.canvas.width = this.width;
+ this.canvas.height = this.height;
+ this.drawingContext = this.canvas.getContext('2d');
+ this._pixelDensity = 1;
+ //used for webgl texturing only
+ this.isTexture = false;
+ /**
+ * Array containing the values for all the pixels in the display window.
+ * These values are numbers. This array is the size (include an appropriate
+ * factor for pixelDensity) of the display window x4,
+ * representing the R, G, B, A values in order for each pixel, moving from
+ * left to right across each row, then down each column. Retina and other
+ * high denisty displays may have more pixels[] (by a factor of
+ * pixelDensity^2).
+ * For example, if the image is 100x100 pixels, there will be 40,000. With
+ * pixelDensity = 2, there will be 160,000. The first four values
+ * (indices 0-3) in the array will be the R, G, B, A values of the pixel at
+ * (0, 0). The second four values (indices 4-7) will contain the R, G, B, A
+ * values of the pixel at (1, 0). More generally, to set values for a pixel
+ * at (x, y):
+ * var d = pixelDensity;
+ * for (var i = 0; i < d; i++) {
+ * for (var j = 0; j < d; j++) {
+ * // loop over
+ * idx = 4*((y * d + j) * width * d + (x * d + i));
+ * pixels[idx] = r;
+ * pixels[idx+1] = g;
+ * pixels[idx+2] = b;
+ * pixels[idx+3] = a;
+ * }
+ * }
+ *
+ *
+ * Before accessing this array, the data must loaded with the loadPixels()
+ * function. After the array data has been modified, the updatePixels()
+ * function must be run to update the changes.
+ * @property pixels[]
+ * @example
+ *
+ *
+ * img = createImage(66, 66);
+ * img.loadPixels();
+ * for (i = 0; i < img.width; i++) {
+ * for (j = 0; j < img.height; j++) {
+ * img.set(i, j, color(0, 90, 102));
+ * }
+ * }
+ * img.updatePixels();
+ * image(img, 17, 17);
+ *
+ *
+ *
+ *
+ * var pink = color(255, 102, 204);
+ * img = createImage(66, 66);
+ * img.loadPixels();
+ * for (var i = 0; i < 4*(width*height/2); i+=4) {
+ * img.pixels[i] = red(pink);
+ * img.pixels[i+1] = green(pink);
+ * img.pixels[i+2] = blue(pink);
+ * img.pixels[i+3] = alpha(pink);
+ * }
+ * img.updatePixels();
+ * image(img, 17, 17);
+ *
+ *
+ *
+ * @alt
+ * 66x66 turquoise rect in center of canvas
+ * 66x66 pink rect in center of canvas
+ *
+ */
+ this.pixels = [];
+};
+
+/**
+ * Helper fxn for sharing pixel methods
+ *
+ */
+p5.Image.prototype._setProperty = function (prop, value) {
+ this[prop] = value;
+};
+
+/**
+ * Loads the pixels data for this image into the [pixels] attribute.
+ *
+ * @method loadPixels
+ * @example
+ *
+ * var myImage;
+ * var halfImage;
+ *
+ * function preload() {
+ * myImage = loadImage("assets/rockies.jpg");
+ * }
+ *
+ * function setup() {
+ * myImage.loadPixels();
+ * halfImage = 4 * width * height/2;
+ * for(var i = 0; i < halfImage; i++){
+ * myImage.pixels[i+halfImage] = myImage.pixels[i];
+ * }
+ * myImage.updatePixels();
+ * }
+ *
+ * function draw() {
+ * image(myImage, 0, 0);
+ * }
+ *
+ *
+ * @alt
+ * 2 images of rocky mountains vertically stacked
+ *
+ */
+p5.Image.prototype.loadPixels = function(){
+ p5.Renderer2D.prototype.loadPixels.call(this);
+};
+
+/**
+ * Updates the backing canvas for this image with the contents of
+ * the [pixels] array.
+ *
+ * @method updatePixels
+ * @param {Integer|undefined} x x-offset of the target update area for the
+ * underlying canvas
+ * @param {Integer|undefined} y y-offset of the target update area for the
+ * underlying canvas
+ * @param {Integer|undefined} w height of the target update area for the
+ * underlying canvas
+ * @param {Integer|undefined} h height of the target update area for the
+ * underlying canvas
+ * @example
+ *
+ * var myImage;
+ * var halfImage;
+ *
+ * function preload() {
+ * myImage = loadImage("assets/rockies.jpg");
+ * }
+ *
+ * function setup() {
+ * myImage.loadPixels();
+ * halfImage = 4 * width * height/2;
+ * for(var i = 0; i < halfImage; i++){
+ * myImage.pixels[i+halfImage] = myImage.pixels[i];
+ * }
+ * myImage.updatePixels();
+ * }
+ *
+ * function draw() {
+ * image(myImage, 0, 0);
+ * }
+ *
+ *
+ * @alt
+ * 2 images of rocky mountains vertically stacked
+ *
+ */
+p5.Image.prototype.updatePixels = function(x, y, w, h){
+ p5.Renderer2D.prototype.updatePixels.call(this, x, y, w, h);
+};
+
+/**
+ * Get a region of pixels from an image.
+ *
+ * If no params are passed, those whole image is returned,
+ * if x and y are the only params passed a single pixel is extracted
+ * if all params are passed a rectangle region is extracted and a p5.Image
+ * is returned.
+ *
+ * Returns undefined if the region is outside the bounds of the image
+ *
+ * @method get
+ * @param {Number} [x] x-coordinate of the pixel
+ * @param {Number} [y] y-coordinate of the pixel
+ * @param {Number} [w] width
+ * @param {Number} [h] height
+ * @return {Array/Color | p5.Image} color of pixel at x,y in array format
+ * [R, G, B, A] or p5.Image
+ * @example
+ *
+ * var myImage;
+ * var c;
+ *
+ * function preload() {
+ * myImage = loadImage("assets/rockies.jpg");
+ * }
+ *
+ * function setup() {
+ * background(myImage);
+ * noStroke();
+ * c = myImage.get(60, 90);
+ * fill(c);
+ * rect(25, 25, 50, 50);
+ * }
+ *
+ * //get() returns color here
+ *
+ *
+ * @alt
+ * image of rocky mountains with 50x50 green rect in front
+ *
+ */
+p5.Image.prototype.get = function(x, y, w, h){
+ return p5.Renderer2D.prototype.get.call(this, x, y, w, h);
+};
+
+/**
+ * Set the color of a single pixel or write an image into
+ * this p5.Image.
+ *
+ * Note that for a large number of pixels this will
+ * be slower than directly manipulating the pixels array
+ * and then calling updatePixels().
+ *
+ * @method set
+ * @param {Number} x x-coordinate of the pixel
+ * @param {Number} y y-coordinate of the pixel
+ * @param {Number|Array|Object} a grayscale value | pixel array |
+ * a p5.Color | image to copy
+ * @example
+ *
+ *
+ * img = createImage(66, 66);
+ * img.loadPixels();
+ * for (i = 0; i < img.width; i++) {
+ * for (j = 0; j < img.height; j++) {
+ * img.set(i, j, color(0, 90, 102, i % img.width * 2));
+ * }
+ * }
+ * img.updatePixels();
+ * image(img, 17, 17);
+ * image(img, 34, 34);
+ *
+ *
+ *
+ * @alt
+ * 2 gradated dark turquoise rects fade left. 1 center 1 bottom right of canvas
+ *
+ */
+p5.Image.prototype.set = function(x, y, imgOrCol){
+ p5.Renderer2D.prototype.set.call(this, x, y, imgOrCol);
+};
+
+/**
+ * Resize the image to a new width and height. To make the image scale
+ * proportionally, use 0 as the value for the wide or high parameter.
+ * For instance, to make the width of an image 150 pixels, and change
+ * the height using the same proportion, use resize(150, 0).
+ *
+ * @method resize
+ * @param {Number} width the resized image width
+ * @param {Number} height the resized image height
+ * @example
+ *
+ * var img;
+ *
+ * function setup() {
+ * img = loadImage("assets/rockies.jpg");
+ * }
+
+ * function draw() {
+ * image(img, 0, 0);
+ * }
+ *
+ * function mousePressed() {
+ * img.resize(50, 100);
+ * }
+ *
+ *
+ * @alt
+ * image of rocky mountains. zoomed in
+ *
+ */
+p5.Image.prototype.resize = function(width, height){
+
+ // Copy contents to a temporary canvas, resize the original
+ // and then copy back.
+ //
+ // There is a faster approach that involves just one copy and swapping the
+ // this.canvas reference. We could switch to that approach if (as i think
+ // is the case) there an expectation that the user would not hold a
+ // reference to the backing canvas of a p5.Image. But since we do not
+ // enforce that at the moment, I am leaving in the slower, but safer
+ // implementation.
+
+ // auto-resize
+ if (width === 0 && height === 0) {
+ width = this.canvas.width;
+ height = this.canvas.height;
+ } else if (width === 0) {
+ width = this.canvas.width * height / this.canvas.height;
+ } else if (height === 0) {
+ height = this.canvas.height * width / this.canvas.width;
+ }
+
+ width = Math.floor(width);
+ height = Math.floor(height);
+
+ var tempCanvas = document.createElement('canvas');
+ tempCanvas.width = width;
+ tempCanvas.height = height;
+ tempCanvas.getContext('2d').drawImage(this.canvas,
+ 0, 0, this.canvas.width, this.canvas.height,
+ 0, 0, tempCanvas.width, tempCanvas.height
+ );
+
+
+ // Resize the original canvas, which will clear its contents
+ this.canvas.width = this.width = width;
+ this.canvas.height = this.height = height;
+
+ //Copy the image back
+
+ this.drawingContext.drawImage(tempCanvas,
+ 0, 0, width, height,
+ 0, 0, width, height
+ );
+
+ if(this.pixels.length > 0){
+ this.loadPixels();
+ }
+};
+
+/**
+ * Copies a region of pixels from one image to another. If no
+ * srcImage is specified this is used as the source. If the source
+ * and destination regions aren't the same size, it will
+ * automatically resize source pixels to fit the specified
+ * target region.
+ *
+ * @method copy
+ * @param {p5.Image|undefined} srcImage source image
+ * @param {Integer} sx X coordinate of the source's upper left corner
+ * @param {Integer} sy Y coordinate of the source's upper left corner
+ * @param {Integer} sw source image width
+ * @param {Integer} sh source image height
+ * @param {Integer} dx X coordinate of the destination's upper left corner
+ * @param {Integer} dy Y coordinate of the destination's upper left corner
+ * @param {Integer} dw destination image width
+ * @param {Integer} dh destination image height
+ * @example
+ *
+ * var photo;
+ * var bricks;
+ * var x;
+ * var y;
+ *
+ * function preload() {
+ * photo = loadImage("assets/rockies.jpg");
+ * bricks = loadImage("assets/bricks.jpg");
+ * }
+ *
+ * function setup() {
+ * x = bricks.width/2;
+ * y = bricks.height/2;
+ * photo.copy(bricks, 0, 0, x, y, 0, 0, x, y);
+ * image(photo, 0, 0);
+ * }
+ *
+ *
+ * @alt
+ * image of rocky mountains and smaller image on top of bricks at top left
+ *
+ */
+p5.Image.prototype.copy = function () {
+ p5.prototype.copy.apply(this, arguments);
+};
+
+/**
+ * Masks part of an image from displaying by loading another
+ * image and using it's blue channel as an alpha channel for
+ * this image.
+ *
+ * @method mask
+ * @param {p5.Image} srcImage source image
+ * @example
+ *
+ * var photo, maskImage;
+ * function preload() {
+ * photo = loadImage("assets/rockies.jpg");
+ * maskImage = loadImage("assets/mask2.png");
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ * photo.mask(maskImage);
+ * image(photo, 0, 0);
+ * }
+ *
+ *
+ * @alt
+ * image of rocky mountains with white at right
+ *
+ *
+ * http://blogs.adobe.com/webplatform/2013/01/28/blending-features-in-canvas/
+ *
+ */
+// TODO: - Accept an array of alpha values.
+// - Use other channels of an image. p5 uses the
+// blue channel (which feels kind of arbitrary). Note: at the
+// moment this method does not match native processings original
+// functionality exactly.
+p5.Image.prototype.mask = function(p5Image) {
+ if(p5Image === undefined){
+ p5Image = this;
+ }
+ var currBlend = this.drawingContext.globalCompositeOperation;
+
+ var scaleFactor = 1;
+ if (p5Image instanceof p5.Renderer) {
+ scaleFactor = p5Image._pInst._pixelDensity;
+ }
+
+ var copyArgs = [
+ p5Image,
+ 0,
+ 0,
+ scaleFactor*p5Image.width,
+ scaleFactor*p5Image.height,
+ 0,
+ 0,
+ this.width,
+ this.height
+ ];
+
+ this.drawingContext.globalCompositeOperation = 'destination-in';
+ p5.Image.prototype.copy.apply(this, copyArgs);
+ this.drawingContext.globalCompositeOperation = currBlend;
+};
+
+/**
+ * Applies an image filter to a p5.Image
+ *
+ * @method filter
+ * @param {String} operation one of threshold, gray, invert, posterize and
+ * opaque see Filters.js for docs on each available
+ * filter
+ * @param {Number|undefined} value
+ * @example
+ *
+ * var photo1;
+ * var photo2;
+ *
+ * function preload() {
+ * photo1 = loadImage("assets/rockies.jpg");
+ * photo2 = loadImage("assets/rockies.jpg");
+ * }
+ *
+ * function setup() {
+ * photo2.filter("gray");
+ * image(photo1, 0, 0);
+ * image(photo2, width/2, 0);
+ * }
+ *
+ *
+ * @alt
+ * 2 images of rocky mountains left one in color, right in black and white
+ *
+ */
+p5.Image.prototype.filter = function(operation, value) {
+ Filters.apply(this.canvas, Filters[operation.toLowerCase()], value);
+};
+
+/**
+ * Copies a region of pixels from one image to another, using a specified
+ * blend mode to do the operation.
+ *
+ * @method blend
+ * @param {p5.Image|undefined} srcImage source image
+ * @param {Integer} sx X coordinate of the source's upper left corner
+ * @param {Integer} sy Y coordinate of the source's upper left corner
+ * @param {Integer} sw source image width
+ * @param {Integer} sh source image height
+ * @param {Integer} dx X coordinate of the destination's upper left corner
+ * @param {Integer} dy Y coordinate of the destination's upper left corner
+ * @param {Integer} dw destination image width
+ * @param {Integer} dh destination image height
+ * @param {Integer} blendMode the blend mode
+ *
+ * Available blend modes are: normal | multiply | screen | overlay |
+ * darken | lighten | color-dodge | color-burn | hard-light |
+ * soft-light | difference | exclusion | hue | saturation |
+ * color | luminosity
+ *
+ *
+ * http://blogs.adobe.com/webplatform/2013/01/28/blending-features-in-canvas/
+ * @example
+ *
+ * var mountains;
+ * var bricks;
+ *
+ * function preload() {
+ * mountains = loadImage("assets/rockies.jpg");
+ * bricks = loadImage("assets/bricks_third.jpg");
+ * }
+ *
+ * function setup() {
+ * mountains.blend(bricks, 0, 0, 33, 100, 67, 0, 33, 100, ADD);
+ * image(mountains, 0, 0);
+ * image(bricks, 0, 0);
+ * }
+ *
+ *
+ * var mountains;
+ * var bricks;
+ *
+ * function preload() {
+ * mountains = loadImage("assets/rockies.jpg");
+ * bricks = loadImage("assets/bricks_third.jpg");
+ * }
+ *
+ * function setup() {
+ * mountains.blend(bricks, 0, 0, 33, 100, 67, 0, 33, 100, DARKEST);
+ * image(mountains, 0, 0);
+ * image(bricks, 0, 0);
+ * }
+ *
+ *
+ * var mountains;
+ * var bricks;
+ *
+ * function preload() {
+ * mountains = loadImage("assets/rockies.jpg");
+ * bricks = loadImage("assets/bricks_third.jpg");
+ * }
+ *
+ * function setup() {
+ * mountains.blend(bricks, 0, 0, 33, 100, 67, 0, 33, 100, LIGHTEST);
+ * image(mountains, 0, 0);
+ * image(bricks, 0, 0);
+ * }
+ *
+ *
+ * @alt
+ * image of rocky mountains. Brick images on left and right. Right overexposed
+ * image of rockies. Brickwall images on left and right. Right mortar transparent
+ * image of rockies. Brickwall images on left and right. Right translucent
+ *
+ */
+p5.Image.prototype.blend = function() {
+ p5.prototype.blend.apply(this, arguments);
+};
+
+/**
+ * Saves the image to a file and force the browser to download it.
+ * Accepts two strings for filename and file extension
+ * Supports png (default) and jpg.
+ *
+ * @method save
+ * @param {String} filename give your file a name
+ * @param {String} extension 'png' or 'jpg'
+ * @example
+ *
+ * var photo;
+ *
+ * function preload() {
+ * photo = loadImage("assets/rockies.jpg");
+ * }
+ *
+ * function draw() {
+ * image(photo, 0, 0);
+ * }
+ *
+ * function keyTyped() {
+ * if (key == 's') {
+ * photo.save("photo", "png");
+ * }
+ * }
+ *
+ *
+ * @alt
+ * image of rocky mountains.
+ *
+ */
+p5.Image.prototype.save = function(filename, extension) {
+ var mimeType;
+ if (!extension) {
+ extension = 'png';
+ mimeType = 'image/png';
+ }
+ else {
+ // en.wikipedia.org/wiki/Comparison_of_web_browsers#Image_format_support
+ switch(extension.toLowerCase()){
+ case 'png':
+ mimeType = 'image/png';
+ break;
+ case 'jpeg':
+ mimeType = 'image/jpeg';
+ break;
+ case 'jpg':
+ mimeType = 'image/jpeg';
+ break;
+ default:
+ mimeType = 'image/png';
+ break;
+ }
+ }
+ var downloadMime = 'image/octet-stream';
+ var imageData = this.canvas.toDataURL(mimeType);
+ imageData = imageData.replace(mimeType, downloadMime);
+
+ //Make the browser download the file
+ p5.prototype.downloadFile(imageData, filename, extension);
+};
+
+module.exports = p5.Image;
+},{"../core/core":37,"./filters":54}],58:[function(_dereq_,module,exports){
+/**
+ * @module Image
+ * @submodule Pixels
+ * @for p5
+ * @requires core
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+var Filters = _dereq_('./filters');
+_dereq_('../color/p5.Color');
+
+/**
+ * Uint8ClampedArray
+ * containing the values for all the pixels in the display window.
+ * These values are numbers. This array is the size (include an appropriate
+ * factor for pixelDensity) of the display window x4,
+ * representing the R, G, B, A values in order for each pixel, moving from
+ * left to right across each row, then down each column. Retina and other
+ * high denisty displays will have more pixels[] (by a factor of
+ * pixelDensity^2).
+ * For example, if the image is 100x100 pixels, there will be 40,000. On a
+ * retina display, there will be 160,000.
+ *
+ * The first four values (indices 0-3) in the array will be the R, G, B, A
+ * values of the pixel at (0, 0). The second four values (indices 4-7) will
+ * contain the R, G, B, A values of the pixel at (1, 0). More generally, to
+ * set values for a pixel at (x, y):
+ *
+ * var d = pixelDensity;
+ * for (var i = 0; i < d; i++) {
+ * for (var j = 0; j < d; j++) {
+ * // loop over
+ * idx = 4 * ((y * d + j) * width * d + (x * d + i));
+ * pixels[idx] = r;
+ * pixels[idx+1] = g;
+ * pixels[idx+2] = b;
+ * pixels[idx+3] = a;
+ * }
+ * }
+ *
+ *
+ * While the above method is complex, it is flexible enough to work with
+ * any pixelDensity. Note that set() will automatically take care of
+ * setting all the appropriate values in pixels[] for a given (x, y) at
+ * any pixelDensity, but the performance may not be as fast when lots of
+ * modifications are made to the pixel array.
+ *
+ * Before accessing this array, the data must loaded with the loadPixels()
+ * function. After the array data has been modified, the updatePixels()
+ * function must be run to update the changes.
+ *
+ * Note that this is not a standard javascript array. This means that
+ * standard javascript functions such as slice()
or
+ * arrayCopy()
do not
+ * work.
+ *
+ * @property pixels[]
+ * @example
+ *
+ *
+ * var pink = color(255, 102, 204);
+ * loadPixels();
+ * var d = pixelDensity();
+ * var halfImage = 4 * (width * d) * (height/2 * d);
+ * for (var i = 0; i < halfImage; i+=4) {
+ * pixels[i] = red(pink);
+ * pixels[i+1] = green(pink);
+ * pixels[i+2] = blue(pink);
+ * pixels[i+3] = alpha(pink);
+ * }
+ * updatePixels();
+ *
+ *
+ *
+ * @alt
+ * top half of canvas pink, bottom grey
+ *
+ */
+p5.prototype.pixels = [];
+
+/**
+ * Copies a region of pixels from one image to another, using a specified
+ * blend mode to do the operation.
+ * Available blend modes are: BLEND | DARKEST | LIGHTEST | DIFFERENCE |
+ * MULTIPLY| EXCLUSION | SCREEN | REPLACE | OVERLAY | HARD_LIGHT |
+ * SOFT_LIGHT | DODGE | BURN | ADD | NORMAL
+ *
+ *
+ * @method blend
+ * @param {p5.Image|undefined} srcImage source image
+ * @param {Integer} sx X coordinate of the source's upper left corner
+ * @param {Integer} sy Y coordinate of the source's upper left corner
+ * @param {Integer} sw source image width
+ * @param {Integer} sh source image height
+ * @param {Integer} dx X coordinate of the destination's upper left corner
+ * @param {Integer} dy Y coordinate of the destination's upper left corner
+ * @param {Integer} dw destination image width
+ * @param {Integer} dh destination image height
+ * @param {Integer} blendMode the blend mode
+ *
+ * @example
+ *
+ * var img0;
+ * var img1;
+ *
+ * function preload() {
+ * img0 = loadImage("assets/rockies.jpg");
+ * img1 = loadImage("assets/bricks_third.jpg");
+ * }
+ *
+ * function setup() {
+ * background(img0);
+ * image(img1, 0, 0);
+ * blend(img1, 0, 0, 33, 100, 67, 0, 33, 100, LIGHTEST);
+ * }
+ *
+ *
+ * var img0;
+ * var img1;
+ *
+ * function preload() {
+ * img0 = loadImage("assets/rockies.jpg");
+ * img1 = loadImage("assets/bricks_third.jpg");
+ * }
+ *
+ * function setup() {
+ * background(img0);
+ * image(img1, 0, 0);
+ * blend(img1, 0, 0, 33, 100, 67, 0, 33, 100, DARKEST);
+ * }
+ *
+ *
+ * var img0;
+ * var img1;
+ *
+ * function preload() {
+ * img0 = loadImage("assets/rockies.jpg");
+ * img1 = loadImage("assets/bricks_third.jpg");
+ * }
+ *
+ * function setup() {
+ * background(img0);
+ * image(img1, 0, 0);
+ * blend(img1, 0, 0, 33, 100, 67, 0, 33, 100, ADD);
+ * }
+ *
+ *
+ * @alt
+ * image of rocky mountains. Brick images on left and right. Right overexposed
+ * image of rockies. Brickwall images on left and right. Right mortar transparent
+ * image of rockies. Brickwall images on left and right. Right translucent
+ *
+ *
+ */
+p5.prototype.blend = function() {
+ if (this._renderer) {
+ this._renderer.blend.apply(this._renderer, arguments);
+ } else {
+ p5.Renderer2D.prototype.blend.apply(this, arguments);
+ }
+};
+
+/**
+ * Copies a region of the canvas to another region of the canvas
+ * and copies a region of pixels from an image used as the srcImg parameter
+ * into the canvas srcImage is specified this is used as the source. If
+ * the source and destination regions aren't the same size, it will
+ * automatically resize source pixels to fit the specified
+ * target region.
+ *
+ * @method copy
+ * @param {p5.Image|undefined} srcImage source image
+ * @param {Integer} sx X coordinate of the source's upper left corner
+ * @param {Integer} sy Y coordinate of the source's upper left corner
+ * @param {Integer} sw source image width
+ * @param {Integer} sh source image height
+ * @param {Integer} dx X coordinate of the destination's upper left corner
+ * @param {Integer} dy Y coordinate of the destination's upper left corner
+ * @param {Integer} dw destination image width
+ * @param {Integer} dh destination image height
+ *
+ * @example
+ *
+ * var img;
+ *
+ * function preload() {
+ * img = loadImage("assets/rockies.jpg");
+ * }
+ *
+ * function setup() {
+ * background(img);
+ * copy(img, 7, 22, 10, 10, 35, 25, 50, 50);
+ * stroke(255);
+ * noFill();
+ * // Rectangle shows area being copied
+ * rect(7, 22, 10, 10);
+ * }
+ *
+ *
+ * @alt
+ * image of rocky mountains. Brick images on left and right. Right overexposed
+ * image of rockies. Brickwall images on left and right. Right mortar transparent
+ * image of rockies. Brickwall images on left and right. Right translucent
+ *
+ */
+p5.prototype.copy = function () {
+ p5.Renderer2D._copyHelper.apply(this, arguments);
+};
+
+/**
+ * Applies a filter to the canvas.
+ *
+ *
+ * The presets options are:
+ *
+ *
+ * THRESHOLD
+ * 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.
+ *
+ *
+ * GRAY
+ * Converts any colors in the image to grayscale equivalents. No parameter
+ * is used.
+ *
+ *
+ * OPAQUE
+ * Sets the alpha channel to entirely opaque. No parameter is used.
+ *
+ *
+ * INVERT
+ * Sets each pixel to its inverse value. No parameter is used.
+ *
+ *
+ * POSTERIZE
+ * 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.
+ *
+ *
+ * BLUR
+ * Executes a Guassian blur with the level parameter specifying the extent
+ * of the blurring. If no parameter is used, the blur is equivalent to
+ * Guassian blur of radius 1. Larger values increase the blur.
+ *
+ *
+ * ERODE
+ * Reduces the light areas. No parameter is used.
+ *
+ *
+ * DILATE
+ * Increases the light areas. No parameter is used.
+ *
+ * @method filter
+ * @param {Constant} filterType
+ * @param {Number} filterParam an optional parameter unique
+ * to each filter, see above
+ *
+ *
+ * @example
+ *
+ *
+ * var img;
+ * function preload() {
+ * img = loadImage("assets/bricks.jpg");
+ * }
+ * function setup() {
+ * image(img, 0, 0);
+ * filter(THRESHOLD);
+ * }
+ *
+ *
+ *
+ *
+ *
+ * var img;
+ * function preload() {
+ * img = loadImage("assets/bricks.jpg");
+ * }
+ * function setup() {
+ * image(img, 0, 0);
+ * filter(GRAY);
+ * }
+ *
+ *
+ *
+ *
+ *
+ * var img;
+ * function preload() {
+ * img = loadImage("assets/bricks.jpg");
+ * }
+ * function setup() {
+ * image(img, 0, 0);
+ * filter(OPAQUE);
+ * }
+ *
+ *
+ *
+ *
+ *
+ * var img;
+ * function preload() {
+ * img = loadImage("assets/bricks.jpg");
+ * }
+ * function setup() {
+ * image(img, 0, 0);
+ * filter(INVERT);
+ * }
+ *
+ *
+ *
+ *
+ *
+ * var img;
+ * function preload() {
+ * img = loadImage("assets/bricks.jpg");
+ * }
+ * function setup() {
+ * image(img, 0, 0);
+ * filter(POSTERIZE,3);
+ * }
+ *
+ *
+ *
+ *
+ *
+ * var img;
+ * function preload() {
+ * img = loadImage("assets/bricks.jpg");
+ * }
+ * function setup() {
+ * image(img, 0, 0);
+ * filter(DILATE);
+ * }
+ *
+ *
+ *
+ *
+ *
+ * var img;
+ * function preload() {
+ * img = loadImage("assets/bricks.jpg");
+ * }
+ * function setup() {
+ * image(img, 0, 0);
+ * filter(BLUR,3);
+ * }
+ *
+ *
+ *
+ *
+ *
+ * var img;
+ * function preload() {
+ * img = loadImage("assets/bricks.jpg");
+ * }
+ * function setup() {
+ * image(img, 0, 0);
+ * filter(ERODE);
+ * }
+ *
+ *
+ *
+ * @alt
+ * black and white image of a brick wall.
+ * greyscale image of a brickwall
+ * image of a brickwall
+ * jade colored image of a brickwall
+ * red and pink image of a brickwall
+ * image of a brickwall
+ * blurry image of a brickwall
+ * image of a brickwall
+ * image of a brickwall with less detail
+ *
+ */
+p5.prototype.filter = function(operation, value) {
+ Filters.apply(this.canvas, Filters[operation.toLowerCase()], value);
+};
+
+/**
+ * Returns an array of [R,G,B,A] values for any pixel or grabs a section of
+ * an image. If no parameters are specified, the entire image is returned.
+ * Use the x and y parameters to get the value of one pixel. Get a section of
+ * the display window by specifying additional w and h parameters. When
+ * getting an image, the x and y parameters define the coordinates for the
+ * upper-left corner of the image, regardless of the current imageMode().
+ *
+ * If the pixel requested is outside of the image window, [0,0,0,255] is
+ * returned. To get the numbers scaled according to the current color ranges
+ * and taking into account colorMode, use getColor instead of get.
+ *
+ * Getting the color of a single pixel with get(x, y) is easy, but not as fast
+ * as grabbing the data directly from pixels[]. The equivalent statement to
+ * get(x, y) using pixels[] with pixel density d is
+ *
+ * var off = (y * width + x) * d * 4;
+ * [pixels[off],
+ * pixels[off+1],
+ * pixels[off+2],
+ * pixels[off+3]]
+ *
+ * See the reference for pixels[] for more information.
+ *
+ * @method get
+ * @param {Number} [x] x-coordinate of the pixel
+ * @param {Number} [y] y-coordinate of the pixel
+ * @param {Number} [w] width
+ * @param {Number} [h] height
+ * @return {Array|p5.Image} values of pixel at x,y in array format
+ * [R, G, B, A] or p5.Image
+ * @example
+ *
+ *
+ * var img;
+ * function preload() {
+ * img = loadImage("assets/rockies.jpg");
+ * }
+ * function setup() {
+ * image(img, 0, 0);
+ * var c = get();
+ * image(c, width/2, 0);
+ * }
+ *
+ *
+ *
+ *
+ *
+ * var img;
+ * function preload() {
+ * img = loadImage("assets/rockies.jpg");
+ * }
+ * function setup() {
+ * image(img, 0, 0);
+ * var c = get(50, 90);
+ * fill(c);
+ * noStroke();
+ * rect(25, 25, 50, 50);
+ * }
+ *
+ *
+ *
+ * @alt
+ * 2 images of the rocky mountains, side-by-side
+ * Image of the rocky mountains with 50x50 green rect in center of canvas
+ *
+ */
+p5.prototype.get = function(x, y, w, h){
+ return this._renderer.get(x, y, w, h);
+};
+
+/**
+ * Loads the pixel data for the display window into the pixels[] array. This
+ * function must always be called before reading from or writing to pixels[].
+ *
+ * @method loadPixels
+ * @example
+ *
+ *
+ * var img;
+ * function preload() {
+ * img = loadImage("assets/rockies.jpg");
+ * }
+ *
+ * function setup() {
+ * image(img, 0, 0);
+ * var d = pixelDensity();
+ * var halfImage = 4 * (img.width * d) *
+ (img.height/2 * d);
+ * loadPixels();
+ * for (var i = 0; i < halfImage; i++) {
+ * pixels[i+halfImage] = pixels[i];
+ * }
+ * updatePixels();
+ * }
+ *
+ *
+ *
+ * @alt
+ * two images of the rocky mountains. one on top, one on bottom of canvas.
+ *
+ */
+p5.prototype.loadPixels = function() {
+ this._renderer.loadPixels();
+};
+
+/**
+ * Changes the color of any pixel, or writes an image directly to the
+ * display window.
+ * The x and y parameters specify the pixel to change and the c parameter
+ * specifies the color value. This can be a p5.Color object, or [R, G, B, A]
+ * pixel array. It can also be a single grayscale value.
+ * When setting an image, the x and y parameters define the coordinates for
+ * the upper-left corner of the image, regardless of the current imageMode().
+ *
+ *
+ * After using set(), you must call updatePixels() for your changes to
+ * appear. This should be called once all pixels have been set.
+ *
+ * Setting the color of a single pixel with set(x, y) is easy, but not as
+ * fast as putting the data directly into pixels[]. Setting the pixels[]
+ * values directly may be complicated when working with a retina display,
+ * but will perform better when lots of pixels need to be set directly on
+ * every loop.
+ * See the reference for pixels[] for more information.
+ *
+ * @method set
+ * @param {Number} x x-coordinate of the pixel
+ * @param {Number} y y-coordinate of the pixel
+ * @param {Number|Array|Object} c insert a grayscale value | a pixel array |
+ * a p5.Color object | a p5.Image to copy
+ * @example
+ *
+ *
+ * var black = color(0);
+ * set(30, 20, black);
+ * set(85, 20, black);
+ * set(85, 75, black);
+ * set(30, 75, black);
+ * updatePixels();
+ *
+ *
+ *
+ *
+ *
+ * for (var i = 30; i < width-15; i++) {
+ * for (var j = 20; j < height-25; j++) {
+ * var c = color(204-j, 153-i, 0);
+ * set(i, j, c);
+ * }
+ * }
+ * updatePixels();
+ *
+ *
+ *
+ *
+ *
+ * var img;
+ * function preload() {
+ * img = loadImage("assets/rockies.jpg");
+ * }
+ *
+ * function setup() {
+ * set(0, 0, img);
+ * updatePixels();
+ * line(0, 0, width, height);
+ * line(0, height, width, 0);
+ * }
+ *
+ *
+ *
+ * @alt
+ * 4 black points in the shape of a square middle-right of canvas.
+ * square with orangey-brown gradient lightening at bottom right.
+ * image of the rocky mountains. with lines like an 'x' through the center.
+ */
+p5.prototype.set = function (x, y, imgOrCol) {
+ this._renderer.set(x, y, imgOrCol);
+};
+/**
+ * Updates the display window with the data in the pixels[] array.
+ * Use in conjunction with loadPixels(). If you're only reading pixels from
+ * the array, there's no need to call updatePixels() — updating is only
+ * necessary to apply changes. updatePixels() should be called anytime the
+ * pixels array is manipulated or set() is called.
+ *
+ * @method updatePixels
+ * @param {Number} [x] x-coordinate of the upper-left corner of region
+ * to update
+ * @param {Number} [y] y-coordinate of the upper-left corner of region
+ * to update
+ * @param {Number} [w] width of region to update
+ * @param {Number} [w] height of region to update
+ * @example
+ *
+ *
+ * var img;
+ * function preload() {
+ * img = loadImage("assets/rockies.jpg");
+ * }
+ *
+ * function setup() {
+ * image(img, 0, 0);
+ * var halfImage = 4 * (img.width * pixelDensity()) *
+ * (img.height * pixelDensity()/2);
+ * loadPixels();
+ * for (var i = 0; i < halfImage; i++) {
+ * pixels[i+halfImage] = pixels[i];
+ * }
+ * updatePixels();
+ * }
+ *
+ *
+ * @alt
+ * two images of the rocky mountains. one on top, one on bottom of canvas.
+ */
+p5.prototype.updatePixels = function (x, y, w, h) {
+ // graceful fail - if loadPixels() or set() has not been called, pixel
+ // array will be empty, ignore call to updatePixels()
+ if (this.pixels.length === 0) {
+ return;
+ }
+ this._renderer.updatePixels(x, y, w, h);
+};
+
+module.exports = p5;
+
+},{"../color/p5.Color":31,"../core/core":37,"./filters":54}],59:[function(_dereq_,module,exports){
+/**
+ * @module IO
+ * @submodule Input
+ * @for p5
+ * @requires core
+ * @requires reqwest
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+var reqwest = _dereq_('reqwest');
+var opentype = _dereq_('opentype.js');
+_dereq_('../core/error_helpers');
+
+/**
+ * Checks if we are in preload and returns the last arg which will be the
+ * _decrementPreload function if called from a loadX() function. Should
+ * only be used in loadX() functions.
+ * @private
+ */
+p5._getDecrementPreload = function () {
+ var decrementPreload = arguments[arguments.length - 1];
+
+ // when in preload decrementPreload will always be the last arg as it is set
+ // with args.push() before invocation in _wrapPreload
+ if ((window.preload || (this && this.preload)) &&
+ typeof decrementPreload === 'function') {
+ return decrementPreload;
+ } else {
+ return null;
+ }
+};
+
+/**
+ * Loads an opentype font file (.otf, .ttf) from a file or a URL,
+ * and returns a PFont Object. This method is asynchronous,
+ * meaning it may not finish before the next line in your sketch
+ * is executed.
+ *
+ * The path to the font should be relative to the HTML file
+ * that links in your sketch. Loading an from a URL or other
+ * remote location may be blocked due to your browser's built-in
+ * security.
+ *
+ * @method loadFont
+ * @param {String} path name of the file or url to load
+ * @param {Function} [callback] function to be executed after
+ * loadFont()
+ * completes
+ * @return {Object} p5.Font object
+ * @example
+ *
+ * Calling loadFont() inside preload() guarantees that the load
+ * operation will have completed before setup() and draw() are called.
+ *
+ *
+ * var myFont;
+ * function preload() {
+ * myFont = loadFont('assets/AvenirNextLTPro-Demi.otf');
+ * }
+ *
+ * function setup() {
+ * fill('#ED225D');
+ * textFont(myFont);
+ * textSize(36);
+ * text('p5*js', 10, 50);
+ * }
+ *
+ *
+ * Outside of preload(), you may supply a callback function to handle the
+ * object:
+ *
+ *
+ * function setup() {
+ * loadFont('assets/AvenirNextLTPro-Demi.otf', drawText);
+ * }
+ *
+ * function drawText(font) {
+ * fill('#ED225D');
+ * textFont(font, 36);
+ * text('p5*js', 10, 50);
+ * }
+ *
+ *
+ *
+ * You can also use the string name of the font to style other HTML
+ * elements.
+ *
+ *
+ * var myFont;
+ *
+ * function preload() {
+ * myFont = loadFont('assets/Avenir.otf');
+ * }
+ *
+ * function setup() {
+ * var myDiv = createDiv('hello there');
+ * myDiv.style('font-family', 'Avenir');
+ * }
+ *
+ *
+ * @alt
+ * p5*js in p5's theme dark pink
+ * p5*js in p5's theme dark pink
+ *
+ */
+p5.prototype.loadFont = function (path, onSuccess, onError) {
+
+ var p5Font = new p5.Font(this);
+ var decrementPreload = p5._getDecrementPreload.apply(this, arguments);
+
+ opentype.load(path, function (err, font) {
+
+ if (err) {
+
+ if ((typeof onError !== 'undefined') && (onError !== decrementPreload)) {
+ return onError(err);
+ }
+ p5._friendlyFileLoadError(4, path);
+ console.error(err, path);
+ return;
+ }
+
+ p5Font.font = font;
+
+ if (typeof onSuccess !== 'undefined') {
+ onSuccess(p5Font);
+ }
+
+ if (decrementPreload && (onSuccess !== decrementPreload)) {
+ decrementPreload();
+ }
+
+ // check that we have an acceptable font type
+ var validFontTypes = [ 'ttf', 'otf', 'woff', 'woff2' ],
+ fileNoPath = path.split('\\').pop().split('/').pop(),
+ lastDotIdx = fileNoPath.lastIndexOf('.'), fontFamily, newStyle,
+ fileExt = lastDotIdx < 1 ? null : fileNoPath.substr(lastDotIdx + 1);
+
+ // if so, add it to the DOM (name-only) for use with p5.dom
+ if (validFontTypes.indexOf(fileExt) > -1) {
+
+ fontFamily = fileNoPath.substr(0, lastDotIdx);
+ newStyle = document.createElement('style');
+ newStyle.appendChild(document.createTextNode('\n@font-face {' +
+ '\nfont-family: ' + fontFamily + ';\nsrc: url(' + path + ');\n}\n'));
+ document.head.appendChild(newStyle);
+ }
+
+ });
+
+ return p5Font;
+};
+
+//BufferedReader
+p5.prototype.createInput = function () {
+ // TODO
+ throw 'not yet implemented';
+};
+
+p5.prototype.createReader = function () {
+ // TODO
+ throw 'not yet implemented';
+};
+
+p5.prototype.loadBytes = function () {
+ // TODO
+ throw 'not yet implemented';
+};
+
+/**
+ * Loads a JSON file from a file or a URL, and returns an Object or Array.
+ * This method is asynchronous, meaning it may not finish before the next
+ * line in your sketch is executed.
+ *
+ * @method loadJSON
+ * @param {String} path name of the file or url to load
+ * @param {Function} [callback] function to be executed after
+ * loadJSON() completes, data is passed
+ * in as first argument
+ * @param {Function} [errorCallback] function to be executed if
+ * there is an error, response is passed
+ * in as first argument
+ * @param {String} [datatype] "json" or "jsonp"
+ * @return {Object|Array} JSON data
+ * @example
+ *
+ * Calling loadJSON() inside preload() guarantees to complete the
+ * operation before setup() and draw() are called.
+ *
+ *
+ * var weather;
+ * function preload() {
+ * var url = 'http://api.openweathermap.org/data/2.5/weather?q=London,UK'+
+ * '&APPID=7bbbb47522848e8b9c26ba35c226c734';
+ * weather = loadJSON(url);
+ * }
+ *
+ * function setup() {
+ * noLoop();
+ * }
+ *
+ * function draw() {
+ * background(200);
+ * // get the humidity value out of the loaded JSON
+ * var humidity = weather.main.humidity;
+ * fill(0, humidity); // use the humidity value to set the alpha
+ * ellipse(width/2, height/2, 50, 50);
+ * }
+ *
+ *
+ *
+ * Outside of preload(), you may supply a callback function to handle the
+ * object:
+ *
+ * function setup() {
+ * noLoop();
+ * var url = 'http://api.openweathermap.org/data/2.5/weather?q=NewYork'+
+ * '&APPID=7bbbb47522848e8b9c26ba35c226c734';
+ * loadJSON(url, drawWeather);
+ * }
+ *
+ * function draw() {
+ * background(200);
+ * }
+ *
+ * function drawWeather(weather) {
+ * // get the humidity value out of the loaded JSON
+ * var humidity = weather.main.humidity;
+ * fill(0, humidity); // use the humidity value to set the alpha
+ * ellipse(width/2, height/2, 50, 50);
+ * }
+ *
+ *
+ * @alt
+ * 50x50 ellipse that changes from black to white depending on the current humidity
+ * 50x50 ellipse that changes from black to white depending on the current humidity
+ *
+ */
+p5.prototype.loadJSON = function () {
+ var path = arguments[0];
+ var callback = arguments[1];
+ var errorCallback;
+ var decrementPreload = p5._getDecrementPreload.apply(this, arguments);
+
+ var ret = {}; // object needed for preload
+ // assume jsonp for URLs
+ var t = 'json'; //= path.indexOf('http') === -1 ? 'json' : 'jsonp';
+
+ // check for explicit data type argument
+ for (var i = 2; i < arguments.length; i++) {
+ var arg = arguments[i];
+ if (typeof arg === 'string') {
+ if (arg === 'jsonp' || arg === 'json') {
+ t = arg;
+ }
+ } else if (typeof arg === 'function') {
+ errorCallback = arg;
+ }
+ }
+
+ reqwest({
+ url: path,
+ type: t,
+ crossOrigin: true,
+ error: function (resp) {
+ // pass to error callback if defined
+ if (errorCallback) {
+ errorCallback(resp);
+ } else { // otherwise log error msg
+ console.log(resp.statusText);
+ }
+ },
+ success: function (resp) {
+ for (var k in resp) {
+ ret[k] = resp[k];
+ }
+ if (typeof callback !== 'undefined') {
+ callback(resp);
+ }
+ if (decrementPreload && (callback !== decrementPreload)) {
+ decrementPreload();
+ }
+ }
+ });
+
+ return ret;
+};
+
+/**
+ * Reads the contents of a file and creates a String array of its individual
+ * lines. If the name of the file is used as the parameter, as in the above
+ * example, the file must be located in the sketch directory/folder.
+ *
+ * Alternatively, the file maybe be loaded from anywhere on the local
+ * computer using an absolute path (something that starts with / on Unix and
+ * Linux, or a drive letter on Windows), or the filename parameter can be a
+ * URL for a file found on a network.
+ *
+ * This method is asynchronous, meaning it may not finish before the next
+ * line in your sketch is executed.
+ *
+ * @method loadStrings
+ * @param {String} filename name of the file or url to load
+ * @param {Function} [callback] function to be executed after loadStrings()
+ * completes, Array is passed in as first
+ * argument
+ * @param {Function} [errorCallback] function to be executed if
+ * there is an error, response is passed
+ * in as first argument
+ * @return {Array} Array of Strings
+ * @example
+ *
+ * Calling loadStrings() inside preload() guarantees to complete the
+ * operation before setup() and draw() are called.
+ *
+ *
+ * var result;
+ * function preload() {
+ * result = loadStrings('assets/test.txt');
+ * }
+
+ * function setup() {
+ * background(200);
+ * var ind = floor(random(result.length));
+ * text(result[ind], 10, 10, 80, 80);
+ * }
+ *
+ *
+ * Outside of preload(), you may supply a callback function to handle the
+ * object:
+ *
+ *
+ * function setup() {
+ * loadStrings('assets/test.txt', pickString);
+ * }
+ *
+ * function pickString(result) {
+ * background(200);
+ * var ind = floor(random(result.length));
+ * text(result[ind], 10, 10, 80, 80);
+ * }
+ *
+ *
+ * @alt
+ * randomly generated text from a file, for example "i smell like butter"
+ * randomly generated text from a file, for example "i have three feet"
+ *
+ */
+p5.prototype.loadStrings = function (path, callback, errorCallback) {
+ var ret = [];
+ var req = new XMLHttpRequest();
+ var decrementPreload = p5._getDecrementPreload.apply(this, arguments);
+
+ req.addEventListener('error', function (resp) {
+ if (errorCallback) {
+ errorCallback(resp);
+ } else {
+ console.log(resp.responseText);
+ }
+ });
+
+ req.open('GET', path, true);
+ req.onreadystatechange = function () {
+ if (req.readyState === 4) {
+ if (req.status === 200) {
+ var arr = req.responseText.match(/[^\r\n]+/g);
+ for (var k in arr) {
+ ret[k] = arr[k];
+ }
+ if (typeof callback !== 'undefined') {
+ callback(ret);
+ }
+ if (decrementPreload && (callback !== decrementPreload)) {
+ decrementPreload();
+ }
+ } else {
+ if (errorCallback) {
+ errorCallback(req);
+ } else {
+ console.log(req.statusText);
+ }
+ //p5._friendlyFileLoadError(3, path);
+ }
+ }
+ };
+ req.send(null);
+ return ret;
+};
+
+/**
+ * Reads the contents of a file or URL and creates a p5.Table object with
+ * its values. If a file is specified, it must be located in the sketch's
+ * "data" folder. The filename parameter can also be a URL to a file found
+ * online. By default, the file is assumed to be comma-separated (in CSV
+ * format). Table only looks for a header row if the 'header' option is
+ * included.
+ *
+ * Possible options include:
+ *
+ * - csv - parse the table as comma-separated values
+ * - tsv - parse the table as tab-separated values
+ * - header - this table has a header (title) row
+ *
+ *
+ *
+ * When passing in multiple options, pass them in as separate parameters,
+ * seperated by commas. For example:
+ *
+ *
+ * loadTable("my_csv_file.csv", "csv", "header")
+ *
+ *
+ *
+ * All files loaded and saved use UTF-8 encoding.
+ *
+ * This method is asynchronous, meaning it may not finish before the next
+ * line in your sketch is executed. Calling loadTable() inside preload()
+ * guarantees to complete the operation before setup() and draw() are called.
+ *
Outside of preload(), you may supply a callback function to handle the
+ * object:
+ *
+ *
+ * @method loadTable
+ * @param {String} filename name of the file or URL to load
+ * @param {String|Strings} [options] "header" "csv" "tsv"
+ * @param {Function} [callback] function to be executed after
+ * loadTable() completes. On success, the
+ * Table object is passed in as the
+ * first argument; otherwise, false
+ * is passed in.
+ * @return {Object} Table object containing data
+ *
+ * @example
+ *
+ *
+ * // Given the following CSV file called "mammals.csv"
+ * // located in the project's "assets" folder:
+ * //
+ * // id,species,name
+ * // 0,Capra hircus,Goat
+ * // 1,Panthera pardus,Leopard
+ * // 2,Equus zebra,Zebra
+ *
+ * var table;
+ *
+ * function preload() {
+ * //my table is comma separated value "csv"
+ * //and has a header specifying the columns labels
+ * table = loadTable("assets/mammals.csv", "csv", "header");
+ * //the file can be remote
+ * //table = loadTable("http://p5js.org/reference/assets/mammals.csv",
+ * // "csv", "header");
+ * }
+ *
+ * function setup() {
+ * //count the columns
+ * print(table.getRowCount() + " total rows in table");
+ * print(table.getColumnCount() + " total columns in table");
+ *
+ * print(table.getColumn("name"));
+ * //["Goat", "Leopard", "Zebra"]
+ *
+ * //cycle through the table
+ * for (var r = 0; r < table.getRowCount(); r++)
+ * for (var c = 0; c < table.getColumnCount(); c++) {
+ * print(table.getString(r, c));
+ * }
+ * }
+ *
+ *
+ *
+ * @alt
+ * randomly generated text from a file, for example "i smell like butter"
+ * randomly generated text from a file, for example "i have three feet"
+ *
+ */
+p5.prototype.loadTable = function (path) {
+ var callback = null;
+ var options = [];
+ var header = false;
+ var sep = ',';
+ var separatorSet = false;
+ var decrementPreload = p5._getDecrementPreload.apply(this, arguments);
+
+ for (var i = 1; i < arguments.length; i++) {
+ if ((typeof (arguments[i]) === 'function') &&
+ (arguments[i] !== decrementPreload)) {
+ callback = arguments[i];
+ } else if (typeof (arguments[i]) === 'string') {
+ options.push(arguments[i]);
+ if (arguments[i] === 'header') {
+ header = true;
+ }
+ if (arguments[i] === 'csv') {
+ if (separatorSet) {
+ throw new Error('Cannot set multiple separator types.');
+ } else {
+ sep = ',';
+ separatorSet = true;
+ }
+ } else if (arguments[i] === 'tsv') {
+ if (separatorSet) {
+ throw new Error('Cannot set multiple separator types.');
+ } else {
+ sep = '\t';
+ separatorSet = true;
+ }
+ }
+ }
+ }
+
+ var t = new p5.Table();
+ reqwest({
+ url: path,
+ crossOrigin: true,
+ type: 'csv'
+ })
+ .then(function (resp) {
+ resp = resp.responseText;
+
+ var state = {};
+
+ // define constants
+ var PRE_TOKEN = 0,
+ MID_TOKEN = 1,
+ POST_TOKEN = 2,
+ POST_RECORD = 4;
+
+ var QUOTE = '\"',
+ CR = '\r',
+ LF = '\n';
+
+ var records = [];
+ var offset = 0;
+ var currentRecord = null;
+ var currentChar;
+
+ var recordBegin = function () {
+ state.escaped = false;
+ currentRecord = [];
+ tokenBegin();
+ };
+
+ var recordEnd = function () {
+ state.currentState = POST_RECORD;
+ records.push(currentRecord);
+ currentRecord = null;
+ };
+
+ var tokenBegin = function () {
+ state.currentState = PRE_TOKEN;
+ state.token = '';
+ };
+
+ var tokenEnd = function () {
+ currentRecord.push(state.token);
+ tokenBegin();
+ };
+
+ while (true) {
+ currentChar = resp[offset++];
+
+ // EOF
+ if (currentChar == null) {
+ if (state.escaped) {
+ throw new Error('Unclosed quote in file.');
+ }
+ if (currentRecord) {
+ tokenEnd();
+ recordEnd();
+ break;
+ }
+ }
+ if (currentRecord === null) {
+ recordBegin();
+ }
+
+ // Handle opening quote
+ if (state.currentState === PRE_TOKEN) {
+ if (currentChar === QUOTE) {
+ state.escaped = true;
+ state.currentState = MID_TOKEN;
+ continue;
+ }
+ state.currentState = MID_TOKEN;
+ }
+
+ // mid-token and escaped, look for sequences and end quote
+ if (state.currentState === MID_TOKEN && state.escaped) {
+ if (currentChar === QUOTE) {
+ if (resp[offset] === QUOTE) {
+ state.token += QUOTE;
+ offset++;
+ } else {
+ state.escaped = false;
+ state.currentState = POST_TOKEN;
+ }
+ } else {
+ state.token += currentChar;
+ }
+ continue;
+ }
+
+ // fall-through: mid-token or post-token, not escaped
+ if (currentChar === CR) {
+ if (resp[offset] === LF) {
+ offset++;
+ }
+ tokenEnd();
+ recordEnd();
+ } else if (currentChar === LF) {
+ tokenEnd();
+ recordEnd();
+ } else if (currentChar === sep) {
+ tokenEnd();
+ } else if (state.currentState === MID_TOKEN) {
+ state.token += currentChar;
+ }
+ }
+
+ // set up column names
+ if (header) {
+ t.columns = records.shift();
+ } else {
+ for (i = 0; i < records[0].length; i++) {
+ t.columns[i] = 'null';
+ }
+ }
+ var row;
+ for (i = 0; i < records.length; i++) {
+ //Handles row of 'undefined' at end of some CSVs
+ if (i === records.length - 1 && records[i].length === 1) {
+ if (records[i][0] === 'undefined') {
+ break;
+ }
+ }
+ row = new p5.TableRow();
+ row.arr = records[i];
+ row.obj = makeObject(records[i], t.columns);
+ t.addRow(row);
+ }
+ if (callback !== null) {
+ callback(t);
+ }
+ if (decrementPreload && (callback !== decrementPreload)) {
+ decrementPreload();
+ }
+ })
+ .fail(function (err, msg) {
+ p5._friendlyFileLoadError(2, path);
+ // don't get error callback mixed up with decrementPreload
+ if ((typeof callback === 'function') &&
+ (callback !== decrementPreload)) {
+ callback(false);
+ }
+ });
+
+ return t;
+};
+
+// helper function to turn a row into a JSON object
+function makeObject(row, headers) {
+ var ret = {};
+ headers = headers || [];
+ if (typeof (headers) === 'undefined') {
+ for (var j = 0; j < row.length; j++) {
+ headers[j.toString()] = j;
+ }
+ }
+ for (var i = 0; i < headers.length; i++) {
+ var key = headers[i];
+ var val = row[i];
+ ret[key] = val;
+ }
+ return ret;
+}
+
+/*global parseXML */
+p5.prototype.parseXML = function (two) {
+ var one = new p5.XML();
+ var i;
+ if (two.children.length) {
+ for ( i = 0; i < two.children.length; i++ ) {
+ var node = parseXML(two.children[i]);
+ one.addChild(node);
+ }
+ one.setName(two.nodeName);
+ one._setCont(two.textContent);
+ one._setAttributes(two);
+ for (var j = 0; j < one.children.length; j++) {
+ one.children[j].parent = one;
+ }
+ return one;
+ }
+ else {
+ one.setName(two.nodeName);
+ one._setCont(two.textContent);
+ one._setAttributes(two);
+ return one;
+ }
+};
+
+/**
+ * Reads the contents of a file and creates an XML object with its values.
+ * If the name of the file is used as the parameter, as in the above example,
+ * the file must be located in the sketch directory/folder.
+ *
+ * Alternatively, the file maybe be loaded from anywhere on the local
+ * computer using an absolute path (something that starts with / on Unix and
+ * Linux, or a drive letter on Windows), or the filename parameter can be a
+ * URL for a file found on a network.
+ *
+ * This method is asynchronous, meaning it may not finish before the next
+ * line in your sketch is executed. Calling loadXML() inside preload()
+ * guarantees to complete the operation before setup() and draw() are called.
+ *
+ * Outside of preload(), you may supply a callback function to handle the
+ * object:
+ *
+ * @method loadXML
+ * @param {String} filename name of the file or URL to load
+ * @param {Function} [callback] function to be executed after loadXML()
+ * completes, XML object is passed in as
+ * first argument
+ * @param {Function} [errorCallback] function to be executed if
+ * there is an error, response is passed
+ * in as first argument
+ * @return {Object} XML object containing data
+ */
+p5.prototype.loadXML = function (path, callback, errorCallback) {
+ var ret = {};
+ var decrementPreload = p5._getDecrementPreload.apply(this, arguments);
+ reqwest({
+ url: path,
+ type: 'xml',
+ crossOrigin: true,
+ error: function (resp) {
+ // pass to error callback if defined
+ if (errorCallback) {
+ errorCallback(resp);
+ } else { // otherwise log error msg
+ console.log(resp.statusText);
+ }
+ //p5._friendlyFileLoadError(1,path);
+ }
+ })
+ .then(function (resp) {
+ var xml = parseXML(resp.documentElement);
+ for(var key in xml) {
+ ret[key] = xml[key];
+ }
+ if (typeof callback !== 'undefined') {
+ callback(ret);
+ }
+ if (decrementPreload && (callback !== decrementPreload)) {
+ decrementPreload();
+ }
+ });
+ return ret;
+};
+
+// name clash with window.open
+// p5.prototype.open = function() {
+// // TODO
+
+// };
+
+p5.prototype.selectFolder = function () {
+ // TODO
+ throw 'not yet implemented';
+
+};
+
+p5.prototype.selectInput = function () {
+ // TODO
+ throw 'not yet implemented';
+
+};
+
+/**
+ * Method for executing an HTTP GET request. If data type is not specified,
+ * p5 will try to guess based on the URL, defaulting to text.
+ *
+ * @method httpGet
+ * @param {String} path name of the file or url to load
+ * @param {Object} [data] param data passed sent with request
+ * @param {String} [datatype] "json", "jsonp", "xml", or "text"
+ * @param {Function} [callback] function to be executed after
+ * httpGet() completes, data is passed in
+ * as first argument
+ * @param {Function} [errorCallback] function to be executed if
+ * there is an error, response is passed
+ * in as first argument
+ */
+p5.prototype.httpGet = function () {
+ var args = new Array(arguments.length);
+ for (var i = 0; i < args.length; ++i) {
+ args[i] = arguments[i];
+ }
+ args.push('GET');
+ p5.prototype.httpDo.apply(this, args);
+};
+
+/**
+ * Method for executing an HTTP POST request. If data type is not specified,
+ * p5 will try to guess based on the URL, defaulting to text.
+ *
+ * @method httpPost
+ * @param {String} path name of the file or url to load
+ * @param {Object} [data] param data passed sent with request
+ * @param {String} [datatype] "json", "jsonp", "xml", or "text"
+ * @param {Function} [callback] function to be executed after
+ * httpGet() completes, data is passed in
+ * as first argument
+ * @param {Function} [errorCallback] function to be executed if
+ * there is an error, response is passed
+ * in as first argument
+ */
+p5.prototype.httpPost = function () {
+ var args = new Array(arguments.length);
+ for (var i = 0; i < args.length; ++i) {
+ args[i] = arguments[i];
+ }
+ args.push('POST');
+ p5.prototype.httpDo.apply(this, args);
+};
+
+/**
+ * Method for executing an HTTP request. If data type is not specified,
+ * p5 will try to guess based on the URL, defaulting to text.
+ * You may also pass a single object specifying all parameters for the
+ * request following the examples inside the reqwest() calls here:
+ *
+ * https://github.com/ded/reqwest#api
+ *
+ * @method httpDo
+ * @param {String} path name of the file or url to load
+ * @param {String} [method] either "GET", "POST", or "PUT",
+ * defaults to "GET"
+ * @param {Object} [data] param data passed sent with request
+ * @param {String} [datatype] "json", "jsonp", "xml", or "text"
+ * @param {Function} [callback] function to be executed after
+ * httpGet() completes, data is passed in
+ * as first argument
+ * @param {Function} [errorCallback] function to be executed if
+ * there is an error, response is passed
+ * in as first argument
+ */
+p5.prototype.httpDo = function () {
+ if (typeof arguments[0] === 'object') {
+ reqwest(arguments[0]);
+ } else {
+ var method = 'GET';
+ var path = arguments[0];
+ var data = {};
+ var type = '';
+ var callback;
+ var errorCallback;
+
+ for (var i = 1; i < arguments.length; i++) {
+ var a = arguments[i];
+ if (typeof a === 'string') {
+ if (a === 'GET' || a === 'POST' || a === 'PUT') {
+ method = a;
+ } else {
+ type = a;
+ }
+ } else if (typeof a === 'object') {
+ data = a;
+ } else if (typeof a === 'function') {
+ if (!callback) {
+ callback = a;
+ } else {
+ errorCallback = a;
+ }
+ }
+ }
+
+ // do some sort of smart type checking
+ if (type === '') {
+ if (path.indexOf('json') !== -1) {
+ type = 'json';
+ } else if (path.indexOf('xml') !== -1) {
+ type = 'xml';
+ } else {
+ type = 'text';
+ }
+ }
+
+ reqwest({
+ url: path,
+ method: method,
+ data: data,
+ type: type,
+ crossOrigin: true,
+ success: function (resp) {
+ if (typeof callback !== 'undefined') {
+ if (type === 'text') {
+ callback(resp.response);
+ } else {
+ callback(resp);
+ }
+ }
+ },
+ error: function (resp) {
+ if (errorCallback) {
+ errorCallback(resp);
+ } else {
+ console.log(resp.statusText);
+ }
+ }
+ });
+ }
+};
+
+/**
+ * @module IO
+ * @submodule Output
+ * @for p5
+ */
+
+window.URL = window.URL || window.webkitURL;
+
+// private array of p5.PrintWriter objects
+p5.prototype._pWriters = [];
+
+p5.prototype.beginRaw = function () {
+ // TODO
+ throw 'not yet implemented';
+
+};
+
+p5.prototype.beginRecord = function () {
+ // TODO
+ throw 'not yet implemented';
+
+};
+
+p5.prototype.createOutput = function () {
+ // TODO
+
+ throw 'not yet implemented';
+};
+
+p5.prototype.createWriter = function (name, extension) {
+ var newPW;
+ // check that it doesn't already exist
+ for (var i in p5.prototype._pWriters) {
+ if (p5.prototype._pWriters[i].name === name) {
+ // if a p5.PrintWriter w/ this name already exists...
+ // return p5.prototype._pWriters[i]; // return it w/ contents intact.
+ // or, could return a new, empty one with a unique name:
+ newPW = new p5.PrintWriter(name + window.millis(), extension);
+ p5.prototype._pWriters.push(newPW);
+ return newPW;
+ }
+ }
+ newPW = new p5.PrintWriter(name, extension);
+ p5.prototype._pWriters.push(newPW);
+ return newPW;
+};
+
+p5.prototype.endRaw = function () {
+ // TODO
+
+ throw 'not yet implemented';
+};
+
+p5.prototype.endRecord = function () {
+ // TODO
+ throw 'not yet implemented';
+
+};
+
+p5.PrintWriter = function (filename, extension) {
+ var self = this;
+ this.name = filename;
+ this.content = '';
+ this.print = function (data) {
+ this.content += data;
+ };
+ this.print = function (data) {
+ this.content += data + '\n';
+ };
+ this.flush = function () {
+ this.content = '';
+ };
+ this.close = function () {
+ // convert String to Array for the writeFile Blob
+ var arr = [];
+ arr.push(this.content);
+ p5.prototype.writeFile(arr, filename, extension);
+ // remove from _pWriters array and delete self
+ for (var i in p5.prototype._pWriters) {
+ if (p5.prototype._pWriters[i].name === this.name) {
+ // remove from _pWriters array
+ p5.prototype._pWriters.splice(i, 1);
+ }
+ }
+ self.flush();
+ self = {};
+ };
+};
+
+p5.prototype.saveBytes = function () {
+ // TODO
+ throw 'not yet implemented';
+
+};
+
+// object, filename, options --> saveJSON, saveStrings, saveTable
+// filename, [extension] [canvas] --> saveImage
+
+/**
+ * Save an image, text, json, csv, wav, or html. Prompts download to
+ * the client's computer. Note that it is not recommended to call save()
+ * within draw if it's looping, as the save() function will open a new save
+ * dialog every frame.
+ * The default behavior is to save the canvas as an image. You can
+ * optionally specify a filename.
+ * For example:
+ *
+ * save();
+ * save('myCanvas.jpg'); // save a specific canvas with a filename
+ *
+ *
+ * Alternately, the first parameter can be a pointer to a canvas
+ * p5.Element, an Array of Strings,
+ * an Array of JSON, a JSON object, a p5.Table, a p5.Image, or a
+ * p5.SoundFile (requires p5.sound). The second parameter is a filename
+ * (including extension). The third parameter is for options specific
+ * to this type of object. This method will save a file that fits the
+ * given paramaters. For example:
+ *
+ *
+ *
+ * save('myCanvas.jpg'); // Saves canvas as an image
+ *
+ * var cnv = createCanvas(100, 100);
+ * save(cnv, 'myCanvas.jpg'); // Saves canvas as an image
+ *
+ * var gb = createGraphics(100, 100);
+ * save(gb, 'myGraphics.jpg'); // Saves p5.Renderer object as an image
+ *
+ * save(myTable, 'myTable.html'); // Saves table as html file
+ * save(myTable, 'myTable.csv',); // Comma Separated Values
+ * save(myTable, 'myTable.tsv'); // Tab Separated Values
+ *
+ * save(myJSON, 'my.json'); // Saves pretty JSON
+ * save(myJSON, 'my.json', true); // Optimizes JSON filesize
+ *
+ * save(img, 'my.png'); // Saves pImage as a png image
+ *
+ * save(arrayOfStrings, 'my.txt'); // Saves strings to a text file with line
+ * // breaks after each item in the array
+ *
+ *
+ * @method save
+ * @param {[Object|String]} objectOrFilename If filename is provided, will
+ * save canvas as an image with
+ * either png or jpg extension
+ * depending on the filename.
+ * If object is provided, will
+ * save depending on the object
+ * and filename (see examples
+ * above).
+ * @param {[String]} filename If an object is provided as the first
+ * parameter, then the second parameter
+ * indicates the filename,
+ * and should include an appropriate
+ * file extension (see examples above).
+ * @param {[Boolean/String]} options Additional options depend on
+ * filetype. For example, when saving JSON,
+ * true
indicates that the
+ * output will be optimized for filesize,
+ * rather than readability.
+ */
+p5.prototype.save = function (object, _filename, _options) {
+ // parse the arguments and figure out which things we are saving
+ var args = arguments;
+ // =================================================
+ // OPTION 1: saveCanvas...
+
+ // if no arguments are provided, save canvas
+ var cnv = this._curElement.elt;
+ if (args.length === 0) {
+ p5.prototype.saveCanvas(cnv);
+ return;
+ }
+ // otherwise, parse the arguments
+
+ // if first param is a p5Graphics, then saveCanvas
+ else if (args[0] instanceof p5.Renderer ||
+ args[0] instanceof p5.Graphics) {
+ p5.prototype.saveCanvas(args[0].elt, args[1], args[2]);
+ return;
+ }
+
+ // if 1st param is String and only one arg, assume it is canvas filename
+ else if (args.length === 1 && typeof (args[0]) === 'string') {
+ p5.prototype.saveCanvas(cnv, args[0]);
+ }
+
+ // =================================================
+ // OPTION 2: extension clarifies saveStrings vs. saveJSON
+ else {
+ var extension = _checkFileExtension(args[1], args[2])[1];
+ switch (extension) {
+ case 'json':
+ p5.prototype.saveJSON(args[0], args[1], args[2]);
+ return;
+ case 'txt':
+ p5.prototype.saveStrings(args[0], args[1], args[2]);
+ return;
+ // =================================================
+ // OPTION 3: decide based on object...
+ default:
+ if (args[0] instanceof Array) {
+ p5.prototype.saveStrings(args[0], args[1], args[2]);
+ } else if (args[0] instanceof p5.Table) {
+ p5.prototype.saveTable(args[0], args[1], args[2], args[3]);
+ } else if (args[0] instanceof p5.Image) {
+ p5.prototype.saveCanvas(args[0].canvas, args[1]);
+ } else if (args[0] instanceof p5.SoundFile) {
+ p5.prototype.saveSound(args[0], args[1], args[2], args[3]);
+ }
+ }
+ }
+};
+
+/**
+ * Writes the contents of an Array or a JSON object to a .json file.
+ * The file saving process and location of the saved file will
+ * vary between web browsers.
+ *
+ * @method saveJSON
+ * @param {Array|Object} json
+ * @param {String} filename
+ * @param {Boolean} [optimize] If true, removes line breaks
+ * and spaces from the output
+ * file to optimize filesize
+ * (but not readability).
+ * @example
+ *
+ * var json;
+ *
+ * function setup() {
+ *
+ * json = {}; // new JSON Object
+ *
+ * json.id = 0;
+ * json.species = 'Panthera leo';
+ * json.name = 'Lion';
+ *
+ * // To save, un-comment the line below, then click 'run'
+ * // saveJSON(json, 'lion.json');
+ * }
+ *
+ * // Saves the following to a file called "lion.json":
+ * // {
+ * // "id": 0,
+ * // "species": "Panthera leo",
+ * // "name": "Lion"
+ * // }
+ *
+ *
+ * @alt
+ * no image displayed
+ *
+ */
+p5.prototype.saveJSON = function (json, filename, opt) {
+ var stringify;
+ if (opt) {
+ stringify = JSON.stringify(json);
+ } else {
+ stringify = JSON.stringify(json, undefined, 2);
+ }
+ console.log(stringify);
+ this.saveStrings(stringify.split('\n'), filename, 'json');
+};
+
+p5.prototype.saveJSONObject = p5.prototype.saveJSON;
+p5.prototype.saveJSONArray = p5.prototype.saveJSON;
+
+p5.prototype.saveStream = function () {
+ // TODO
+ throw 'not yet implemented';
+
+};
+
+/**
+ * Writes an array of Strings to a text file, one line per String.
+ * The file saving process and location of the saved file will
+ * vary between web browsers.
+ *
+ * @method saveStrings
+ * @param {Array} list string array to be written
+ * @param {String} filename filename for output
+ * @example
+ *
+ * var words = 'apple bear cat dog';
+ *
+ * // .split() outputs an Array
+ * var list = split(words, ' ');
+ *
+ * // To save the file, un-comment next line and click 'run'
+ * // saveStrings(list, 'nouns.txt');
+ *
+ * // Saves the following to a file called 'nouns.txt':
+ * //
+ * // apple
+ * // bear
+ * // cat
+ * // dog
+ *
+ *
+ * @alt
+ * no image displayed
+ *
+ */
+p5.prototype.saveStrings = function (list, filename, extension) {
+ var ext = extension || 'txt';
+ var pWriter = this.createWriter(filename, ext);
+ for (var i = 0; i < list.length; i++) {
+ if (i < list.length - 1) {
+ pWriter.print(list[i]);
+ } else {
+ pWriter.print(list[i]);
+ }
+ }
+ pWriter.close();
+ pWriter.flush();
+};
+
+p5.prototype.saveXML = function () {
+ // TODO
+ throw 'not yet implemented';
+
+};
+
+p5.prototype.selectOutput = function () {
+ // TODO
+ throw 'not yet implemented';
+
+};
+
+// =======
+// HELPERS
+// =======
+
+function escapeHelper(content) {
+ return content
+ .replace(/&/g, '&')
+ .replace(//g, '>')
+ .replace(/"/g, '"')
+ .replace(/'/g, ''');
+}
+
+/**
+ * Writes the contents of a Table object to a file. Defaults to a
+ * text file with comma-separated-values ('csv') but can also
+ * use tab separation ('tsv'), or generate an HTML table ('html').
+ * The file saving process and location of the saved file will
+ * vary between web browsers.
+ *
+ * @method saveTable
+ * @param {p5.Table} Table the Table object to save to a file
+ * @param {String} filename the filename to which the Table should be saved
+ * @param {String} [options] can be one of "tsv", "csv", or "html"
+ * @example
+ *
+ * var table;
+ *
+ * function setup() {
+ * table = new p5.Table();
+ *
+ * table.addColumn('id');
+ * table.addColumn('species');
+ * table.addColumn('name');
+ *
+ * var newRow = table.addRow();
+ * newRow.setNum('id', table.getRowCount() - 1);
+ * newRow.setString('species', 'Panthera leo');
+ * newRow.setString('name', 'Lion');
+ *
+ * // To save, un-comment next line then click 'run'
+ * // saveTable(table, 'new.csv');
+ * }
+ *
+ * // Saves the following to a file called 'new.csv':
+ * // id,species,name
+ * // 0,Panthera leo,Lion
+ *
+ *
+ * @alt
+ * no image displayed
+ *
+ */
+p5.prototype.saveTable = function (table, filename, options) {
+ var pWriter = this.createWriter(filename, options);
+
+ var header = table.columns;
+
+ var sep = ','; // default to CSV
+ if (options === 'tsv') {
+ sep = '\t';
+ }
+ if (options !== 'html') {
+ // make header if it has values
+ if (header[0] !== '0') {
+ for (var h = 0; h < header.length; h++) {
+ if (h < header.length - 1) {
+ pWriter.print(header[h] + sep);
+ } else {
+ pWriter.print(header[h]);
+ }
+ }
+ }
+
+ // make rows
+ for (var i = 0; i < table.rows.length; i++) {
+ var j;
+ for (j = 0; j < table.rows[i].arr.length; j++) {
+ if (j < table.rows[i].arr.length - 1) {
+ pWriter.print(table.rows[i].arr[j] + sep);
+ } else if (i < table.rows.length - 1) {
+ pWriter.print(table.rows[i].arr[j]);
+ } else {
+ pWriter.print(table.rows[i].arr[j]); // no line break
+ }
+ }
+ }
+ }
+
+ // otherwise, make HTML
+ else {
+ pWriter.print('');
+ pWriter.print('');
+ var str = ' ';
+ pWriter.print(str);
+ pWriter.print('');
+
+ pWriter.print('');
+ pWriter.print(' ');
+
+ // make header if it has values
+ if (header[0] !== '0') {
+ pWriter.print(' ');
+ for (var k = 0; k < header.length; k++) {
+ var e = escapeHelper(header[k]);
+ pWriter.print(' ' + e);
+ pWriter.print(' | ');
+ }
+ pWriter.print('
');
+ }
+
+ // make rows
+ for (var row = 0; row < table.rows.length; row++) {
+ pWriter.print(' ');
+ for (var col = 0; col < table.columns.length; col++) {
+ var entry = table.rows[row].getString(col);
+ var htmlEntry = escapeHelper(entry);
+ pWriter.print(' ' + htmlEntry);
+ pWriter.print(' | ');
+ }
+ pWriter.print('
');
+ }
+ pWriter.print('
');
+ pWriter.print('');
+ pWriter.print('');
+ }
+ // close and flush the pWriter
+ pWriter.close();
+ pWriter.flush();
+}; // end saveTable()
+
+/**
+ * Generate a blob of file data as a url to prepare for download.
+ * Accepts an array of data, a filename, and an extension (optional).
+ * This is a private function because it does not do any formatting,
+ * but it is used by saveStrings, saveJSON, saveTable etc.
+ *
+ * @param {Array} dataToDownload
+ * @param {String} filename
+ * @param {[String]} extension
+ * @private
+ */
+p5.prototype.writeFile = function (dataToDownload, filename, extension) {
+ var type = 'application\/octet-stream';
+ if (p5.prototype._isSafari()) {
+ type = 'text\/plain';
+ }
+ var blob = new Blob(dataToDownload, {
+ 'type': type
+ });
+ var href = window.URL.createObjectURL(blob);
+ p5.prototype.downloadFile(href, filename, extension);
+};
+
+/**
+ * Forces download. Accepts a url to filedata/blob, a filename,
+ * and an extension (optional).
+ * This is a private function because it does not do any formatting,
+ * but it is used by saveStrings, saveJSON, saveTable etc.
+ *
+ * @param {String} href i.e. an href generated by createObjectURL
+ * @param {[String]} filename
+ * @param {[String]} extension
+ */
+p5.prototype.downloadFile = function (href, fName, extension) {
+ var fx = _checkFileExtension(fName, extension);
+ var filename = fx[0];
+ var ext = fx[1];
+
+ var a = document.createElement('a');
+ a.href = href;
+ a.download = filename;
+
+ // Firefox requires the link to be added to the DOM before click()
+ a.onclick = destroyClickedElement;
+ a.style.display = 'none';
+ document.body.appendChild(a);
+
+ // Safari will open this file in the same page as a confusing Blob.
+ if (p5.prototype._isSafari()) {
+ var aText = 'Hello, Safari user! To download this file...\n';
+ aText += '1. Go to File --> Save As.\n';
+ aText += '2. Choose "Page Source" as the Format.\n';
+ aText += '3. Name it with this extension: .\"' + ext + '\"';
+ alert(aText);
+ }
+ a.click();
+ href = null;
+};
+
+/**
+ * Returns a file extension, or another string
+ * if the provided parameter has no extension.
+ *
+ * @param {String} filename
+ * @return {Array} [fileName, fileExtension]
+ *
+ * @private
+ */
+function _checkFileExtension(filename, extension) {
+ if (!extension || extension === true || extension === 'true') {
+ extension = '';
+ }
+ if (!filename) {
+ filename = 'untitled';
+ }
+ var ext = '';
+ // make sure the file will have a name, see if filename needs extension
+ if (filename && filename.indexOf('.') > -1) {
+ ext = filename.split('.').pop();
+ }
+ // append extension if it doesn't exist
+ if (extension) {
+ if (ext !== extension) {
+ ext = extension;
+ filename = filename + '.' + ext;
+ }
+ }
+ return [filename, ext];
+}
+p5.prototype._checkFileExtension = _checkFileExtension;
+
+/**
+ * Returns true if the browser is Safari, false if not.
+ * Safari makes trouble for downloading files.
+ *
+ * @return {Boolean} [description]
+ * @private
+ */
+p5.prototype._isSafari = function () {
+ var x = Object.prototype.toString.call(window.HTMLElement);
+ return x.indexOf('Constructor') > 0;
+};
+
+/**
+ * Helper function, a callback for download that deletes
+ * an invisible anchor element from the DOM once the file
+ * has been automatically downloaded.
+ *
+ * @private
+ */
+function destroyClickedElement(event) {
+ document.body.removeChild(event.target);
+}
+
+module.exports = p5;
+
+},{"../core/core":37,"../core/error_helpers":40,"opentype.js":8,"reqwest":27}],60:[function(_dereq_,module,exports){
+/**
+ * @module IO
+ * @submodule Table
+ * @requires core
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+
+
+/**
+ * Table Options
+ * Generic class for handling tabular data, typically from a
+ * CSV, TSV, or other sort of spreadsheet file.
+ * CSV files are
+ *
+ * comma separated values, often with the data in quotes. TSV
+ * files use tabs as separators, and usually don't bother with the
+ * quotes.
+ * File names should end with .csv if they're comma separated.
+ * A rough "spec" for CSV can be found
+ * here.
+ * To load files, use the loadTable method.
+ * To save tables to your computer, use the save method
+ * or the saveTable method.
+ *
+ * Possible options include:
+ *
+ * - csv - parse the table as comma-separated values
+ *
- tsv - parse the table as tab-separated values
+ *
- header - this table has a header (title) row
+ *
+ */
+
+/**
+ * Table objects store data with multiple rows and columns, much
+ * like in a traditional spreadsheet. Tables can be generated from
+ * scratch, dynamically, or using data from an existing file.
+ *
+ * @class p5.Table
+ * @constructor
+ * @param {Array} [rows] An array of p5.TableRow objects
+ * @return {p5.Table} p5.Table generated
+ */
+p5.Table = function (rows) {
+ /**
+ * @property columns
+ * @type {Array}
+ */
+ this.columns = [];
+
+ /**
+ * @property rows
+ * @type {Array}
+ */
+ this.rows = [];
+};
+
+/**
+ * Use addRow() to add a new row of data to a p5.Table object. By default,
+ * an empty row is created. Typically, you would store a reference to
+ * the new row in a TableRow object (see newRow in the example above),
+ * and then set individual values using set().
+ *
+ * If a p5.TableRow object is included as a parameter, then that row is
+ * duplicated and added to the table.
+ *
+ * @method addRow
+ * @param {p5.TableRow} [row] row to be added to the table
+ *
+ * @example
+ *
+ *
+ * // Given the CSV file "mammals.csv"
+ * // in the project's "assets" folder:
+ * //
+ * // id,species,name
+ * // 0,Capra hircus,Goat
+ * // 1,Panthera pardus,Leopard
+ * // 2,Equus zebra,Zebra
+ *
+ * var table;
+ *
+ * function preload() {
+ * //my table is comma separated value "csv"
+ * //and has a header specifying the columns labels
+ * table = loadTable("assets/mammals.csv", "csv", "header");
+ * }
+ *
+ * function setup() {
+ * //add a row
+ * var newRow = table.addRow();
+ * newRow.setString("id", table.getRowCount() - 1);
+ * newRow.setString("species", "Canis Lupus");
+ * newRow.setString("name", "Wolf");
+ *
+ * //print the results
+ * for (var r = 0; r < table.getRowCount(); r++)
+ * for (var c = 0; c < table.getColumnCount(); c++)
+ * print(table.getString(r, c));
+ * }
+ *
+ *
+ *
+ * @alt
+ * no image displayed
+ *
+ */
+p5.Table.prototype.addRow = function(row) {
+ // make sure it is a valid TableRow
+ var r = row || new p5.TableRow();
+
+ if (typeof(r.arr) === 'undefined' || typeof(r.obj) === 'undefined') {
+ //r = new p5.prototype.TableRow(r);
+ throw 'invalid TableRow: ' + r;
+ }
+ r.table = this;
+ this.rows.push(r);
+ return r;
+};
+
+/**
+ * Removes a row from the table object.
+ *
+ * @method removeRow
+ * @param {Number} id ID number of the row to remove
+ *
+ * @example
+ *
+ *
+ * // Given the CSV file "mammals.csv"
+ * // in the project's "assets" folder:
+ * //
+ * // id,species,name
+ * // 0,Capra hircus,Goat
+ * // 1,Panthera pardus,Leopard
+ * // 2,Equus zebra,Zebra
+ *
+ * var table;
+ *
+ * function preload() {
+ * //my table is comma separated value "csv"
+ * //and has a header specifying the columns labels
+ * table = loadTable("assets/mammals.csv", "csv", "header");
+ * }
+ *
+ * function setup() {
+ * //remove the first row
+ * var r = table.removeRow(0);
+ *
+ * //print the results
+ * for (var r = 0; r < table.getRowCount(); r++)
+ * for (var c = 0; c < table.getColumnCount(); c++)
+ * print(table.getString(r, c));
+ * }
+ *
+ *
+ *
+ * @alt
+ * no image displayed
+ *
+ */
+p5.Table.prototype.removeRow = function(id) {
+ this.rows[id].table = null; // remove reference to table
+ var chunk = this.rows.splice(id+1, this.rows.length);
+ this.rows.pop();
+ this.rows = this.rows.concat(chunk);
+};
+
+
+/**
+ * Returns a reference to the specified p5.TableRow. The reference
+ * can then be used to get and set values of the selected row.
+ *
+ * @method getRow
+ * @param {Number} rowID ID number of the row to get
+ * @return {TableRow} p5.TableRow object
+ *
+ * @example
+ *
+ *
+ * // Given the CSV file "mammals.csv"
+ * // in the project's "assets" folder:
+ * //
+ * // id,species,name
+ * // 0,Capra hircus,Goat
+ * // 1,Panthera pardus,Leopard
+ * // 2,Equus zebra,Zebra
+ *
+ * var table;
+ *
+ * function preload() {
+ * //my table is comma separated value "csv"
+ * //and has a header specifying the columns labels
+ * table = loadTable("assets/mammals.csv", "csv", "header");
+ * }
+ *
+ * function setup() {
+ * var row = table.getRow(1);
+ * //print it column by column
+ * //note: a row is an object, not an array
+ * for (var c = 0; c < table.getColumnCount(); c++)
+ * print(row.getString(c));
+ * }
+ *
+ *
+ *
+ *@alt
+ * no image displayed
+ *
+ */
+p5.Table.prototype.getRow = function(r) {
+ return this.rows[r];
+};
+
+/**
+ * Gets all rows from the table. Returns an array of p5.TableRows.
+ *
+ * @method getRows
+ * @return {Array} Array of p5.TableRows
+ *
+ * @example
+ *
+ *
+ * // Given the CSV file "mammals.csv"
+ * // in the project's "assets" folder:
+ * //
+ * // id,species,name
+ * // 0,Capra hircus,Goat
+ * // 1,Panthera pardus,Leopard
+ * // 2,Equus zebra,Zebra
+ *
+ * var table;
+ *
+ * function preload() {
+ * //my table is comma separated value "csv"
+ * //and has a header specifying the columns labels
+ * table = loadTable("assets/mammals.csv", "csv", "header");
+ * }
+ *
+ * function setup() {
+ * var rows = table.getRows();
+ *
+ * //warning: rows is an array of objects
+ * for (var r = 0; r < rows.length; r++)
+ * rows[r].set("name", "Unicorn");
+ *
+ * //print the results
+ * for (var r = 0; r < table.getRowCount(); r++)
+ * for (var c = 0; c < table.getColumnCount(); c++)
+ * print(table.getString(r, c));
+ * }
+ *
+ *
+ *
+ * @alt
+ * no image displayed
+ *
+ */
+p5.Table.prototype.getRows = function() {
+ return this.rows;
+};
+
+/**
+ * Finds the first row in the Table that contains the value
+ * provided, and returns a reference to that row. Even if
+ * multiple rows are possible matches, only the first matching
+ * row is returned. The column to search may be specified by
+ * either its ID or title.
+ *
+ * @method findRow
+ * @param {String} value The value to match
+ * @param {Number|String} column ID number or title of the
+ * column to search
+ * @return {TableRow}
+ *
+ * @example
+ *
+ *
+ * // Given the CSV file "mammals.csv"
+ * // in the project's "assets" folder:
+ * //
+ * // id,species,name
+ * // 0,Capra hircus,Goat
+ * // 1,Panthera pardus,Leopard
+ * // 2,Equus zebra,Zebra
+ *
+ * var table;
+ *
+ * function preload() {
+ * //my table is comma separated value "csv"
+ * //and has a header specifying the columns labels
+ * table = loadTable("assets/mammals.csv", "csv", "header");
+ * }
+ *
+ * function setup() {
+ * //find the animal named zebra
+ * var row = table.findRow("Zebra", "name");
+ * //find the corresponding species
+ * print(row.getString("species"));
+ * }
+ *
+ *
+ *
+ * @alt
+ * no image displayed
+ *
+ */
+p5.Table.prototype.findRow = function(value, column) {
+ // try the Object
+ if (typeof(column) === 'string') {
+ for (var i = 0; i < this.rows.length; i++){
+ if (this.rows[i].obj[column] === value) {
+ return this.rows[i];
+ }
+ }
+ }
+ // try the Array
+ else {
+ for (var j = 0; j < this.rows.length; j++){
+ if (this.rows[j].arr[column] === value) {
+ return this.rows[j];
+ }
+ }
+ }
+ // otherwise...
+ return null;
+};
+
+/**
+ * Finds the rows in the Table that contain the value
+ * provided, and returns references to those rows. Returns an
+ * Array, so for must be used to iterate through all the rows,
+ * as shown in the example above. The column to search may be
+ * specified by either its ID or title.
+ *
+ * @method findRows
+ * @param {String} value The value to match
+ * @param {Number|String} column ID number or title of the
+ * column to search
+ * @return {Array} An Array of TableRow objects
+ *
+ * @example
+ *
+ *
+ * // Given the CSV file "mammals.csv"
+ * // in the project's "assets" folder:
+ * //
+ * // id,species,name
+ * // 0,Capra hircus,Goat
+ * // 1,Panthera pardus,Leopard
+ * // 2,Equus zebra,Zebra
+ *
+ * var table;
+ *
+ * function preload() {
+ * //my table is comma separated value "csv"
+ * //and has a header specifying the columns labels
+ * table = loadTable("assets/mammals.csv", "csv", "header");
+ * }
+ *
+ * function setup() {
+ * //add another goat
+ * var newRow = table.addRow();
+ * newRow.setString("id", table.getRowCount() - 1);
+ * newRow.setString("species", "Scape Goat");
+ * newRow.setString("name", "Goat");
+ *
+ * //find the rows containing animals named Goat
+ * var rows = table.findRows("Goat", "name");
+ * print(rows.length + " Goats found");
+ * }
+ *
+ *
+ *
+ *@alt
+ * no image displayed
+ *
+ */
+p5.Table.prototype.findRows = function(value, column) {
+ var ret = [];
+ if (typeof(column) === 'string') {
+ for (var i = 0; i < this.rows.length; i++){
+ if (this.rows[i].obj[column] === value) {
+ ret.push( this.rows[i] );
+ }
+ }
+ }
+ // try the Array
+ else {
+ for (var j = 0; j < this.rows.length; j++){
+ if (this.rows[j].arr[column] === value) {
+ ret.push( this.rows[j] );
+ }
+ }
+ }
+ return ret;
+};
+
+/**
+ * Finds the first row in the Table that matches the regular
+ * expression provided, and returns a reference to that row.
+ * Even if multiple rows are possible matches, only the first
+ * matching row is returned. The column to search may be
+ * specified by either its ID or title.
+ *
+ * @method matchRow
+ * @param {String} regexp The regular expression to match
+ * @param {String|Number} column The column ID (number) or
+ * title (string)
+ * @return {TableRow} TableRow object
+ */
+p5.Table.prototype.matchRow = function(regexp, column) {
+ if (typeof(column) === 'number') {
+ for (var j = 0; j < this.rows.length; j++) {
+ if ( this.rows[j].arr[column].match(regexp) ) {
+ return this.rows[j];
+ }
+ }
+ }
+
+ else {
+ for (var i = 0; i < this.rows.length; i++) {
+ if ( this.rows[i].obj[column].match(regexp) ) {
+ return this.rows[i];
+ }
+ }
+ }
+ return null;
+};
+
+/**
+ * Finds the rows in the Table that match the regular expression provided,
+ * and returns references to those rows. Returns an array, so for must be
+ * used to iterate through all the rows, as shown in the example. The
+ * column to search may be specified by either its ID or title.
+ *
+ * @method matchRows
+ * @param {String} regexp The regular expression to match
+ * @param {String|Number} [column] The column ID (number) or
+ * title (string)
+ * @return {Array} An Array of TableRow objects
+ * @example
+ * var table;
+ *
+ * function setup() {
+ *
+ * table = new p5.Table();
+ *
+ * table.addColumn('name');
+ * table.addColumn('type');
+ *
+ * var newRow = table.addRow();
+ * newRow.setString('name', 'Lion');
+ * newRow.setString('type', 'Mammal');
+ *
+ * newRow = table.addRow();
+ * newRow.setString('name', 'Snake');
+ * newRow.setString('type', 'Reptile');
+ *
+ * newRow = table.addRow();
+ * newRow.setString('name', 'Mosquito');
+ * newRow.setString('type', 'Insect');
+ *
+ * newRow = table.addRow();
+ * newRow.setString('name', 'Lizard');
+ * newRow.setString('type', 'Reptile');
+ *
+ * var rows = table.matchRows('R.*', 'type');
+ * for (var i = 0; i < rows.length; i++) {
+ * print(rows[i].getString('name') + ': ' + rows[i].getString('type'));
+ * }
+ * }
+ * // Sketch prints:
+ * // Snake: Reptile
+ * // Lizard: Reptile
+ */
+p5.Table.prototype.matchRows = function(regexp, column) {
+ var ret = [];
+ if (typeof(column) === 'number') {
+ for (var j = 0; j < this.rows.length; j++) {
+ if ( this.rows[j].arr[column].match(regexp) ) {
+ ret.push( this.rows[j] );
+ }
+ }
+ }
+
+ else {
+ for (var i = 0; i < this.rows.length; i++) {
+ if ( this.rows[i].obj[column].match(regexp) ) {
+ ret.push( this.rows[i] );
+ }
+ }
+ }
+ return ret;
+};
+
+
+/**
+ * Retrieves all values in the specified column, and returns them
+ * as an array. The column may be specified by either its ID or title.
+ *
+ * @method getColumn
+ * @param {String|Number} column String or Number of the column to return
+ * @return {Array} Array of column values
+ *
+ * @example
+ *
+ *
+ * // Given the CSV file "mammals.csv"
+ * // in the project's "assets" folder:
+ * //
+ * // id,species,name
+ * // 0,Capra hircus,Goat
+ * // 1,Panthera pardus,Leopard
+ * // 2,Equus zebra,Zebra
+ *
+ * var table;
+ *
+ * function preload() {
+ * //my table is comma separated value "csv"
+ * //and has a header specifying the columns labels
+ * table = loadTable("assets/mammals.csv", "csv", "header");
+ * }
+ *
+ * function setup() {
+ * //getColumn returns an array that can be printed directly
+ * print(table.getColumn("species"));
+ * //outputs ["Capra hircus", "Panthera pardus", "Equus zebra"]
+ * }
+ *
+ *
+ *
+ *@alt
+ * no image displayed
+ *
+ */
+p5.Table.prototype.getColumn = function(value) {
+ var ret = [];
+ if (typeof(value) === 'string'){
+ for (var i = 0; i < this.rows.length; i++){
+ ret.push (this.rows[i].obj[value]);
+ }
+ } else {
+ for (var j = 0; j < this.rows.length; j++){
+ ret.push (this.rows[j].arr[value]);
+ }
+ }
+ return ret;
+};
+
+/**
+ * Removes all rows from a Table. While all rows are removed,
+ * columns and column titles are maintained.
+ *
+ * @method clearRows
+ *
+ * @example
+ *
+ *
+ * // Given the CSV file "mammals.csv"
+ * // in the project's "assets" folder:
+ * //
+ * // id,species,name
+ * // 0,Capra hircus,Goat
+ * // 1,Panthera pardus,Leopard
+ * // 2,Equus zebra,Zebra
+ *
+ * var table;
+ *
+ * function preload() {
+ * //my table is comma separated value "csv"
+ * //and has a header specifying the columns labels
+ * table = loadTable("assets/mammals.csv", "csv", "header");
+ * }
+ *
+ * function setup() {
+ * table.clearRows();
+ * print(table.getRowCount() + " total rows in table");
+ * print(table.getColumnCount() + " total columns in table");
+ * }
+ *
+ *
+ *
+ *@alt
+ * no image displayed
+ *
+ */
+p5.Table.prototype.clearRows = function() {
+ delete this.rows;
+ this.rows = [];
+};
+
+/**
+ * Use addColumn() to add a new column to a Table object.
+ * Typically, you will want to specify a title, so the column
+ * may be easily referenced later by name. (If no title is
+ * specified, the new column's title will be null.)
+ *
+ * @method addColumn
+ * @param {String} [title] title of the given column
+ *
+ * @example
+ *
+ *
+ * // Given the CSV file "mammals.csv"
+ * // in the project's "assets" folder:
+ * //
+ * // id,species,name
+ * // 0,Capra hircus,Goat
+ * // 1,Panthera pardus,Leopard
+ * // 2,Equus zebra,Zebra
+ *
+ * var table;
+ *
+ * function preload() {
+ * //my table is comma separated value "csv"
+ * //and has a header specifying the columns labels
+ * table = loadTable("assets/mammals.csv", "csv", "header");
+ * }
+ *
+ * function setup() {
+ * table.addColumn("carnivore");
+ * table.set(0, "carnivore", "no");
+ * table.set(1, "carnivore", "yes");
+ * table.set(2, "carnivore", "no");
+ *
+ * //print the results
+ * for (var r = 0; r < table.getRowCount(); r++)
+ * for (var c = 0; c < table.getColumnCount(); c++)
+ * print(table.getString(r, c));
+ * }
+ *
+ *
+ *
+ *@alt
+ * no image displayed
+ *
+ */
+p5.Table.prototype.addColumn = function(title) {
+ var t = title || null;
+ this.columns.push(t);
+};
+
+/**
+ * Returns the total number of columns in a Table.
+ *
+ * @return {Number} Number of columns in this table
+ */
+p5.Table.prototype.getColumnCount = function() {
+ return this.columns.length;
+};
+
+/**
+ * Returns the total number of rows in a Table.
+ *
+ * @method getRowCount
+ * @return {Number} Number of rows in this table
+
+ */
+p5.Table.prototype.getRowCount = function() {
+ return this.rows.length;
+};
+
+/**
+ * Removes any of the specified characters (or "tokens").
+ *
+ * If no column is specified, then the values in all columns and
+ * rows are processed. A specific column may be referenced by
+ * either its ID or title.
+ *
+ * @method removeTokens
+ * @param {String} chars String listing characters to be removed
+ * @param {String|Number} [column] Column ID (number)
+ * or name (string)
+ */
+p5.Table.prototype.removeTokens = function(chars, column) {
+ var escape= function(s) {
+ return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
+ };
+ var charArray = [];
+ for (var i = 0; i < chars.length; i++) {
+ charArray.push( escape( chars.charAt(i) ) );
+ }
+ var regex = new RegExp(charArray.join('|'), 'g');
+
+ if (typeof(column) === 'undefined'){
+ for (var c = 0; c < this.columns.length; c++) {
+ for (var d = 0; d < this.rows.length; d++) {
+ var s = this.rows[d].arr[c];
+ s = s.replace(regex, '');
+ this.rows[d].arr[c] = s;
+ this.rows[d].obj[this.columns[c]] = s;
+ }
+ }
+ }
+ else if (typeof(column) === 'string'){
+ for (var j = 0; j < this.rows.length; j++) {
+ var val = this.rows[j].obj[column];
+ val = val.replace(regex, '');
+ this.rows[j].obj[column] = val;
+ var pos = this.columns.indexOf(column);
+ this.rows[j].arr[pos] = val;
+ }
+ }
+ else {
+ for (var k = 0; k < this.rows.length; k++) {
+ var str = this.rows[k].arr[column];
+ str = str.replace(regex, '');
+ this.rows[k].arr[column] = str;
+ this.rows[k].obj[this.columns[column]] = str;
+ }
+ }
+};
+
+/**
+ * Trims leading and trailing whitespace, such as spaces and tabs,
+ * from String table values. If no column is specified, then the
+ * values in all columns and rows are trimmed. A specific column
+ * may be referenced by either its ID or title.
+ *
+ * @method trim
+ * @param {String|Number} column Column ID (number)
+ * or name (string)
+ */
+p5.Table.prototype.trim = function(column) {
+ var regex = new RegExp( (' '), 'g');
+
+ if (typeof(column) === 'undefined'){
+ for (var c = 0; c < this.columns.length; c++) {
+ for (var d = 0; d < this.rows.length; d++) {
+ var s = this.rows[d].arr[c];
+ s = s.replace(regex, '');
+ this.rows[d].arr[c] = s;
+ this.rows[d].obj[this.columns[c]] = s;
+ }
+ }
+ }
+ else if (typeof(column) === 'string'){
+ for (var j = 0; j < this.rows.length; j++) {
+ var val = this.rows[j].obj[column];
+ val = val.replace(regex, '');
+ this.rows[j].obj[column] = val;
+ var pos = this.columns.indexOf(column);
+ this.rows[j].arr[pos] = val;
+ }
+ }
+ else {
+ for (var k = 0; k < this.rows.length; k++) {
+ var str = this.rows[k].arr[column];
+ str = str.replace(regex, '');
+ this.rows[k].arr[column] = str;
+ this.rows[k].obj[this.columns[column]] = str;
+ }
+ }
+};
+
+/**
+ * Use removeColumn() to remove an existing column from a Table
+ * object. The column to be removed may be identified by either
+ * its title (a String) or its index value (an int).
+ * removeColumn(0) would remove the first column, removeColumn(1)
+ * would remove the second column, and so on.
+ *
+ * @method removeColumn
+ * @param {String|Number} column columnName (string) or ID (number)
+ *
+ * @example
+ *
+ *
+ * // Given the CSV file "mammals.csv"
+ * // in the project's "assets" folder:
+ * //
+ * // id,species,name
+ * // 0,Capra hircus,Goat
+ * // 1,Panthera pardus,Leopard
+ * // 2,Equus zebra,Zebra
+ *
+ * var table;
+ *
+ * function preload() {
+ * //my table is comma separated value "csv"
+ * //and has a header specifying the columns labels
+ * table = loadTable("assets/mammals.csv", "csv", "header");
+ * }
+ *
+ * function setup() {
+ * table.removeColumn("id");
+ * print(table.getColumnCount());
+ * }
+ *
+ *
+ *
+ *@alt
+ * no image displayed
+ *
+ */
+p5.Table.prototype.removeColumn = function(c) {
+ var cString;
+ var cNumber;
+ if (typeof(c) === 'string') {
+ // find the position of c in the columns
+ cString = c;
+ cNumber = this.columns.indexOf(c);
+ console.log('string');
+ }
+ else{
+ cNumber = c;
+ cString = this.columns[c];
+ }
+
+ var chunk = this.columns.splice(cNumber+1, this.columns.length);
+ this.columns.pop();
+ this.columns = this.columns.concat(chunk);
+
+ for (var i = 0; i < this.rows.length; i++){
+ var tempR = this.rows[i].arr;
+ var chip = tempR.splice(cNumber+1, tempR.length);
+ tempR.pop();
+ this.rows[i].arr = tempR.concat(chip);
+ delete this.rows[i].obj[cString];
+ }
+
+};
+
+
+/**
+ * Stores a value in the Table's specified row and column.
+ * The row is specified by its ID, while the column may be specified
+ * by either its ID or title.
+ *
+ * @method set
+ * @param {String|Number} column column ID (Number)
+ * or title (String)
+ * @param {String|Number} value value to assign
+ *
+ * @example
+ *
+ *
+ * // Given the CSV file "mammals.csv"
+ * // in the project's "assets" folder:
+ * //
+ * // id,species,name
+ * // 0,Capra hircus,Goat
+ * // 1,Panthera pardus,Leopard
+ * // 2,Equus zebra,Zebra
+ *
+ * var table;
+ *
+ * function preload() {
+ * //my table is comma separated value "csv"
+ * //and has a header specifying the columns labels
+ * table = loadTable("assets/mammals.csv", "csv", "header");
+ * }
+ *
+ * function setup() {
+ * table.set(0, "species", "Canis Lupus");
+ * table.set(0, "name", "Wolf");
+ *
+ * //print the results
+ * for (var r = 0; r < table.getRowCount(); r++)
+ * for (var c = 0; c < table.getColumnCount(); c++)
+ * print(table.getString(r, c));
+ * }
+ *
+ *
+ *
+ *@alt
+ * no image displayed
+ *
+ */
+p5.Table.prototype.set = function(row, column, value) {
+ this.rows[row].set(column, value);
+};
+
+/**
+ * Stores a Float value in the Table's specified row and column.
+ * The row is specified by its ID, while the column may be specified
+ * by either its ID or title.
+ *
+ * @method setNum
+ * @param {Number} row row ID
+ * @param {String|Number} column column ID (Number)
+ * or title (String)
+ * @param {Number} value value to assign
+ *
+ * @example
+ *
+ *
+ * // Given the CSV file "mammals.csv"
+ * // in the project's "assets" folder:
+ * //
+ * // id,species,name
+ * // 0,Capra hircus,Goat
+ * // 1,Panthera pardus,Leopard
+ * // 2,Equus zebra,Zebra
+ *
+ * var table;
+ *
+ * function preload() {
+ * //my table is comma separated value "csv"
+ * //and has a header specifying the columns labels
+ * table = loadTable("assets/mammals.csv", "csv", "header");
+ * }
+ *
+ * function setup() {
+ * table.setNum(1, "id", 1);
+ *
+ * print(table.getColumn(0));
+ * //["0", 1, "2"]
+ * }
+ *
+ *
+ *
+ *@alt
+ * no image displayed
+ */
+p5.Table.prototype.setNum = function(row, column, value){
+ this.rows[row].setNum(column, value);
+};
+
+
+/**
+ * Stores a String value in the Table's specified row and column.
+ * The row is specified by its ID, while the column may be specified
+ * by either its ID or title.
+ *
+ * @method setString
+ * @param {Number} row row ID
+ * @param {String|Number} column column ID (Number)
+ * or title (String)
+ * @param {String} value value to assign
+ */
+p5.Table.prototype.setString = function(row, column, value){
+ this.rows[row].setString(column, value);
+};
+
+/**
+ * Retrieves a value from the Table's specified row and column.
+ * The row is specified by its ID, while the column may be specified by
+ * either its ID or title.
+ *
+ * @method get
+ * @param {Number} row row ID
+ * @param {String|Number} column columnName (string) or
+ * ID (number)
+ * @return {String|Number}
+ *
+ * @example
+ *
+ *
+ * // Given the CSV file "mammals.csv"
+ * // in the project's "assets" folder:
+ * //
+ * // id,species,name
+ * // 0,Capra hircus,Goat
+ * // 1,Panthera pardus,Leopard
+ * // 2,Equus zebra,Zebra
+ *
+ * var table;
+ *
+ * function preload() {
+ * //my table is comma separated value "csv"
+ * //and has a header specifying the columns labels
+ * table = loadTable("assets/mammals.csv", "csv", "header");
+ * }
+ *
+ * function setup() {
+ * print(table.get(0, 1));
+ * //Capra hircus
+ * print(table.get(0, "species"));
+ * //Capra hircus
+ * }
+ *
+ *
+ *
+ *@alt
+ * no image displayed
+ *
+ */
+p5.Table.prototype.get = function(row, column) {
+ return this.rows[row].get(column);
+};
+
+/**
+ * Retrieves a Float value from the Table's specified row and column.
+ * The row is specified by its ID, while the column may be specified by
+ * either its ID or title.
+ *
+ * @method getNum
+ * @param {Number} row row ID
+ * @param {String|Number} column columnName (string) or
+ * ID (number)
+ * @return {Number}
+ *
+ * @example
+ *
+ *
+ * // Given the CSV file "mammals.csv"
+ * // in the project's "assets" folder:
+ * //
+ * // id,species,name
+ * // 0,Capra hircus,Goat
+ * // 1,Panthera pardus,Leopard
+ * // 2,Equus zebra,Zebra
+ *
+ * var table;
+ *
+ * function preload() {
+ * //my table is comma separated value "csv"
+ * //and has a header specifying the columns labels
+ * table = loadTable("assets/mammals.csv", "csv", "header");
+ * }
+ *
+ * function setup() {
+ * print(table.getNum(1, 0) + 100);
+ * //id 1 + 100 = 101
+ * }
+ *
+ *
+ *
+ *@alt
+ * no image displayed
+ *
+ */
+p5.Table.prototype.getNum = function(row, column) {
+ return this.rows[row].getNum(column);
+};
+
+/**
+ * Retrieves a String value from the Table's specified row and column.
+ * The row is specified by its ID, while the column may be specified by
+ * either its ID or title.
+ *
+ * @method getString
+ * @param {Number} row row ID
+ * @param {String|Number} column columnName (string) or
+ * ID (number)
+ * @return {String}
+ *
+ * @example
+ *
+ *
+ * // Given the CSV file "mammals.csv"
+ * // in the project's "assets" folder:
+ * //
+ * // id,species,name
+ * // 0,Capra hircus,Goat
+ * // 1,Panthera pardus,Leopard
+ * // 2,Equus zebra,Zebra
+ *
+ * var table;
+ *
+ * function preload() {
+ * //my table is comma separated value "csv"
+ * //and has a header specifying the columns labels
+ * table = loadTable("assets/mammals.csv", "csv", "header");
+ * }
+ *
+ * function setup() {
+ * var tableArray = table.getArray();
+ *
+ * //output each row as array
+ * for (var i = 0; i < tableArray.length; i++)
+ * print(tableArray[i]);
+ * }
+ *
+ *
+ *
+ *@alt
+ * no image displayed
+ *
+ */
+p5.Table.prototype.getString = function(row, column) {
+ return this.rows[row].getString(column);
+};
+
+/**
+ * Retrieves all table data and returns as an object. If a column name is
+ * passed in, each row object will be stored with that attribute as its
+ * title.
+ *
+ * @method getObject
+ * @param {String} headerColumn Name of the column which should be used to
+ * title each row object (optional)
+ * @return {Object}
+ *
+ * @example
+ *
+ *
+ * // Given the CSV file "mammals.csv"
+ * // in the project's "assets" folder:
+ * //
+ * // id,species,name
+ * // 0,Capra hircus,Goat
+ * // 1,Panthera pardus,Leopard
+ * // 2,Equus zebra,Zebra
+ *
+ * var table;
+ *
+ * function preload() {
+ * //my table is comma separated value "csv"
+ * //and has a header specifying the columns labels
+ * table = loadTable("assets/mammals.csv", "csv", "header");
+ * }
+ *
+ * function setup() {
+ * var tableObject = table.getObject();
+ *
+ * print(tableObject);
+ * //outputs an object
+ * }
+ *
+ *
+ *
+ *@alt
+ * no image displayed
+ *
+ */
+p5.Table.prototype.getObject = function (headerColumn) {
+ var tableObject = {};
+ var obj, cPos, index;
+
+ for(var i = 0; i < this.rows.length; i++) {
+ obj = this.rows[i].obj;
+
+ if (typeof(headerColumn) === 'string'){
+ cPos = this.columns.indexOf(headerColumn); // index of columnID
+ if (cPos >= 0) {
+ index = obj[headerColumn];
+ tableObject[index] = obj;
+ } else {
+ throw 'This table has no column named "' + headerColumn +'"';
+ }
+ } else {
+ tableObject[i] = this.rows[i].obj;
+ }
+ }
+ return tableObject;
+};
+
+/**
+ * Retrieves all table data and returns it as a multidimensional array.
+ *
+ * @method getArray
+ * @return {Array}
+ */
+p5.Table.prototype.getArray = function () {
+ var tableArray = [];
+ for(var i = 0; i < this.rows.length; i++) {
+ tableArray.push(this.rows[i].arr);
+ }
+ return tableArray;
+};
+
+module.exports = p5.Table;
+
+},{"../core/core":37}],61:[function(_dereq_,module,exports){
+/**
+ * @module IO
+ * @submodule Table
+ * @requires core
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+
+/**
+ * A TableRow object represents a single row of data values,
+ * stored in columns, from a table.
+ *
+ * A Table Row contains both an ordered array, and an unordered
+ * JSON object.
+ *
+ * @class p5.TableRow
+ * @constructor
+ * @param {String} [str] optional: populate the row with a
+ * string of values, separated by the
+ * separator
+ * @param {String} [separator] comma separated values (csv) by default
+ */
+p5.TableRow = function (str, separator) {
+ var arr = [];
+ var obj = {};
+ if (str){
+ separator = separator || ',';
+ arr = str.split(separator);
+ }
+ for (var i = 0; i < arr.length; i++){
+ var key = i;
+ var val = arr[i];
+ obj[key] = val;
+ }
+ this.arr = arr;
+ this.obj = obj;
+ this.table = null;
+};
+
+/**
+ * Stores a value in the TableRow's specified column.
+ * The column may be specified by either its ID or title.
+ *
+ * @method set
+ * @param {String|Number} column Column ID (Number)
+ * or Title (String)
+ * @param {String|Number} value The value to be stored
+ */
+p5.TableRow.prototype.set = function(column, value) {
+ // if typeof column is string, use .obj
+ if (typeof(column) === 'string'){
+ var cPos = this.table.columns.indexOf(column); // index of columnID
+ if (cPos >= 0) {
+ this.obj[column] = value;
+ this.arr[cPos] = value;
+ }
+ else {
+ throw 'This table has no column named "' + column +'"';
+ }
+ }
+
+ // if typeof column is number, use .arr
+ else {
+ if (column < this.table.columns.length) {
+ this.arr[column] = value;
+ var cTitle = this.table.columns[column];
+ this.obj[cTitle] = value;
+ }
+ else {
+ throw 'Column #' + column + ' is out of the range of this table';
+ }
+ }
+};
+
+
+/**
+ * Stores a Float value in the TableRow's specified column.
+ * The column may be specified by either its ID or title.
+ *
+ * @method setNum
+ * @param {String|Number} column Column ID (Number)
+ * or Title (String)
+ * @param {Number} value The value to be stored
+ * as a Float
+ */
+p5.TableRow.prototype.setNum = function(column, value){
+ var floatVal = parseFloat(value, 10);
+ this.set(column, floatVal);
+};
+
+
+/**
+ * Stores a String value in the TableRow's specified column.
+ * The column may be specified by either its ID or title.
+ *
+ * @method setString
+ * @param {String|Number} column Column ID (Number)
+ * or Title (String)
+ * @param {String} value The value to be stored
+ * as a String
+ */
+p5.TableRow.prototype.setString = function(column, value){
+ var stringVal = value.toString();
+ this.set(column, stringVal);
+};
+
+/**
+ * Retrieves a value from the TableRow's specified column.
+ * The column may be specified by either its ID or title.
+ *
+ * @method get
+ * @param {String|Number} column columnName (string) or
+ * ID (number)
+ * @return {String|Number}
+ */
+p5.TableRow.prototype.get = function(column) {
+ if (typeof(column) === 'string'){
+ return this.obj[column];
+ } else {
+ return this.arr[column];
+ }
+};
+
+/**
+ * Retrieves a Float value from the TableRow's specified
+ * column. The column may be specified by either its ID or
+ * title.
+ *
+ * @method getNum
+ * @param {String|Number} column columnName (string) or
+ * ID (number)
+ * @return {Number} Float Floating point number
+ */
+p5.TableRow.prototype.getNum = function(column) {
+ var ret;
+ if (typeof(column) === 'string'){
+ ret = parseFloat(this.obj[column], 10);
+ } else {
+ ret = parseFloat(this.arr[column], 10);
+ }
+
+ if (ret.toString() === 'NaN') {
+ throw 'Error: ' + this.obj[column]+ ' is NaN (Not a Number)';
+ }
+ return ret;
+};
+
+/**
+ * Retrieves an String value from the TableRow's specified
+ * column. The column may be specified by either its ID or
+ * title.
+ *
+ * @method getString
+ * @param {String|Number} column columnName (string) or
+ * ID (number)
+ * @return {String} String
+ */
+p5.TableRow.prototype.getString = function(column) {
+ if (typeof(column) === 'string'){
+ return this.obj[column].toString();
+ } else {
+ return this.arr[column].toString();
+ }
+};
+
+module.exports = p5.TableRow;
+
+},{"../core/core":37}],62:[function(_dereq_,module,exports){
+/**
+ * @module IO
+ * @submodule XML
+ * @requires core
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+
+/**
+ * XML is a representation of an XML object, able to parse XML code. Use
+ * loadXML() to load external XML files and create XML objects.
+ *
+ * @class p5.XML
+ * @constructor
+ * @return {p5.XML} p5.XML object generated
+ * @example
+ *
+ * // The following short XML file called "mammals.xml" is parsed
+ * // in the code below.
+ * //
+ * //
+ * // <mammals>
+ * // <animal id="0" species="Capra hircus">Goat</animal>
+ * // <animal id="1" species="Panthera pardus">Leopard</animal>
+ * // <animal id="2" species="Equus zebra">Zebra</animal>
+ * // </mammals>
+ *
+ * var xml;
+ *
+ * function preload() {
+ * xml = loadXML("assets/mammals.xml");
+ * }
+ *
+ * function setup() {
+ * var children = xml.getChildren("animal");
+ *
+ * for (var i = 0; i < children.length; i++) {
+ * var id = children[i].getNumber("id");
+ * var coloring = children[i].getString("species");
+ * var name = children[i].getContent();
+ * print(id + ", " + coloring + ", " + name);
+ * }
+ * }
+ *
+ * // Sketch prints:
+ * // 0, Capra hircus, Goat
+ * // 1, Panthera pardus, Leopard
+ * // 2, Equus zebra, Zebra
+ *
+ *
+ * @alt
+ * no image displayed
+ *
+ */
+p5.XML = function () {
+ this.name = null; //done
+ this.attributes = {}; //done
+ this.children = [];
+ this.parent = null;
+ this.content = null; //done
+};
+
+
+/**
+ * Gets a copy of the element's parent. Returns the parent as another
+ * p5.XML object.
+ *
+ * @method getParent
+ * @return {Object} element parent
+ * @example
+ *
+ * // The following short XML file called "mammals.xml" is parsed
+ * // in the code below.
+ * //
+ * //
+ * // <mammals>
+ * // <animal id="0" species="Capra hircus">Goat</animal>
+ * // <animal id="1" species="Panthera pardus">Leopard</animal>
+ * // <animal id="2" species="Equus zebra">Zebra</animal>
+ * // </mammals>
+ *
+ * var xml;
+ *
+ * function preload() {
+ * xml = loadXML("assets/mammals.xml");
+ * }
+ *
+ * function setup() {
+ * var children = xml.getChildren("animal");
+ * var parent = children[1].getParent();
+ * print(parent.getName());
+ * }
+ *
+ * // Sketch prints:
+ * // mammals
+ *
+ */
+p5.XML.prototype.getParent = function() {
+ return this.parent;
+};
+
+/**
+ * Gets the element's full name, which is returned as a String.
+ *
+ * @method getName
+ * @return {String} the name of the node
+ * @example<animal
+ *
+ * // The following short XML file called "mammals.xml" is parsed
+ * // in the code below.
+ * //
+ * //
+ * // <mammals>
+ * // <animal id="0" species="Capra hircus">Goat</animal>
+ * // <animal id="1" species="Panthera pardus">Leopard</animal>
+ * // <animal id="2" species="Equus zebra">Zebra</animal>
+ * // </mammals>
+ *
+ * var xml;
+ *
+ * function preload() {
+ * xml = loadXML("assets/mammals.xml");
+ * }
+ *
+ * function setup() {
+ * print(xml.getName());
+ * }
+ *
+ * // Sketch prints:
+ * // mammals
+ *
+ */
+p5.XML.prototype.getName = function() {
+ return this.name;
+};
+
+/**
+ * Sets the element's name, which is specified as a String.
+ *
+ * @method setName
+ * @param {String} the new name of the node
+ * @example<animal
+ *
+ * // The following short XML file called "mammals.xml" is parsed
+ * // in the code below.
+ * //
+ * //
+ * // <mammals>
+ * // <animal id="0" species="Capra hircus">Goat</animal>
+ * // <animal id="1" species="Panthera pardus">Leopard</animal>
+ * // <animal id="2" species="Equus zebra">Zebra</animal>
+ * // </mammals>
+ *
+ * var xml;
+ *
+ * function preload() {
+ * xml = loadXML("assets/mammals.xml");
+ * }
+ *
+ * function setup() {
+ * print(xml.getName());
+ * xml.setName("fish");
+ * print(xml.getName());
+ * }
+ *
+ * // Sketch prints:
+ * // mammals
+ * // fish
+ *
+ */
+p5.XML.prototype.setName = function(name) {
+ this.name = name;
+};
+
+/**
+ * Checks whether or not the element has any children, and returns the result
+ * as a boolean.
+ *
+ * @method hasChildren
+ * @return {boolean}
+ * @example<animal
+ *
+ * // The following short XML file called "mammals.xml" is parsed
+ * // in the code below.
+ * //
+ * //
+ * // <mammals>
+ * // <animal id="0" species="Capra hircus">Goat</animal>
+ * // <animal id="1" species="Panthera pardus">Leopard</animal>
+ * // <animal id="2" species="Equus zebra">Zebra</animal>
+ * // </mammals>
+ *
+ * var xml;
+ *
+ * function preload() {
+ * xml = loadXML("assets/mammals.xml");
+ * }
+ *
+ * function setup() {
+ * print(xml.hasChildren());
+ * }
+ *
+ * // Sketch prints:
+ * // true
+ *
+ */
+p5.XML.prototype.hasChildren = function() {
+ return this.children.length > 0;
+};
+
+/**
+ * Get the names of all of the element's children, and returns the names as an
+ * array of Strings. This is the same as looping through and calling getName()
+ * on each child element individually.
+ *
+ * @method listChildren
+ * @return {Array} names of the children of the element
+ * @example<animal
+ *
+ * // The following short XML file called "mammals.xml" is parsed
+ * // in the code below.
+ * //
+ * //
+ * // <mammals>
+ * // <animal id="0" species="Capra hircus">Goat</animal>
+ * // <animal id="1" species="Panthera pardus">Leopard</animal>
+ * // <animal id="2" species="Equus zebra">Zebra</animal>
+ * // </mammals>
+ *
+ * var xml;
+ *
+ * function preload() {
+ * xml = loadXML("assets/mammals.xml");
+ * }
+ *
+ * function setup() {
+ * print(xml.listChildren());
+ * }
+ *
+ * // Sketch prints:
+ * // ["animal", "animal", "animal"]
+ *
+ */
+p5.XML.prototype.listChildren = function() {
+ return this.children.map(function(c) { return c.name; });
+};
+
+/**
+ * Returns all of the element's children as an array of p5.XML objects. When
+ * the name parameter is specified, then it will return all children that match
+ * that name.
+ *
+ * @method getChildren
+ * @param {String} [name] element name
+ * @return {Array} children of the element
+ * @example<animal
+ *
+ * // The following short XML file called "mammals.xml" is parsed
+ * // in the code below.
+ * //
+ * //
+ * // <mammals>
+ * // <animal id="0" species="Capra hircus">Goat</animal>
+ * // <animal id="1" species="Panthera pardus">Leopard</animal>
+ * // <animal id="2" species="Equus zebra">Zebra</animal>
+ * // </mammals>
+ *
+ * var xml;
+ *
+ * function preload() {
+ * xml = loadXML("assets/mammals.xml");
+ * }
+ *
+ * function setup() {
+ * var animals = xml.getChildren("animal");
+ *
+ * for (var i = 0; i < animals.length; i++) {
+ * print(animals[i].getContent());
+ * }
+ * }
+ *
+ * // Sketch prints:
+ * // "Goat"
+ * // "Leopard"
+ * // "Zebra"
+ *
+ */
+p5.XML.prototype.getChildren = function(param) {
+ if (param) {
+ return this.children.filter(function(c) { return c.name === param; });
+ }
+ else {
+ return this.children;
+ }
+};
+
+/**
+ * Returns the first of the element's children that matches the name parameter
+ * or the child of the given index.It returns undefined if no matching
+ * child is found.
+ *
+ * @method getChild
+ * @param {String|Number} name element name or index
+ * @return {p5.XML}
+ * @example<animal
+ *
+ * // The following short XML file called "mammals.xml" is parsed
+ * // in the code below.
+ * //
+ * //
+ * // <mammals>
+ * // <animal id="0" species="Capra hircus">Goat</animal>
+ * // <animal id="1" species="Panthera pardus">Leopard</animal>
+ * // <animal id="2" species="Equus zebra">Zebra</animal>
+ * // </mammals>
+ *
+ * var xml;
+ *
+ * function preload() {
+ * xml = loadXML("assets/mammals.xml");
+ * }
+ *
+ * function setup() {
+ * var firstChild = xml.getChild("animal");
+ * print(firstChild.getContent());
+ * }
+ *
+ * // Sketch prints:
+ * // "Goat"
+ *
+ *
+ * var xml;
+ *
+ * function preload() {
+ * xml = loadXML("assets/mammals.xml");
+ * }
+ *
+ * function setup() {
+ * var secondChild = xml.getChild(1);
+ * print(secondChild.getContent());
+ * }
+ *
+ * // Sketch prints:
+ * // "Leopard"
+ *
+ */
+p5.XML.prototype.getChild = function(param) {
+ if(typeof param === 'string') {
+ return this.children.find(function(c) {
+ return c.name === param;
+ });
+ }
+ else {
+ return this.children[param];
+ }
+};
+
+/**
+ * Appends a new child to the element. The child can be specified with
+ * either a String, which will be used as the new tag's name, or as a
+ * reference to an existing p5.XML object.
+ * A reference to the newly created child is returned as an p5.XML object.
+ *
+ * @method addChild
+ * @param {Object} a p5.XML Object which will be the child to be added
+ */
+p5.XML.prototype.addChild = function(node) {
+ if (node instanceof p5.XML) {
+ this.children.push(node);
+ } else {
+ // PEND
+ }
+};
+
+/**
+ * Removes the element specified by name or index.
+ *
+ * @method removeChild
+ * @param {String|Number} name element name or index
+ * @example
+ *
+ * // The following short XML file called "mammals.xml" is parsed
+ * // in the code below.
+ * //
+ * //
+ * // <mammals>
+ * // <animal id="0" species="Capra hircus">Goat</animal>
+ * // <animal id="1" species="Panthera pardus">Leopard</animal>
+ * // <animal id="2" species="Equus zebra">Zebra</animal>
+ * // </mammals>
+ *
+ * var xml;
+ *
+ * function preload() {
+ * xml = loadXML("assets/mammals.xml");
+ * }
+ *
+ * function setup() {
+ * xml.removeChild("animal");
+ * var children = xml.getChildren();
+ * for (var i=0; i
+ *
+ * var xml;
+ *
+ * function preload() {
+ * xml = loadXML("assets/mammals.xml");
+ * }
+ *
+ * function setup() {
+ * xml.removeChild(1);
+ * var children = xml.getChildren();
+ * for (var i=0; i
+ */
+p5.XML.prototype.removeChild = function(param) {
+ var ind = -1;
+ if(typeof param === 'string') {
+ for (var i=0; i
+ * // The following short XML file called "mammals.xml" is parsed
+ * // in the code below.
+ * //
+ * //
+ * // <mammals>
+ * // <animal id="0" species="Capra hircus">Goat</animal>
+ * // <animal id="1" species="Panthera pardus">Leopard</animal>
+ * // <animal id="2" species="Equus zebra">Zebra</animal>
+ * // </mammals>
+ *
+ * var xml;
+ *
+ * function preload() {
+ * xml = loadXML("assets/mammals.xml");
+ * }
+ *
+ * function setup() {
+ * var firstChild = xml.getChild("animal");
+ * print(firstChild.getAttributeCount());
+ * }
+ *
+ * // Sketch prints:
+ * // 2
+ *
+ */
+p5.XML.prototype.getAttributeCount = function() {
+ return Object.keys(this.attributes).length;
+};
+
+/**
+ * Gets all of the specified element's attributes, and returns them as an
+ * array of Strings.
+ *
+ * @method listAttributes
+ * @return {Array} an array of strings containing the names of attributes
+ * @example
+ *
+ * // The following short XML file called "mammals.xml" is parsed
+ * // in the code below.
+ * //
+ * //
+ * // <mammals>
+ * // <animal id="0" species="Capra hircus">Goat</animal>
+ * // <animal id="1" species="Panthera pardus">Leopard</animal>
+ * // <animal id="2" species="Equus zebra">Zebra</animal>
+ * // </mammals>
+ *
+ * var xml;
+ *
+ * function preload() {
+ * xml = loadXML("assets/mammals.xml");
+ * }
+ *
+ * function setup() {
+ * var firstChild = xml.getChild("animal");
+ * print(firstChild.listAttributes());
+ * }
+ *
+ * // Sketch prints:
+ * // ["id", "species"]
+ *
+ */
+p5.XML.prototype.listAttributes = function() {
+ return Object.keys(this.attributes);
+};
+
+/**
+ * Checks whether or not an element has the specified attribute.
+ *
+ * @method hasAttribute
+ * @param {String} the attribute to be checked
+ * @return {boolean} true if attribute found else false
+ * @example
+ *
+ * // The following short XML file called "mammals.xml" is parsed
+ * // in the code below.
+ * //
+ * //
+ * // <mammals>
+ * // <animal id="0" species="Capra hircus">Goat</animal>
+ * // <animal id="1" species="Panthera pardus">Leopard</animal>
+ * // <animal id="2" species="Equus zebra">Zebra</animal>
+ * // </mammals>
+ *
+ * var xml;
+ *
+ * function preload() {
+ * xml = loadXML("assets/mammals.xml");
+ * }
+ *
+ * function setup() {
+ * var firstChild = xml.getChild("animal");
+ * print(firstChild.hasAttribute("species"));
+ * print(firstChild.hasAttribute("color"));
+ * }
+ *
+ * // Sketch prints:
+ * // true
+ * // false
+ *
+ */
+p5.XML.prototype.hasAttribute = function(name) {
+ return this.attributes[name] ? true : false;
+};
+
+/**
+ * Returns an attribute value of the element as an Number. If the defaultValue
+ * parameter is specified and the attribute doesn't exist, then defaultValue
+ * is returned. If no defaultValue is specified and the attribute doesn't
+ * exist, the value 0 is returned.
+ *
+ * @method getNumber
+ * @param {String} name the non-null full name of the attribute
+ * @param {Number} [defaultValue] the default value of the attribute
+ * @return {Number}
+ * @example
+ *
+ * // The following short XML file called "mammals.xml" is parsed
+ * // in the code below.
+ * //
+ * //
+ * // <mammals>
+ * // <animal id="0" species="Capra hircus">Goat</animal>
+ * // <animal id="1" species="Panthera pardus">Leopard</animal>
+ * // <animal id="2" species="Equus zebra">Zebra</animal>
+ * // </mammals>
+ *
+ * var xml;
+ *
+ * function preload() {
+ * xml = loadXML("assets/mammals.xml");
+ * }
+ *
+ * function setup() {
+ * var firstChild = xml.getChild("animal");
+ * print(firstChild.getNumber("id"));
+ * }
+ *
+ * // Sketch prints:
+ * // 0
+ *
+ */
+p5.XML.prototype.getNumber = function(name, defaultValue) {
+ return Number(this.attributes[name]) || defaultValue || 0;
+};
+
+/**
+ * Returns an attribute value of the element as an String. If the defaultValue
+ * parameter is specified and the attribute doesn't exist, then defaultValue
+ * is returned. If no defaultValue is specified and the attribute doesn't
+ * exist, null is returned.
+ *
+ * @method getString
+ * @param {String} name the non-null full name of the attribute
+ * @param {Number} [defaultValue] the default value of the attribute
+ * @return {Number}
+ * @example
+ *
+ * // The following short XML file called "mammals.xml" is parsed
+ * // in the code below.
+ * //
+ * //
+ * // <mammals>
+ * // <animal id="0" species="Capra hircus">Goat</animal>
+ * // <animal id="1" species="Panthera pardus">Leopard</animal>
+ * // <animal id="2" species="Equus zebra">Zebra</animal>
+ * // </mammals>
+ *
+ * var xml;
+ *
+ * function preload() {
+ * xml = loadXML("assets/mammals.xml");
+ * }
+ *
+ * function setup() {
+ * var firstChild = xml.getChild("animal");
+ * print(firstChild.getString("species"));
+ * }
+ *
+ * // Sketch prints:
+ * // "Capra hircus"
+ *
+ */
+p5.XML.prototype.getString = function(name, defaultValue) {
+ return String(this.attributes[name]) || defaultValue || null;
+};
+
+/**
+ * Sets the content of an element's attribute. The first parameter specifies
+ * the attribute name, while the second specifies the new content.
+ *
+ * @method setAttribute
+ * @param {String} name the full name of the attribute
+ * @param {Number} value the value of the attribute
+ * @example
+ *
+ * // The following short XML file called "mammals.xml" is parsed
+ * // in the code below.
+ * //
+ * //
+ * // <mammals>
+ * // <animal id="0" species="Capra hircus">Goat</animal>
+ * // <animal id="1" species="Panthera pardus">Leopard</animal>
+ * // <animal id="2" species="Equus zebra">Zebra</animal>
+ * // </mammals>
+ *
+ * var xml;
+ *
+ * function preload() {
+ * xml = loadXML("assets/mammals.xml");
+ * }
+ *
+ * function setup() {
+ * var firstChild = xml.getChild("animal");
+ * print(firstChild.getString("species"));
+ * firstChild.setAttribute("species", "Jamides zebra");
+ * print(firstChild.getString("species"));
+ * }
+ *
+ * // Sketch prints:
+ * // "Capra hircus"
+ * // "Jamides zebra"
+ *
+ */
+p5.XML.prototype.setAttribute = function(name, value) {
+ if (this.attributes[name]) {
+ this.attributes[name] = value;
+ }
+};
+
+/**
+ * Returns the content of an element. If there is no such content,
+ * defaultValue is returned if specified, otherwise null is returned.
+ *
+ * @method getContent
+ * @param {String} [defaultValue] value returned if no content is found
+ * @return {String}
+ * @example
+ *
+ * // The following short XML file called "mammals.xml" is parsed
+ * // in the code below.
+ * //
+ * //
+ * // <mammals>
+ * // <animal id="0" species="Capra hircus">Goat</animal>
+ * // <animal id="1" species="Panthera pardus">Leopard</animal>
+ * // <animal id="2" species="Equus zebra">Zebra</animal>
+ * // </mammals>
+ *
+ * var xml;
+ *
+ * function preload() {
+ * xml = loadXML("assets/mammals.xml");
+ * }
+ *
+ * function setup() {
+ * var firstChild = xml.getChild("animal");
+ * print(firstChild.getContent());
+ * }
+ *
+ * // Sketch prints:
+ * // "Goat"
+ *
+ */
+p5.XML.prototype.getContent = function(defaultValue) {
+ return this.content || defaultValue || null;
+};
+
+/**
+ * Sets the element's content.
+ *
+ * @method setContent
+ * @param {String} text the new content
+ * @example
+ *
+ * // The following short XML file called "mammals.xml" is parsed
+ * // in the code below.
+ * //
+ * //
+ * // <mammals>
+ * // <animal id="0" species="Capra hircus">Goat</animal>
+ * // <animal id="1" species="Panthera pardus">Leopard</animal>
+ * // <animal id="2" species="Equus zebra">Zebra</animal>
+ * // </mammals>
+ *
+ * var xml;
+ *
+ * function preload() {
+ * xml = loadXML("assets/mammals.xml");
+ * }
+ *
+ * function setup() {
+ * var firstChild = xml.getChild("animal");
+ * print(firstChild.getContent());
+ * firstChild.setContent("Mountain Goat");
+ * print(firstChild.getContent());
+ * }
+ *
+ * // Sketch prints:
+ * // "Goat"
+ * // "Mountain Goat"
+ *
+ */
+p5.XML.prototype.setContent = function( content ) {
+ if(!this.children.length) {
+ this.content = content;
+ }
+};
+
+/* HELPERS */
+/**
+ * This method is called while the parsing of XML (when loadXML() is
+ * called). The difference between this method and the setContent()
+ * method defined later is that this one is used to set the content
+ * when the node in question has more nodes under it and so on and
+ * not directly text content. While in the other one is used when
+ * the node in question directly has text inside it.
+ *
+ */
+p5.XML.prototype._setCont = function(content) {
+ var str;
+ str = content;
+ str = str.replace(/\s\s+/g, ',');
+ //str = str.split(',');
+ this.content = str;
+};
+
+/**
+ * This method is called while the parsing of XML (when loadXML() is
+ * called). The XML node is passed and its attributes are stored in the
+ * p5.XML's attribute Object.
+ *
+ */
+p5.XML.prototype._setAttributes = function(node) {
+ var i, att = {};
+ for( i = 0; i < node.attributes.length; i++) {
+ att[node.attributes[i].nodeName] = node.attributes[i].nodeValue;
+ }
+ this.attributes = att;
+};
+
+module.exports = p5.XML;
+},{"../core/core":37}],63:[function(_dereq_,module,exports){
+/**
+ * @module Math
+ * @submodule Calculation
+ * @for p5
+ * @requires core
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+
+/**
+ * Calculates the absolute value (magnitude) of a number. Maps to Math.abs().
+ * The absolute value of a number is always positive.
+ *
+ * @method abs
+ * @param {Number} n number to compute
+ * @return {Number} absolute value of given number
+ * @example
+ *
+ * function setup() {
+ * var x = -3;
+ * var y = abs(x);
+ *
+ * print(x); // -3
+ * print(y); // 3
+ * }
+ *
+ *
+ * @alt
+ * no image displayed
+ *
+ */
+p5.prototype.abs = Math.abs;
+
+/**
+ * Calculates the closest int value that is greater than or equal to the
+ * value of the parameter. Maps to Math.ceil(). For example, ceil(9.03)
+ * returns the value 10.
+ *
+ * @method ceil
+ * @param {Number} n number to round up
+ * @return {Number} rounded up number
+ * @example
+ *
+ * function draw() {
+ * background(200);
+ * // map, mouseX between 0 and 5.
+ * var ax = map(mouseX, 0, 100, 0, 5);
+ * var ay = 66;
+ *
+ * //Get the ceiling of the mapped number.
+ * var bx = ceil(map(mouseX, 0, 100, 0,5));
+ * var by = 33;
+ *
+ * // Multiply the mapped numbers by 20 to more easily
+ * // see the changes.
+ * stroke(0);
+ * fill(0);
+ * line(0, ay, ax * 20, ay);
+ * line(0, by, bx * 20, by);
+ *
+ * // Reformat the float returned by map and draw it.
+ * noStroke();
+ * text(nfc(ax, 2,2), ax, ay - 5);
+ * text(nfc(bx,1,1), bx, by - 5);
+ * }
+ *
+ *
+ * @alt
+ * 2 horizontal lines & number sets. increase with mouse x. bottom to 2 decimals
+ *
+ */
+p5.prototype.ceil = Math.ceil;
+
+/**
+ * Constrains a value between a minimum and maximum value.
+ *
+ * @method constrain
+ * @param {Number} n number to constrain
+ * @param {Number} low minimum limit
+ * @param {Number} high maximum limit
+ * @return {Number} constrained number
+ * @example
+ *
+ * function draw() {
+ * background(200);
+ *
+ * var leftWall = 25;
+ * var rightWall = 75;
+ *
+ * // xm is just the mouseX, while
+ * // xc is the mouseX, but constrained
+ * // between the leftWall and rightWall!
+ * var xm = mouseX;
+ * var xc = constrain(mouseX, leftWall, rightWall);
+ *
+ * // Draw the walls.
+ * stroke(150);
+ * line(leftWall, 0, leftWall, height);
+ * line(rightWall, 0, rightWall, height);
+ *
+ * // Draw xm and xc as circles.
+ * noStroke();
+ * fill(150);
+ * ellipse(xm, 33, 9,9); // Not Constrained
+ * fill(0);
+ * ellipse(xc, 66, 9,9); // Constrained
+ * }
+ *
+ *
+ * @alt
+ * 2 vertical lines. 2 ellipses move with mouse X 1 does not move passed lines
+ *
+ */
+p5.prototype.constrain = function(n, low, high) {
+ return Math.max(Math.min(n, high), low);
+};
+
+/**
+ * Calculates the distance between two points.
+ *
+ * @method dist
+ * @param {Number} x1 x-coordinate of the first point
+ * @param {Number} y1 y-coordinate of the first point
+ * @param {Number} [z1] z-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} [z2] z-coordinate of the second point
+ * @return {Number} distance between the two points
+ * @example
+ *
+ * // Move your mouse inside the canvas to see the
+ * // change in distance between two points!
+ * function draw() {
+ * background(200);
+ * fill(0);
+ *
+ * var x1 = 10;
+ * var y1 = 90;
+ * var x2 = mouseX;
+ * var y2 = mouseY;
+ *
+ * line(x1, y1, x2, y2);
+ * ellipse(x1, y1, 7, 7);
+ * ellipse(x2, y2, 7, 7);
+ *
+ * // d is the length of the line
+ * // the distance from point 1 to point 2.
+ * var d = int(dist(x1, y1, x2, y2));
+ *
+ * // Let's write d along the line we are drawing!
+ * push();
+ * translate( (x1+x2)/2, (y1+y2)/2 );
+ * rotate( atan2(y2-y1,x2-x1) );
+ * text(nfc(d,1,1), 0, -5);
+ * pop();
+ * // Fancy!
+ * }
+ *
+ *
+ * @alt
+ * 2 ellipses joined by line. 1 ellipse moves with mouse X&Y. Distance displayed.
+ *
+ */
+p5.prototype.dist = function(x1, y1, z1, x2, y2, z2) {
+ if (arguments.length === 4) {
+ // In the case of 2d: z1 means x2 and x2 means y2
+ return Math.sqrt( (z1-x1)*(z1-x1) + (x2-y1)*(x2-y1) );
+ } else if (arguments.length === 6) {
+ return Math.sqrt( (x2-x1)*(x2-x1) + (y2-y1)*(y2-y1) + (z2-z1)*(z2-z1) );
+ }
+};
+
+/**
+ * Returns Euler's number e (2.71828...) raised to the power of the n
+ * parameter. Maps to Math.exp().
+ *
+ * @method exp
+ * @param {Number} n exponent to raise
+ * @return {Number} e^n
+ * @example
+ *
+ * function draw() {
+ * background(200);
+ *
+ * // Compute the exp() function with a value between 0 and 2
+ * var xValue = map(mouseX, 0, width, 0, 2);
+ * var yValue = exp(xValue);
+ *
+ * var y = map(yValue, 0, 8, height, 0);
+ *
+ * var legend = "exp (" + nfc(xValue, 3) +")\n= " + nf(yValue, 1, 4);
+ * stroke(150);
+ * line(mouseX, y, mouseX, height);
+ * fill(0);
+ * text(legend, 5, 15);
+ * noStroke();
+ * ellipse (mouseX,y, 7, 7);
+ *
+ * // Draw the exp(x) curve,
+ * // over the domain of x from 0 to 2
+ * noFill();
+ * stroke(0);
+ * beginShape();
+ * for (var x = 0; x < width; x++) {
+ * xValue = map(x, 0, width, 0, 2);
+ * yValue = exp(xValue);
+ * y = map(yValue, 0, 8, height, 0);
+ * vertex(x, y);
+ * }
+ *
+ * endShape();
+ * line(0, 0, 0, height);
+ * line(0, height-1, width, height-1);
+ * }
+ *
+ *
+ * @alt
+ * ellipse moves along a curve with mouse x. e^n displayed.
+ *
+ */
+p5.prototype.exp = Math.exp;
+
+/**
+ * Calculates the closest int value that is less than or equal to the
+ * value of the parameter. Maps to Math.floor().
+ *
+ * @method floor
+ * @param {Number} n number to round down
+ * @return {Number} rounded down number
+ * @example
+ *
+ * function draw() {
+ * background(200);
+ * //map, mouseX between 0 and 5.
+ * var ax = map(mouseX, 0, 100, 0, 5);
+ * var ay = 66;
+ *
+ * //Get the floor of the mapped number.
+ * var bx = floor(map(mouseX, 0, 100, 0,5));
+ * var by = 33;
+ *
+ * // Multiply the mapped numbers by 20 to more easily
+ * // see the changes.
+ * stroke(0);
+ * fill(0);
+ * line(0, ay, ax * 20, ay);
+ * line(0, by, bx * 20, by);
+ *
+ * // Reformat the float returned by map and draw it.
+ * noStroke();
+ * text(nfc(ax, 2,2), ax, ay - 5);
+ * text(nfc(bx,1,1), bx, by - 5);
+ * }
+ *
+ *
+ * @alt
+ * 2 horizontal lines & number sets. increase with mouse x. bottom to 2 decimals
+ *
+ */
+p5.prototype.floor = Math.floor;
+
+/**
+ * Calculates a number between two numbers at a specific increment. The amt
+ * parameter is the amount to interpolate between the two values where 0.0
+ * equal to the first point, 0.1 is very near the first point, 0.5 is
+ * half-way in between, etc. The lerp function is convenient for creating
+ * motion along a straight path and for drawing dotted lines.
+ *
+ * @method lerp
+ * @param {Number} start first value
+ * @param {Number} stop second value
+ * @param {Number} amt number between 0.0 and 1.0
+ * @return {Number} lerped value
+ * @example
+ *
+ * function setup() {
+ * background(200);
+ * var a = 20;
+ * var b = 80;
+ * var c = lerp(a,b, .2);
+ * var d = lerp(a,b, .5);
+ * var e = lerp(a,b, .8);
+ *
+ * var y = 50
+ *
+ * strokeWeight(5);
+ * stroke(0); // Draw the original points in black
+ * point(a, y);
+ * point(b, y);
+ *
+ * stroke(100); // Draw the lerp points in gray
+ * point(c, y);
+ * point(d, y);
+ * point(e, y);
+ * }
+ *
+ *
+ * @alt
+ * 5 points horizontally staggered mid-canvas. mid 3 are grey, outer black
+ *
+ */
+p5.prototype.lerp = function(start, stop, amt) {
+ return amt*(stop-start)+start;
+};
+
+/**
+ * Calculates the natural logarithm (the base-e logarithm) of a number. This
+ * function expects the n parameter to be a value greater than 0.0. Maps to
+ * Math.log().
+ *
+ * @method log
+ * @param {Number} n number greater than 0
+ * @return {Number} natural logarithm of n
+ * @example
+ *
+ * function draw() {
+ * background(200);
+ * var maxX = 2.8;
+ * var maxY = 1.5;
+ *
+ * // Compute the natural log of a value between 0 and maxX
+ * var xValue = map(mouseX, 0, width, 0, maxX);
+ * if (xValue > 0) { // Cannot take the log of a negative number.
+ * var yValue = log(xValue);
+ * var y = map(yValue, -maxY, maxY, height, 0);
+ *
+ * // Display the calculation occurring.
+ * var legend = "log(" + nf(xValue, 1, 2) + ")\n= " + nf(yValue, 1, 3);
+ * stroke(150);
+ * line(mouseX, y, mouseX, height);
+ * fill(0);
+ * text (legend, 5, 15);
+ * noStroke();
+ * ellipse (mouseX, y, 7, 7);
+ * }
+ *
+ * // Draw the log(x) curve,
+ * // over the domain of x from 0 to maxX
+ * noFill();
+ * stroke(0);
+ * beginShape();
+ * for(var x=0; x < width; x++) {
+ * xValue = map(x, 0, width, 0, maxX);
+ * yValue = log(xValue);
+ * y = map(yValue, -maxY, maxY, height, 0);
+ * vertex(x, y);
+ * }
+ * endShape();
+ * line(0,0,0,height);
+ * line(0,height/2,width, height/2);
+ * }
+ *
+ *
+ * @alt
+ * ellipse moves along a curve with mouse x. natural logarithm of n displayed.
+ *
+ */
+p5.prototype.log = Math.log;
+
+/**
+ * Calculates the magnitude (or length) of a vector. A vector is a direction
+ * in space commonly used in computer graphics and linear algebra. Because it
+ * has no "start" position, the magnitude of a vector can be thought of as
+ * the distance from the coordinate 0,0 to its x,y value. Therefore, mag() is
+ * a shortcut for writing dist(0, 0, x, y).
+ *
+ * @method mag
+ * @param {Number} a first value
+ * @param {Number} b second value
+ * @return {Number} magnitude of vector from (0,0) to (a,b)
+ * @example
+ *
+ * function setup() {
+ * var x1 = 20;
+ * var x2 = 80;
+ * var y1 = 30;
+ * var y2 = 70;
+ *
+ * line(0, 0, x1, y1);
+ * print(mag(x1, y1)); // Prints "36.05551"
+ * line(0, 0, x2, y1);
+ * print(mag(x2, y1)); // Prints "85.44004"
+ * line(0, 0, x1, y2);
+ * print(mag(x1, y2)); // Prints "72.8011"
+ * line(0, 0, x2, y2);
+ * print(mag(x2, y2)); // Prints "106.30146"
+ * }
+ *
+ *
+ * @alt
+ * 4 lines of different length radiate from top left of canvas.
+ *
+ */
+p5.prototype.mag = function(x, y) {
+ return Math.sqrt(x*x+y*y);
+};
+
+/**
+ * Re-maps a number from one range to another.
+ *
+ * In the first example above, the number 25 is converted from a value in the
+ * range of 0 to 100 into a value that ranges from the left edge of the
+ * window (0) to the right edge (width).
+ *
+ * @method map
+ * @param {Number} value the incoming value to be converted
+ * @param {Number} start1 lower bound of the value's current range
+ * @param {Number} stop1 upper bound of the value's current range
+ * @param {Number} start2 lower bound of the value's target range
+ * @param {Number} stop2 upper bound of the value's target range
+ * @return {Number} remapped number
+ * @example
+ *
+ * var value = 25;
+ * var m = map(value, 0, 100, 0, width);
+ * ellipse(m, 50, 10, 10);
+ *
+ *
+ *
+ * function setup() {
+ * noStroke();
+ * }
+ *
+ * function draw() {
+ * background(204);
+ * var x1 = map(mouseX, 0, width, 25, 75);
+ * ellipse(x1, 25, 25, 25);
+ * var x2 = map(mouseX, 0, width, 0, 100);
+ * ellipse(x2, 75, 25, 25);
+ * }
+ *
+ *
+ * @alt
+ * 10 by 10 white ellipse with in mid left canvas
+ * 2 25 by 25 white ellipses move with mouse x. Bottom has more range from X
+ *
+ */
+p5.prototype.map = function(n, start1, stop1, start2, stop2) {
+ return ((n-start1)/(stop1-start1))*(stop2-start2)+start2;
+};
+
+/**
+ * Determines the largest value in a sequence of numbers, and then returns
+ * that value. max() accepts any number of Number parameters, or an Array
+ * of any length.
+ *
+ * @method max
+ * @param {Number|Array} n0 Numbers to compare
+ * @return {Number} maximum Number
+ * @example
+ *
+ * function setup() {
+ * // Change the elements in the array and run the sketch
+ * // to show how max() works!
+ * numArray = new Array(2,1,5,4,8,9);
+ * fill(0);
+ * noStroke();
+ * text("Array Elements", 0, 10);
+ * // Draw all numbers in the array
+ * var spacing = 15;
+ * var elemsY = 25;
+ * for(var i = 0; i < numArray.length; i++) {
+ * text(numArray[i], i * spacing, elemsY);
+ * }
+ * maxX = 33;
+ * maxY = 80;
+ * // Draw the Maximum value in the array.
+ * textSize(32);
+ * text(max(numArray), maxX, maxY);
+ * }
+ *
+ *
+ * @alt
+ * Small text at top reads: Array Elements 2 1 5 4 8 9. Large text at center: 9
+ *
+ */
+p5.prototype.max = function() {
+ if (arguments[0] instanceof Array) {
+ return Math.max.apply(null,arguments[0]);
+ } else {
+ return Math.max.apply(null,arguments);
+ }
+};
+
+/**
+ * Determines the smallest value in a sequence of numbers, and then returns
+ * that value. min() accepts any number of Number parameters, or an Array
+ * of any length.
+ *
+ * @method min
+ * @param {Number|Array} n0 Numbers to compare
+ * @return {Number} minimum Number
+ * @example
+ *
+ * function setup() {
+ * // Change the elements in the array and run the sketch
+ * // to show how min() works!
+ * numArray = new Array(2,1,5,4,8,9);
+ * fill(0);
+ * noStroke();
+ * text("Array Elements", 0, 10);
+ * // Draw all numbers in the array
+ * var spacing = 15;
+ * var elemsY = 25;
+ * for(var i = 0; i < numArray.length; i++) {
+ * text(numArray[i], i * spacing, elemsY);
+ * }
+ * maxX = 33;
+ * maxY = 80;
+ * // Draw the Minimum value in the array.
+ * textSize(32);
+ * text(min(numArray), maxX, maxY);
+ * }
+ *
+ *
+ * @alt
+ * Small text at top reads: Array Elements 2 1 5 4 8 9. Large text at center: 1
+ *
+ */
+p5.prototype.min = function() {
+ if (arguments[0] instanceof Array) {
+ return Math.min.apply(null,arguments[0]);
+ } else {
+ return Math.min.apply(null,arguments);
+ }
+};
+
+/**
+ * Normalizes a number from another range into a value between 0 and 1.
+ * Identical to map(value, low, high, 0, 1).
+ * Numbers outside of the range are not clamped to 0 and 1, because
+ * out-of-range values are often intentional and useful. (See the second
+ * example above.)
+ *
+ * @method norm
+ * @param {Number} value incoming value to be normalized
+ * @param {Number} start lower bound of the value's current range
+ * @param {Number} stop upper bound of the value's current range
+ * @return {Number} normalized number
+ * @example
+ *
+ * function draw() {
+ * background(200);
+ * currentNum = mouseX;
+ * lowerBound = 0;
+ * upperBound = width; //100;
+ * normalized = norm(currentNum, lowerBound, upperBound);
+ * lineY = 70
+ * line(0, lineY, width, lineY);
+ * //Draw an ellipse mapped to the non-normalized value.
+ * noStroke();
+ * fill(50)
+ * var s = 7; // ellipse size
+ * ellipse(currentNum, lineY, s, s);
+ *
+ * // Draw the guide
+ * guideY = lineY + 15;
+ * text("0", 0, guideY);
+ * textAlign(RIGHT);
+ * text("100", width, guideY);
+ *
+ * // Draw the normalized value
+ * textAlign(LEFT);
+ * fill(0);
+ * textSize(32);
+ * normalY = 40;
+ * normalX = 20;
+ * text(normalized, normalX, normalY);
+ * }
+ *
+ *
+ * @alt
+ * ellipse moves with mouse. 0 shown left & 100 right and updating values center
+ *
+ */
+p5.prototype.norm = function(n, start, stop) {
+ return this.map(n, start, stop, 0, 1);
+};
+
+/**
+ * Facilitates exponential expressions. The pow() function is an efficient
+ * way of multiplying numbers by themselves (or their reciprocals) in large
+ * quantities. For example, pow(3, 5) is equivalent to the expression
+ * 3*3*3*3*3 and pow(3, -5) is equivalent to 1 / 3*3*3*3*3. Maps to
+ * Math.pow().
+ *
+ * @method pow
+ * @param {Number} n base of the exponential expression
+ * @param {Number} e power by which to raise the base
+ * @return {Number} n^e
+ * @example
+ *
+ * function setup() {
+ * //Exponentially increase the size of an ellipse.
+ * eSize = 3; // Original Size
+ * eLoc = 10; // Original Location
+ *
+ * ellipse(eLoc, eLoc, eSize, eSize);
+ *
+ * ellipse(eLoc*2, eLoc*2, pow(eSize, 2), pow(eSize, 2));
+ *
+ * ellipse(eLoc*4, eLoc*4, pow(eSize, 3), pow(eSize, 3));
+ *
+ * ellipse(eLoc*8, eLoc*8, pow(eSize, 4), pow(eSize, 4));
+ * }
+ *
+ *
+ * @alt
+ * small to large ellipses radiating from top left of canvas
+ *
+ */
+p5.prototype.pow = Math.pow;
+
+/**
+ * Calculates the integer closest to the n parameter. For example,
+ * round(133.8) returns the value 134. Maps to Math.round().
+ *
+ * @method round
+ * @param {Number} n number to round
+ * @return {Number} rounded number
+ * @example
+ *
+ * function draw() {
+ * background(200);
+ * //map, mouseX between 0 and 5.
+ * var ax = map(mouseX, 0, 100, 0, 5);
+ * var ay = 66;
+ *
+ * // Round the mapped number.
+ * var bx = round(map(mouseX, 0, 100, 0,5));
+ * var by = 33;
+ *
+ * // Multiply the mapped numbers by 20 to more easily
+ * // see the changes.
+ * stroke(0);
+ * fill(0);
+ * line(0, ay, ax * 20, ay);
+ * line(0, by, bx * 20, by);
+ *
+ * // Reformat the float returned by map and draw it.
+ * noStroke();
+ * text(nfc(ax, 2,2), ax, ay - 5);
+ * text(nfc(bx,1,1), bx, by - 5);
+ * }
+ *
+ *
+ * @alt
+ * horizontal center line squared values displayed on top and regular on bottom.
+ *
+ */
+p5.prototype.round = Math.round;
+
+/**
+ * Squares a number (multiplies a number by itself). The result is always a
+ * positive number, as multiplying two negative numbers always yields a
+ * positive result. For example, -1 * -1 = 1.
+ *
+ * @method sq
+ * @param {Number} n number to square
+ * @return {Number} squared number
+ * @example
+ *
+ * function draw() {
+ * background(200);
+ * eSize = 7;
+ * x1 = map(mouseX, 0, width, 0, 10);
+ * y1 = 80;
+ * x2 = sq(x1);
+ * y2 = 20;
+ *
+ * // Draw the non-squared.
+ * line(0, y1, width, y1);
+ * ellipse(x1, y1, eSize, eSize);
+ *
+ * // Draw the squared.
+ * line(0, y2, width, y2);
+ * ellipse(x2, y2, eSize, eSize);
+ *
+ * // Draw dividing line.
+ * stroke(100)
+ * line(0, height/2, width, height/2);
+ *
+ * // Draw text.
+ * var spacing = 15;
+ * noStroke();
+ * fill(0);
+ * text("x = " + x1, 0, y1 + spacing);
+ * text("sq(x) = " + x2, 0, y2 + spacing);
+ * }
+ *
+ *
+ * @alt
+ * horizontal center line squared values displayed on top and regular on bottom.
+ *
+ */
+p5.prototype.sq = function(n) { return n*n; };
+
+/**
+ * Calculates the square root of a number. The square root of a number is
+ * always positive, even though there may be a valid negative root. The
+ * square root s of number a is such that s*s = a. It is the opposite of
+ * squaring. Maps to Math.sqrt().
+ *
+ * @method sqrt
+ * @param {Number} n non-negative number to square root
+ * @return {Number} square root of number
+ * @example
+ *
+ * function draw() {
+ * background(200);
+ * eSize = 7;
+ * x1 = mouseX;
+ * y1 = 80;
+ * x2 = sqrt(x1);
+ * y2 = 20;
+ *
+ * // Draw the non-squared.
+ * line(0, y1, width, y1);
+ * ellipse(x1, y1, eSize, eSize);
+ *
+ * // Draw the squared.
+ * line(0, y2, width, y2);
+ * ellipse(x2, y2, eSize, eSize);
+ *
+ * // Draw dividing line.
+ * stroke(100)
+ * line(0, height/2, width, height/2);
+ *
+ * // Draw text.
+ * noStroke();
+ * fill(0);
+ * var spacing = 15;
+ * text("x = " + x1, 0, y1 + spacing);
+ * text("sqrt(x) = " + x2, 0, y2 + spacing);
+ * }
+ *
+ *
+ * @alt
+ * horizontal center line squareroot values displayed on top and regular on bottom.
+ *
+ */
+p5.prototype.sqrt = Math.sqrt;
+
+module.exports = p5;
+
+},{"../core/core":37}],64:[function(_dereq_,module,exports){
+/**
+ * @module Math
+ * @submodule Math
+ * @for p5
+ * @requires core
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+
+
+/**
+ * Creates a new p5.Vector (the datatype for storing vectors). This provides a
+ * two or three dimensional vector, specifically a Euclidean (also known as
+ * geometric) vector. A vector is an entity that has both magnitude and
+ * direction.
+ *
+ * @method createVector
+ * @param {Number} [x] x component of the vector
+ * @param {Number} [y] y component of the vector
+ * @param {Number} [z] z component of the vector
+ */
+p5.prototype.createVector = function (x, y, z) {
+ if (this instanceof p5) {
+ return new p5.Vector(this, arguments);
+ } else {
+ return new p5.Vector(x, y, z);
+ }
+};
+
+module.exports = p5;
+
+},{"../core/core":37}],65:[function(_dereq_,module,exports){
+//////////////////////////////////////////////////////////////
+
+// http://mrl.nyu.edu/~perlin/noise/
+// Adapting from PApplet.java
+// which was adapted from toxi
+// which was adapted from the german demo group farbrausch
+// as used in their demo "art": http://www.farb-rausch.de/fr010src.zip
+
+// someday we might consider using "improved noise"
+// http://mrl.nyu.edu/~perlin/paper445.pdf
+// See: https://github.com/shiffman/The-Nature-of-Code-Examples-p5.js/
+// blob/master/introduction/Noise1D/noise.js
+
+/**
+ * @module Math
+ * @submodule Noise
+ * @for p5
+ * @requires core
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+
+var PERLIN_YWRAPB = 4;
+var PERLIN_YWRAP = 1<random() function.
+ * It was invented by Ken Perlin in the 1980s and been used since in
+ * graphical applications to produce procedural textures, natural motion,
+ * shapes, terrains etc.
The main difference to the
+ * random() function is that Perlin noise is defined in an infinite
+ * n-dimensional space where each pair of coordinates corresponds to a
+ * fixed semi-random value (fixed only for the lifespan of the program; see
+ * the noiseSeed() function). p5.js can compute 1D, 2D and 3D noise,
+ * depending on the number of coordinates given. The resulting value will
+ * always be between 0.0 and 1.0. The noise value can be animated by moving
+ * through the noise space as demonstrated in the example above. The 2nd
+ * and 3rd dimension can also be interpreted as time.
The actual
+ * noise is structured similar to an audio signal, in respect to the
+ * function's use of frequencies. Similar to the concept of harmonics in
+ * physics, perlin noise is computed over several octaves which are added
+ * together for the final result.
Another way to adjust the
+ * character of the resulting sequence is the scale of the input
+ * coordinates. As the function works within an infinite space the value of
+ * the coordinates doesn't matter as such, only the distance between
+ * successive coordinates does (eg. when using noise() within a
+ * loop). As a general rule the smaller the difference between coordinates,
+ * the smoother the resulting noise sequence will be. Steps of 0.005-0.03
+ * work best for most applications, but this will differ depending on use.
+ *
+ *
+ * @method noise
+ * @param {Number} x x-coordinate in noise space
+ * @param {Number} y y-coordinate in noise space
+ * @param {Number} z z-coordinate in noise space
+ * @return {Number} Perlin noise value (between 0 and 1) at specified
+ * coordinates
+ * @example
+ *
+ * var xoff = 0.0;
+ *
+ * function draw() {
+ * background(204);
+ * xoff = xoff + .01;
+ * var n = noise(xoff) * width;
+ * line(n, 0, n, height);
+ * }
+ *
+ *
+ *
+ * var noiseScale=0.02;
+ *
+ * function draw() {
+ * background(0);
+ * for (var x=0; x < width; x++) {
+ * var noiseVal = noise((mouseX+x)*noiseScale, mouseY*noiseScale);
+ * stroke(noiseVal*255);
+ * line(x, mouseY+noiseVal*80, x, height);
+ * }
+ * }
+ *
+ *
+ *
+ * @alt
+ * vertical line moves left to right with updating noise values.
+ * horizontal wave pattern effected by mouse x-position & updating noise values.
+ *
+ */
+
+p5.prototype.noise = function(x,y,z) {
+ y = y || 0;
+ z = z || 0;
+
+ if (perlin == null) {
+ perlin = new Array(PERLIN_SIZE + 1);
+ for (var i = 0; i < PERLIN_SIZE + 1; i++) {
+ perlin[i] = Math.random();
+ }
+ }
+
+ if (x<0) { x=-x; }
+ if (y<0) { y=-y; }
+ if (z<0) { z=-z; }
+
+ var xi=Math.floor(x), yi=Math.floor(y), zi=Math.floor(z);
+ var xf = x - xi;
+ var yf = y - yi;
+ var zf = z - zi;
+ var rxf, ryf;
+
+ var r=0;
+ var ampl=0.5;
+
+ var n1,n2,n3;
+
+ for (var o=0; o=1.0) { xi++; xf--; }
+ if (yf>=1.0) { yi++; yf--; }
+ if (zf>=1.0) { zi++; zf--; }
+ }
+ return r;
+};
+
+
+/**
+ *
+ * Adjusts the character and level of detail produced by the Perlin noise
+ * function. Similar to harmonics in physics, noise is computed over
+ * several octaves. Lower octaves contribute more to the output signal and
+ * as such define the overall intensity of the noise, whereas higher octaves
+ * create finer grained details in the noise sequence.
+ *
+ * By default, noise is computed over 4 octaves with each octave contributing
+ * exactly half than its predecessor, starting at 50% strength for the 1st
+ * octave. This falloff amount can be changed by adding an additional function
+ * parameter. Eg. a falloff factor of 0.75 means each octave will now have
+ * 75% impact (25% less) of the previous lower octave. Any value between
+ * 0.0 and 1.0 is valid, however note that values greater than 0.5 might
+ * result in greater than 1.0 values returned by noise().
+ *
+ * By changing these parameters, the signal created by the noise()
+ * function can be adapted to fit very specific needs and characteristics.
+ *
+ * @method noiseDetail
+ * @param {Number} lod number of octaves to be used by the noise
+ * @param {Number} falloff falloff factor for each octave
+ * @example
+ *
+ *
+ *
+ * var noiseVal;
+ * var noiseScale=0.02;
+ *
+ * function setup() {
+ * createCanvas(100,100);
+ * }
+ *
+ * function draw() {
+ * background(0);
+ * for (var y = 0; y < height; y++) {
+ * for (var x = 0; x < width/2; x++) {
+ * noiseDetail(2,0.2);
+ * noiseVal = noise((mouseX+x) * noiseScale,
+ * (mouseY+y) * noiseScale);
+ * stroke(noiseVal*255);
+ * point(x,y);
+ * noiseDetail(8,0.65);
+ * noiseVal = noise((mouseX + x + width/2) * noiseScale,
+ * (mouseY + y) * noiseScale);
+ * stroke(noiseVal*255);
+ * point(x + width/2, y);
+ * }
+ * }
+ * }
+ *
+ *
+ *
+ * @alt
+ * 2 vertical grey smokey patterns affected my mouse x-position and noise.
+ *
+ */
+p5.prototype.noiseDetail = function(lod, falloff) {
+ if (lod>0) { perlin_octaves=lod; }
+ if (falloff>0) { perlin_amp_falloff=falloff; }
+};
+
+/**
+ * Sets the seed value for noise(). By default, noise()
+ * produces different results each time the program is run. Set the
+ * value parameter to a constant to return the same pseudo-random
+ * numbers each time the software is run.
+ *
+ * @method noiseSeed
+ * @param {Number} seed the seed value
+ * @example
+ *
+ * var xoff = 0.0;
+ *
+ * function setup() {
+ * noiseSeed(99);
+ * stroke(0, 10);
+ * }
+ *
+ * function draw() {
+ * xoff = xoff + .01;
+ * var n = noise(xoff) * width;
+ * line(n, 0, n, height);
+ * }
+ *
+ *
+ *
+ * @alt
+ * vertical grey lines drawing in pattern affected by noise.
+ *
+ */
+p5.prototype.noiseSeed = function(seed) {
+ // Linear Congruential Generator
+ // Variant of a Lehman Generator
+ var lcg = (function() {
+ // Set to values from http://en.wikipedia.org/wiki/Numerical_Recipes
+ // m is basically chosen to be large (as it is the max period)
+ // and for its relationships to a and c
+ var m = 4294967296,
+ // a - 1 should be divisible by m's prime factors
+ a = 1664525,
+ // c and m should be co-prime
+ c = 1013904223,
+ seed, z;
+ return {
+ setSeed : function(val) {
+ // pick a random seed if val is undefined or null
+ // the >>> 0 casts the seed to an unsigned 32-bit integer
+ z = seed = (val == null ? Math.random() * m : val) >>> 0;
+ },
+ getSeed : function() {
+ return seed;
+ },
+ rand : function() {
+ // define the recurrence relationship
+ z = (a * z + c) % m;
+ // return a float in [0, 1)
+ // if z = m then z / m = 0 therefore (z % m) / m < 1 always
+ return z / m;
+ }
+ };
+ }());
+
+ lcg.setSeed(seed);
+ perlin = new Array(PERLIN_SIZE + 1);
+ for (var i = 0; i < PERLIN_SIZE + 1; i++) {
+ perlin[i] = lcg.rand();
+ }
+};
+
+module.exports = p5;
+
+},{"../core/core":37}],66:[function(_dereq_,module,exports){
+/**
+ * @module Math
+ * @submodule Math
+ * @requires constants
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+var polarGeometry = _dereq_('./polargeometry');
+var constants = _dereq_('../core/constants');
+
+/**
+ * A class to describe a two or three dimensional vector, specifically
+ * a Euclidean (also known as geometric) vector. A vector is an entity
+ * that has both magnitude and direction. The datatype, however, stores
+ * the components of the vector (x, y for 2D, and x, y, z for 3D). The magnitude
+ * and direction can be accessed via the methods mag() and heading().
+ *
+ * In many of the p5.js examples, you will see p5.Vector used to describe a
+ * position, velocity, or acceleration. For example, if you consider a rectangle
+ * moving across the screen, at any given instant it has a position (a vector
+ * that points from the origin to its location), a velocity (the rate at which
+ * the object's position changes per time unit, expressed as a vector), and
+ * acceleration (the rate at which the object's velocity changes per time
+ * unit, expressed as a vector).
+ *
+ * Since vectors represent groupings of values, we cannot simply use
+ * traditional addition/multiplication/etc. Instead, we'll need to do some
+ * "vector" math, which is made easy by the methods inside the p5.Vector class.
+ *
+ * @class p5.Vector
+ * @constructor
+ * @param {Number} [x] x component of the vector
+ * @param {Number} [y] y component of the vector
+ * @param {Number} [z] z component of the vector
+ * @example
+ *
+ *
+ * var v1 = createVector(40, 50);
+ * var v2 = createVector(40, 50);
+ *
+ * ellipse(v1.x, v1.y, 50, 50);
+ * ellipse(v2.x, v2.y, 50, 50);
+ * v1.add(v2);
+ * ellipse(v1.x, v1.y, 50, 50);
+ *
+ *
+ *
+ * @alt
+ * 2 white ellipses. One center-left the other bottom right and off canvas
+ *
+ */
+p5.Vector = function() {
+ var x,y,z;
+ // This is how it comes in with createVector()
+ if(arguments[0] instanceof p5) {
+ // save reference to p5 if passed in
+ this.p5 = arguments[0];
+ x = arguments[1][0] || 0;
+ y = arguments[1][1] || 0;
+ z = arguments[1][2] || 0;
+ // This is what we'll get with new p5.Vector()
+ } else {
+ x = arguments[0] || 0;
+ y = arguments[1] || 0;
+ z = arguments[2] || 0;
+ }
+ /**
+ * The x component of the vector
+ * @property x
+ * @type {Number}
+ */
+ this.x = x;
+ /**
+ * The y component of the vector
+ * @property y
+ * @type {Number}
+ */
+ this.y = y;
+ /**
+ * The z component of the vector
+ * @property z
+ * @type {Number}
+ */
+ this.z = z;
+};
+
+/**
+ * Returns a string representation of a vector v by calling String(v)
+ * or v.toString(). This method is useful for logging vectors in the
+ * console.
+ * @method toString
+ * @example
+ *
+ * function setup() {
+ * var v = createVector(20,30);
+ * print(String(v)); // prints "p5.Vector Object : [20, 30, 0]"
+ * }
+ *
+ *
+ */
+p5.Vector.prototype.toString = function p5VectorToString() {
+ return 'p5.Vector Object : ['+ this.x +', '+ this.y +', '+ this.z + ']';
+};
+
+/**
+ * Sets the x, y, and z component of the vector using two or three separate
+ * variables, the data from a p5.Vector, or the values from a float array.
+ * @method set
+ *
+ * @param {Number|p5.Vector|Array} [x] the x component of the vector or a
+ * p5.Vector or an Array
+ * @param {Number} [y] the y component of the vector
+ * @param {Number} [z] the z component of the vector
+ * @example
+ *
+ *
+ * function setup() {
+ * var v = createVector(1, 2, 3);
+ * v.set(4,5,6); // Sets vector to [4, 5, 6]
+ *
+ * var v1 = createVector(0, 0, 0);
+ * var arr = [1, 2, 3];
+ * v1.set(arr); // Sets vector to [1, 2, 3]
+ * }
+ *
+ *
+ */
+p5.Vector.prototype.set = function (x, y, z) {
+ if (x instanceof p5.Vector) {
+ this.x = x.x || 0;
+ this.y = x.y || 0;
+ this.z = x.z || 0;
+ return this;
+ }
+ if (x instanceof Array) {
+ this.x = x[0] || 0;
+ this.y = x[1] || 0;
+ this.z = x[2] || 0;
+ return this;
+ }
+ this.x = x || 0;
+ this.y = y || 0;
+ this.z = z || 0;
+ return this;
+};
+
+/**
+ * Gets a copy of the vector, returns a p5.Vector object.
+ *
+ * @method copy
+ * @return {p5.Vector} the copy of the p5.Vector object
+ * @example
+ *
+ *
+ * var v1 = createVector(1, 2, 3);
+ * var v2 = v1.copy();
+ * print(v1.x == v2.x && v1.y == v2.y && v1.z == v2.z);
+ * // Prints "true"
+ *
+ *
+ */
+p5.Vector.prototype.copy = function () {
+ if (this.p5) {
+ return new p5.Vector(this.p5,[this.x, this.y, this.z]);
+ } else {
+ return new p5.Vector(this.x,this.y,this.z);
+ }
+};
+
+/**
+ * Adds x, y, and z components to a vector, adds one vector to another, or
+ * adds two independent vectors together. The version of the method that adds
+ * two vectors together is a static method and returns a p5.Vector, the others
+ * acts directly on the vector. See the examples for more context.
+ *
+ * @method add
+ * @chainable
+ * @param {Number|p5.Vector|Array} x the x component of the vector to be
+ * added or a p5.Vector or an Array
+ * @param {Number} [y] the y component of the vector to be
+ * added
+ * @param {Number} [z] the z component of the vector to be
+ * added
+ * @return {p5.Vector} the p5.Vector object.
+ * @example
+ *
+ *
+ * var v = createVector(1, 2, 3);
+ * v.add(4,5,6);
+ * // v's compnents are set to [5, 7, 9]
+ *
+ *
+ *
+ *
+ * // Static method
+ * var v1 = createVector(1, 2, 3);
+ * var v2 = createVector(2, 3, 4);
+ *
+ * var v3 = p5.Vector.add(v1, v2);
+ * // v3 has components [3, 5, 7]
+ *
+ *
+ */
+p5.Vector.prototype.add = function (x, y, z) {
+ if (x instanceof p5.Vector) {
+ this.x += x.x || 0;
+ this.y += x.y || 0;
+ this.z += x.z || 0;
+ return this;
+ }
+ if (x instanceof Array) {
+ this.x += x[0] || 0;
+ this.y += x[1] || 0;
+ this.z += x[2] || 0;
+ return this;
+ }
+ this.x += x || 0;
+ this.y += y || 0;
+ this.z += z || 0;
+ return this;
+};
+
+/**
+ * Subtracts x, y, and z components from a vector, subtracts one vector from
+ * another, or subtracts two independent vectors. The version of the method
+ * that subtracts two vectors is a static method and returns a p5.Vector, the
+ * other acts directly on the vector. See the examples for more context.
+ *
+ * @method sub
+ * @chainable
+ * @param {Number|p5.Vector|Array} x the x component of the vector or a
+ * p5.Vector or an Array
+ * @param {Number} [y] the y component of the vector
+ * @param {Number} [z] the z component of the vector
+ * @return {p5.Vector} p5.Vector object.
+ * @example
+ *
+ *
+ * var v = createVector(4, 5, 6);
+ * v.sub(1, 1, 1);
+ * // v's compnents are set to [3, 4, 5]
+ *
+ *
+ *
+ *
+ *
+ * // Static method
+ * var v1 = createVector(2, 3, 4);
+ * var v2 = createVector(1, 2, 3);
+ *
+ * var v3 = p5.Vector.sub(v1, v2);
+ * // v3 has compnents [1, 1, 1]
+ *
+ *
+ */
+p5.Vector.prototype.sub = function (x, y, z) {
+ if (x instanceof p5.Vector) {
+ this.x -= x.x || 0;
+ this.y -= x.y || 0;
+ this.z -= x.z || 0;
+ return this;
+ }
+ if (x instanceof Array) {
+ this.x -= x[0] || 0;
+ this.y -= x[1] || 0;
+ this.z -= x[2] || 0;
+ return this;
+ }
+ this.x -= x || 0;
+ this.y -= y || 0;
+ this.z -= z || 0;
+ return this;
+};
+
+/**
+ * Multiply the vector by a scalar. The static version of this method
+ * creates a new p5.Vector while the non static version acts on the vector
+ * directly. See the examples for more context.
+ *
+ * @method mult
+ * @chainable
+ * @param {Number} n the number to multiply with the vector
+ * @return {p5.Vector} a reference to the p5.Vector object (allow chaining)
+ * @example
+ *
+ *
+ * var v = createVector(1, 2, 3);
+ * v.mult(2);
+ * // v's compnents are set to [2, 4, 6]
+ *
+ *
+ *
+ *
+ *
+ * // Static method
+ * var v1 = createVector(1, 2, 3);
+ * var v2 = p5.Vector.mult(v1, 2);
+ * // v2 has compnents [2, 4, 6]
+ *
+ *
+ */
+p5.Vector.prototype.mult = function (n) {
+ this.x *= n || 0;
+ this.y *= n || 0;
+ this.z *= n || 0;
+ return this;
+};
+
+/**
+ * Divide the vector by a scalar. The static version of this method creates a
+ * new p5.Vector while the non static version acts on the vector directly.
+ * See the examples for more context.
+ *
+ * @method div
+ * @chainable
+ * @param {number} n the number to divide the vector by
+ * @return {p5.Vector} a reference to the p5.Vector object (allow chaining)
+ * @example
+ *
+ *
+ * var v = createVector(6, 4, 2);
+ * v.div(2); //v's compnents are set to [3, 2, 1]
+ *
+ *
+ *
+ *
+ *
+ * // Static method
+ * var v1 = createVector(6, 4, 2);
+ * var v2 = p5.Vector.div(v, 2);
+ * // v2 has compnents [3, 2, 1]
+ *
+ *
+ */
+p5.Vector.prototype.div = function (n) {
+ this.x /= n;
+ this.y /= n;
+ this.z /= n;
+ return this;
+};
+
+/**
+ * Calculates the magnitude (length) of the vector and returns the result as
+ * a float (this is simply the equation sqrt(x*x + y*y + z*z).)
+ *
+ * @method mag
+ * @return {Number} magnitude of the vector
+ * @example
+ *
+ *
+ * var v = createVector(20.0, 30.0, 40.0);
+ * var m = v.mag();
+ * print(m); // Prints "53.85164807134504"
+ *
+ *
+ */
+p5.Vector.prototype.mag = function () {
+ return Math.sqrt(this.magSq());
+};
+
+/**
+ * Calculates the squared magnitude of the vector and returns the result
+ * as a float (this is simply the equation (x*x + y*y + z*z).)
+ * Faster if the real length is not required in the
+ * case of comparing vectors, etc.
+ *
+ * @method magSq
+ * @return {number} squared magnitude of the vector
+ * @example
+ *
+ *
+ * // Static method
+ * var v1 = createVector(6, 4, 2);
+ * print(v1.magSq()); // Prints "56"
+ *
+ *
+ */
+p5.Vector.prototype.magSq = function () {
+ var x = this.x, y = this.y, z = this.z;
+ return (x * x + y * y + z * z);
+};
+
+/**
+ * Calculates the dot product of two vectors. The version of the method
+ * that computes the dot product of two independent vectors is a static
+ * method. See the examples for more context.
+ *
+ *
+ * @method dot
+ * @param {Number|p5.Vector} x x component of the vector or a p5.Vector
+ * @param {Number} [y] y component of the vector
+ * @param {Number} [z] z component of the vector
+ * @return {Number} the dot product
+ *
+ * @example
+ *
+ *
+ * var v1 = createVector(1, 2, 3);
+ * var v2 = createVector(2, 3, 4);
+ *
+ * print(v1.dot(v2)); // Prints "20"
+ *
+ *
+ *
+ *
+ *
+ * //Static method
+ * var v1 = createVector(1, 2, 3);
+ * var v2 = createVector(3, 2, 1);
+ * print (p5.Vector.dot(v1, v2)); // Prints "10"
+ *
+ *
+ */
+p5.Vector.prototype.dot = function (x, y, z) {
+ if (x instanceof p5.Vector) {
+ return this.dot(x.x, x.y, x.z);
+ }
+ return this.x * (x || 0) +
+ this.y * (y || 0) +
+ this.z * (z || 0);
+};
+
+/**
+ * Calculates and returns a vector composed of the cross product between
+ * two vectors. Both the static and non static methods return a new p5.Vector.
+ * See the examples for more context.
+ *
+ * @method cross
+ * @param {p5.Vector} v p5.Vector to be crossed
+ * @return {p5.Vector} p5.Vector composed of cross product
+ * @example
+ *
+ *
+ * var v1 = createVector(1, 2, 3);
+ * var v2 = createVector(1, 2, 3);
+ *
+ * v1.cross(v2); // v's components are [0, 0, 0]
+ *
+ *
+ *
+ *
+ *
+ * // Static method
+ * var v1 = createVector(1, 0, 0);
+ * var v2 = createVector(0, 1, 0);
+ *
+ * var crossProduct = p5.Vector.cross(v1, v2);
+ * // crossProduct has components [0, 0, 1]
+ *
+ *
+ */
+p5.Vector.prototype.cross = function (v) {
+ var x = this.y * v.z - this.z * v.y;
+ var y = this.z * v.x - this.x * v.z;
+ var z = this.x * v.y - this.y * v.x;
+ if (this.p5) {
+ return new p5.Vector(this.p5,[x,y,z]);
+ } else {
+ return new p5.Vector(x,y,z);
+ }
+};
+
+/**
+ * Calculates the Euclidean distance between two points (considering a
+ * point as a vector object).
+ *
+ * @method dist
+ * @param {p5.Vector} v the x, y, and z coordinates of a p5.Vector
+ * @return {Number} the distance
+ * @example
+ *
+ *
+ * var v1 = createVector(1, 0, 0);
+ * var v2 = createVector(0, 1, 0);
+ *
+ * var distance = v1.dist(v2); // distance is 1.4142...
+ *
+ *
+ *
+ *
+ * // Static method
+ * var v1 = createVector(1, 0, 0);
+ * var v2 = createVector(0, 1, 0);
+ *
+ * var distance = p5.Vector.dist(v1,v2);
+ * // distance is 1.4142...
+ *
+ *
+ */
+p5.Vector.prototype.dist = function (v) {
+ var d = v.copy().sub(this);
+ return d.mag();
+};
+
+/**
+ * Normalize the vector to length 1 (make it a unit vector).
+ *
+ * @method normalize
+ * @return {p5.Vector} normalized p5.Vector
+ * @example
+ *
+ *
+ * var v = createVector(10, 20, 2);
+ * // v has compnents [10.0, 20.0, 2.0]
+ * v.normalize();
+ * // v's compnents are set to
+ * // [0.4454354, 0.8908708, 0.089087084]
+ *
+ *
+ *
+ */
+p5.Vector.prototype.normalize = function () {
+ return this.mag() === 0 ? this : this.div(this.mag());
+};
+
+/**
+ * Limit the magnitude of this vector to the value used for the max
+ * parameter.
+ *
+ * @method limit
+ * @param {Number} max the maximum magnitude for the vector
+ * @return {p5.Vector} the modified p5.Vector
+ * @example
+ *
+ *
+ * var v = createVector(10, 20, 2);
+ * // v has compnents [10.0, 20.0, 2.0]
+ * v.limit(5);
+ * // v's compnents are set to
+ * // [2.2271771, 4.4543543, 0.4454354]
+ *
+ *
+ */
+p5.Vector.prototype.limit = function (max) {
+ var mSq = this.magSq();
+ if(mSq > max*max) {
+ this.div(Math.sqrt(mSq)); //normalize it
+ this.mult(max);
+ }
+ return this;
+};
+
+/**
+ * Set the magnitude of this vector to the value used for the len
+ * parameter.
+ *
+ * @method setMag
+ * @param {number} len the new length for this vector
+ * @return {p5.Vector} the modified p5.Vector
+ * @example
+ *
+ *
+ * var v1 = createVector(10, 20, 2);
+ * // v has compnents [10.0, 20.0, 2.0]
+ * v1.setMag(10);
+ * // v's compnents are set to [6.0, 8.0, 0.0]
+ *
+ *
+ */
+p5.Vector.prototype.setMag = function (n) {
+ return this.normalize().mult(n);
+};
+
+/**
+ * Calculate the angle of rotation for this vector (only 2D vectors)
+ *
+ * @method heading
+ * @return {Number} the angle of rotation
+ * @example
+ *
+ * function setup() {
+ * var v1 = createVector(30,50);
+ * print(v1.heading()); // 1.0303768265243125
+ *
+ * var v1 = createVector(40,50);
+ * print(v1.heading()); // 0.8960553845713439
+ *
+ * var v1 = createVector(30,70);
+ * print(v1.heading()); // 1.1659045405098132
+ * }
+ *
+ */
+p5.Vector.prototype.heading = function () {
+ var h = Math.atan2(this.y, this.x);
+ if (this.p5) {
+ if (this.p5._angleMode === constants.RADIANS) {
+ return h;
+ } else {
+ return polarGeometry.radiansToDegrees(h);
+ }
+ } else {
+ return h;
+ }
+};
+
+/**
+ * Rotate the vector by an angle (only 2D vectors), magnitude remains the
+ * same
+ *
+ * @method rotate
+ * @param {number} angle the angle of rotation
+ * @return {p5.Vector} the modified p5.Vector
+ * @example
+ *
+ *
+ * var v = createVector(10.0, 20.0);
+ * // v has compnents [10.0, 20.0, 0.0]
+ * v.rotate(HALF_PI);
+ * // v's compnents are set to [-20.0, 9.999999, 0.0]
+ *
+ *
+ */
+p5.Vector.prototype.rotate = function (a) {
+ if (this.p5) {
+ if (this.p5._angleMode === constants.DEGREES) {
+ a = polarGeometry.degreesToRadians(a);
+ }
+ }
+ var newHeading = this.heading() + a;
+ var mag = this.mag();
+ this.x = Math.cos(newHeading) * mag;
+ this.y = Math.sin(newHeading) * mag;
+ return this;
+};
+
+/**
+ * Linear interpolate the vector to another vector
+ *
+ * @method lerp
+ * @param {p5.Vector} x the x component or the p5.Vector to lerp to
+ * @param {p5.Vector} [y] y the y component
+ * @param {p5.Vector} [z] z the z component
+ * @param {Number} amt the amount of interpolation; some value between 0.0
+ * (old vector) and 1.0 (new vector). 0.1 is very near
+ * the new vector. 0.5 is halfway in between.
+ * @return {p5.Vector} the modified p5.Vector
+ * @example
+ *
+ *
+ * var v = createVector(1, 1, 0);
+ *
+ * v.lerp(3, 3, 0, 0.5); // v now has components [2,2,0]
+ *
+ *
+ *
+ *
+ *
+ * var v1 = createVector(0, 0, 0);
+ * var v2 = createVector(100, 100, 0);
+ *
+ * var v3 = p5.Vector.lerp(v1, v2, 0.5);
+ * // v3 has components [50,50,0]
+ *
+ *
+ */
+p5.Vector.prototype.lerp = function (x, y, z, amt) {
+ if (x instanceof p5.Vector) {
+ return this.lerp(x.x, x.y, x.z, y);
+ }
+ this.x += (x - this.x) * amt || 0;
+ this.y += (y - this.y) * amt || 0;
+ this.z += (z - this.z) * amt || 0;
+ return this;
+};
+
+/**
+ * Return a representation of this vector as a float array. This is only
+ * for temporary use. If used in any other fashion, the contents should be
+ * copied by using the p5.Vector.copy() method to copy into your own
+ * array.
+ *
+ * @method array
+ * @return {Array} an Array with the 3 values
+ * @example
+ *
+ * function setup() {
+ * var v = createVector(20,30);
+ * print(v.array()); // Prints : Array [20, 30, 0]
+ * }
+ *
+ *
+ *
+ * var v = createVector(10.0, 20.0, 30.0);
+ * var f = v.array();
+ * print(f[0]); // Prints "10.0"
+ * print(f[1]); // Prints "20.0"
+ * print(f[2]); // Prints "30.0"
+ *
+ *
+ */
+p5.Vector.prototype.array = function () {
+ return [this.x || 0, this.y || 0, this.z || 0];
+};
+
+/**
+ * Equality check against a p5.Vector
+ *
+ * @method equals
+ * @param {Number|p5.Vector|Array} [x] the x component of the vector or a
+ * p5.Vector or an Array
+ * @param {Number} [y] the y component of the vector
+ * @param {Number} [z] the z component of the vector
+ * @return {Boolean} whether the vectors are equals
+ * @example
+ *
+ * v1 = createVector(5,10,20);
+ * v2 = createVector(5,10,20);
+ * v3 = createVector(13,10,19);
+ *
+ * print(v1.equals(v2.x,v2.y,v2.z)); // true
+ * print(v1.equals(v3.x,v3.y,v3.z)); // false
+ *
+ *
+ *
+ * var v1 = createVector(10.0, 20.0, 30.0);
+ * var v2 = createVector(10.0, 20.0, 30.0);
+ * var v3 = createVector(0.0, 0.0, 0.0);
+ * print (v1.equals(v2)) // true
+ * print (v1.equals(v3)) // false
+ *
+ *
+ */
+p5.Vector.prototype.equals = function (x, y, z) {
+ var a, b, c;
+ if (x instanceof p5.Vector) {
+ a = x.x || 0;
+ b = x.y || 0;
+ c = x.z || 0;
+ } else if (x instanceof Array) {
+ a = x[0] || 0;
+ b = x[1] || 0;
+ c = x[2] || 0;
+ } else {
+ a = x || 0;
+ b = y || 0;
+ c = z || 0;
+ }
+ return this.x === a && this.y === b && this.z === c;
+};
+
+
+// Static Methods
+
+
+/**
+ * Make a new 2D unit vector from an angle
+ *
+ * @method fromAngle
+ * @static
+ * @param {Number} angle the desired angle
+ * @return {p5.Vector} the new p5.Vector object
+ * @example
+ *
+ *
+ * function draw() {
+ * background (200);
+ *
+ * // Create a variable, proportional to the mouseX,
+ * // varying from 0-360, to represent an angle in degrees.
+ * angleMode(DEGREES);
+ * var myDegrees = map(mouseX, 0,width, 0,360);
+ *
+ * // Display that variable in an onscreen text.
+ * // (Note the nfc() function to truncate additional decimal places,
+ * // and the "\xB0" character for the degree symbol.)
+ * var readout = "angle = " + nfc(myDegrees,1,1) + "\xB0"
+ * noStroke();
+ * fill (0);
+ * text (readout, 5, 15);
+ *
+ * // Create a p5.Vector using the fromAngle function,
+ * // and extract its x and y components.
+ * var v = p5.Vector.fromAngle(radians(myDegrees));
+ * var vx = v.x;
+ * var vy = v.y;
+ *
+ * push();
+ * translate (width/2, height/2);
+ * noFill();
+ * stroke (150);
+ * line (0,0, 30,0);
+ * stroke (0);
+ * line (0,0, 30*vx, 30*vy);
+ * pop()
+ * }
+ *
+ *
+ */
+p5.Vector.fromAngle = function(angle) {
+ if (this.p5) {
+ if (this.p5._angleMode === constants.DEGREES) {
+ angle = polarGeometry.degreesToRadians(angle);
+ }
+ }
+ if (this.p5) {
+ return new p5.Vector(this.p5,[Math.cos(angle),Math.sin(angle),0]);
+ } else {
+ return new p5.Vector(Math.cos(angle),Math.sin(angle),0);
+ }
+};
+
+/**
+ * Make a new 2D unit vector from a random angle
+ *
+ * @method random2D
+ * @static
+ * @return {p5.Vector} the new p5.Vector object
+ * @example
+ *
+ *
+ * var v = p5.Vector.random2D();
+ * // May make v's attributes something like:
+ * // [0.61554617, -0.51195765, 0.0] or
+ * // [-0.4695841, -0.14366731, 0.0] or
+ * // [0.6091097, -0.22805278, 0.0]
+ *
+ *
+ */
+p5.Vector.random2D = function () {
+ var angle;
+ // A lot of nonsense to determine if we know about a
+ // p5 sketch and whether we should make a random angle in degrees or radians
+ if (this.p5) {
+ if (this.p5._angleMode === constants.DEGREES) {
+ angle = this.p5.random(360);
+ } else {
+ angle = this.p5.random(constants.TWO_PI);
+ }
+ } else {
+ angle = Math.random()*Math.PI*2;
+ }
+ return this.fromAngle(angle);
+};
+
+/**
+ * Make a new random 3D unit vector.
+ *
+ * @method random3D
+ * @static
+ * @return {p5.Vector} the new p5.Vector object
+ * @example
+ *
+ *
+ * var v = p5.Vector.random3D();
+ * // May make v's attributes something like:
+ * // [0.61554617, -0.51195765, 0.599168] or
+ * // [-0.4695841, -0.14366731, -0.8711202] or
+ * // [0.6091097, -0.22805278, -0.7595902]
+ *
+ *
+ */
+p5.Vector.random3D = function () {
+ var angle,vz;
+ // If we know about p5
+ if (this.p5) {
+ angle = this.p5.random(0,constants.TWO_PI);
+ vz = this.p5.random(-1,1);
+ } else {
+ angle = Math.random()*Math.PI*2;
+ vz = Math.random()*2-1;
+ }
+ var vx = Math.sqrt(1-vz*vz)*Math.cos(angle);
+ var vy = Math.sqrt(1-vz*vz)*Math.sin(angle);
+ if (this.p5) {
+ return new p5.Vector(this.p5,[vx,vy,vz]);
+ } else {
+ return new p5.Vector(vx,vy,vz);
+ }
+};
+
+
+/**
+ * Adds two vectors together and returns a new one.
+ *
+ * @static
+ * @param {p5.Vector} v1 a p5.Vector to add
+ * @param {p5.Vector} v2 a p5.Vector to add
+ * @param {p5.Vector} target if undefined a new vector will be created
+ * @return {p5.Vector} the resulting p5.Vector
+ *
+ */
+
+p5.Vector.add = function (v1, v2, target) {
+ if (!target) {
+ target = v1.copy();
+ } else {
+ target.set(v1);
+ }
+ target.add(v2);
+ return target;
+};
+
+/**
+ * Subtracts one p5.Vector from another and returns a new one. The second
+ * vector (v2) is subtracted from the first (v1), resulting in v1-v2.
+ *
+ * @static
+ * @param {p5.Vector} v1 a p5.Vector to subtract from
+ * @param {p5.Vector} v2 a p5.Vector to subtract
+ * @param {p5.Vector} target if undefined a new vector will be created
+ * @return {p5.Vector} the resulting p5.Vector
+ */
+
+p5.Vector.sub = function (v1, v2, target) {
+ if (!target) {
+ target = v1.copy();
+ } else {
+ target.set(v1);
+ }
+ target.sub(v2);
+ return target;
+};
+
+
+/**
+ * Multiplies a vector by a scalar and returns a new vector.
+ *
+ * @static
+ * @param {p5.Vector} v the p5.Vector to multiply
+ * @param {Number} n the scalar
+ * @param {p5.Vector} target if undefined a new vector will be created
+ * @return {p5.Vector} the resulting new p5.Vector
+ */
+p5.Vector.mult = function (v, n, target) {
+ if (!target) {
+ target = v.copy();
+ } else {
+ target.set(v);
+ }
+ target.mult(n);
+ return target;
+};
+
+/**
+ * Divides a vector by a scalar and returns a new vector.
+ *
+ * @static
+ * @param {p5.Vector} v the p5.Vector to divide
+ * @param {Number} n the scalar
+ * @param {p5.Vector} target if undefined a new vector will be created
+ * @return {p5.Vector} the resulting new p5.Vector
+ */
+p5.Vector.div = function (v, n, target) {
+ if (!target) {
+ target = v.copy();
+ } else {
+ target.set(v);
+ }
+ target.div(n);
+ return target;
+};
+
+
+/**
+ * Calculates the dot product of two vectors.
+ *
+ * @static
+ * @param {p5.Vector} v1 the first p5.Vector
+ * @param {p5.Vector} v2 the second p5.Vector
+ * @return {Number} the dot product
+ */
+p5.Vector.dot = function (v1, v2) {
+ return v1.dot(v2);
+};
+
+/**
+ * Calculates the cross product of two vectors.
+ *
+ * @static
+ * @param {p5.Vector} v1 the first p5.Vector
+ * @param {p5.Vector} v2 the second p5.Vector
+ * @return {Number} the cross product
+ */
+p5.Vector.cross = function (v1, v2) {
+ return v1.cross(v2);
+};
+
+/**
+ * Calculates the Euclidean distance between two points (considering a
+ * point as a vector object).
+ *
+ * @static
+ * @param {p5.Vector} v1 the first p5.Vector
+ * @param {p5.Vector} v2 the second p5.Vector
+ * @return {Number} the distance
+ */
+p5.Vector.dist = function (v1,v2) {
+ return v1.dist(v2);
+};
+
+/**
+ * Linear interpolate a vector to another vector and return the result as a
+ * new vector.
+ *
+ * @static
+ * @param {p5.Vector} v1 a starting p5.Vector
+ * @param {p5.Vector} v2 the p5.Vector to lerp to
+ * @param {Number} the amount of interpolation; some value between 0.0
+ * (old vector) and 1.0 (new vector). 0.1 is very near
+ * the new vector. 0.5 is halfway in between.
+ */
+p5.Vector.lerp = function (v1, v2, amt, target) {
+ if (!target) {
+ target = v1.copy();
+ } else {
+ target.set(v1);
+ }
+ target.lerp(v2, amt);
+ return target;
+};
+
+/**
+ * Calculates and returns the angle (in radians) between two vectors.
+ * @method angleBetween
+ * @static
+ * @param {p5.Vector} v1 the x, y, and z components of a p5.Vector
+ * @param {p5.Vector} v2 the x, y, and z components of a p5.Vector
+ * @return {Number} the angle between (in radians)
+ * @example
+ *
+ *
+ * var v1 = createVector(1, 0, 0);
+ * var v2 = createVector(0, 1, 0);
+ *
+ * var angle = p5.Vector.angleBetween(v1, v2);
+ * // angle is PI/2
+ *
+ *
+ */
+p5.Vector.angleBetween = function (v1, v2) {
+ var angle = Math.acos(v1.dot(v2) / (v1.mag() * v2.mag()));
+ if (this.p5) {
+ if (this.p5._angleMode === constants.DEGREES) {
+ angle = polarGeometry.radiansToDegrees(angle);
+ }
+ }
+ return angle;
+};
+
+/**
+ * @static
+ */
+p5.Vector.mag = function (vecT){
+ var x = vecT.x,
+ y = vecT.y,
+ z = vecT.z;
+ var magSq = x * x + y * y + z * z;
+ return Math.sqrt(magSq);
+};
+
+module.exports = p5.Vector;
+
+},{"../core/constants":36,"../core/core":37,"./polargeometry":67}],67:[function(_dereq_,module,exports){
+
+module.exports = {
+
+ degreesToRadians: function(x) {
+ return 2 * Math.PI * x / 360;
+ },
+
+ radiansToDegrees: function(x) {
+ return 360 * x / (2 * Math.PI);
+ }
+
+};
+
+},{}],68:[function(_dereq_,module,exports){
+/**
+ * @module Math
+ * @submodule Random
+ * @for p5
+ * @requires core
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+
+var seeded = false;
+
+// Linear Congruential Generator
+// Variant of a Lehman Generator
+var lcg = (function() {
+ // Set to values from http://en.wikipedia.org/wiki/Numerical_Recipes
+ // m is basically chosen to be large (as it is the max period)
+ // and for its relationships to a and c
+ var m = 4294967296,
+ // a - 1 should be divisible by m's prime factors
+ a = 1664525,
+ // c and m should be co-prime
+ c = 1013904223,
+ seed, z;
+ return {
+ setSeed : function(val) {
+ // pick a random seed if val is undefined or null
+ // the >>> 0 casts the seed to an unsigned 32-bit integer
+ z = seed = (val == null ? Math.random() * m : val) >>> 0;
+ },
+ getSeed : function() {
+ return seed;
+ },
+ rand : function() {
+ // define the recurrence relationship
+ z = (a * z + c) % m;
+ // return a float in [0, 1)
+ // if z = m then z / m = 0 therefore (z % m) / m < 1 always
+ return z / m;
+ }
+ };
+}());
+
+/**
+ * Sets the seed value for random().
+ *
+ * By default, random() produces different results each time the program
+ * is run. Set the seed parameter to a constant to return the same
+ * pseudo-random numbers each time the software is run.
+ *
+ * @method randomSeed
+ * @param {Number} seed the seed value
+ * @example
+ *
+ *
+ * randomSeed(99);
+ * for (var i=0; i < 100; i++) {
+ * var r = random(0, 255);
+ * stroke(r);
+ * line(i, 0, i, 100);
+ * }
+ *
+ *
+ *
+ * @alt
+ * many vertical lines drawn in white, black or grey.
+ *
+ */
+p5.prototype.randomSeed = function(seed) {
+ lcg.setSeed(seed);
+ seeded = true;
+};
+
+/**
+ * Return a random floating-point number.
+ *
+ * Takes either 0, 1 or 2 arguments.
+ *
+ * If no argument is given, returns a random number from 0
+ * up to (but not including) 1.
+ *
+ * If one argument is given and it is a number, returns a random number from 0
+ * up to (but not including) the number.
+ *
+ * If one argument is given and it is an array, returns a random element from
+ * that array.
+ *
+ * If two arguments are given, returns a random number from the
+ * first argument up to (but not including) the second argument.
+ *
+ * @method random
+ * @param {Number} [min] the lower bound (inclusive)
+ * @param {Number} [max] the upper bound (exclusive)
+ * @return {Number|mixed} the random number or a random element in choices
+ * @example
+ *
+ *
+ * for (var i = 0; i < 100; i++) {
+ * var r = random(50);
+ * stroke(r*5);
+ * line(50, i, 50+r, i);
+ * }
+ *
+ *
+ *
+ *
+ * for (var i = 0; i < 100; i++) {
+ * var r = random(-50, 50);
+ * line(50,i,50+r,i);
+ * }
+ *
+ *
+ *
+ *
+ * // Get a random element from an array using the random(Array) syntax
+ * var words = [ "apple", "bear", "cat", "dog" ];
+ * var word = random(words); // select random word
+ * text(word,10,50); // draw the word
+ *
+ *
+ *
+ * @alt
+ * 100 horizontal lines from center canvas to right. size+fill change each time
+ * 100 horizontal lines from center of canvas. height & side change each render
+ * word displayed at random. Either apple, bear, cat, or dog
+ *
+ */
+/**
+ * @method random
+ * @param {Array} choices the array to choose from
+ * @return {mixed} the random element from the array
+ * @example
+ */
+p5.prototype.random = function (min, max) {
+
+ var rand;
+
+ if (seeded) {
+ rand = lcg.rand();
+ } else {
+ rand = Math.random();
+ }
+ if (typeof min === 'undefined') {
+ return rand;
+ } else
+ if (typeof max === 'undefined') {
+ if (min instanceof Array) {
+ return min[Math.floor(rand * min.length)];
+ } else {
+ return rand * min;
+ }
+ } else {
+ if (min > max) {
+ var tmp = min;
+ min = max;
+ max = tmp;
+ }
+
+ return rand * (max-min) + min;
+ }
+};
+
+
+/**
+ *
+ * Returns a random number fitting a Gaussian, or
+ * normal, distribution. There is theoretically no minimum or maximum
+ * value that randomGaussian() might return. Rather, there is
+ * just a very low probability that values far from the mean will be
+ * returned; and a higher probability that numbers near the mean will
+ * be returned.
+ *
+ * Takes either 0, 1 or 2 arguments.
+ * If no args, returns a mean of 0 and standard deviation of 1.
+ * If one arg, that arg is the mean (standard deviation is 1).
+ * If two args, first is mean, second is standard deviation.
+ *
+ * @method randomGaussian
+ * @param {Number} mean the mean
+ * @param {Number} sd the standard deviation
+ * @return {Number} the random number
+ * @example
+ *
+ * for (var y = 0; y < 100; y++) {
+ * var x = randomGaussian(50,15);
+ * line(50, y, x, y);
+ *}
+ *
+ *
+ *
+ *
+ *var distribution = new Array(360);
+ *
+ *function setup() {
+ * createCanvas(100, 100);
+ * for (var i = 0; i < distribution.length; i++) {
+ * distribution[i] = floor(randomGaussian(0,15));
+ * }
+ *}
+ *
+ *function draw() {
+ * background(204);
+ *
+ * translate(width/2, width/2);
+ *
+ * for (var i = 0; i < distribution.length; i++) {
+ * rotate(TWO_PI/distribution.length);
+ * stroke(0);
+ * var dist = abs(distribution[i]);
+ * line(0, 0, dist, 0);
+ * }
+ *}
+ *
+ *
+ * @alt
+ * 100 horizontal lines from center of canvas. height & side change each render
+ * black lines radiate from center of canvas. size determined each render
+ */
+var y2;
+var previous = false;
+p5.prototype.randomGaussian = function(mean, sd) {
+ var y1,x1,x2,w;
+ if (previous) {
+ y1 = y2;
+ previous = false;
+ } else {
+ do {
+ x1 = this.random(2) - 1;
+ x2 = this.random(2) - 1;
+ w = x1 * x1 + x2 * x2;
+ } while (w >= 1);
+ w = Math.sqrt((-2 * Math.log(w))/w);
+ y1 = x1 * w;
+ y2 = x2 * w;
+ previous = true;
+ }
+
+ var m = mean || 0;
+ var s = sd || 1;
+ return y1*s + m;
+};
+
+module.exports = p5;
+
+},{"../core/core":37}],69:[function(_dereq_,module,exports){
+/**
+ * @module Math
+ * @submodule Trigonometry
+ * @for p5
+ * @requires core
+ * @requires polargeometry
+ * @requires constants
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+var polarGeometry = _dereq_('./polargeometry');
+var constants = _dereq_('../core/constants');
+
+p5.prototype._angleMode = constants.RADIANS;
+
+/**
+ * The inverse of cos(), returns the arc cosine of a value. This function
+ * expects the values in the range of -1 to 1 and values are returned in
+ * the range 0 to PI (3.1415927).
+ *
+ * @method acos
+ * @param {Number} value the value whose arc cosine is to be returned
+ * @return {Number} the arc cosine of the given value
+ *
+ * @example
+ *
+ *
+ * var a = PI;
+ * var c = cos(a);
+ * var ac = acos(c);
+ * // Prints: "3.1415927 : -1.0 : 3.1415927"
+ * print(a + " : " + c + " : " + ac);
+ *
+ *
+ *
+ *
+ *
+ * var a = PI + PI/4.0;
+ * var c = cos(a);
+ * var ac = acos(c);
+ * // Prints: "3.926991 : -0.70710665 : 2.3561943"
+ * print(a + " : " + c + " : " + ac);
+ *
+ *
+ */
+p5.prototype.acos = function(ratio) {
+ if (this._angleMode === constants.RADIANS) {
+ return Math.acos(ratio);
+ } else {
+ return polarGeometry.radiansToDegrees(Math.acos(ratio));
+ }
+};
+
+/**
+ * The inverse of sin(), returns the arc sine of a value. This function
+ * expects the values in the range of -1 to 1 and values are returned
+ * in the range -PI/2 to PI/2.
+ *
+ * @method asin
+ * @param {Number} value the value whose arc sine is to be returned
+ * @return {Number} the arc sine of the given value
+ *
+ * @example
+ *
+ *
+ * var a = PI + PI/3;
+ * var s = sin(a);
+ * var as = asin(s);
+ * // Prints: "1.0471976 : 0.86602545 : 1.0471976"
+ * print(a + " : " + s + " : " + as);
+ *
+ *
+ *
+ *
+ *
+ * var a = PI + PI/3.0;
+ * var s = sin(a);
+ * var as = asin(s);
+ * // Prints: "4.1887903 : -0.86602545 : -1.0471976"
+ * print(a + " : " + s + " : " + as);
+ *
+ *
+ *
+ */
+p5.prototype.asin = function(ratio) {
+ if (this._angleMode === constants.RADIANS) {
+ return Math.asin(ratio);
+ } else {
+ return polarGeometry.radiansToDegrees(Math.asin(ratio));
+ }
+};
+
+/**
+ * The inverse of tan(), returns the arc tangent of a value. This function
+ * expects the values in the range of -Infinity to Infinity (exclusive) and
+ * values are returned in the range -PI/2 to PI/2.
+ *
+ * @method atan
+ * @param {Number} value the value whose arc tangent is to be returned
+ * @return {Number} the arc tangent of the given value
+ *
+ * @example
+ *
+ *
+ * var a = PI + PI/3;
+ * var t = tan(a);
+ * var at = atan(t);
+ * // Prints: "1.0471976 : 1.7320509 : 1.0471976"
+ * print(a + " : " + t + " : " + at);
+ *
+ *
+ *
+ *
+ *
+ * var a = PI + PI/3.0;
+ * var t = tan(a);
+ * var at = atan(t);
+ * // Prints: "4.1887903 : 1.7320513 : 1.0471977"
+ * print(a + " : " + t + " : " + at);
+ *
+ *
+ *
+ */
+p5.prototype.atan = function(ratio) {
+ if (this._angleMode === constants.RADIANS) {
+ return Math.atan(ratio);
+ } else {
+ return polarGeometry.radiansToDegrees(Math.atan(ratio));
+ }
+};
+
+/**
+ * Calculates the angle (in radians) from a specified point to the coordinate
+ * origin as measured from the positive x-axis. Values are returned as a
+ * float in the range from PI to -PI. The atan2() function is most often used
+ * for orienting geometry to the position of the cursor.
+ *
+ * Note: The y-coordinate of the point is the first parameter, and the
+ * x-coordinate is the second parameter, due the the structure of calculating
+ * the tangent.
+ *
+ * @method atan2
+ * @param {Number} y y-coordinate of the point
+ * @param {Number} x x-coordinate of the point
+ * @return {Number} the arc tangent of the given point
+ *
+ * @example
+ *
+ *
+ * function draw() {
+ * background(204);
+ * translate(width/2, height/2);
+ * var a = atan2(mouseY-height/2, mouseX-width/2);
+ * rotate(a);
+ * rect(-30, -5, 60, 10);
+ * }
+ *
+ *
+ *
+ * @alt
+ * 60 by 10 rect at center of canvas rotates with mouse movements
+ *
+ */
+p5.prototype.atan2 = function (y, x) {
+ if (this._angleMode === constants.RADIANS) {
+ return Math.atan2(y, x);
+ } else {
+ return polarGeometry.radiansToDegrees(Math.atan2(y, x));
+ }
+};
+
+/**
+ * Calculates the cosine of an angle. This function takes into account the
+ * current angleMode. Values are returned in the range -1 to 1.
+ *
+ * @method cos
+ * @param {Number} angle the angle
+ * @return {Number} the cosine of the angle
+ *
+ * @example
+ *
+ *
+ * var a = 0.0;
+ * var inc = TWO_PI/25.0;
+ * for (var i = 0; i < 25; i++) {
+ * line(i*4, 50, i*4, 50+cos(a)*40.0);
+ * a = a + inc;
+ * }
+ *
+ *
+ *
+ * @alt
+ * vertical black lines form wave patterns, extend-down on left and right side
+ *
+ */
+p5.prototype.cos = function(angle) {
+ if (this._angleMode === constants.RADIANS) {
+ return Math.cos(angle);
+ } else {
+ return Math.cos(this.radians(angle));
+ }
+};
+
+/**
+ * Calculates the sine of an angle. This function takes into account the
+ * current angleMode. Values are returned in the range -1 to 1.
+ *
+ * @method sin
+ * @param {Number} angle the angle
+ * @return {Number} the sine of the angle
+ *
+ * @example
+ *
+ *
+ * var a = 0.0;
+ * var inc = TWO_PI/25.0;
+ * for (var i = 0; i < 25; i++) {
+ * line(i*4, 50, i*4, 50+sin(a)*40.0);
+ * a = a + inc;
+ * }
+ *
+ *
+ *
+ * @alt
+ * vertical black lines extend down and up from center to form wave pattern
+ *
+ */
+p5.prototype.sin = function(angle) {
+ if (this._angleMode === constants.RADIANS) {
+ return Math.sin(angle);
+ } else {
+ return Math.sin(this.radians(angle));
+ }
+};
+
+/**
+ * Calculates the tangent of an angle. This function takes into account
+ * the current angleMode. Values are returned in the range -1 to 1.
+ *
+ * @method tan
+ * @param {Number} angle the angle
+ * @return {Number} the tangent of the angle
+ *
+ * @example
+ *
+ *
+ * var a = 0.0;
+ * var inc = TWO_PI/50.0;
+ * for (var i = 0; i < 100; i = i+2) {
+ * line(i, 50, i, 50+tan(a)*2.0);
+ * a = a + inc;
+ * }
+ *
+ *
+ *
+ * @alt
+ * vertical black lines end down and up from center to form spike pattern
+ *
+ */
+p5.prototype.tan = function(angle) {
+ if (this._angleMode === constants.RADIANS) {
+ return Math.tan(angle);
+ } else {
+ return Math.tan(this.radians(angle));
+ }
+};
+
+/**
+ * Converts a radian measurement to its corresponding value in degrees.
+ * Radians and degrees are two ways of measuring the same thing. There are
+ * 360 degrees in a circle and 2*PI radians in a circle. For example,
+ * 90° = PI/2 = 1.5707964.
+ *
+ * @method degrees
+ * @param {Number} radians the radians value to convert to degrees
+ * @return {Number} the converted angle
+ *
+ *
+ * @example
+ *
+ *
+ * var rad = PI/4;
+ * var deg = degrees(rad);
+ * print(rad + " radians is " + deg + " degrees");
+ * // Prints: 0.7853981633974483 radians is 45 degrees
+ *
+ *
+ *
+ */
+p5.prototype.degrees = function(angle) {
+ return polarGeometry.radiansToDegrees(angle);
+};
+
+/**
+ * Converts a degree measurement to its corresponding value in radians.
+ * Radians and degrees are two ways of measuring the same thing. There are
+ * 360 degrees in a circle and 2*PI radians in a circle. For example,
+ * 90° = PI/2 = 1.5707964.
+ *
+ * @method radians
+ * @param {Number} degrees the degree value to convert to radians
+ * @return {Number} the converted angle
+ *
+ * @example
+ *
+ *
+ * var deg = 45.0;
+ * var rad = radians(deg);
+ * print(deg + " degrees is " + rad + " radians");
+ * // Prints: 45 degrees is 0.7853981633974483 radians
+ *
+ *
+ */
+p5.prototype.radians = function(angle) {
+ return polarGeometry.degreesToRadians(angle);
+};
+
+/**
+ * Sets the current mode of p5 to given mode. Default mode is RADIANS.
+ *
+ * @method angleMode
+ * @param {Constant} mode either RADIANS or DEGREES
+ *
+ * @example
+ *
+ *
+ * function draw(){
+ * background(204);
+ * angleMode(DEGREES); // Change the mode to DEGREES
+ * var a = atan2(mouseY-height/2, mouseX-width/2);
+ * translate(width/2, height/2);
+ * push();
+ * rotate(a);
+ * rect(-20, -5, 40, 10); // Larger rectangle is rotating in degrees
+ * pop();
+ * angleMode(RADIANS); // Change the mode to RADIANS
+ * rotate(a); // var a stays the same
+ * rect(-40, -5, 20, 10); // Smaller rectangle is rotating in radians
+ * }
+ *
+ *
+ *
+ * @alt
+ * 40 by 10 rect in center rotates with mouse moves. 20 by 10 rect moves faster.
+ *
+ *
+ */
+p5.prototype.angleMode = function(mode) {
+ if (mode === constants.DEGREES || mode === constants.RADIANS) {
+ this._angleMode = mode;
+ }
+};
+
+module.exports = p5;
+
+},{"../core/constants":36,"../core/core":37,"./polargeometry":67}],70:[function(_dereq_,module,exports){
+/**
+ * @module Typography
+ * @submodule Attributes
+ * @for p5
+ * @requires core
+ * @requires constants
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+
+/**
+ * Sets the current alignment for drawing text. Accepts two
+ * arguments: horizAlign (LEFT, CENTER, or RIGHT) and
+ * vertAlign (TOP, BOTTOM, CENTER, or BASELINE).
+ *
+ * The horizAlign parameter is in reference to the x value
+ * of the text() function, while the vertAlign parameter is
+ * in reference to the y value.
+ *
+ * So if you write textAlign(LEFT), you are aligning the left
+ * edge of your text to the x value you give in text(). If you
+ * write textAlign(RIGHT, TOP), you are aligning the right edge
+ * of your text to the x value and the top of edge of the text
+ * to the y value.
+ *
+ * @method textAlign
+ * @param {Constant} horizAlign horizontal alignment, either LEFT,
+ * CENTER, or RIGHT
+ * @param {Constant} vertAlign vertical alignment, either TOP,
+ * BOTTOM, CENTER, or BASELINE
+ * @return {Number}
+ * @example
+ *
+ *
+ * textSize(16);
+ * textAlign(RIGHT);
+ * text("ABCD", 50, 30);
+ * textAlign(CENTER);
+ * text("EFGH", 50, 50);
+ * textAlign(LEFT);
+ * text("IJKL", 50, 70);
+ *
+ *
+ *
+ * @alt
+ *Letters ABCD displayed at top right, EFGH at center and IJKL at bottom left.
+ *
+ */
+p5.prototype.textAlign = function(horizAlign, vertAlign) {
+ return this._renderer.textAlign.apply(this._renderer, arguments);
+};
+
+/**
+ * Sets/gets the spacing, in pixels, between lines of text. This
+ * setting will be used in all subsequent calls to the text() function.
+ *
+ * @method textLeading
+ * @param {Number} leading the size in pixels for spacing between lines
+ * @return {Object|Number}
+ * @example
+ *
+ *
+ * // Text to display. The "\n" is a "new line" character
+ * lines = "L1\nL2\nL3";
+ * textSize(12);
+ *
+ * textLeading(10); // Set leading to 10
+ * text(lines, 10, 25);
+ *
+ * textLeading(20); // Set leading to 20
+ * text(lines, 40, 25);
+ *
+ * textLeading(30); // Set leading to 30
+ * text(lines, 70, 25);
+ *
+ *
+ *
+ * @alt
+ *set L1 L2 & L3 displayed vertically 3 times. spacing increases for each set
+ *
+ */
+p5.prototype.textLeading = function(theLeading) {
+ return this._renderer.textLeading.apply(this._renderer, arguments);
+};
+
+/**
+ * Sets/gets the current font size. This size will be used in all subsequent
+ * calls to the text() function. Font size is measured in pixels.
+ *
+ * @method textSize
+ * @param {Number} theSize the size of the letters in units of pixels
+ * @return {Object|Number}
+ * @example
+ *
+ *
+ * textSize(12);
+ * text("Font Size 12", 10, 30);
+ * textSize(14);
+ * text("Font Size 14", 10, 60);
+ * textSize(16);
+ * text("Font Size 16", 10, 90);
+ *
+ *
+ *
+ * @alt
+ *Font Size 12 displayed small, Font Size 14 medium & Font Size 16 large
+ *
+ */
+p5.prototype.textSize = function(theSize) {
+ return this._renderer.textSize.apply(this._renderer, arguments);
+};
+
+/**
+ * Sets/gets the style of the text for system fonts to NORMAL, ITALIC, or BOLD.
+ * Note: this may be is overridden by CSS styling. For non-system fonts
+ * (opentype, truetype, etc.) please load styled fonts instead.
+ *
+ * @method textStyle
+ * @param {Number/Constant} theStyle styling for text, either NORMAL,
+ * ITALIC, or BOLD
+ * @return {Object|String}
+ * @example
+ *
+ *
+ * strokeWeight(0);
+ * textSize(12);
+ * textStyle(NORMAL);
+ * text("Font Style Normal", 10, 30);
+ * textStyle(ITALIC);
+ * text("Font Style Italic", 10, 60);
+ * textStyle(BOLD);
+ * text("Font Style Bold", 10, 90);
+ *
+ *
+ *
+ * @alt
+ *words Font Style Normal displayed normally, Italic in italic and bold in bold
+ *
+ */
+p5.prototype.textStyle = function(theStyle) {
+ return this._renderer.textStyle.apply(this._renderer, arguments);
+};
+
+/**
+ * Calculates and returns the width of any character or text string.
+ *
+ * @method textWidth
+ * @param {String} theText the String of characters to measure
+ * @return {Number}
+ * @example
+ *
+ *
+ * textSize(28);
+ *
+ * var aChar = 'P';
+ * var cWidth = textWidth(aChar);
+ * text(aChar, 0, 40);
+ * line(cWidth, 0, cWidth, 50);
+ *
+ * var aString = "p5.js";
+ * var sWidth = textWidth(aString);
+ * text(aString, 0, 85);
+ * line(sWidth, 50, sWidth, 100);
+ *
+ *
+ *
+ * @alt
+ *Letter P and p5.js are displayed with vertical lines at end. P is wide
+ *
+ */
+p5.prototype.textWidth = function(theText) {
+ if (theText.length === 0) {
+ return 0;
+ }
+ return this._renderer.textWidth.apply(this._renderer, arguments);
+};
+
+/**
+ * Returns the ascent of the current font at its current size. The ascent
+ * represents the distance, in pixels, of the tallest character above
+ * the baseline.
+ *
+ * @return {Number}
+ * @example
+ *
+ *
+ * var base = height * 0.75;
+ * var scalar = 0.8; // Different for each font
+ *
+ * textSize(32); // Set initial text size
+ * var asc = textAscent() * scalar; // Calc ascent
+ * line(0, base - asc, width, base - asc);
+ * text("dp", 0, base); // Draw text on baseline
+ *
+ * textSize(64); // Increase text size
+ * asc = textAscent() * scalar; // Recalc ascent
+ * line(40, base - asc, width, base - asc);
+ * text("dp", 40, base); // Draw text on baseline
+ *
+ *
+ */
+p5.prototype.textAscent = function() {
+ return this._renderer.textAscent();
+};
+
+/**
+ * Returns the descent of the current font at its current size. The descent
+ * represents the distance, in pixels, of the character with the longest
+ * descender below the baseline.
+ *
+ * @return {Number}
+ * @example
+ *
+ *
+ * var base = height * 0.75;
+ * var scalar = 0.8; // Different for each font
+ *
+ * textSize(32); // Set initial text size
+ * var desc = textDescent() * scalar; // Calc ascent
+ * line(0, base+desc, width, base+desc);
+ * text("dp", 0, base); // Draw text on baseline
+ *
+ * textSize(64); // Increase text size
+ * desc = textDescent() * scalar; // Recalc ascent
+ * line(40, base + desc, width, base + desc);
+ * text("dp", 40, base); // Draw text on baseline
+ *
+ *
+ */
+p5.prototype.textDescent = function() {
+ return this._renderer.textDescent();
+};
+
+/**
+ * Helper function to measure ascent and descent.
+ */
+p5.prototype._updateTextMetrics = function() {
+ return this._renderer._updateTextMetrics();
+};
+
+module.exports = p5;
+
+},{"../core/core":37}],71:[function(_dereq_,module,exports){
+/**
+ * @module Typography
+ * @submodule Loading & Displaying
+ * @for p5
+ * @requires core
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+var constants = _dereq_('../core/constants');
+
+_dereq_('../core/error_helpers');
+
+
+/**
+ * Draws text to the screen. Displays the information specified in the first
+ * parameter on the screen in the position specified by the additional
+ * parameters. A default font will be used unless a font is set with the
+ * textFont() function and a default size will be used unless a font is set
+ * with textSize(). Change the color of the text with the fill() function.
+ * Change the outline of the text with the stroke() and strokeWeight()
+ * functions.
+ *
+ * The text displays in relation to the textAlign() function, which gives the
+ * option to draw to the left, right, and center of the coordinates.
+ *
+ * The x2 and y2 parameters define a rectangular area to display within and
+ * may only be used with string data. When these parameters are specified,
+ * they are interpreted based on the current rectMode() setting. Text that
+ * does not fit completely within the rectangle specified will not be drawn
+ * to the screen.
+ *
+ * @method text
+ * @param {String} str the alphanumeric symbols to be displayed
+ * @param {Number} x x-coordinate of text
+ * @param {Number} y y-coordinate of text
+ * @param {Number} x2 by default, the width of the text box,
+ * see rectMode() for more info
+ * @param {Number} y2 by default, the height of the text box,
+ * see rectMode() for more info
+ * @return {Object} this
+ * @example
+ *
+ *
+ * textSize(32);
+ * text("word", 10, 30);
+ * fill(0, 102, 153);
+ * text("word", 10, 60);
+ * fill(0, 102, 153, 51);
+ * text("word", 10, 90);
+ *
+ *
+ *
+ *
+ * s = "The quick brown fox jumped over the lazy dog.";
+ * fill(50);
+ * text(s, 10, 10, 70, 80); // Text wraps within text box
+ *
+ *
+ *
+ * @alt
+ *'word' displayed 3 times going from black, blue to translucent blue
+ * The quick brown fox jumped over the lazy dog.
+ *
+ */
+p5.prototype.text = function(str, x, y, maxWidth, maxHeight) {
+ var args = new Array(arguments.length);
+ for (var i = 0; i < args.length; ++i) {
+ args[i] = arguments[i];
+ }
+ this._validateParameters(
+ 'text',
+ args,
+ [
+ ['*', 'Number', 'Number'],
+ ['*', 'Number', 'Number', 'Number', 'Number']
+ ]
+ );
+
+ return (!(this._renderer._doFill || this._renderer._doStroke)) ? this :
+ this._renderer.text.apply(this._renderer, arguments);
+};
+
+/**
+ * Sets the current font that will be drawn with the text() function.
+ *
+ * @method textFont
+ * @param {Object|String} f a font loaded via loadFont(), or a String
+ * representing a
web safe font (a font
+ * that is generally available across all systems).
+ * @return {Object} this
+ * @example
+ *
+ *
+ * fill(0);
+ * textSize(12);
+ * textFont("Georgia");
+ * text("Georgia", 12, 30);
+ * textFont("Helvetica");
+ * text("Helvetica", 12, 60);
+ *
+ *
+ *
+ *
+ * var fontRegular, fontItalic, fontBold;
+ * function preload() {
+ * fontRegular = loadFont("assets/Regular.otf");
+ * fontItalic = loadFont("assets/Italic.ttf");
+ * fontBold = loadFont("assets/Bold.ttf");
+ * }
+ * function setup() {
+ * background(210);
+ * fill(0).strokeWeight(0).textSize(10);
+ * textFont(fontRegular);
+ * text("Font Style Normal", 10, 30);
+ * textFont(fontItalic);
+ * text("Font Style Italic", 10, 50);
+ * textFont(fontBold);
+ * text("Font Style Bold", 10, 70);
+ * }
+ *
+ *
+ *
+ * @alt
+ *words Font Style Normal displayed normally, Italic in italic and bold in bold
+ *
+ */
+p5.prototype.textFont = function(theFont, theSize) {
+
+ if (arguments.length) {
+
+ if (!theFont) {
+
+ throw Error('null font passed to textFont');
+ }
+
+ this._renderer._setProperty('_textFont', theFont);
+
+ if (theSize) {
+
+ this._renderer._setProperty('_textSize', theSize);
+ this._renderer._setProperty('_textLeading',
+ theSize * constants._DEFAULT_LEADMULT);
+ }
+
+ return this._renderer._applyTextProperties();
+ }
+
+ return this;
+};
+
+module.exports = p5;
+
+},{"../core/constants":36,"../core/core":37,"../core/error_helpers":40}],72:[function(_dereq_,module,exports){
+/**
+ * This module defines the p5.Font class and functions for
+ * drawing text to the display canvas.
+ * @module Typography
+ * @submodule Font
+ * @requires core
+ * @requires constants
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+var constants = _dereq_('../core/constants');
+
+/*
+ * TODO:
+ *
+ * API:
+ * -- textBounds()
+ * -- getPath()
+ * -- getPoints()
+ *
+ * ===========================================
+ * -- PFont functions:
+ * PFont.list()
+ *
+ * -- kerning
+ * -- alignment: justified?
+ * -- integrate p5.dom? (later)
+ */
+
+/**
+ * Base class for font handling
+ * @class p5.Font
+ * @constructor
+ * @param {Object} [pInst] pointer to p5 instance
+ */
+p5.Font = function(p) {
+
+ this.parent = p;
+
+ this.cache = {};
+
+ /**
+ * Underlying opentype font implementation
+ * @property font
+ */
+ this.font = undefined;
+};
+
+p5.Font.prototype.list = function() {
+
+ // TODO
+ throw 'not yet implemented';
+};
+
+/**
+ * Returns a tight bounding box for the given text string using this
+ * font (currently only supports single lines)
+ *
+ * @method textBounds
+ * @param {String} line a line of text
+ * @param {Number} x x-position
+ * @param {Number} y y-position
+ * @param {Number} fontSize font size to use (optional)
+ * @param {Object} options opentype options (optional)
+ *
+ * @return {Object} a rectangle object with properties: x, y, w, h
+ *
+ * @example
+ *
+ *
+ * var font;
+ * var textString = 'Lorem ipsum dolor sit amet.';
+ * function preload() {
+ * font = loadFont('./assets/Regular.otf');
+ * };
+ * function setup() {
+ * background(210);
+ *
+ * var bbox = font.textBounds(textString, 10, 30, 12);
+ * fill(255);
+ * stroke(0);
+ * rect(bbox.x, bbox.y, bbox.w, bbox.h);
+ * fill(0);
+ * noStroke();
+ *
+ * textFont(font);
+ * textSize(12);
+ * text(textString, 10, 30);
+ * };
+ *
+ *
+ *
+ * @alt
+ *words Lorem ipsum dol go off canvas and contained by white bounding box
+ *
+ */
+p5.Font.prototype.textBounds = function(str, x, y, fontSize, options) {
+
+ x = x !== undefined ? x : 0;
+ y = y !== undefined ? y : 0;
+ fontSize = fontSize || this.parent._renderer._textSize;
+
+ // Check cache for existing bounds. Take into consideration the text alignment
+ // settings. Default alignment should match opentype's origin: left-aligned &
+ // alphabetic baseline.
+ var p = (options && options.renderer && options.renderer._pInst) ||
+ this.parent,
+ ctx = p._renderer.drawingContext,
+ alignment = ctx.textAlign || constants.LEFT,
+ baseline = ctx.textBaseline || constants.BASELINE;
+ var result = this.cache[cacheKey('textBounds', str, x, y, fontSize, alignment,
+ baseline)];
+
+ if (!result) {
+
+ var xCoords = [], yCoords = [], self = this,
+ scale = this._scale(fontSize), minX, minY, maxX, maxY;
+
+ this.font.forEachGlyph(str, x, y, fontSize, options,
+ function(glyph, gX, gY, gFontSize) {
+
+ xCoords.push(gX);
+ yCoords.push(gY);
+
+ var gm = glyph.getMetrics();
+
+ if (glyph.name !== 'space') {
+
+ xCoords.push(gX + (gm.xMax * scale));
+ yCoords.push(gY + (-gm.yMin * scale));
+ yCoords.push(gY + (-gm.yMax * scale));
+
+ } else { // NOTE: deals with broken metrics for spaces in opentype.js
+
+ xCoords.push(gX + self.font.charToGlyph(' ').advanceWidth *
+ self._scale(fontSize));
+ }
+ });
+
+ // fix to #1409 (not sure why these max() functions were here)
+ /*minX = Math.max(0, Math.min.apply(null, xCoords));
+ minY = Math.max(0, Math.min.apply(null, yCoords));
+ maxX = Math.max(0, Math.max.apply(null, xCoords));
+ maxY = Math.max(0, Math.max.apply(null, yCoords));*/
+ minX = Math.min.apply(null, xCoords);
+ minY = Math.min.apply(null, yCoords);
+ maxX = Math.max.apply(null, xCoords);
+ maxY = Math.max.apply(null, yCoords);
+
+ result = {
+ x: minX,
+ y: minY,
+ h: maxY - minY,
+ w: maxX - minX,
+ advance: minX - x
+ };
+
+ // Bounds are now calculated, so shift the x & y to match alignment settings
+ var textWidth = result.w + result.advance;
+ var pos = this._handleAlignment(p, ctx, str, result.x, result.y, textWidth);
+ result.x = pos.x;
+ result.y = pos.y;
+
+ this.cache[cacheKey('textBounds', str, x, y, fontSize, alignment,
+ baseline)] = result;
+ }
+ //else console.log('cache-hit');
+
+ return result;
+};
+
+
+/**
+ * Computes an array of points following the path for specified text
+ *
+ * @param {String} txt a line of text
+ * @param {Number} x x-position
+ * @param {Number} y y-position
+ * @param {Number} fontSize font size to use (optional)
+ * @param {Object} options an (optional) object that can contain:
+ *
+ *
sampleFactor - the ratio of path-length to number of samples
+ * (default=.25); higher values yield more points and are therefore
+ * more precise
+ *
+ *
simplifyThreshold - if set to a non-zero value, collinear points will be
+ * be removed from the polygon; the value represents the threshold angle to use
+ * when determining whether two edges are collinear
+ *
+ * @return {Array} an array of points, each with x, y, alpha (the path angle)
+ */
+p5.Font.prototype.textToPoints = function(txt, x, y, fontSize, options) {
+
+ var xoff = 0, result = [], glyphs = this._getGlyphs(txt);
+
+ fontSize = fontSize || this.parent._renderer._textSize;
+
+ for (var i = 0; i < glyphs.length; i++) {
+
+ var gpath = glyphs[i].getPath(x, y, fontSize),
+ paths = splitPaths(gpath.commands);
+
+ for (var j = 0; j < paths.length; j++) {
+
+ var pts = pathToPoints(paths[j], options);
+
+ for (var k = 0; k < pts.length; k++) {
+ pts[k].x += xoff;
+ result.push(pts[k]);
+ }
+ }
+
+ xoff += glyphs[i].advanceWidth * this._scale(fontSize);
+ }
+
+ return result;
+};
+
+// ----------------------------- End API ------------------------------
+
+/**
+ * Returns the set of opentype glyphs for the supplied string.
+ *
+ * Note that there is not a strict one-to-one mapping between characters
+ * and glyphs, so the list of returned glyphs can be larger or smaller
+ * than the length of the given string.
+ *
+ * @param {String} str the string to be converted
+ * @return {array} the opentype glyphs
+ */
+p5.Font.prototype._getGlyphs = function(str) {
+
+ return this.font.stringToGlyphs(str);
+};
+
+/**
+ * Returns an opentype path for the supplied string and position.
+ *
+ * @param {String} line a line of text
+ * @param {Number} x x-position
+ * @param {Number} y y-position
+ * @param {Object} options opentype options (optional)
+ * @return {Object} the opentype path
+ */
+p5.Font.prototype._getPath = function(line, x, y, options) {
+
+ var p = (options && options.renderer && options.renderer._pInst) ||
+ this.parent,
+ ctx = p._renderer.drawingContext,
+ pos = this._handleAlignment(p, ctx, line, x, y);
+
+ return this.font.getPath(line, pos.x, pos.y, p._renderer._textSize, options);
+};
+
+/*
+ * Creates an SVG-formatted path-data string
+ * (See http://www.w3.org/TR/SVG/paths.html#PathData)
+ * from the given opentype path or string/position
+ *
+ * @param {Object} path an opentype path, OR the following:
+ *
+ * @param {String} line a line of text
+ * @param {Number} x x-position
+ * @param {Number} y y-position
+ * @param {Object} options opentype options (optional), set options.decimals
+ * to set the decimal precision of the path-data
+ *
+ * @return {Object} this p5.Font object
+ */
+p5.Font.prototype._getPathData = function(line, x, y, options) {
+
+ var decimals = 3;
+
+ // create path from string/position
+ if (typeof line === 'string' && arguments.length > 2) {
+
+ line = this._getPath(line, x, y, options);
+ }
+ // handle options specified in 2nd arg
+ else if (typeof x === 'object') {
+
+ options = x;
+ }
+
+ // handle svg arguments
+ if (options && typeof options.decimals === 'number') {
+
+ decimals = options.decimals;
+ }
+
+ return line.toPathData(decimals);
+};
+
+/*
+ * Creates an SVG
element, as a string,
+ * from the given opentype path or string/position
+ *
+ * @param {Object} path an opentype path, OR the following:
+ *
+ * @param {String} line a line of text
+ * @param {Number} x x-position
+ * @param {Number} y y-position
+ * @param {Object} options opentype options (optional), set options.decimals
+ * to set the decimal precision of the path-data in the element,
+ * options.fill to set the fill color for the element,
+ * options.stroke to set the stroke color for the element,
+ * options.strokeWidth to set the strokeWidth for the element.
+ *
+ * @return {Object} this p5.Font object
+ */
+p5.Font.prototype._getSVG = function(line, x, y, options) {
+
+ var decimals = 3;
+
+ // create path from string/position
+ if (typeof line === 'string' && arguments.length > 2) {
+
+ line = this._getPath(line, x, y, options);
+ }
+ // handle options specified in 2nd arg
+ else if (typeof x === 'object') {
+
+ options = x;
+ }
+
+ // handle svg arguments
+ if (options) {
+ if (typeof options.decimals === 'number') {
+ decimals = options.decimals;
+ }
+ if (typeof options.strokeWidth === 'number') {
+ line.strokeWidth = options.strokeWidth;
+ }
+ if (typeof options.fill !== 'undefined') {
+ line.fill = options.fill;
+ }
+ if (typeof options.stroke !== 'undefined') {
+ line.stroke = options.stroke;
+ }
+ }
+
+ return line.toSVG(decimals);
+};
+
+/*
+ * Renders an opentype path or string/position
+ * to the current graphics context
+ *
+ * @param {Object} path an opentype path, OR the following:
+ *
+ * @param {String} line a line of text
+ * @param {Number} x x-position
+ * @param {Number} y y-position
+ * @param {Object} options opentype options (optional)
+ *
+ * @return {Object} this p5.Font object
+ */
+p5.Font.prototype._renderPath = function(line, x, y, options) {
+
+ var pdata, pg = (options && options.renderer) || this.parent._renderer,
+ ctx = pg.drawingContext;
+
+ if (typeof line === 'object' && line.commands) {
+
+ pdata = line.commands;
+ } else {
+
+ //pos = handleAlignment(p, ctx, line, x, y);
+ pdata = this._getPath(line, x, y, options).commands;
+ }
+
+ ctx.beginPath();
+ for (var i = 0; i < pdata.length; i += 1) {
+
+ var cmd = pdata[i];
+ if (cmd.type === 'M') {
+ ctx.moveTo(cmd.x, cmd.y);
+ } else if (cmd.type === 'L') {
+ ctx.lineTo(cmd.x, cmd.y);
+ } else if (cmd.type === 'C') {
+ ctx.bezierCurveTo(cmd.x1, cmd.y1, cmd.x2, cmd.y2, cmd.x, cmd.y);
+ } else if (cmd.type === 'Q') {
+ ctx.quadraticCurveTo(cmd.x1, cmd.y1, cmd.x, cmd.y);
+ } else if (cmd.type === 'Z') {
+ ctx.closePath();
+ }
+ }
+
+ // only draw stroke if manually set by user
+ if (pg._doStroke && pg._strokeSet) {
+
+ ctx.stroke();
+ }
+
+ if (pg._doFill) {
+
+ // if fill hasn't been set by user, use default-text-fill
+ ctx.fillStyle = pg._fillSet ? ctx.fillStyle : constants._DEFAULT_TEXT_FILL;
+ ctx.fill();
+ }
+
+ return this;
+};
+
+p5.Font.prototype._textWidth = function(str, fontSize) {
+
+ if (str === ' ') { // special case for now
+
+ return this.font.charToGlyph(' ').advanceWidth * this._scale(fontSize);
+ }
+
+ var bounds = this.textBounds(str, 0, 0, fontSize);
+ return bounds.w + bounds.advance;
+};
+
+p5.Font.prototype._textAscent = function(fontSize) {
+
+ return this.font.ascender * this._scale(fontSize);
+};
+
+p5.Font.prototype._textDescent = function(fontSize) {
+
+ return -this.font.descender * this._scale(fontSize);
+};
+
+p5.Font.prototype._scale = function(fontSize) {
+
+ return (1 / this.font.unitsPerEm) * (fontSize ||
+ this.parent._renderer._textSize);
+};
+
+p5.Font.prototype._handleAlignment = function(p, ctx, line, x, y, textWidth) {
+ var fontSize = p._renderer._textSize,
+ textAscent = this._textAscent(fontSize),
+ textDescent = this._textDescent(fontSize);
+
+ textWidth = textWidth !== undefined ? textWidth :
+ this._textWidth(line, fontSize);
+
+ if (ctx.textAlign === constants.CENTER) {
+ x -= textWidth / 2;
+ } else if (ctx.textAlign === constants.RIGHT) {
+ x -= textWidth;
+ }
+
+ if (ctx.textBaseline === constants.TOP) {
+ y += textAscent;
+ } else if (ctx.textBaseline === constants._CTX_MIDDLE) {
+ y += textAscent / 2;
+ } else if (ctx.textBaseline === constants.BOTTOM) {
+ y -= textDescent;
+ }
+
+ return { x: x, y: y };
+};
+
+// path-utils
+
+function pathToPoints(cmds, options) {
+
+ var opts = parseOpts(options, {
+ sampleFactor: 0.1,
+ simplifyThreshold: 0,
+ });
+
+ var len = pointAtLength(cmds,0,1), // total-length
+ t = len / (len * opts.sampleFactor),
+ pts = [];
+
+ for (var i = 0; i < len; i += t) {
+ pts.push(pointAtLength(cmds, i));
+ }
+
+ if (opts.simplifyThreshold) {
+ /*var count = */simplify(pts, opts.simplifyThreshold);
+ //console.log('Simplify: removed ' + count + ' pts');
+ }
+
+ return pts;
+}
+
+function simplify(pts, angle) {
+
+ angle = (typeof angle === 'undefined') ? 0 : angle;
+
+ var num = 0;
+ for (var i = pts.length - 1; pts.length > 3 && i >= 0; --i) {
+
+ if (collinear(at(pts, i - 1), at(pts, i), at(pts, i + 1), angle)) {
+
+ // Remove the middle point
+ pts.splice(i % pts.length, 1);
+ num++;
+ }
+ }
+ return num;
+}
+
+function splitPaths(cmds) {
+
+ var paths = [], current;
+ for (var i = 0; i < cmds.length; i++) {
+ if (cmds[i].type === 'M') {
+ if (current) {
+ paths.push(current);
+ }
+ current = [];
+ }
+ current.push(cmdToArr(cmds[i]));
+ }
+ paths.push(current);
+
+ return paths;
+}
+
+function cmdToArr(cmd) {
+
+ var arr = [ cmd.type ];
+ if (cmd.type === 'M' || cmd.type === 'L') { // moveto or lineto
+ arr.push(cmd.x, cmd.y);
+ } else if (cmd.type === 'C') {
+ arr.push(cmd.x1, cmd.y1, cmd.x2, cmd.y2, cmd.x, cmd.y);
+ } else if (cmd.type === 'Q') {
+ arr.push(cmd.x1, cmd.y1, cmd.x, cmd.y);
+ }
+ // else if (cmd.type === 'Z') { /* no-op */ }
+ return arr;
+}
+
+function parseOpts(options, defaults) {
+
+ if (typeof options !== 'object') {
+ options = defaults;
+ }
+ else {
+ for (var key in defaults) {
+ if (typeof options[key] === 'undefined') {
+ options[key] = defaults[key];
+ }
+ }
+ }
+ return options;
+}
+
+//////////////////////// Helpers ////////////////////////////
+
+function at(v, i) {
+ var s = v.length;
+ return v[i < 0 ? i % s + s : i % s];
+}
+
+function collinear(a, b, c, thresholdAngle) {
+
+ if (!thresholdAngle) {
+ return areaTriangle(a, b, c) === 0;
+ }
+
+ if (typeof collinear.tmpPoint1 === 'undefined') {
+ collinear.tmpPoint1 = [];
+ collinear.tmpPoint2 = [];
+ }
+
+ var ab = collinear.tmpPoint1, bc = collinear.tmpPoint2;
+ ab.x = b.x - a.x;
+ ab.y = b.y - a.y;
+ bc.x = c.x - b.x;
+ bc.y = c.y - b.y;
+
+ var dot = ab.x * bc.x + ab.y * bc.y,
+ magA = Math.sqrt(ab.x * ab.x + ab.y * ab.y),
+ magB = Math.sqrt(bc.x * bc.x + bc.y * bc.y),
+ angle = Math.acos(dot / (magA * magB));
+
+ return angle < thresholdAngle;
+}
+
+function areaTriangle(a, b, c) {
+ return (((b[0] - a[0]) * (c[1] - a[1])) - ((c[0] - a[0]) * (b[1] - a[1])));
+}
+
+// Portions of below code copyright 2008 Dmitry Baranovskiy (via MIT license)
+
+function findDotsAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) {
+
+ var t1 = 1 - t, t13 = Math.pow(t1, 3), t12 = Math.pow(t1, 2), t2 = t * t,
+ t3 = t2 * t, x = t13 * p1x + t12 * 3 * t * c1x + t1 * 3 * t * t * c2x +
+ t3 * p2x, y = t13 * p1y + t12 * 3 * t * c1y + t1 * 3 * t * t * c2y +
+ t3 * p2y, mx = p1x + 2 * t * (c1x - p1x) + t2 * (c2x - 2 * c1x + p1x),
+ my = p1y + 2 * t * (c1y - p1y) + t2 * (c2y - 2 * c1y + p1y),
+ nx = c1x + 2 * t * (c2x - c1x) + t2 * (p2x - 2 * c2x + c1x),
+ ny = c1y + 2 * t * (c2y - c1y) + t2 * (p2y - 2 * c2y + c1y),
+ ax = t1 * p1x + t * c1x, ay = t1 * p1y + t * c1y,
+ cx = t1 * c2x + t * p2x, cy = t1 * c2y + t * p2y,
+ alpha = (90 - Math.atan2(mx - nx, my - ny) * 180 / Math.PI);
+
+ if (mx > nx || my < ny) { alpha += 180; }
+
+ return { x: x, y: y, m: { x: mx, y: my }, n: { x: nx, y: ny },
+ start: { x: ax, y: ay }, end: { x: cx, y: cy }, alpha: alpha
+ };
+}
+
+function getPointAtSegmentLength(p1x,p1y,c1x,c1y,c2x,c2y,p2x,p2y,length) {
+ return (length == null) ? bezlen(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) :
+ findDotsAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y,
+ getTatLen(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, length));
+}
+
+function pointAtLength(path, length, istotal) {
+ path = path2curve(path);
+ var x, y, p, l, sp = '', subpaths = {}, point, len = 0;
+ for (var i = 0, ii = path.length; i < ii; i++) {
+ p = path[i];
+ if (p[0] === 'M') {
+ x = +p[1];
+ y = +p[2];
+ } else {
+ l = getPointAtSegmentLength(x, y, p[1], p[2], p[3], p[4], p[5], p[6]);
+ if (len + l > length) {
+ if (!istotal) {
+ point = getPointAtSegmentLength(x, y, p[1], p[2], p[3], p[4], p[5],
+ p[6], length - len);
+ return { x: point.x, y: point.y, alpha: point.alpha };
+ }
+ }
+ len += l;
+ x = +p[5];
+ y = +p[6];
+ }
+ sp += p.shift() + p;
+ }
+ subpaths.end = sp;
+
+ point = istotal ? len : findDotsAtSegment
+ (x, y, p[0], p[1], p[2], p[3], p[4], p[5], 1);
+
+ if (point.alpha) {
+ point = { x: point.x, y: point.y, alpha: point.alpha };
+ }
+
+ return point;
+}
+
+function pathToAbsolute(pathArray) {
+
+ var res = [], x = 0, y = 0, mx = 0, my = 0, start = 0;
+ if (pathArray[0][0] === 'M') {
+ x = +pathArray[0][1];
+ y = +pathArray[0][2];
+ mx = x;
+ my = y;
+ start++;
+ res[0] = ['M', x, y];
+ }
+
+ var dots,crz = pathArray.length===3 && pathArray[0][0]==='M' &&
+ pathArray[1][0].toUpperCase()==='R' && pathArray[2][0].toUpperCase()==='Z';
+
+ for (var r, pa, i = start, ii = pathArray.length; i < ii; i++) {
+ res.push(r = []);
+ pa = pathArray[i];
+ if (pa[0] !== String.prototype.toUpperCase.call(pa[0])) {
+ r[0] = String.prototype.toUpperCase.call(pa[0]);
+ switch (r[0]) {
+ case 'A':
+ r[1] = pa[1];
+ r[2] = pa[2];
+ r[3] = pa[3];
+ r[4] = pa[4];
+ r[5] = pa[5];
+ r[6] = +(pa[6] + x);
+ r[7] = +(pa[7] + y);
+ break;
+ case 'V':
+ r[1] = +pa[1] + y;
+ break;
+ case 'H':
+ r[1] = +pa[1] + x;
+ break;
+ case 'R':
+ dots = [x, y].concat(pa.slice(1));
+ for (var j = 2, jj = dots.length; j < jj; j++) {
+ dots[j] = +dots[j] + x;
+ dots[++j] = +dots[j] + y;
+ }
+ res.pop();
+ res = res.concat(catmullRom2bezier(dots, crz));
+ break;
+ case 'M':
+ mx = +pa[1] + x;
+ my = +pa[2] + y;
+ break;
+ default:
+ for (j = 1, jj = pa.length; j < jj; j++) {
+ r[j] = +pa[j] + ((j % 2) ? x : y);
+ }
+ }
+ } else if (pa[0] === 'R') {
+ dots = [x, y].concat(pa.slice(1));
+ res.pop();
+ res = res.concat(catmullRom2bezier(dots, crz));
+ r = ['R'].concat(pa.slice(-2));
+ } else {
+ for (var k = 0, kk = pa.length; k < kk; k++) {
+ r[k] = pa[k];
+ }
+ }
+ switch (r[0]) {
+ case 'Z':
+ x = mx;
+ y = my;
+ break;
+ case 'H':
+ x = r[1];
+ break;
+ case 'V':
+ y = r[1];
+ break;
+ case 'M':
+ mx = r[r.length - 2];
+ my = r[r.length - 1];
+ break;
+ default:
+ x = r[r.length - 2];
+ y = r[r.length - 1];
+ }
+ }
+ return res;
+}
+
+function path2curve(path, path2) {
+
+ var p = pathToAbsolute(path), p2 = path2 && pathToAbsolute(path2),
+ attrs = { x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null },
+ attrs2 = { x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null },
+
+ processPath = function(path, d, pcom) {
+ var nx, ny, tq = { T: 1, Q: 1 };
+ if (!path) { return ['C', d.x, d.y, d.x, d.y, d.x, d.y]; }
+ if (!(path[0] in tq)) { d.qx = d.qy = null; }
+ switch (path[0]) {
+ case 'M':
+ d.X = path[1];
+ d.Y = path[2];
+ break;
+ case 'A':
+ path = ['C'].concat(a2c.apply(0, [d.x, d.y].concat(path.slice(1))));
+ break;
+ case 'S':
+ if (pcom === 'C' || pcom === 'S') {
+ nx = d.x * 2 - d.bx;
+ ny = d.y * 2 - d.by;
+ } else {
+ nx = d.x;
+ ny = d.y;
+ }
+ path = ['C', nx, ny].concat(path.slice(1));
+ break;
+ case 'T':
+ if (pcom === 'Q' || pcom === 'T') {
+ d.qx = d.x * 2 - d.qx;
+ d.qy = d.y * 2 - d.qy;
+ } else {
+ d.qx = d.x;
+ d.qy = d.y;
+ }
+ path = ['C'].concat(q2c(d.x, d.y, d.qx, d.qy, path[1], path[2]));
+ break;
+ case 'Q':
+ d.qx = path[1];
+ d.qy = path[2];
+ path = ['C'].concat(q2c(d.x,d.y,path[1],path[2],path[3],path[4]));
+ break;
+ case 'L':
+ path = ['C'].concat(l2c(d.x, d.y, path[1], path[2]));
+ break;
+ case 'H':
+ path = ['C'].concat(l2c(d.x, d.y, path[1], d.y));
+ break;
+ case 'V':
+ path = ['C'].concat(l2c(d.x, d.y, d.x, path[1]));
+ break;
+ case 'Z':
+ path = ['C'].concat(l2c(d.x, d.y, d.X, d.Y));
+ break;
+ }
+ return path;
+ },
+
+ fixArc = function(pp, i) {
+ if (pp[i].length > 7) {
+ pp[i].shift();
+ var pi = pp[i];
+ while (pi.length) {
+ pcoms1[i] = 'A';
+ if (p2) { pcoms2[i] = 'A'; }
+ pp.splice(i++, 0, ['C'].concat(pi.splice(0, 6)));
+ }
+ pp.splice(i, 1);
+ ii = Math.max(p.length, p2 && p2.length || 0);
+ }
+ },
+
+ fixM = function(path1, path2, a1, a2, i) {
+ if (path1 && path2 && path1[i][0] === 'M' && path2[i][0] !== 'M') {
+ path2.splice(i, 0, ['M', a2.x, a2.y]);
+ a1.bx = 0;
+ a1.by = 0;
+ a1.x = path1[i][1];
+ a1.y = path1[i][2];
+ ii = Math.max(p.length, p2 && p2.length || 0);
+ }
+ },
+
+ pcoms1 = [], // path commands of original path p
+ pcoms2 = [], // path commands of original path p2
+ pfirst = '', // temporary holder for original path command
+ pcom = ''; // holder for previous path command of original path
+
+ for (var i = 0, ii = Math.max(p.length, p2 && p2.length || 0); i < ii; i++) {
+ if (p[i]) { pfirst = p[i][0]; } // save current path command
+
+ if (pfirst !== 'C') {
+ pcoms1[i] = pfirst; // Save current path command
+ if (i) { pcom = pcoms1[i - 1]; } // Get previous path command pcom
+ }
+ p[i] = processPath(p[i], attrs, pcom);
+
+ if (pcoms1[i] !== 'A' && pfirst === 'C') { pcoms1[i] = 'C'; }
+
+ fixArc(p, i); // fixArc adds also the right amount of A:s to pcoms1
+
+ if (p2) { // the same procedures is done to p2
+ if (p2[i]) { pfirst = p2[i][0]; }
+ if (pfirst !== 'C') {
+ pcoms2[i] = pfirst;
+ if (i) { pcom = pcoms2[i - 1]; }
+ }
+ p2[i] = processPath(p2[i], attrs2, pcom);
+
+ if (pcoms2[i] !== 'A' && pfirst === 'C') { pcoms2[i] = 'C'; }
+
+ fixArc(p2, i);
+ }
+ fixM(p, p2, attrs, attrs2, i);
+ fixM(p2, p, attrs2, attrs, i);
+ var seg = p[i], seg2 = p2 && p2[i], seglen = seg.length,
+ seg2len = p2 && seg2.length;
+ attrs.x = seg[seglen - 2];
+ attrs.y = seg[seglen - 1];
+ attrs.bx = parseFloat(seg[seglen - 4]) || attrs.x;
+ attrs.by = parseFloat(seg[seglen - 3]) || attrs.y;
+ attrs2.bx = p2 && (parseFloat(seg2[seg2len - 4]) || attrs2.x);
+ attrs2.by = p2 && (parseFloat(seg2[seg2len - 3]) || attrs2.y);
+ attrs2.x = p2 && seg2[seg2len - 2];
+ attrs2.y = p2 && seg2[seg2len - 1];
+ }
+
+ return p2 ? [p, p2] : p;
+}
+
+function a2c(x1, y1, rx, ry, angle, lac, sweep_flag, x2, y2, recursive) {
+ // for more information of where this Math came from visit:
+ // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
+ var PI = Math.PI, _120 = PI * 120 / 180, f1, f2, cx, cy,
+ rad = PI / 180 * (+angle || 0), res = [], xy,
+ rotate = function (x, y, rad) {
+ var X = x * Math.cos(rad) - y * Math.sin(rad),
+ Y = x * Math.sin(rad) + y * Math.cos(rad);
+ return { x: X, y: Y };
+ };
+ if (!recursive) {
+ xy = rotate(x1, y1, -rad);
+ x1 = xy.x;
+ y1 = xy.y;
+ xy = rotate(x2, y2, -rad);
+ x2 = xy.x;
+ y2 = xy.y;
+ var x = (x1 - x2) / 2, y = (y1 - y2) / 2,
+ h = (x * x) / (rx * rx) + (y * y) / (ry * ry);
+ if (h > 1) {
+ h = Math.sqrt(h);
+ rx = h * rx;
+ ry = h * ry;
+ }
+ var rx2 = rx * rx, ry2 = ry * ry,
+ k = (lac === sweep_flag ? -1 : 1) * Math.sqrt(Math.abs
+ ((rx2 * ry2 - rx2 * y * y - ry2 * x * x)/(rx2 * y * y + ry2 * x * x)));
+
+ cx = k * rx * y / ry + (x1 + x2) / 2;
+ cy = k * -ry * x / rx + (y1 + y2) / 2;
+ f1 = Math.asin(((y1 - cy) / ry).toFixed(9));
+ f2 = Math.asin(((y2 - cy) / ry).toFixed(9));
+
+ f1 = x1 < cx ? PI - f1 : f1;
+ f2 = x2 < cx ? PI - f2 : f2;
+
+ if (f1 < 0) { f1 = PI * 2 + f1; }
+ if (f2 < 0) { f2 = PI * 2 + f2; }
+
+ if (sweep_flag && f1 > f2) {
+ f1 = f1 - PI * 2;
+ }
+ if (!sweep_flag && f2 > f1) {
+ f2 = f2 - PI * 2;
+ }
+ } else {
+ f1 = recursive[0];
+ f2 = recursive[1];
+ cx = recursive[2];
+ cy = recursive[3];
+ }
+ var df = f2 - f1;
+ if (Math.abs(df) > _120) {
+ var f2old = f2, x2old = x2, y2old = y2;
+ f2 = f1 + _120 * (sweep_flag && f2 > f1 ? 1 : -1);
+ x2 = cx + rx * Math.cos(f2);
+ y2 = cy + ry * Math.sin(f2);
+ res = a2c(x2, y2, rx, ry, angle, 0, sweep_flag, x2old, y2old,
+ [f2, f2old, cx, cy]);
+ }
+ df = f2 - f1;
+ var c1 = Math.cos(f1),
+ s1 = Math.sin(f1),
+ c2 = Math.cos(f2),
+ s2 = Math.sin(f2),
+ t = Math.tan(df / 4),
+ hx = 4 / 3 * rx * t,
+ hy = 4 / 3 * ry * t,
+ m1 = [x1, y1],
+ m2 = [x1 + hx * s1, y1 - hy * c1],
+ m3 = [x2 + hx * s2, y2 - hy * c2],
+ m4 = [x2, y2];
+ m2[0] = 2 * m1[0] - m2[0];
+ m2[1] = 2 * m1[1] - m2[1];
+ if (recursive) {
+ return [m2, m3, m4].concat(res);
+ } else {
+ res = [m2, m3, m4].concat(res).join().split(',');
+ var newres = [];
+ for (var i = 0, ii = res.length; i < ii; i++) {
+ newres[i] = i % 2 ? rotate(res[i - 1], res[i], rad).y : rotate(res[i],
+ res[i + 1], rad).x;
+ }
+ return newres;
+ }
+}
+
+// http://schepers.cc/getting-to-the-point
+function catmullRom2bezier(crp, z) {
+ var d = [];
+ for (var i = 0, iLen = crp.length; iLen - 2 * !z > i; i += 2) {
+ var p = [{
+ x: +crp[i - 2],
+ y: +crp[i - 1]
+ }, {
+ x: +crp[i],
+ y: +crp[i + 1]
+ }, {
+ x: +crp[i + 2],
+ y: +crp[i + 3]
+ }, {
+ x: +crp[i + 4],
+ y: +crp[i + 5]
+ }];
+ if (z) {
+ if (!i) {
+ p[0] = {
+ x: +crp[iLen - 2],
+ y: +crp[iLen - 1]
+ };
+ } else if (iLen - 4 === i) {
+ p[3] = {
+ x: +crp[0],
+ y: +crp[1]
+ };
+ } else if (iLen - 2 === i) {
+ p[2] = {
+ x: +crp[0],
+ y: +crp[1]
+ };
+ p[3] = {
+ x: +crp[2],
+ y: +crp[3]
+ };
+ }
+ } else {
+ if (iLen - 4 === i) {
+ p[3] = p[2];
+ } else if (!i) {
+ p[0] = {
+ x: +crp[i],
+ y: +crp[i + 1]
+ };
+ }
+ }
+ d.push(['C', (-p[0].x + 6 * p[1].x + p[2].x) / 6, (-p[0].y + 6 * p[1].y +
+ p[2].y) / 6, (p[1].x + 6 * p[2].x - p[3].x) / 6, (p[1].y + 6 * p[2].y -
+ p[3].y) / 6, p[2].x, p[2].y ]);
+ }
+
+ return d;
+}
+
+function l2c(x1, y1, x2, y2) { return [x1, y1, x2, y2, x2, y2]; }
+
+function q2c(x1, y1, ax, ay, x2, y2) {
+ var _13 = 1 / 3, _23 = 2 / 3;
+ return [
+ _13 * x1 + _23 * ax, _13 * y1 + _23 * ay,
+ _13 * x2 + _23 * ax, _13 * y2 + _23 * ay, x2, y2
+ ];
+}
+
+function bezlen(x1, y1, x2, y2, x3, y3, x4, y4, z) {
+ if (z == null) { z = 1; }
+ z = z > 1 ? 1 : z < 0 ? 0 : z;
+ var z2 = z / 2,
+ n = 12, Tvalues = [-0.1252, 0.1252, -0.3678, 0.3678, -0.5873, 0.5873,
+ -0.7699, 0.7699, -0.9041, 0.9041, -0.9816, 0.9816],
+ sum = 0, Cvalues = [0.2491, 0.2491, 0.2335, 0.2335, 0.2032, 0.2032,
+ 0.1601, 0.1601, 0.1069, 0.1069, 0.0472, 0.0472 ];
+ for (var i = 0; i < n; i++) {
+ var ct = z2 * Tvalues[i] + z2,
+ xbase = base3(ct, x1, x2, x3, x4),
+ ybase = base3(ct, y1, y2, y3, y4),
+ comb = xbase * xbase + ybase * ybase;
+ sum += Cvalues[i] * Math.sqrt(comb);
+ }
+ return z2 * sum;
+}
+
+function getTatLen(x1, y1, x2, y2, x3, y3, x4, y4, ll) {
+ if (ll < 0 || bezlen(x1, y1, x2, y2, x3, y3, x4, y4) < ll) {
+ return;
+ }
+ var t = 1, step = t / 2, t2 = t - step, l, e = 0.01;
+ l = bezlen(x1, y1, x2, y2, x3, y3, x4, y4, t2);
+ while (Math.abs(l - ll) > e) {
+ step /= 2;
+ t2 += (l < ll ? 1 : -1) * step;
+ l = bezlen(x1, y1, x2, y2, x3, y3, x4, y4, t2);
+ }
+ return t2;
+}
+
+function base3(t, p1, p2, p3, p4) {
+ var t1 = -3 * p1 + 9 * p2 - 9 * p3 + 3 * p4,
+ t2 = t * t1 + 6 * p1 - 12 * p2 + 6 * p3;
+ return t * t2 - 3 * p1 + 3 * p2;
+}
+
+function cacheKey() {
+ var args = new Array(arguments.length);
+ for (var i = 0; i < args.length; ++i) {
+ args[i] = arguments[i];
+ }
+ i = args.length;
+ var hash = '';
+ while (i--) {
+ hash += (args[i] === Object(args[i])) ?
+ JSON.stringify(args[i]) : args[i];
+ }
+ return hash;
+}
+
+module.exports = p5.Font;
+
+},{"../core/constants":36,"../core/core":37}],73:[function(_dereq_,module,exports){
+/**
+ * @module Data
+ * @submodule Array Functions
+ * @for p5
+ * @requires core
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+
+/**
+ * Adds a value to the end of an array. Extends the length of
+ * the array by one. Maps to Array.push().
+ *
+ * @method append
+ * @param {Array} array Array to append
+ * @param {any} value to be added to the Array
+ * @example
+ *
+ * function setup() {
+ *
+ * var myArray = new Array("Mango", "Apple", "Papaya")
+ * print(myArray) // ["Mango", "Apple", "Papaya"]
+ *
+ * append(myArray, "Peach")
+ * print(myArray) // ["Mango", "Apple", "Papaya", "Peach"]
+ *
+ * }
+ *
+ */
+p5.prototype.append = function(array, value) {
+ array.push(value);
+ return array;
+};
+
+/**
+ * Copies an array (or part of an array) to another array. The src array is
+ * copied to the dst array, beginning at the position specified by
+ * srcPosition and into the position specified by dstPosition. The number of
+ * elements to copy is determined by length. Note that copying values
+ * overwrites existing values in the destination array. To append values
+ * instead of overwriting them, use concat().
+ *
+ * The simplified version with only two arguments, arrayCopy(src, dst),
+ * copies an entire array to another of the same size. It is equivalent to
+ * arrayCopy(src, 0, dst, 0, src.length).
+ *
+ * Using this function is far more efficient for copying array data than
+ * iterating through a for() loop and copying each element individually.
+ *
+ * @method arrayCopy
+ * @param {Array} src the source Array
+ * @param {Number} [srcPosition] starting position in the source Array
+ * @param {Array} dst the destination Array
+ * @param {Number} [dstPosition] starting position in the destination Array
+ * @param {Number} [length] number of Array elements to be copied
+ *
+ * @example
+ *
+ * function setup() {
+ *
+ * var src = new Array("A", "B", "C");
+ * var dst = new Array( 1 , 2 , 3 );
+ * var srcPosition = 1;
+ * var dstPosition = 0;
+ * var length = 2;
+ *
+ * print(src); // ["A", "B", "C"]
+ * print(dst); // [ 1 , 2 , 3 ]
+ *
+ * arrayCopy(src, srcPosition, dst, dstPosition, length);
+ * print(dst); // ["B", "C", 3]
+ *
+ * }
+ *
+ */
+p5.prototype.arrayCopy = function(
+ src,
+ srcPosition,
+ dst,
+ dstPosition,
+ length) {
+
+ // the index to begin splicing from dst array
+ var start,
+ end;
+
+ if (typeof length !== 'undefined') {
+
+ end = Math.min(length, src.length);
+ start = dstPosition;
+ src = src.slice(srcPosition, end + srcPosition);
+
+ } else {
+
+ if (typeof dst !== 'undefined') { // src, dst, length
+ // rename so we don't get confused
+ end = dst;
+ end = Math.min(end, src.length);
+ } else { // src, dst
+ end = src.length;
+ }
+
+ start = 0;
+ // rename so we don't get confused
+ dst = srcPosition;
+ src = src.slice(0, end);
+ }
+
+ // Since we are not returning the array and JavaScript is pass by reference
+ // we must modify the actual values of the array
+ // instead of reassigning arrays
+ Array.prototype.splice.apply(dst, [start, end].concat(src));
+
+};
+
+/**
+ * Concatenates two arrays, maps to Array.concat(). Does not modify the
+ * input arrays.
+ *
+ * @method concat
+ * @param {Array} a first Array to concatenate
+ * @param {Array} b second Array to concatenate
+ * @return {Array} concatenated array
+ *
+ * @example
+ *
+ * function setup() {
+ * var arr1 = new Array("A", "B", "C");
+ * var arr2 = new Array( 1 , 2 , 3 );
+ *
+ * print(arr1); // ["A","B","C"]
+ * print(arr2); // [1,2,3]
+ *
+ * var arr3 = concat(arr1, arr2);
+ *
+ * print(arr1); // ["A","B","C"]
+ * print(arr2); // [1,2,3]
+ * print(arr3); // ["A","B","C",1,2,3]
+ *
+ * }
+ *
+ */
+p5.prototype.concat = function(list0, list1) {
+ return list0.concat(list1);
+};
+
+/**
+ * Reverses the order of an array, maps to Array.reverse()
+ *
+ * @method reverse
+ * @param {Array} list Array to reverse
+ * @example
+ *
+ * function setup() {
+ * var myArray = new Array("A", "B", "C");
+ * print(myArray); // ["A","B","C"]
+ *
+ * reverse(myArray);
+ * print(myArray); // ["C","B","A"]
+ * }
+ *
+ */
+p5.prototype.reverse = function(list) {
+ return list.reverse();
+};
+
+/**
+ * Decreases an array by one element and returns the shortened array,
+ * maps to Array.pop().
+ *
+ * @method shorten
+ * @param {Array} list Array to shorten
+ * @return {Array} shortened Array
+ * @example
+ *
+ * function setup() {
+ * var myArray = new Array("A", "B", "C");
+ * print(myArray); // ["A","B","C"]
+ *
+ * var newArray = shorten(myArray);
+ * print(myArray); // ["A","B","C"]
+ * print(newArray); // ["A","B"]
+ * }
+ *
+ */
+p5.prototype.shorten = function(list) {
+ list.pop();
+ return list;
+};
+
+/**
+ * Randomizes the order of the elements of an array. Implements
+ *
+ * Fisher-Yates Shuffle Algorithm.
+ *
+ * @method shuffle
+ * @param {Array} array Array to shuffle
+ * @param {Boolean} [bool] modify passed array
+ * @return {Array} shuffled Array
+ * @example
+ *
+ * function setup() {
+ * var regularArr = ['ABC', 'def', createVector(), TAU, Math.E];
+ * print(regularArr);
+ * shuffle(regularArr, true); // force modifications to passed array
+ * print(regularArr);
+ *
+ * // By default shuffle() returns a shuffled cloned array:
+ * var newArr = shuffle(regularArr);
+ * print(regularArr);
+ * print(newArr);
+ * }
+ *
+ */
+p5.prototype.shuffle = function(arr, bool) {
+ var isView = ArrayBuffer && ArrayBuffer.isView && ArrayBuffer.isView(arr);
+ arr = bool || isView ? arr : arr.slice();
+
+ var rnd, tmp, idx = arr.length;
+ while (idx > 1) {
+ rnd = Math.random()*idx | 0;
+
+ tmp = arr[--idx];
+ arr[idx] = arr[rnd];
+ arr[rnd] = tmp;
+ }
+
+ return arr;
+};
+
+/**
+ * Sorts an array of numbers from smallest to largest, or puts an array of
+ * words in alphabetical order. The original array is not modified; a
+ * re-ordered array is returned. The count parameter states the number of
+ * elements to sort. For example, if there are 12 elements in an array and
+ * count is set to 5, only the first 5 elements in the array will be sorted.
+ *
+ * @method sort
+ * @param {Array} list Array to sort
+ * @param {Number} [count] number of elements to sort, starting from 0
+ *
+ * @example
+ *
+ * function setup() {
+ * var words = new Array("banana", "apple", "pear","lime");
+ * print(words); // ["banana", "apple", "pear", "lime"]
+ * var count = 4; // length of array
+ *
+ * words = sort(words, count);
+ * print(words); // ["apple", "banana", "lime", "pear"]
+ * }
+ *
+ *
+ * function setup() {
+ * var numbers = new Array(2,6,1,5,14,9,8,12);
+ * print(numbers); // [2,6,1,5,14,9,8,12]
+ * var count = 5; // Less than the length of the array
+ *
+ * numbers = sort(numbers, count);
+ * print(numbers); // [1,2,5,6,14,9,8,12]
+ * }
+ *
+ */
+p5.prototype.sort = function(list, count) {
+ var arr = count ? list.slice(0, Math.min(count, list.length)) : list;
+ var rest = count ? list.slice(Math.min(count, list.length)) : [];
+ if (typeof arr[0] === 'string') {
+ arr = arr.sort();
+ } else {
+ arr = arr.sort(function(a,b){return a-b;});
+ }
+ return arr.concat(rest);
+};
+
+/**
+ * Inserts a value or an array of values into an existing array. The first
+ * parameter specifies the initial array to be modified, and the second
+ * parameter defines the data to be inserted. The third parameter is an index
+ * value which specifies the array position from which to insert data.
+ * (Remember that array index numbering starts at zero, so the first position
+ * is 0, the second position is 1, and so on.)
+ *
+ * @method splice
+ * @param {Array} list Array to splice into
+ * @param {any} value value to be spliced in
+ * @param {Number} position in the array from which to insert data
+ *
+ * @example
+ *
+ * function setup() {
+ * var myArray = new Array(0,1,2,3,4);
+ * var insArray = new Array("A","B","C");
+ * print(myArray); // [0,1,2,3,4]
+ * print(insArray); // ["A","B","C"]
+ *
+ * splice(myArray, insArray, 3);
+ * print(myArray); // [0,1,2,"A","B","C",3,4]
+ * }
+ *
+ */
+p5.prototype.splice = function(list, value, index) {
+
+ // note that splice returns spliced elements and not an array
+ Array.prototype.splice.apply(list, [index, 0].concat(value));
+
+ return list;
+};
+
+/**
+ * Extracts an array of elements from an existing array. The list parameter
+ * defines the array from which the elements will be copied, and the start
+ * and count parameters specify which elements to extract. If no count is
+ * given, elements will be extracted from the start to the end of the array.
+ * When specifying the start, remember that the first array element is 0.
+ * This function does not change the source array.
+ *
+ * @method subset
+ * @param {Array} list Array to extract from
+ * @param {Number} start position to begin
+ * @param {Number} [count] number of values to extract
+ * @return {Array} Array of extracted elements
+ *
+ * @example
+ *
+ * function setup() {
+ * var myArray = new Array(1,2,3,4,5);
+ * print(myArray); // [1,2,3,4,5]
+ *
+ * var sub1 = subset(myArray, 0, 3);
+ * var sub2 = subset(myArray, 2, 2);
+ * print(sub1); // [1,2,3]
+ * print(sub2); // [3,4]
+ * }
+ *
+ */
+p5.prototype.subset = function(list, start, count) {
+ if (typeof count !== 'undefined') {
+ return list.slice(start, start + count);
+ } else {
+ return list.slice(start, list.length);
+ }
+};
+
+module.exports = p5;
+
+},{"../core/core":37}],74:[function(_dereq_,module,exports){
+/**
+ * @module Data
+ * @submodule Conversion
+ * @for p5
+ * @requires core
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+
+/**
+ * Converts a string to its floating point representation. The contents of a
+ * string must resemble a number, or NaN (not a number) will be returned.
+ * For example, float("1234.56") evaluates to 1234.56, but float("giraffe")
+ * will return NaN.
+ *
+ * @method float
+ * @param {String} str float string to parse
+ * @return {Number} floating point representation of string
+ * @example
+ *
+ * var str = '20';
+ * var diameter = float(str);
+ * ellipse(width/2, height/2, diameter, diameter);
+ *
+ *
+ * @alt
+ * 20 by 20 white ellipse in the center of the canvas
+ *
+ */
+p5.prototype.float = function(str) {
+ return parseFloat(str);
+};
+
+/**
+ * Converts a boolean, string, or float to its integer representation.
+ * When an array of values is passed in, then an int array of the same length
+ * is returned.
+ *
+ * @method int
+ * @param {String|Boolean|Number|Array} n value to parse
+ * @return {Number} integer representation of value
+ * @example
+ *
+ * print(int("10")); // 10
+ * print(int(10.31)); // 10
+ * print(int(-10)); // -10
+ * print(int(true)); // 1
+ * print(int(false)); // 0
+ * print(int([false, true, "10.3", 9.8])); // [0, 1, 10, 9]
+ *
+ */
+p5.prototype.int = function(n, radix) {
+ if (typeof n === 'string') {
+ radix = radix || 10;
+ return parseInt(n, radix);
+ } else if (typeof n === 'number') {
+ return n | 0;
+ } else if (typeof n === 'boolean') {
+ return n ? 1 : 0;
+ } else if (n instanceof Array) {
+ return n.map(function(n) { return p5.prototype.int(n, radix); });
+ }
+};
+
+/**
+ * Converts a boolean, string or number to its string representation.
+ * When an array of values is passed in, then an array of strings of the same
+ * length is returned.
+ *
+ * @method str
+ * @param {String|Boolean|Number|Array} n value to parse
+ * @return {String} string representation of value
+ * @example
+ *
+ * print(str("10")); // "10"
+ * print(str(10.31)); // "10.31"
+ * print(str(-10)); // "-10"
+ * print(str(true)); // "true"
+ * print(str(false)); // "false"
+ * print(str([true, "10.3", 9.8])); // [ "true", "10.3", "9.8" ]
+ *
+ */
+p5.prototype.str = function(n) {
+ if (n instanceof Array) {
+ return n.map(p5.prototype.str);
+ } else {
+ return String(n);
+ }
+};
+
+/**
+ * Converts a number or string to its boolean representation.
+ * For a number, any non-zero value (positive or negative) evaluates to true,
+ * while zero evaluates to false. For a string, the value "true" evaluates to
+ * true, while any other value evaluates to false. When an array of number or
+ * string values is passed in, then a array of booleans of the same length is
+ * returned.
+ *
+ * @method boolean
+ * @param {String|Boolean|Number|Array} n value to parse
+ * @return {Boolean} boolean representation of value
+ * @example
+ *
+ * print(boolean(0)); // false
+ * print(boolean(1)); // true
+ * print(boolean("true")); // true
+ * print(boolean("abcd")); // false
+ * print(boolean([0, 12, "true"])); // [false, true, false]
+ *
+ */
+p5.prototype.boolean = function(n) {
+ if (typeof n === 'number') {
+ return n !== 0;
+ } else if (typeof n === 'string') {
+ return n.toLowerCase() === 'true';
+ } else if (typeof n === 'boolean') {
+ return n;
+ } else if (n instanceof Array) {
+ return n.map(p5.prototype.boolean);
+ }
+};
+
+/**
+ * Converts a number, string or boolean to its byte representation.
+ * A byte can be only a whole number between -128 and 127, so when a value
+ * outside of this range is converted, it wraps around to the corresponding
+ * byte representation. When an array of number, string or boolean values is
+ * passed in, then an array of bytes the same length is returned.
+ *
+ * @method byte
+ * @param {String|Boolean|Number|Array} n value to parse
+ * @return {Number} byte representation of value
+ * @example
+ *
+ * print(byte(127)); // 127
+ * print(byte(128)); // -128
+ * print(byte(23.4)); // 23
+ * print(byte("23.4")); // 23
+ * print(byte(true)); // 1
+ * print(byte([0, 255, "100"])); // [0, -1, 100]
+ *
+ */
+p5.prototype.byte = function(n) {
+ var nn = p5.prototype.int(n, 10);
+ if (typeof nn === 'number') {
+ return ((nn + 128) % 256) - 128;
+ } else if (nn instanceof Array) {
+ return nn.map(p5.prototype.byte);
+ }
+};
+
+/**
+ * Converts a number or string to its corresponding single-character
+ * string representation. If a string parameter is provided, it is first
+ * parsed as an integer and then translated into a single-character string.
+ * When an array of number or string values is passed in, then an array of
+ * single-character strings of the same length is returned.
+ *
+ * @method char
+ * @param {String|Number|Array} n value to parse
+ * @return {String} string representation of value
+ * @example
+ *
+ * print(char(65)); // "A"
+ * print(char("65")); // "A"
+ * print(char([65, 66, 67])); // [ "A", "B", "C" ]
+ * print(join(char([65, 66, 67]), '')); // "ABC"
+ *
+ */
+p5.prototype.char = function(n) {
+ if (typeof n === 'number' && !isNaN(n)) {
+ return String.fromCharCode(n);
+ } else if (n instanceof Array) {
+ return n.map(p5.prototype.char);
+ } else if (typeof n === 'string') {
+ return p5.prototype.char(parseInt(n, 10));
+ }
+};
+
+/**
+ * Converts a single-character string to its corresponding integer
+ * representation. When an array of single-character string values is passed
+ * in, then an array of integers of the same length is returned.
+ *
+ * @method unchar
+ * @param {String|Array} n value to parse
+ * @return {Number} integer representation of value
+ * @example
+ *
+ * print(unchar("A")); // 65
+ * print(unchar(["A", "B", "C"])); // [ 65, 66, 67 ]
+ * print(unchar(split("ABC", ""))); // [ 65, 66, 67 ]
+ *
+ */
+p5.prototype.unchar = function(n) {
+ if (typeof n === 'string' && n.length === 1) {
+ return n.charCodeAt(0);
+ } else if (n instanceof Array) {
+ return n.map(p5.prototype.unchar);
+ }
+};
+
+/**
+ * Converts a number to a string in its equivalent hexadecimal notation. If a
+ * second parameter is passed, it is used to set the number of characters to
+ * generate in the hexadecimal notation. When an array is passed in, an
+ * array of strings in hexadecimal notation of the same length is returned.
+ *
+ * @method hex
+ * @param {Number|Array} n value to parse
+ * @return {String} hexadecimal string representation of value
+ * @example
+ *
+ * print(hex(255)); // "000000FF"
+ * print(hex(255, 6)); // "0000FF"
+ * print(hex([0, 127, 255], 6)); // [ "000000", "00007F", "0000FF" ]
+ *
+ */
+p5.prototype.hex = function(n, digits) {
+ digits = (digits === undefined || digits === null) ? digits = 8 : digits;
+ if (n instanceof Array) {
+ return n.map(function(n) { return p5.prototype.hex(n, digits); });
+ } else if (typeof n === 'number') {
+ if (n < 0) {
+ n = 0xFFFFFFFF + n + 1;
+ }
+ var hex = Number(n).toString(16).toUpperCase();
+ while (hex.length < digits) {
+ hex = '0' + hex;
+ }
+ if (hex.length >= digits) {
+ hex = hex.substring(hex.length - digits, hex.length);
+ }
+ return hex;
+ }
+};
+
+/**
+ * Converts a string representation of a hexadecimal number to its equivalent
+ * integer value. When an array of strings in hexadecimal notation is passed
+ * in, an array of integers of the same length is returned.
+ *
+ * @method unhex
+ * @param {String|Array} n value to parse
+ * @return {Number} integer representation of hexadecimal value
+ * @example
+ *
+ * print(unhex("A")); // 10
+ * print(unhex("FF")); // 255
+ * print(unhex(["FF", "AA", "00"])); // [ 255, 170, 0 ]
+ *
+ */
+p5.prototype.unhex = function(n) {
+ if (n instanceof Array) {
+ return n.map(p5.prototype.unhex);
+ } else {
+ return parseInt('0x' + n, 16);
+ }
+};
+
+module.exports = p5;
+
+},{"../core/core":37}],75:[function(_dereq_,module,exports){
+/**
+ * @module Data
+ * @submodule String Functions
+ * @for p5
+ * @requires core
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+
+//return p5; //LM is this a mistake?
+
+/**
+ * Combines an array of Strings into one String, each separated by the
+ * character(s) used for the separator parameter. To join arrays of ints or
+ * floats, it's necessary to first convert them to Strings using nf() or
+ * nfs().
+ *
+ * @method join
+ * @param {Array} list array of Strings to be joined
+ * @param {String} separator String to be placed between each item
+ * @return {String} joined String
+ * @example
+ *
+ *
+ * var array = ["Hello", "world!"]
+ * var separator = " "
+ * var message = join(array, separator);
+ * text(message, 5, 50);
+ *
+ *
+ *
+ * @alt
+ * "hello world!" displayed middle left of canvas.
+ *
+ */
+p5.prototype.join = function(list, separator) {
+ return list.join(separator);
+};
+
+/**
+ * This function is used to apply a regular expression to a piece of text,
+ * and return matching groups (elements found inside parentheses) as a
+ * String array. If there are no matches, a null value will be returned.
+ * If no groups are specified in the regular expression, but the sequence
+ * matches, an array of length 1 (with the matched text as the first element
+ * of the array) will be returned.
+ *
+ * To use the function, first check to see if the result is null. If the
+ * result is null, then the sequence did not match at all. If the sequence
+ * did match, an array is returned.
+ *
+ * If there are groups (specified by sets of parentheses) in the regular
+ * expression, then the contents of each will be returned in the array.
+ * Element [0] of a regular expression match returns the entire matching
+ * string, and the match groups start at element [1] (the first group is [1],
+ * the second [2], and so on).
+ *
+ * @method match
+ * @param {String} str the String to be searched
+ * @param {String} regexp the regexp to be used for matching
+ * @return {Array} Array of Strings found
+ * @example
+ *
+ *
+ * var string = "Hello p5js*!"
+ * var regexp = "p5js\\*"
+ * var match = match(string, regexp);
+ * text(match, 5, 50);
+ *
+ *
+ *
+ * @alt
+ * "p5js*" displayed middle left of canvas.
+ *
+ */
+p5.prototype.match = function(str, reg) {
+ return str.match(reg);
+};
+
+/**
+ * This function is used to apply a regular expression to a piece of text,
+ * and return a list of matching groups (elements found inside parentheses)
+ * as a two-dimensional String array. If there are no matches, a null value
+ * will be returned. If no groups are specified in the regular expression,
+ * but the sequence matches, a two dimensional array is still returned, but
+ * the second dimension is only of length one.
+ *
+ * To use the function, first check to see if the result is null. If the
+ * result is null, then the sequence did not match at all. If the sequence
+ * did match, a 2D array is returned.
+ *
+ * If there are groups (specified by sets of parentheses) in the regular
+ * expression, then the contents of each will be returned in the array.
+ * Assuming a loop with counter variable i, element [i][0] of a regular
+ * expression match returns the entire matching string, and the match groups
+ * start at element [i][1] (the first group is [i][1], the second [i][2],
+ * and so on).
+ *
+ * @method matchAll
+ * @param {String} str the String to be searched
+ * @param {String} regexp the regexp to be used for matching
+ * @return {Array} 2d Array of Strings found
+ * @example
+ *
+ *
+ * var string = "Hello p5js*! Hello world!"
+ * var regexp = "Hello"
+ * matchAll(string, regexp);
+ *
+ *
+
+ */
+p5.prototype.matchAll = function(str, reg) {
+ var re = new RegExp(reg, 'g');
+ var match = re.exec(str);
+ var matches = [];
+ while (match !== null) {
+ matches.push(match);
+ // matched text: match[0]
+ // match start: match.index
+ // capturing group n: match[n]
+ match = re.exec(str);
+ }
+ return matches;
+};
+
+/**
+ * Utility function for formatting numbers into strings. There are two
+ * versions: one for formatting floats, and one for formatting ints.
+ * The values for the digits, left, and right parameters should always
+ * be positive integers.
+ *
+ * @method nf
+ * @param {Number|Array} num the Number to format
+ * @param {Number} [left] number of digits to the left of the
+ * decimal point
+ * @param {Number} [right] number of digits to the right of the
+ * decimal point
+ * @return {String|Array} formatted String
+ * @example
+ *
+ *
+ * function setup() {
+ * background(200);
+ * var num = 112.53106115;
+ *
+ * noStroke();
+ * fill(0);
+ * textSize(14);
+ * // Draw formatted numbers
+ * text(nf(num, 5, 2), 10, 20);
+ *
+ * text(nf(num, 4, 3), 10, 55);
+ *
+ * text(nf(num, 3, 6), 10, 85);
+ *
+ * // Draw dividing lines
+ * stroke(120);
+ * line(0, 30, width, 30);
+ * line(0, 65, width, 65);
+ * }
+ *
+ *
+ *
+ * @alt
+ * "0011253" top left, "0112.531" mid left, "112.531061" bottom left canvas
+ *
+ */
+p5.prototype.nf = function () {
+ if (arguments[0] instanceof Array) {
+ var a = arguments[1];
+ var b = arguments[2];
+ return arguments[0].map(function (x) {
+ return doNf(x, a, b);
+ });
+ }
+ else{
+ var typeOfFirst = Object.prototype.toString.call(arguments[0]);
+ if(typeOfFirst === '[object Arguments]'){
+ if(arguments[0].length===3){
+ return this.nf(arguments[0][0],arguments[0][1],arguments[0][2]);
+ }
+ else if(arguments[0].length===2){
+ return this.nf(arguments[0][0],arguments[0][1]);
+ }
+ else{
+ return this.nf(arguments[0][0]);
+ }
+ }
+ else {
+ return doNf.apply(this, arguments);
+ }
+ }
+};
+
+function doNf() {
+ var num = arguments[0];
+ var neg = num < 0;
+ var n = neg ? num.toString().substring(1) : num.toString();
+ var decimalInd = n.indexOf('.');
+ var intPart = decimalInd !== -1 ? n.substring(0, decimalInd) : n;
+ var decPart = decimalInd !== -1 ? n.substring(decimalInd + 1) : '';
+ var str = neg ? '-' : '';
+ if (arguments.length === 3) {
+ var decimal = '';
+ if(decimalInd !== -1 || arguments[2] - decPart.length > 0){
+ decimal = '.';
+ }
+ if (decPart.length > arguments[2]) {
+ decPart = decPart.substring(0, arguments[2]);
+ }
+ for (var i = 0; i < arguments[1] - intPart.length; i++) {
+ str += '0';
+ }
+ str += intPart;
+ str += decimal;
+ str += decPart;
+ for (var j = 0; j < arguments[2] - decPart.length; j++) {
+ str += '0';
+ }
+ return str;
+ }
+ else {
+ for (var k = 0; k < Math.max(arguments[1] - intPart.length, 0); k++) {
+ str += '0';
+ }
+ str += n;
+ return str;
+ }
+}
+
+/**
+ * Utility function for formatting numbers into strings and placing
+ * appropriate commas to mark units of 1000. There are two versions: one
+ * for formatting ints, and one for formatting an array of ints. The value
+ * for the right parameter should always be a positive integer.
+ *
+ * @method nfc
+ * @param {Number|Array} num the Number to format
+ * @param {Number} [right] number of digits to the right of the
+ * decimal point
+ * @return {String|Array} formatted String
+ * @example
+ *
+ *
+ * function setup() {
+ * background(200);
+ * var num = 11253106.115;
+ * var numArr = new Array(1,1,2);
+ *
+ * noStroke();
+ * fill(0);
+ * textSize(12);
+ *
+ * // Draw formatted numbers
+ * text(nfc(num, 4, 2), 10, 30);
+ * text(nfc(numArr, 2, 1), 10, 80);
+ *
+ * // Draw dividing line
+ * stroke(120);
+ * line(0, 50, width, 50);
+ * }
+ *
+ *
+ *
+ * @alt
+ * "11,253,106.115" top middle and "1.00,1.00,2.00" displayed bottom mid
+ *
+ */
+p5.prototype.nfc = function () {
+ if (arguments[0] instanceof Array) {
+ var a = arguments[1];
+ return arguments[0].map(function (x) {
+ return doNfc(x, a);
+ });
+ } else {
+ return doNfc.apply(this, arguments);
+ }
+};
+function doNfc() {
+ var num = arguments[0].toString();
+ var dec = num.indexOf('.');
+ var rem = dec !== -1 ? num.substring(dec) : '';
+ var n = dec !== -1 ? num.substring(0, dec) : num;
+ n = n.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
+ if (arguments[1] === 0) {
+ rem = '';
+ }
+ else if(arguments[1] !== undefined){
+ if(arguments[1] > rem.length){
+ rem+= dec === -1 ? '.' : '';
+ var len = arguments[1] - rem.length + 1;
+ for(var i =0; i< len; i++){
+ rem += '0';
+ }
+ }
+ else{
+ rem = rem.substring(0, arguments[1] + 1);
+ }
+ }
+ return n + rem;
+}
+
+/**
+ * Utility function for formatting numbers into strings. Similar to nf() but
+ * puts a "+" in front of positive numbers and a "-" in front of negative
+ * numbers. There are two versions: one for formatting floats, and one for
+ * formatting ints. The values for left, and right parameters
+ * should always be positive integers.
+ *
+ * @method nfp
+ * @param {Number|Array} num the Number to format
+ * @param {Number} [left] number of digits to the left of the decimal
+ * point
+ * @param {Number} [right] number of digits to the right of the
+ * decimal point
+ * @return {String|Array} formatted String
+ * @example
+ *
+ *
+ * function setup() {
+ * background(200);
+ * var num1 = 11253106.115;
+ * var num2 = -11253106.115;
+ *
+ * noStroke();
+ * fill(0);
+ * textSize(12);
+ *
+ * // Draw formatted numbers
+ * text(nfp(num1, 4, 2), 10, 30);
+ * text(nfp(num2, 4, 2), 10, 80);
+ *
+ * // Draw dividing line
+ * stroke(120);
+ * line(0, 50, width, 50);
+ * }
+ *
+ *
+ *
+ * @alt
+ * "+11253106.11" top middle and "-11253106.11" displayed bottom middle
+ *
+ */
+p5.prototype.nfp = function() {
+ var nfRes = this.nf.apply(this, arguments);
+ if (nfRes instanceof Array) {
+ return nfRes.map(addNfp);
+ } else {
+ return addNfp(nfRes);
+ }
+};
+
+function addNfp() {
+ return (
+ parseFloat(arguments[0]) > 0) ?
+ '+'+arguments[0].toString() :
+ arguments[0].toString();
+}
+
+/**
+ * Utility function for formatting numbers into strings. Similar to nf() but
+ * puts a " " (space) in front of positive numbers and a "-" in front of
+ * negative numbers. There are two versions: one for formatting floats, and
+ * one for formatting ints. The values for the digits, left, and right
+ * parameters should always be positive integers.
+ *
+ * @method nfs
+ * @param {Number|Array} num the Number to format
+ * @param {Number} [left] number of digits to the left of the decimal
+ * point
+ * @param {Number} [right] number of digits to the right of the
+ * decimal point
+ * @return {String|Array} formatted String
+ * @example
+ *
+ *
+ * function setup() {
+ * background(200);
+ * var num1 = 11253106.115;
+ * var num2 = -11253106.115;
+ *
+ * noStroke();
+ * fill(0);
+ * textSize(12);
+ * // Draw formatted numbers
+ * text(nfs(num1, 4, 2), 10, 30);
+ *
+ * text(nfs(num2, 4, 2), 10, 80);
+ *
+ * // Draw dividing line
+ * stroke(120);
+ * line(0, 50, width, 50);
+ * }
+ *
+ *
+ *
+ * @alt
+ * "11253106.11" top middle and "-11253106.11" displayed bottom middle
+ *
+ */
+p5.prototype.nfs = function() {
+ var nfRes = this.nf.apply(this, arguments);
+ if (nfRes instanceof Array) {
+ return nfRes.map(addNfs);
+ } else {
+ return addNfs(nfRes);
+ }
+};
+
+function addNfs() {
+ return (
+ parseFloat(arguments[0]) > 0) ?
+ ' '+arguments[0].toString() :
+ arguments[0].toString();
+}
+
+/**
+ * The split() function maps to String.split(), it breaks a String into
+ * pieces using a character or string as the delimiter. The delim parameter
+ * specifies the character or characters that mark the boundaries between
+ * each piece. A String[] array is returned that contains each of the pieces.
+ *
+ * The splitTokens() function works in a similar fashion, except that it
+ * splits using a range of characters instead of a specific character or
+ * sequence.
+ *
+ * @method split
+ * @param {String} value the String to be split
+ * @param {String} delim the String used to separate the data
+ * @return {Array} Array of Strings
+ * @example
+ *
+ *
+ * var names = "Pat,Xio,Alex"
+ * var splitString = split(names, ",");
+ * text(splitString[0], 5, 30);
+ * text(splitString[1], 5, 50);
+ * text(splitString[2], 5, 70);
+ *
+ *
+ *
+ * @alt
+ * "pat" top left, "Xio" mid left and "Alex" displayed bottom left
+ *
+ */
+p5.prototype.split = function(str, delim) {
+ return str.split(delim);
+};
+
+/**
+ * The splitTokens() function splits a String at one or many character
+ * delimiters or "tokens." The delim parameter specifies the character or
+ * characters to be used as a boundary.
+ *
+ * If no delim characters are specified, any whitespace character is used to
+ * split. Whitespace characters include tab (\t), line feed (\n), carriage
+ * return (\r), form feed (\f), and space.
+ *
+ * @method splitTokens
+ * @param {String} value the String to be split
+ * @param {String} [delim] list of individual Strings that will be used as
+ * separators
+ * @return {Array} Array of Strings
+ * @example
+ *
+ *
+ * function setup() {
+ * var myStr = "Mango, Banana, Lime";
+ * var myStrArr = splitTokens(myStr, ",");
+ *
+ * print(myStrArr); // prints : ["Mango"," Banana"," Lime"]
+ * }
+ *
+ *
+ */
+p5.prototype.splitTokens = function() {
+ var d,sqo,sqc,str;
+ str = arguments[1];
+ if (arguments.length > 1) {
+ sqc = /\]/g.exec(str);
+ sqo = /\[/g.exec(str);
+ if ( sqo && sqc ) {
+ str = str.slice(0, sqc.index) + str.slice(sqc.index+1);
+ sqo = /\[/g.exec(str);
+ str = str.slice(0, sqo.index) + str.slice(sqo.index+1);
+ d = new RegExp('[\\['+str+'\\]]','g');
+ } else if ( sqc ) {
+ str = str.slice(0, sqc.index) + str.slice(sqc.index+1);
+ d = new RegExp('[' + str + '\\]]', 'g');
+ } else if(sqo) {
+ str = str.slice(0, sqo.index) + str.slice(sqo.index+1);
+ d = new RegExp('[' + str + '\\[]', 'g');
+ } else {
+ d = new RegExp('[' + str + ']', 'g');
+ }
+ } else {
+ d = /\s/g;
+ }
+ return arguments[0].split(d).filter(function(n){return n;});
+};
+
+/**
+ * Removes whitespace characters from the beginning and end of a String. In
+ * addition to standard whitespace characters such as space, carriage return,
+ * and tab, this function also removes the Unicode "nbsp" character.
+ *
+ * @method trim
+ * @param {String|Array} str a String or Array of Strings to be trimmed
+ * @return {String|Array} a trimmed String or Array of Strings
+ * @example
+ *
+ *
+ * var string = trim(" No new lines\n ");
+ * text(string +" here", 2, 50);
+ *
+ *
+ *
+ * @alt
+ * "No new lines here" displayed center canvas
+ *
+ */
+p5.prototype.trim = function(str) {
+ if (str instanceof Array) {
+ return str.map(this.trim);
+ } else {
+ return str.trim();
+ }
+};
+
+module.exports = p5;
+
+},{"../core/core":37}],76:[function(_dereq_,module,exports){
+/**
+ * @module IO
+ * @submodule Time & Date
+ * @for p5
+ * @requires core
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+
+/**
+ * p5.js communicates with the clock on your computer. The day() function
+ * returns the current day as a value from 1 - 31.
+ *
+ * @method day
+ * @return {Number} the current day
+ * @example
+ *
+ *
+ * var d = day();
+ * text("Current day: \n" + d, 5, 50);
+ *
+ *
+ *
+ * @alt
+ * Current day is displayed
+ *
+ */
+p5.prototype.day = function() {
+ return new Date().getDate();
+};
+
+/**
+ * p5.js communicates with the clock on your computer. The hour() function
+ * returns the current hour as a value from 0 - 23.
+ *
+ * @method hour
+ * @return {Number} the current hour
+ * @example
+ *
+ *
+ * var h = hour();
+ * text("Current hour:\n" + h, 5, 50);
+ *
+ *
+ *
+ * @alt
+ * Current hour is displayed
+ *
+ */
+p5.prototype.hour = function() {
+ return new Date().getHours();
+};
+
+/**
+ * p5.js communicates with the clock on your computer. The minute() function
+ * returns the current minute as a value from 0 - 59.
+ *
+ * @method minute
+ * @return {Number} the current minute
+ * @example
+ *
+ *
+ * var m = minute();
+ * text("Current minute: \n" + m, 5, 50);
+ *
+ *
+ *
+ * @alt
+ * Current minute is displayed
+ *
+ */
+p5.prototype.minute = function() {
+ return new Date().getMinutes();
+};
+
+/**
+ * Returns the number of milliseconds (thousandths of a second) since
+ * starting the program. This information is often used for timing events and
+ * animation sequences.
+ *
+ * @method millis
+ * @return {Number} the number of milliseconds since starting the program
+ * @example
+ *
+ *
+ * var millisecond = millis();
+ * text("Milliseconds \nrunning: \n" + millisecond, 5, 40);
+ *
+ *
+ *
+ * @alt
+ * number of milliseconds since program has started displayed
+ *
+ */
+p5.prototype.millis = function() {
+ return window.performance.now();
+};
+
+/**
+ * p5.js communicates with the clock on your computer. The month() function
+ * returns the current month as a value from 1 - 12.
+ *
+ * @method month
+ * @return {Number} the current month
+ * @example
+ *
+ *
+ * var m = month();
+ * text("Current month: \n" + m, 5, 50);
+ *
+ *
+ *
+ * @alt
+ * Current month is displayed
+ *
+ */
+p5.prototype.month = function() {
+ return new Date().getMonth() + 1; //January is 0!
+};
+
+/**
+ * p5.js communicates with the clock on your computer. The second() function
+ * returns the current second as a value from 0 - 59.
+ *
+ * @method second
+ * @return {Number} the current second
+ * @example
+ *
+ *
+ * var s = second();
+ * text("Current second: \n" + s, 5, 50);
+ *
+ *
+ *
+ * @alt
+ * Current second is displayed
+ *
+ */
+p5.prototype.second = function() {
+ return new Date().getSeconds();
+};
+
+/**
+ * p5.js communicates with the clock on your computer. The year() function
+ * returns the current year as an integer (2014, 2015, 2016, etc).
+ *
+ * @method year
+ * @return {Number} the current year
+ * @example
+ *
+ *
+ * var y = year();
+ * text("Current year: \n" + y, 5, 50);
+ *
+ *
+ *
+ * @alt
+ * Current year is displayed
+ *
+ */
+p5.prototype.year = function() {
+ return new Date().getFullYear();
+};
+
+module.exports = p5;
+
+},{"../core/core":37}],77:[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){
+ var args = new Array(arguments.length);
+ for (var i = 0; i < args.length; ++i) {
+ args[i] = arguments[i];
+ }
+ this._validateParameters(
+ 'camera',
+ args,
+ ['Number', 'Number', 'Number']
+ );
+ //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);
+ * perspective(60 / 180 * PI, width/height, 0.1, 100);
+ * }
+ * 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) {
+ var args = new Array(arguments.length);
+ for (var i = 0; i < args.length; ++i) {
+ args[i] = arguments[i];
+ }
+ this._validateParameters(
+ 'perspective',
+ args,
+ ['Number', 'Number', 'Number', 'Number']
+ );
+ 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.1, 100);
+ * }
+ * 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) {
+ var args = new Array(arguments.length);
+ for (var i = 0; i < args.length; ++i) {
+ args[i] = arguments[i];
+ }
+ this._validateParameters(
+ 'ortho',
+ args,
+ ['Number', 'Number', 'Number', 'Number', 'Number', 'Number']
+ );
+ left /= this.width;
+ right /= this.width;
+ top /= this.height;
+ bottom /= 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":37}],78:[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":37}],79:[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|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
+ * @return {p5} the p5 object
+ * @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
+ * @return {p5} the p5 object
+ * @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) {
+ // TODO(jgessner): Find an example using this and profile it.
+ // var args = new Array(arguments.length);
+ // for (var i = 0; i < args.length; ++i) {
+ // args[i] = arguments[i];
+ // }
+ // this._validateParameters(
+ // 'directionalLight',
+ // args,
+ // [
+ // //rgbaxyz
+ // ['Number', 'Number', 'Number', 'Number', 'Number', 'Number', 'Number'],
+ // //rgbxyz
+ // ['Number', 'Number', 'Number', 'Number', 'Number', 'Number'],
+ // //caxyz
+ // ['Number', 'Number', 'Number', 'Number', 'Number'],
+ // //cxyz
+ // ['Number', 'Number', 'Number', 'Number'],
+ // ['String', 'Number', 'Number', 'Number'],
+ // ['Array', 'Number', 'Number', 'Number'],
+ // ['Object', 'Number', 'Number', 'Number'],
+ // //rgbavector
+ // ['Number', 'Number', 'Number', 'Number', 'Object'],
+ // //rgbvector
+ // ['Number', 'Number', 'Number', 'Object'],
+ // //cavector
+ // ['Number', 'Number', 'Object'],
+ // //cvector
+ // ['Number', 'Object'],
+ // ['String', 'Object'],
+ // ['Array', 'Object'],
+ // ['Object', 'Object']
+ // ]
+ // );
+
+ 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
+ * @return {p5} the p5 object
+ * @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) {
+ // TODO(jgessner): Find an example using this and profile it.
+ // var args = new Array(arguments.length);
+ // for (var i = 0; i < args.length; ++i) {
+ // args[i] = arguments[i];
+ // }
+ // this._validateParameters(
+ // 'pointLight',
+ // arguments,
+ // [
+ // //rgbaxyz
+ // ['Number', 'Number', 'Number', 'Number', 'Number', 'Number', 'Number'],
+ // //rgbxyz
+ // ['Number', 'Number', 'Number', 'Number', 'Number', 'Number'],
+ // //caxyz
+ // ['Number', 'Number', 'Number', 'Number', 'Number'],
+ // //cxyz
+ // ['Number', 'Number', 'Number', 'Number'],
+ // ['String', 'Number', 'Number', 'Number'],
+ // ['Array', 'Number', 'Number', 'Number'],
+ // ['Object', 'Number', 'Number', 'Number'],
+ // //rgbavector
+ // ['Number', 'Number', 'Number', 'Number', 'Object'],
+ // //rgbvector
+ // ['Number', 'Number', 'Number', 'Object'],
+ // //cavector
+ // ['Number', 'Number', 'Object'],
+ // //cvector
+ // ['Number', 'Object'],
+ // ['String', 'Object'],
+ // ['Array', 'Object'],
+ // ['Object', 'Object']
+ // ]
+ // );
+
+ 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":37}],80:[function(_dereq_,module,exports){
+/**
+ * @module Shape
+ * @submodule 3D Models
+ * @for p5
+ * @requires core
+ * @requires p5.Geometry3D
+ */
+
+'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.Geometry3D)} [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.Geometry3D 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":37,"./p5.Geometry":82}],81:[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
+ * @return {p5} the p5 object
+ * @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
+ * @return {p5} the p5 object
+ * @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;
+ 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
+* @return {p5} the p5 object
+ * @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
+ * @return {p5} the p5 object
+ * @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":37}],82:[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
+ * @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;
+};
+
+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.
+ */
+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
+ * @return {p5.Geometry}
+ */
+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
+ * @return {p5.Geometry}
+ */
+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.
+ * @return {p5.Geometry}
+ */
+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":37}],83:[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|Array} [inMatrix] the input p5.Matrix or
+ * an Array of length 16
+ */
+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 | Typed Array} a the matrix to be based on to transpose
+ * @return {p5.Matrix} this
+ */
+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 or Typed Array} a the matrix to be based on to invert
+ * @return {p5.Matrix} this
+ */
+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
+ * @return {[type]} [description]
+ */
+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
+ * @return {p5.Matrix} this
+ */
+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
+ * @return {p5.Matrix} this with mat3
+ * @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 | Array} multMatrix The matrix we want to multiply by
+ * @return {p5.Matrix} this
+ */
+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 | Array }
+ * vector to scale by
+ * @return {p5.Matrix} this
+ */
+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
+ * @return {p5.Matrix} this
+ * 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 {Array} v vector to translate by
+ * @return {p5.Matrix} this
+ */
+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
+ * @return {void}
+ */
+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
+ * @return {void}
+ */
+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":36,"../core/core":37,"../math/polargeometry":67}],84:[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.
+ * @return {[type]} [description]
+ */
+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
+ * @return {p5.RendererGL} [description]
+ * @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.
+ * @return {p5.RendererGL} [description]
+ */
+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'){
+ 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 {Array} vertices Numbers array representing
+ * vertex positions
+ * @return {p5.RendererGL}
+ */
+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":36,"../core/core":37}],85:[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
+ * @return {p5.RendererGL} this
+ */
+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 {Array} arr an array of p5.Vector
+ * @return {Array]} 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":37}],86:[function(_dereq_,module,exports){
+'use strict';
+
+var p5 = _dereq_('../core/core');
+var shader = _dereq_('./shader');
+_dereq_('../core/p5.Renderer');
+_dereq_('./p5.Matrix');
+var uMVMatrixStack = [];
+var RESOLUTION = 1000;
+
+//@TODO should implement public method
+//to override these attributes
+var attributes = {
+ alpha: true,
+ depth: true,
+ stencil: true,
+ antialias: false,
+ premultipliedAlpha: false,
+ preserveDrawingBuffer: false
+};
+
+/**
+ * @class p5.RendererGL
+ * @constructor
+ * @extends p5.Renderer
+ * 3D graphics class.
+ * @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();
+ this.uPMatrix.perspective(60 / 180 * Math.PI, _w / _h, 0.1, 100);
+ 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]
+ * @return {[type]} [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 {[type]} [description]
+ */
+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);
+ shaderProgram.uResolution =
+ gl.getUniformLocation(shaderProgram, 'uResolution');
+ gl.uniform1f(shaderProgram.uResolution, RESOLUTION);
+
+ //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.
+ * @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
+ var mId, shaderProgram;
+ if(this.drawMode !== 'fill' && this.curShaderId === undefined){
+ //default shader: normalMaterial()
+ mId = 'normalVert|normalFrag';
+ shaderProgram = this._initShaders('normalVert', 'normalFrag');
+ this.mHash[mId] = shaderProgram;
+ this.curShaderId = mId;
+ } else if(this.isImmediateDrawing && this.drawMode === 'fill'){
+ mId = 'immediateVert|vertexColorFrag';
+ shaderProgram = this._initShaders('immediateVert', 'vertexColorFrag');
+ this.mHash[mId] = shaderProgram;
+ this.curShaderId = mId;
+ }
+ 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
+ * @return {p5} the p5 object
+ * @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
+ * @return {[type]} [description]
+ * @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 {[type]} w [description]
+ * @param {[tyoe]} h [description]
+ * @return {[type]} [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 {[type]} x [description]
+ * @param {[type]} y [description]
+ * @param {[type]} z [description]
+ * @return {[type]} [description]
+ * @todo implement handle for components or vector as args
+ */
+p5.RendererGL.prototype.translate = function(x, y, z) {
+ //@TODO: figure out how to fit the resolution
+ x = x / RESOLUTION;
+ y = -y / RESOLUTION;
+ z = z / RESOLUTION;
+ 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
+ * @return {this} [description]
+ */
+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]
+ * @return {[type]} [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":37,"../core/p5.Renderer":43,"./p5.Matrix":83,"./shader":88}],87:[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(200, 200);
+ * }
+ *
+ *
+ *
+ * @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
+ * @return {p5} the p5 object
+ * @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
+ * @return {p5} the p5 object
+ * @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];
+ }
+ //@todo validate params here
+ //
+ 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
+ * @return {p5} the p5 object
+ * @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
+ * @return {p5} the p5 object
+ * @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 raduis
+ * @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.
+ * @return {p5} the p5 object
+ * @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
+ * @return {p5} the p5 object
+ * @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];
+ }
+ //@todo validate params here
+ //
+ 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;
+};
+
+module.exports = p5;
+
+},{"../core/core":37,"./p5.Geometry":82}],88:[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 / uResolution *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;\nuniform float uResolution;\n\nvarying vec4 vColor;\n\nvoid main(void) {\n vec4 positionVec4 = vec4(aPosition / uResolution * 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;\nuniform float uResolution;\n\nvarying vec3 vVertexNormal;\nvarying highp vec2 vVertTexCoord;\n\nvoid main(void) {\n vec4 positionVec4 = vec4(aPosition / uResolution * 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 float uResolution;\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 / uResolution, 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 / uResolution, 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 //loc = loc / uResolution;\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}"
+};
+},{}]},{},[28])(28)
+});
\ No newline at end of file
diff --git a/minesweeper/sketch.js b/minesweeper/sketch.js
new file mode 100644
index 0000000..83c9804
--- /dev/null
+++ b/minesweeper/sketch.js
@@ -0,0 +1,239 @@
+//Felix Albrigtsen 2019
+//Main code for minesweeper, dependent on cell.js, and partly on ai.js.
+
+//Display and board settings
+var cellSize = 23;
+var padding = 3;
+var rows = 16;
+var cols = 16;
+var mineCount = 30;
+var canvWidth = (cols * (cellSize + padding)) + 1.5*padding;
+var canvHeight = (rows * (cellSize + padding)) + 1.5*padding;
+
+//Game state variables
+var finished = false;
+var gameover = false;
+var markedBombs = 0;
+var board = [];
+
+//Calculate these values once, instead of each time
+var neighborIndexOffsets = [-cols-1, -cols, -cols+1, -1, 1, cols-1, cols, cols+1];
+
+var refreshPage = true; //Refresh when changing difficulty
+var refreshPage_reset = false; // Do not refresh when hitting R, it takes too long
+
+
+function setDifficulty(level) {
+ switch (level) {
+ case 0:
+ rows = 10;
+ cols = 10;
+ padding = 3;
+ mineCount = 10;
+ break;
+ case 1:
+ rows = 16;
+ cols = 16;
+ padding = 3;
+ mineCount = 38;
+ break;
+ case 2:
+ rows = 20;
+ cols = 22;
+ padding = 2;
+ mineCount = 88;
+ break;
+ case 3:
+ rows = 30;
+ cols = 32;
+ padding = 2;
+ mineCount = 192;
+ break;
+ default:
+ rows = 16;
+ cols = 16;
+ padding = 3;
+ mineCount = 38;
+
+ }
+
+ //CanvWidth = cols * (padding + cellsize)
+ //cellsize = canvWidth / cols - padding
+
+ //var boardMaxHeight = Math.max(document.documentElement.clientHeight, window.innerHeight || 0) * 0.75;
+ //cellSize = ((boardMaxHeight / rows)-padding) + (3-level);
+
+ var boardMaxWidth = Math.max(document.documentElement.clientHeight, window.innerHeight || 0) * 0.7;
+ cellSize = ((boardMaxWidth / cols)-padding) + (3-level);
+
+ while (cols * (padding + cellSize) > Math.max(document.documentElement.clientWidth, window.innerWidth || 0)) {
+ cellSize--;
+ }
+
+ canvWidth = (cols * (cellSize + padding)) + 1.5*padding;
+ canvHeight = (rows * (cellSize + padding)) + 1.5*padding;
+
+ neighborIndexOffsets = [-cols-1, -cols, -cols+1, -1, 1, cols-1, cols, cols+1]
+ reset();
+}
+
+function setup() {
+ noLoop();
+ reset();
+ if (getURLParams().d != undefined) {
+ setDifficulty(parseInt(getURLParams().d));
+ } else {
+ setDifficulty(1);
+ }
+}
+
+function reset() {
+ createCanvas(canvWidth, canvHeight).parent("canvasDiv");
+ textSize(cellSize);
+
+ board = [];
+ finished = false;
+ gameover = false;
+ markedBombs = false;
+
+ for (var i = 0; i < rows; i++) {
+ for (var j = 0; j < cols; j++) {
+ board.push(new Cell(j, i));
+ }
+ }
+
+ placeMines();
+
+ for (var i = 0; i < board.length; i++) {
+ board[i].countNeighbors();
+ }
+
+ draw();
+
+ timer_running = false;
+ if (timerInterval) { clearInterval(timerInterval); }
+ timerInterval = undefined;
+ timer_hasCheated = false;
+ timer_ms = -1;
+ timerTick();
+
+}
+
+function draw() {
+ markedBombs = 0;
+ background(50);
+
+ if (gameover || finished) {
+ stopTimer();
+ }
+
+ for (var i = 0; i < board.length; i++) {
+ if (board[i].revealed) {
+ fill(200);
+ } else {
+ fill(100);
+ if (board[i].marked) {
+ fill(255, 100, 0);
+ markedBombs++;
+ }
+ if (finished) {
+ fill(0, 255, 0);
+ }
+ }
+
+ if (gameover && board[i].mine) {
+ fill(255,0,0);
+ }
+
+ rect(board[i].x * (cellSize + padding) + padding, board[i].y * (cellSize + padding) + padding, cellSize, cellSize);
+
+ if (board[i].revealed) {
+ fill(50);
+ if (board[i].neighbors != 0) {
+ text(board[i].neighbors, (board[i].x + 0.32) * (cellSize + padding), (board[i].y+0.9) * (cellSize + padding));
+ }
+ }
+ }
+}
+
+function placeMines() {
+ var placed = 0;
+ while (placed != mineCount) {
+ //Place mines, make sure that the same square isn't selected again
+ var i = Math.floor(Math.random() * board.length);
+
+ if (!board[i].mine) {
+ board[i].mine = true;
+ placed++;
+ }
+ }
+}
+
+function test_finished() {
+ for (var i = 0; i < board.length; i++) {
+ if (board[i].mine && !board[i].marked) { return false;}
+ if (!board[i].revealed && !board[i].mine) { return false; }
+ }
+
+ return true;
+}
+
+function interact(x, y, mark) {
+ if (document.getElementById("helpOverlay").style.display == "block") { return; }
+
+ var index = (y * cols) + x;
+ //console.log("x: " + x.toString() + " y: " + y.toString());
+ if (mark) {
+ board[index].mark();
+ } else {
+ board[index].reveal();
+ }
+
+ finished = test_finished();
+
+ if (!timer_running) {
+ startTimer();
+ }
+
+ draw();
+}
+
+function mouseClicked() {
+
+ if ((mouseX < 0) || (mouseX >= canvWidth)) { return; }
+ if ((mouseY < 0) || (mouseY >= canvHeight)) { return; }
+
+ var ix = Math.floor((mouseX-padding) / (cellSize + padding));
+ var iy = Math.floor((mouseY-padding) / (cellSize + padding));
+
+ interact(ix, iy, keyIsDown(SHIFT));
+}
+
+function keyPressed() {
+ if (keyIsDown(82)) { //Reset when you click R
+ if (refreshPage_reset) {
+ window.location.reload();
+ } else {
+ reset();
+ }
+ }
+
+ if (keyIsDown(65)) {
+ autoMove(false); //Automove when you click A
+ }
+
+ if (keyIsDown(87)) { //Click with W
+ var ix = Math.floor((mouseX-padding) / (cellSize + padding));
+ var iy = Math.floor((mouseY-padding) / (cellSize + padding));
+
+ interact(ix, iy, false);
+ }
+
+ if (keyIsDown(81)) { //Mark with Q
+ var ix = Math.floor((mouseX-padding) / (cellSize + padding));
+ var iy = Math.floor((mouseY-padding) / (cellSize + padding));
+
+ interact(ix, iy, true);
+ }
+}
+
diff --git a/minesweeper/style.css b/minesweeper/style.css
new file mode 100644
index 0000000..c3a274a
--- /dev/null
+++ b/minesweeper/style.css
@@ -0,0 +1,155 @@
+@charset "utf-8";
+
+body {
+ display: grid;
+ grid-template-columns: 1fr 5fr 2fr 2fr;
+ grid-template-rows: 1fr 7fr 6fr;
+
+ background: rgb(175,175,175);
+ background: radial-gradient(circle, rgba(175,175,175,1) 0%, rgba(53,53,54,1) 100%);
+
+ max-height: 100vh;
+}
+
+#helpOverlay {
+ display: none;
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ right: 0;
+ left: 0;
+ width: 100vw;
+ height: 200vh;
+ background-color: rgba(0,0,0,0.85);
+ cursor: pointer;
+ color: white;
+ text-align: center;
+
+}
+
+.helpText {
+ width: 60%;
+ margin: 0 auto;
+ font-size: 18px;
+}
+
+.helpTable {
+ display: inline-block;
+ color: white;
+ margin: 0 auto;
+ font-size: 18px;
+ padding: 10px;
+ border: 2px solid white;
+ height: 158px;
+}
+
+.helpTable > td {
+ padding-left: 10px;
+ padding-right: 10px;
+}
+
+#titleHead {
+ color: white;
+ font-size: 22px;
+ font-family: "Franklin Gothic Bold", "Arial Black", "sans-serif";
+ text-align: center;
+ margin: auto;
+ margin-bottom: 2%;
+ padding: 20px;
+
+ background-color: midnightblue;
+ width: 400px;
+ height: 70%;
+ border-radius: 10px;
+
+}
+
+#gameDiv {
+ grid-column: 2 / 3;
+ grid-row: 2 / 3;
+ text-align: center;
+ margin: auto 0 600px auto;
+}
+
+#canvasDiv {
+ margin-top: 5px;
+}
+
+#leftPane {
+ grid-column: 3 / 4;
+ grid-row: 1 / 3;
+
+ margin: 10px;
+ padding: 30px;
+ text-align: center;
+ border: 2px solid black;
+ border-radius: 10px;
+ background-color: whitesmoke;
+
+ max-width: 200px;
+ height: 45%;
+ margin: 20% auto auto 5%;
+
+}
+
+#timerText {
+ font-size: 26px;
+ border: 1px solid black;
+ border-radius: 5px;
+ padding: 10px 0px;
+}
+
+#bottomPadding {
+ grid-column: 1/3;
+ grid-row: 3/4;
+ height: 100%;
+}
+
+.paneBtn {
+ margin: 6px;
+ padding: 3px;
+ font-size: 16px;
+ width: 80%;
+
+}
+
+button {
+ border-radius: 4px;
+}
+
+
+@media (max-width: 750px) {
+ body {
+ grid-template-columns: 1fr 2fr;
+ grid-template-rows: 1fr 3fr;
+ max-height: none;
+ }
+ #gameDiv {
+ grid-column: 1 / 3;
+ grid-row: 1 / 2;
+ margin: 15px auto;
+
+ }
+ #leftPane {
+ grid-column: 1 / 3;
+ grid-row: 2 / 3;
+ float: center;
+ margin: 10px auto 0px auto;
+ min-width: 60%;
+ height: 600px;
+ }
+}
+
+
+
+
+/* Temporary */
+img {
+ padding-left: 10px;
+ padding-right: 10px;
+ padding-top: 4px;
+ padding-bottom: 4px;
+}
+
+
+
diff --git a/minesweeper/timer.js b/minesweeper/timer.js
new file mode 100644
index 0000000..45bd788
--- /dev/null
+++ b/minesweeper/timer.js
@@ -0,0 +1,39 @@
+// Felix Albrigtsen
+
+var timerInterval = undefined;
+var timer_hasCheated = false;
+var timer_running = false;
+var timer_ms = 0;
+
+function startTimer() {
+ if (timer_running) { return; }
+ timer_ms = 0;
+ timer_running = true;
+ timerInterval = setInterval(timerTick, 10);
+}
+
+function stopTimer() {
+ if (!timer_running) { return; }
+ timer_running = false;
+ clearInterval(timerInterval);
+ timerInterval = undefined;
+}
+
+function timerTick() {
+ timer_ms++;
+ var t_centisecond = (timer_ms % 100);
+ var t_second = ((timer_ms - t_centisecond) % (100*60))/100;
+ var t_minute = (timer_ms - (t_second*100) - t_centisecond) / (100*60);
+
+ t_centisecond = ("00" + t_centisecond).substr(-2, 2);
+ t_second = ("00" + t_second).substr(-2, 2);
+ t_minute = ("00" + t_minute).substr(-2, 2);
+
+ document.getElementById("timerText").innerHTML = t_minute + ":" + t_second + ":" + t_centisecond;
+
+ if (timer_hasCheated) {
+ document.getElementById("timerText").style.color = "red";
+ } else {
+ document.getElementById("timerText").style.color = "green";
+ }
+}
\ No newline at end of file