/*! p5.p5.IO.js v0.5.11 July 21, 2017 */ (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.p5 = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o postsJSON values[1] // => commentsJSON return values; }); ``` @class Promise @param {function} resolver Useful for tooling. @constructor */ function Promise(resolver) { this[PROMISE_ID] = nextId(); this._result = this._state = undefined; this._subscribers = []; if (noop !== resolver) { typeof resolver !== 'function' && needsResolver(); this instanceof Promise ? initializePromise(this, resolver) : needsNew(); } } Promise.all = all; Promise.race = race; Promise.resolve = resolve; Promise.reject = reject; Promise._setScheduler = setScheduler; Promise._setAsap = setAsap; Promise._asap = asap; Promise.prototype = { constructor: Promise, /** The primary way of interacting with a promise is through its `then` method, which registers callbacks to receive either a promise's eventual value or the reason why the promise cannot be fulfilled. ```js findUser().then(function(user){ // user is available }, function(reason){ // user is unavailable, and you are given the reason why }); ``` Chaining -------- The return value of `then` is itself a promise. This second, 'downstream' promise is resolved with the return value of the first promise's fulfillment or rejection handler, or rejected if the handler throws an exception. ```js findUser().then(function (user) { return user.name; }, function (reason) { return 'default name'; }).then(function (userName) { // If `findUser` fulfilled, `userName` will be the user's name, otherwise it // will be `'default name'` }); findUser().then(function (user) { throw new Error('Found user, but still unhappy'); }, function (reason) { throw new Error('`findUser` rejected and we're unhappy'); }).then(function (value) { // never reached }, function (reason) { // if `findUser` fulfilled, `reason` will be 'Found user, but still unhappy'. // If `findUser` rejected, `reason` will be '`findUser` rejected and we're unhappy'. }); ``` If the downstream promise does not specify a rejection handler, rejection reasons will be propagated further downstream. ```js findUser().then(function (user) { throw new PedagogicalException('Upstream error'); }).then(function (value) { // never reached }).then(function (value) { // never reached }, function (reason) { // The `PedgagocialException` is propagated all the way down to here }); ``` Assimilation ------------ Sometimes the value you want to propagate to a downstream promise can only be retrieved asynchronously. This can be achieved by returning a promise in the fulfillment or rejection handler. The downstream promise will then be pending until the returned promise is settled. This is called *assimilation*. ```js findUser().then(function (user) { return findCommentsByAuthor(user); }).then(function (comments) { // The user's comments are now available }); ``` If the assimliated promise rejects, then the downstream promise will also reject. ```js findUser().then(function (user) { return findCommentsByAuthor(user); }).then(function (comments) { // If `findCommentsByAuthor` fulfills, we'll have the value here }, function (reason) { // If `findCommentsByAuthor` rejects, we'll have the reason here }); ``` Simple Example -------------- Synchronous Example ```javascript let result; try { result = findResult(); // success } catch(reason) { // failure } ``` Errback Example ```js findResult(function(result, err){ if (err) { // failure } else { // success } }); ``` Promise Example; ```javascript findResult().then(function(result){ // success }, function(reason){ // failure }); ``` Advanced Example -------------- Synchronous Example ```javascript let author, books; try { author = findAuthor(); books = findBooksByAuthor(author); // success } catch(reason) { // failure } ``` Errback Example ```js function foundBooks(books) { } function failure(reason) { } findAuthor(function(author, err){ if (err) { failure(err); // failure } else { try { findBoooksByAuthor(author, function(books, err) { if (err) { failure(err); } else { try { foundBooks(books); } catch(reason) { failure(reason); } } }); } catch(error) { failure(err); } // success } }); ``` Promise Example; ```javascript findAuthor(). then(findBooksByAuthor). then(function(books){ // found books }).catch(function(reason){ // something went wrong }); ``` @method then @param {Function} onFulfilled @param {Function} onRejected Useful for tooling. @return {Promise} */ then: then, /** `catch` is simply sugar for `then(undefined, onRejection)` which makes it the same as the catch block of a try/catch statement. ```js function findAuthor(){ throw new Error('couldn't find that author'); } // synchronous try { findAuthor(); } catch(reason) { // something went wrong } // async with promises findAuthor().catch(function(reason){ // something went wrong }); ``` @method catch @param {Function} onRejection Useful for tooling. @return {Promise} */ 'catch': function _catch(onRejection) { return this.then(null, onRejection); } }; function polyfill() { var local = undefined; if (typeof global !== 'undefined') { local = global; } else if (typeof self !== 'undefined') { local = self; } else { try { local = Function('return this')(); } catch (e) { throw new Error('polyfill failed because global object is unavailable in this environment'); } } var P = local.Promise; if (P) { var promiseToString = null; try { promiseToString = Object.prototype.toString.call(P.resolve()); } catch (e) { // silently ignored } if (promiseToString === '[object Promise]' && !P.cast) { return; } } local.Promise = Promise; } // Strange compat.. Promise.polyfill = polyfill; Promise.Promise = Promise; return Promise; }))); }).call(this,_dereq_('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{"_process":3}],2:[function(_dereq_,module,exports){ (function (global, factory) { if (typeof define === 'function' && define.amd) { define(['exports', 'module'], factory); } else if (typeof exports !== 'undefined' && typeof module !== 'undefined') { factory(exports, module); } else { var mod = { exports: {} }; factory(mod.exports, mod); global.fetchJsonp = mod.exports; } })(this, function (exports, module) { 'use strict'; var defaultOptions = { timeout: 5000, jsonpCallback: 'callback', jsonpCallbackFunction: null }; function generateCallbackFunction() { return 'jsonp_' + Date.now() + '_' + Math.ceil(Math.random() * 100000); } // Known issue: Will throw 'Uncaught ReferenceError: callback_*** is not defined' // error if request timeout function clearFunction(functionName) { // IE8 throws an exception when you try to delete a property on window // http://stackoverflow.com/a/1824228/751089 try { delete window[functionName]; } catch (e) { window[functionName] = undefined; } } function removeScript(scriptId) { var script = document.getElementById(scriptId); document.getElementsByTagName('head')[0].removeChild(script); } function fetchJsonp(_url) { var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; // to avoid param reassign var url = _url; var timeout = options.timeout || defaultOptions.timeout; var jsonpCallback = options.jsonpCallback || defaultOptions.jsonpCallback; var timeoutId = undefined; return new Promise(function (resolve, reject) { var callbackFunction = options.jsonpCallbackFunction || generateCallbackFunction(); var scriptId = jsonpCallback + '_' + callbackFunction; window[callbackFunction] = function (response) { resolve({ ok: true, // keep consistent with fetch API json: function json() { return Promise.resolve(response); } }); if (timeoutId) clearTimeout(timeoutId); removeScript(scriptId); clearFunction(callbackFunction); }; // Check if the user set their own params, and if not add a ? to start a list of params url += url.indexOf('?') === -1 ? '?' : '&'; var jsonpScript = document.createElement('script'); jsonpScript.setAttribute('src', '' + url + jsonpCallback + '=' + callbackFunction); jsonpScript.id = scriptId; document.getElementsByTagName('head')[0].appendChild(jsonpScript); timeoutId = setTimeout(function () { reject(new Error('JSONP request to ' + _url + ' timed out')); clearFunction(callbackFunction); removeScript(scriptId); }, timeout); }); } // export as global function /* let local; if (typeof global !== 'undefined') { local = global; } else if (typeof self !== 'undefined') { local = self; } else { try { local = Function('return this')(); } catch (e) { throw new Error('polyfill failed because global object is unavailable in this environment'); } } local.fetchJsonp = fetchJsonp; */ module.exports = fetchJsonp; }); },{}],3:[function(_dereq_,module,exports){ // shim for using process in browser var process = module.exports = {}; // cached from whatever global is present so that test runners that stub it // don't break things. But we need to wrap it in a try catch in case it is // wrapped in strict mode code which doesn't define any globals. It's inside a // function because try/catches deoptimize in certain engines. var cachedSetTimeout; var cachedClearTimeout; function defaultSetTimout() { throw new Error('setTimeout has not been defined'); } function defaultClearTimeout () { throw new Error('clearTimeout has not been defined'); } (function () { try { if (typeof setTimeout === 'function') { cachedSetTimeout = setTimeout; } else { cachedSetTimeout = defaultSetTimout; } } catch (e) { cachedSetTimeout = defaultSetTimout; } try { if (typeof clearTimeout === 'function') { cachedClearTimeout = clearTimeout; } else { cachedClearTimeout = defaultClearTimeout; } } catch (e) { cachedClearTimeout = defaultClearTimeout; } } ()) function runTimeout(fun) { if (cachedSetTimeout === setTimeout) { //normal enviroments in sane situations return setTimeout(fun, 0); } // if setTimeout wasn't available but was latter defined if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) { cachedSetTimeout = setTimeout; return setTimeout(fun, 0); } try { // when when somebody has screwed with setTimeout but no I.E. maddness return cachedSetTimeout(fun, 0); } catch(e){ try { // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally return cachedSetTimeout.call(null, fun, 0); } catch(e){ // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error return cachedSetTimeout.call(this, fun, 0); } } } function runClearTimeout(marker) { if (cachedClearTimeout === clearTimeout) { //normal enviroments in sane situations return clearTimeout(marker); } // if clearTimeout wasn't available but was latter defined if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) { cachedClearTimeout = clearTimeout; return clearTimeout(marker); } try { // when when somebody has screwed with setTimeout but no I.E. maddness return cachedClearTimeout(marker); } catch (e){ try { // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally return cachedClearTimeout.call(null, marker); } catch (e){ // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error. // Some versions of I.E. have different rules for clearTimeout vs setTimeout return cachedClearTimeout.call(this, marker); } } } var queue = []; var draining = false; var currentQueue; var queueIndex = -1; function cleanUpNextTick() { if (!draining || !currentQueue) { return; } draining = false; if (currentQueue.length) { queue = currentQueue.concat(queue); } else { queueIndex = -1; } if (queue.length) { drainQueue(); } } function drainQueue() { if (draining) { return; } var timeout = runTimeout(cleanUpNextTick); draining = true; var len = queue.length; while(len) { currentQueue = queue; queue = []; while (++queueIndex < len) { if (currentQueue) { currentQueue[queueIndex].run(); } } queueIndex = -1; len = queue.length; } currentQueue = null; draining = false; runClearTimeout(timeout); } process.nextTick = function (fun) { var args = new Array(arguments.length - 1); if (arguments.length > 1) { for (var i = 1; i < arguments.length; i++) { args[i - 1] = arguments[i]; } } queue.push(new Item(fun, args)); if (queue.length === 1 && !draining) { runTimeout(drainQueue); } }; // v8 likes predictible objects function Item(fun, array) { this.fun = fun; this.array = array; } Item.prototype.run = function () { this.fun.apply(null, this.array); }; process.title = 'browser'; process.browser = true; process.env = {}; process.argv = []; process.version = ''; // empty string to avoid regexp issues process.versions = {}; function noop() {} process.on = noop; process.addListener = noop; process.once = noop; process.off = noop; process.removeListener = noop; process.removeAllListeners = noop; process.emit = noop; process.binding = function (name) { throw new Error('process.binding is not supported'); }; process.cwd = function () { return '/' }; process.chdir = function (dir) { throw new Error('process.chdir is not supported'); }; process.umask = function() { return 0; }; },{}],4:[function(_dereq_,module,exports){ (function(self) { 'use strict'; if (self.fetch) { return } var support = { searchParams: 'URLSearchParams' in self, iterable: 'Symbol' in self && 'iterator' in Symbol, blob: 'FileReader' in self && 'Blob' in self && (function() { try { new Blob() return true } catch(e) { return false } })(), formData: 'FormData' in self, arrayBuffer: 'ArrayBuffer' in self } if (support.arrayBuffer) { var viewClasses = [ '[object Int8Array]', '[object Uint8Array]', '[object Uint8ClampedArray]', '[object Int16Array]', '[object Uint16Array]', '[object Int32Array]', '[object Uint32Array]', '[object Float32Array]', '[object Float64Array]' ] var isDataView = function(obj) { return obj && DataView.prototype.isPrototypeOf(obj) } var isArrayBufferView = ArrayBuffer.isView || function(obj) { return obj && viewClasses.indexOf(Object.prototype.toString.call(obj)) > -1 } } function normalizeName(name) { if (typeof name !== 'string') { name = String(name) } if (/[^a-z0-9\-#$%&'*+.\^_`|~]/i.test(name)) { throw new TypeError('Invalid character in header field name') } return name.toLowerCase() } function normalizeValue(value) { if (typeof value !== 'string') { value = String(value) } return value } // Build a destructive iterator for the value list function iteratorFor(items) { var iterator = { next: function() { var value = items.shift() return {done: value === undefined, value: value} } } if (support.iterable) { iterator[Symbol.iterator] = function() { return iterator } } return iterator } function Headers(headers) { this.map = {} if (headers instanceof Headers) { headers.forEach(function(value, name) { this.append(name, value) }, this) } else if (Array.isArray(headers)) { headers.forEach(function(header) { this.append(header[0], header[1]) }, this) } else if (headers) { Object.getOwnPropertyNames(headers).forEach(function(name) { this.append(name, headers[name]) }, this) } } Headers.prototype.append = function(name, value) { name = normalizeName(name) value = normalizeValue(value) var oldValue = this.map[name] this.map[name] = oldValue ? oldValue+','+value : value } Headers.prototype['delete'] = function(name) { delete this.map[normalizeName(name)] } Headers.prototype.get = function(name) { name = normalizeName(name) return this.has(name) ? this.map[name] : null } Headers.prototype.has = function(name) { return this.map.hasOwnProperty(normalizeName(name)) } Headers.prototype.set = function(name, value) { this.map[normalizeName(name)] = normalizeValue(value) } Headers.prototype.forEach = function(callback, thisArg) { for (var name in this.map) { if (this.map.hasOwnProperty(name)) { callback.call(thisArg, this.map[name], name, this) } } } Headers.prototype.keys = function() { var items = [] this.forEach(function(value, name) { items.push(name) }) return iteratorFor(items) } Headers.prototype.values = function() { var items = [] this.forEach(function(value) { items.push(value) }) return iteratorFor(items) } Headers.prototype.entries = function() { var items = [] this.forEach(function(value, name) { items.push([name, value]) }) return iteratorFor(items) } if (support.iterable) { Headers.prototype[Symbol.iterator] = Headers.prototype.entries } function consumed(body) { if (body.bodyUsed) { return Promise.reject(new TypeError('Already read')) } body.bodyUsed = true } function fileReaderReady(reader) { return new Promise(function(resolve, reject) { reader.onload = function() { resolve(reader.result) } reader.onerror = function() { reject(reader.error) } }) } function readBlobAsArrayBuffer(blob) { var reader = new FileReader() var promise = fileReaderReady(reader) reader.readAsArrayBuffer(blob) return promise } function readBlobAsText(blob) { var reader = new FileReader() var promise = fileReaderReady(reader) reader.readAsText(blob) return promise } function readArrayBufferAsText(buf) { var view = new Uint8Array(buf) var chars = new Array(view.length) for (var i = 0; i < view.length; i++) { chars[i] = String.fromCharCode(view[i]) } return chars.join('') } function bufferClone(buf) { if (buf.slice) { return buf.slice(0) } else { var view = new Uint8Array(buf.byteLength) view.set(new Uint8Array(buf)) return view.buffer } } function Body() { this.bodyUsed = false this._initBody = function(body) { this._bodyInit = body if (!body) { this._bodyText = '' } else if (typeof body === 'string') { this._bodyText = body } else if (support.blob && Blob.prototype.isPrototypeOf(body)) { this._bodyBlob = body } else if (support.formData && FormData.prototype.isPrototypeOf(body)) { this._bodyFormData = body } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) { this._bodyText = body.toString() } else if (support.arrayBuffer && support.blob && isDataView(body)) { this._bodyArrayBuffer = bufferClone(body.buffer) // IE 10-11 can't handle a DataView body. this._bodyInit = new Blob([this._bodyArrayBuffer]) } else if (support.arrayBuffer && (ArrayBuffer.prototype.isPrototypeOf(body) || isArrayBufferView(body))) { this._bodyArrayBuffer = bufferClone(body) } else { throw new Error('unsupported BodyInit type') } if (!this.headers.get('content-type')) { if (typeof body === 'string') { this.headers.set('content-type', 'text/plain;charset=UTF-8') } else if (this._bodyBlob && this._bodyBlob.type) { this.headers.set('content-type', this._bodyBlob.type) } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) { this.headers.set('content-type', 'application/x-www-form-urlencoded;charset=UTF-8') } } } if (support.blob) { this.blob = function() { var rejected = consumed(this) if (rejected) { return rejected } if (this._bodyBlob) { return Promise.resolve(this._bodyBlob) } else if (this._bodyArrayBuffer) { return Promise.resolve(new Blob([this._bodyArrayBuffer])) } else if (this._bodyFormData) { throw new Error('could not read FormData body as blob') } else { return Promise.resolve(new Blob([this._bodyText])) } } this.arrayBuffer = function() { if (this._bodyArrayBuffer) { return consumed(this) || Promise.resolve(this._bodyArrayBuffer) } else { return this.blob().then(readBlobAsArrayBuffer) } } } this.text = function() { var rejected = consumed(this) if (rejected) { return rejected } if (this._bodyBlob) { return readBlobAsText(this._bodyBlob) } else if (this._bodyArrayBuffer) { return Promise.resolve(readArrayBufferAsText(this._bodyArrayBuffer)) } else if (this._bodyFormData) { throw new Error('could not read FormData body as text') } else { return Promise.resolve(this._bodyText) } } if (support.formData) { this.formData = function() { return this.text().then(decode) } } this.json = function() { return this.text().then(JSON.parse) } return this } // HTTP methods whose capitalization should be normalized var methods = ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT'] function normalizeMethod(method) { var upcased = method.toUpperCase() return (methods.indexOf(upcased) > -1) ? upcased : method } function Request(input, options) { options = options || {} var body = options.body if (input instanceof Request) { if (input.bodyUsed) { throw new TypeError('Already read') } this.url = input.url this.credentials = input.credentials if (!options.headers) { this.headers = new Headers(input.headers) } this.method = input.method this.mode = input.mode if (!body && input._bodyInit != null) { body = input._bodyInit input.bodyUsed = true } } else { this.url = String(input) } this.credentials = options.credentials || this.credentials || 'omit' if (options.headers || !this.headers) { this.headers = new Headers(options.headers) } this.method = normalizeMethod(options.method || this.method || 'GET') this.mode = options.mode || this.mode || null this.referrer = null if ((this.method === 'GET' || this.method === 'HEAD') && body) { throw new TypeError('Body not allowed for GET or HEAD requests') } this._initBody(body) } Request.prototype.clone = function() { return new Request(this, { body: this._bodyInit }) } function decode(body) { var form = new FormData() body.trim().split('&').forEach(function(bytes) { if (bytes) { var split = bytes.split('=') var name = split.shift().replace(/\+/g, ' ') var value = split.join('=').replace(/\+/g, ' ') form.append(decodeURIComponent(name), decodeURIComponent(value)) } }) return form } function parseHeaders(rawHeaders) { var headers = new Headers() rawHeaders.split(/\r?\n/).forEach(function(line) { var parts = line.split(':') var key = parts.shift().trim() if (key) { var value = parts.join(':').trim() headers.append(key, value) } }) return headers } Body.call(Request.prototype) function Response(bodyInit, options) { if (!options) { options = {} } this.type = 'default' this.status = 'status' in options ? options.status : 200 this.ok = this.status >= 200 && this.status < 300 this.statusText = 'statusText' in options ? options.statusText : 'OK' this.headers = new Headers(options.headers) this.url = options.url || '' this._initBody(bodyInit) } Body.call(Response.prototype) Response.prototype.clone = function() { return new Response(this._bodyInit, { status: this.status, statusText: this.statusText, headers: new Headers(this.headers), url: this.url }) } Response.error = function() { var response = new Response(null, {status: 0, statusText: ''}) response.type = 'error' return response } var redirectStatuses = [301, 302, 303, 307, 308] Response.redirect = function(url, status) { if (redirectStatuses.indexOf(status) === -1) { throw new RangeError('Invalid status code') } return new Response(null, {status: status, headers: {location: url}}) } self.Headers = Headers self.Request = Request self.Response = Response self.fetch = function(input, init) { return new Promise(function(resolve, reject) { var request = new Request(input, init) var xhr = new XMLHttpRequest() xhr.onload = function() { var options = { status: xhr.status, statusText: xhr.statusText, headers: parseHeaders(xhr.getAllResponseHeaders() || '') } options.url = 'responseURL' in xhr ? xhr.responseURL : options.headers.get('X-Request-URL') var body = 'response' in xhr ? xhr.response : xhr.responseText resolve(new Response(body, options)) } xhr.onerror = function() { reject(new TypeError('Network request failed')) } xhr.ontimeout = function() { reject(new TypeError('Network request failed')) } xhr.open(request.method, request.url, true) if (request.credentials === 'include') { xhr.withCredentials = true } if ('responseType' in xhr && support.blob) { xhr.responseType = 'blob' } request.headers.forEach(function(value, name) { xhr.setRequestHeader(name, value) }) xhr.send(typeof request._bodyInit === 'undefined' ? null : request._bodyInit) }) } self.fetch.polyfill = true })(typeof self !== 'undefined' ? self : this); },{}],5:[function(_dereq_,module,exports){ /** * @module Shape * @submodule 2D Primitives * @for p5 * @requires core * @requires constants */ 'use strict'; var p5 = _dereq_('./core'); var constants = _dereq_('./constants'); var canvas = _dereq_('./canvas'); _dereq_('./error_helpers'); /** * Draw an arc to the screen. If called with only a, b, c, d, start, and * stop, the arc will be drawn as an open pie. If mode is provided, the arc * will be drawn either open, as a chord, or as a pie as specified. The * origin may be changed with the ellipseMode() function.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

* For more ways to position the canvas, see the * * positioning the canvas wiki page. * * @method createCanvas * @param {Number} w width of the canvas * @param {Number} h height of the canvas * @param {Constant} [renderer] either P2D or WEBGL * @return {HTMLCanvasElement} canvas generated * @example *
* * function setup() { * createCanvas(100, 50); * background(153); * line(0, 0, width, height); * } * *
* * @alt * Black line extending from top-left of canvas to bottom right. * */ p5.prototype.createCanvas = function(w, h, renderer) { //optional: renderer, otherwise defaults to p2d var r = renderer || constants.P2D; var isDefault, c; //4th arg (isDefault) used when called onLoad, //otherwise hidden to the public api if(arguments[3]){ isDefault = (typeof arguments[3] === 'boolean') ? arguments[3] : false; } if(r === constants.WEBGL){ c = document.getElementById(defaultId); if(c){ //if defaultCanvas already exists c.parentNode.removeChild(c); //replace the existing defaultCanvas } c = document.createElement('canvas'); c.id = defaultId; } else { if (isDefault) { c = document.createElement('canvas'); var i = 0; while (document.getElementById('defaultCanvas'+i)) { i++; } defaultId = 'defaultCanvas'+i; c.id = defaultId; } else { // resize the default canvas if new one is created c = this.canvas; } } // set to invisible if still in setup (to prevent flashing with manipulate) if (!this._setupDone) { c.dataset.hidden = true; // tag to show later c.style.visibility='hidden'; } if (this._userNode) { // user input node case this._userNode.appendChild(c); } else { document.body.appendChild(c); } // Init our graphics renderer //webgl mode if (r === constants.WEBGL) { this._setProperty('_renderer', new p5.RendererGL(c, this, true)); this._isdefaultGraphics = true; } //P2D mode else { if (!this._isdefaultGraphics) { this._setProperty('_renderer', new p5.Renderer2D(c, this, true)); this._isdefaultGraphics = true; } } this._renderer.resize(w, h); this._renderer._applyDefaults(); if (isDefault) { // only push once this._elements.push(this._renderer); } return this._renderer; }; /** * Resizes the canvas to given width and height. The canvas will be cleared * and draw will be called immediately, allowing the sketch to re-render itself * in the resized canvas. * @method resizeCanvas * @param {Number} w width of the canvas * @param {Number} h height of the canvas * @param {Boolean} noRedraw don't redraw the canvas immediately * @example *
* function setup() { * createCanvas(windowWidth, windowHeight); * } * * function draw() { * background(0, 100, 200); * } * * function windowResized() { * resizeCanvas(windowWidth, windowHeight); * } *
* * @alt * No image displayed. * */ p5.prototype.resizeCanvas = function (w, h, noRedraw) { if (this._renderer) { // save canvas properties var props = {}; for (var key in this.drawingContext) { var val = this.drawingContext[key]; if (typeof val !== 'object' && typeof val !== 'function') { props[key] = val; } } this._renderer.resize(w, h); // reset canvas properties for (var savedKey in props) { this.drawingContext[savedKey] = props[savedKey]; } if (!noRedraw) { this.redraw(); } } }; /** * Removes the default canvas for a p5 sketch that doesn't * require a canvas * @method noCanvas * @example *
* * function setup() { * noCanvas(); * } * *
* * @alt * no image displayed * */ p5.prototype.noCanvas = function() { if (this.canvas) { this.canvas.parentNode.removeChild(this.canvas); } }; /** * Creates and returns a new p5.Renderer object. Use this class if you need * to draw into an off-screen graphics buffer. The two parameters define the * width and height in pixels. * * @method createGraphics * @param {Number} w width of the offscreen graphics buffer * @param {Number} h height of the offscreen graphics buffer * @param {Constant} [renderer] either P2D or WEBGL * undefined defaults to p2d * @return {p5.Graphics} offscreen graphics buffer * @example *
* * var pg; * function setup() { * createCanvas(100, 100); * pg = createGraphics(100, 100); * } * function draw() { * background(200); * pg.background(100); * pg.noStroke(); * pg.ellipse(pg.width/2, pg.height/2, 50, 50); * image(pg, 50, 50); * image(pg, 0, 0, 50, 50); * } * *
* * @alt * 4 grey squares alternating light and dark grey. White quarter circle mid-left. * */ p5.prototype.createGraphics = function(w, h, renderer){ return new p5.Graphics(w, h, renderer, this); }; /** * Blends the pixels in the display window according to the defined mode. * There is a choice of the following modes to blend the source pixels (A) * with the ones of pixels already in the display window (B): *
    *
  • BLEND - linear interpolation of colours: C = * A*factor + B. This is the default blending mode.
  • *
  • ADD - sum of A and B
  • *
  • DARKEST - only the darkest colour succeeds: C = * min(A*factor, B).
  • *
  • LIGHTEST - only the lightest colour succeeds: C = * max(A*factor, B).
  • *
  • DIFFERENCE - subtract colors from underlying image.
  • *
  • EXCLUSION - similar to DIFFERENCE, but less * extreme.
  • *
  • MULTIPLY - multiply the colors, result will always be * darker.
  • *
  • SCREEN - opposite multiply, uses inverse values of the * colors.
  • *
  • REPLACE - the pixels entirely replace the others and * don't utilize alpha (transparency) values.
  • *
  • OVERLAY - mix of MULTIPLY and SCREEN * . Multiplies dark values, and screens light values.
  • *
  • HARD_LIGHT - SCREEN when greater than 50% * gray, MULTIPLY when lower.
  • *
  • SOFT_LIGHT - mix of DARKEST and * LIGHTEST. Works like OVERLAY, but not as harsh. *
  • *
  • DODGE - lightens light tones and increases contrast, * ignores darks.
  • *
  • BURN - darker areas are applied, increasing contrast, * ignores lights.
  • *
* * @method blendMode * @param {Constant} mode blend mode to set for canvas. * either BLEND, DARKEST, LIGHTEST, DIFFERENCE, MULTIPLY, * EXCLUSION, SCREEN, REPLACE, OVERLAY, HARD_LIGHT, * SOFT_LIGHT, DODGE, BURN, ADD or NORMAL * @example *
* * blendMode(LIGHTEST); * strokeWeight(30); * stroke(80, 150, 255); * line(25, 25, 75, 75); * stroke(255, 50, 50); * line(75, 25, 25, 75); * *
*
* * blendMode(MULTIPLY); * strokeWeight(30); * stroke(80, 150, 255); * line(25, 25, 75, 75); * stroke(255, 50, 50); * line(75, 25, 25, 75); * *
* @alt * translucent image thick red & blue diagonal rounded lines intersecting center * Thick red & blue diagonal rounded lines intersecting center. dark at overlap * */ p5.prototype.blendMode = function(mode) { if (mode === constants.BLEND || mode === constants.DARKEST || mode === constants.LIGHTEST || mode === constants.DIFFERENCE || mode === constants.MULTIPLY || mode === constants.EXCLUSION || mode === constants.SCREEN || mode === constants.REPLACE || mode === constants.OVERLAY || mode === constants.HARD_LIGHT || mode === constants.SOFT_LIGHT || mode === constants.DODGE || mode === constants.BURN || mode === constants.ADD || mode === constants.NORMAL) { this._renderer.blendMode(mode); } else { throw new Error('Mode '+mode+' not recognized.'); } }; module.exports = p5; },{"../webgl/p5.RendererGL":30,"./constants":8,"./core":9,"./p5.Graphics":15,"./p5.Renderer2D":17}],19:[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 }); } }()); },{}],20:[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":9}],21:[function(_dereq_,module,exports){ /** * @module Transform * @submodule Transform * @for p5 * @requires core * @requires constants */ 'use strict'; var p5 = _dereq_('./core'); var constants = _dereq_('./constants'); /** * Multiplies the current matrix by the one specified through the parameters. * This is very slow because it will try to calculate the inverse of the * transform, so avoid it whenever possible. * * @method applyMatrix * @param {Number} n00 numbers which define the 3x2 matrix to be multiplied * @param {Number} n01 numbers which define the 3x2 matrix to be multiplied * @param {Number} n02 numbers which define the 3x2 matrix to be multiplied * @param {Number} n10 numbers which define the 3x2 matrix to be multiplied * @param {Number} n11 numbers which define the 3x2 matrix to be multiplied * @param {Number} n12 numbers which define the 3x2 matrix to be multiplied * @chainable * @example *
* * // Example in the works. * *
* * @alt * no image diplayed * */ p5.prototype.applyMatrix = function(n00, n01, n02, n10, n11, n12) { this._renderer.applyMatrix(n00, n01, n02, n10, n11, n12); return this; }; p5.prototype.popMatrix = function() { throw new Error('popMatrix() not used, see pop()'); }; p5.prototype.printMatrix = function() { throw new Error('printMatrix() not implemented'); }; p5.prototype.pushMatrix = function() { throw new Error('pushMatrix() not used, see push()'); }; /** * Replaces the current matrix with the identity matrix. * * @method resetMatrix * @chainable * @example *
* * // Example in the works. * *
* * @alt * no image diplayed * */ p5.prototype.resetMatrix = function() { this._renderer.resetMatrix(); return this; }; /** * Rotates a shape the amount specified by the angle parameter. This * function accounts for angleMode, so angles can be entered in either * RADIANS or DEGREES. *

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

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

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

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

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

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

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

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

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

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

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

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

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

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

* These functions can only be used within a beginShape()/endShape() pair and * transformations such as translate(), rotate(), and scale() do not work * within a beginContour()/endContour() pair. It is also not possible to use * other shapes, such as ellipse() or rect() within. * * @method endContour * @chainable * @example *
* * translate(50, 50); * stroke(255, 0, 0); * beginShape(); * // Exterior part of shape, clockwise winding * vertex(-40, -40); * vertex(40, -40); * vertex(40, 40); * vertex(-40, 40); * // Interior part of shape, counter-clockwise winding * beginContour(); * vertex(-20, -20); * vertex(-20, 20); * vertex(20, 20); * vertex(20, -20); * endContour(); * endShape(CLOSE); * *
* * @alt * white rect and smaller grey rect with red outlines in center of canvas. * */ p5.prototype.endContour = function() { var vert = contourVertices[0].slice(); // copy all data vert.isVert = contourVertices[0].isVert; vert.moveTo = false; contourVertices.push(vert); // prevent stray lines with multiple contours if (isFirstContour) { vertices.push(vertices[0]); isFirstContour = false; } for (var i = 0; i < contourVertices.length; i++) { vertices.push(contourVertices[i]); } return this; }; /** * The endShape() function is the companion to beginShape() and may only be * called after beginShape(). When endshape() is called, all of image data * defined since the previous call to beginShape() is written into the image * buffer. The constant CLOSE as the value for the MODE parameter to close * the shape (to connect the beginning and the end). * * @method endShape * @param {Constant} [mode] use CLOSE to close the shape * @chainable * @example *
* * noFill(); * * beginShape(); * vertex(20, 20); * vertex(45, 20); * vertex(45, 80); * endShape(CLOSE); * * beginShape(); * vertex(50, 20); * vertex(75, 20); * vertex(75, 80); * endShape(); * *
* * @alt * Triangle line shape with smallest interior angle on bottom and upside-down L. * */ p5.prototype.endShape = function(mode) { if(this._renderer.isP3D){ this._renderer.endShape(mode, isCurve, isBezier, isQuadratic, isContour, shapeKind); }else{ if (vertices.length === 0) { return this; } if (!this._renderer._doStroke && !this._renderer._doFill) { return this; } var closeShape = mode === constants.CLOSE; // if the shape is closed, the first element is also the last element if (closeShape && !isContour) { vertices.push(vertices[0]); } this._renderer.endShape(mode, vertices, isCurve, isBezier, isQuadratic, isContour, shapeKind); // Reset some settings isCurve = false; isBezier = false; isQuadratic = false; isContour = false; isFirstContour = true; // If the shape is closed, the first element was added as last element. // We must remove it again to prevent the list of vertices from growing // over successive calls to endShape(CLOSE) if (closeShape) { vertices.pop(); } } return this; }; /** * Specifies vertex coordinates for quadratic Bezier curves. Each call to * quadraticVertex() defines the position of one control points and one * anchor point of a Bezier curve, adding a new segment to a line or shape. * The first time quadraticVertex() is used within a beginShape() call, it * must be prefaced with a call to vertex() to set the first anchor point. * This function must be used between beginShape() and endShape() and only * when there is no MODE parameter specified to beginShape(). * * @method quadraticVertex * @param {Number} cx x-coordinate for the control point * @param {Number} cy y-coordinate for the control point * @param {Number} x3 x-coordinate for the anchor point * @param {Number} y3 y-coordinate for the anchor point * @chainable * @example *
* * noFill(); * strokeWeight(4); * beginShape(); * vertex(20, 20); * quadraticVertex(80, 20, 50, 50); * endShape(); * *
* *
* * noFill(); * strokeWeight(4); * beginShape(); * vertex(20, 20); * quadraticVertex(80, 20, 50, 50); * quadraticVertex(20, 80, 80, 80); * vertex(80, 60); * endShape(); * *
* * @alt * arched-shaped black line with 4 pixel thick stroke weight. * backwards s-shaped black line with 4 pixel thick stroke weight. * */ p5.prototype.quadraticVertex = function(cx, cy, x3, y3) { //if we're drawing a contour, put the points into an // array for inside drawing if(this._contourInited) { var pt = {}; pt.x = cx; pt.y = cy; pt.x3 = x3; pt.y3 = y3; pt.type = constants.QUADRATIC; this._contourVertices.push(pt); return this; } if (vertices.length > 0) { isQuadratic = true; var vert = []; for (var i = 0; i < arguments.length; i++) { vert[i] = arguments[i]; } vert.isVert = false; if (isContour) { contourVertices.push(vert); } else { vertices.push(vert); } } else { throw 'vertex() must be used once before calling quadraticVertex()'; } return this; }; /** * All shapes are constructed by connecting a series of vertices. vertex() * is used to specify the vertex coordinates for points, lines, triangles, * quads, and polygons. It is used exclusively within the beginShape() and * endShape() functions. * * @method vertex * @param {Number} x x-coordinate of the vertex * @param {Number} y y-coordinate of the vertex * @param {Number|Boolean} [z] z-coordinate of the vertex * @chainable * @example *
* * beginShape(POINTS); * vertex(30, 20); * vertex(85, 20); * vertex(85, 75); * vertex(30, 75); * endShape(); * *
* * @alt * 4 black points in a square shape in middle-right of canvas. * */ p5.prototype.vertex = function(x, y, moveTo) { if(this._renderer.isP3D){ this._renderer.vertex(x, y, moveTo); }else{ var vert = []; vert.isVert = true; vert[0] = x; vert[1] = y; vert[2] = 0; vert[3] = 0; vert[4] = 0; vert[5] = this._renderer._getFill(); vert[6] = this._renderer._getStroke(); if (moveTo) { vert.moveTo = moveTo; } if (isContour) { if (contourVertices.length === 0) { vert.moveTo = true; } contourVertices.push(vert); } else { vertices.push(vert); } } return this; }; module.exports = p5; },{"./constants":8,"./core":9}],23:[function(_dereq_,module,exports){ /*global ImageData:false */ /** * This module defines the filters for use with image buffers. * * This module is basically a collection of functions stored in an object * as opposed to modules. The functions are destructive, modifying * the passed in canvas rather than creating a copy. * * Generally speaking users of this module will use the Filters.apply method * on a canvas to create an effect. * * A number of functions are borrowed/adapted from * http://www.html5rocks.com/en/tutorials/canvas/imagefilters/ * or the java processing implementation. */ 'use strict'; var Filters = {}; /* * Helper functions */ /** * Returns the pixel buffer for a canvas * * @private * * @param {Canvas|ImageData} canvas the canvas to get pixels from * @return {Uint8ClampedArray} a one-dimensional array containing * the data in thc RGBA order, with integer * values between 0 and 255 */ Filters._toPixels = function (canvas) { if (canvas instanceof ImageData) { return canvas.data; } else { return canvas.getContext('2d').getImageData( 0, 0, canvas.width, canvas.height ).data; } }; /** * Returns a 32 bit number containing ARGB data at ith pixel in the * 1D array containing pixels data. * * @private * * @param {Uint8ClampedArray} data array returned by _toPixels() * @param {Integer} i index of a 1D Image Array * @return {Integer} 32 bit integer value representing * ARGB value. */ Filters._getARGB = function (data, i) { var offset = i * 4; return (data[offset+3] << 24) & 0xff000000 | (data[offset] << 16) & 0x00ff0000 | (data[offset+1] << 8) & 0x0000ff00 | data[offset+2] & 0x000000ff; }; /** * Modifies pixels RGBA values to values contained in the data object. * * @private * * @param {Uint8ClampedArray} pixels array returned by _toPixels() * @param {Int32Array} data source 1D array where each value * represents ARGB values */ Filters._setPixels = function (pixels, data) { var offset = 0; for( var i = 0, al = pixels.length; i < al; i++) { offset = i*4; pixels[offset + 0] = (data[i] & 0x00ff0000)>>>16; pixels[offset + 1] = (data[i] & 0x0000ff00)>>>8; pixels[offset + 2] = (data[i] & 0x000000ff); pixels[offset + 3] = (data[i] & 0xff000000)>>>24; } }; /** * Returns the ImageData object for a canvas * https://developer.mozilla.org/en-US/docs/Web/API/ImageData * * @private * * @param {Canvas|ImageData} canvas canvas to get image data from * @return {ImageData} Holder of pixel data (and width and * height) for a canvas */ Filters._toImageData = function (canvas) { if (canvas instanceof ImageData) { return canvas; } else { return canvas.getContext('2d').getImageData( 0, 0, canvas.width, canvas.height ); } }; /** * Returns a blank ImageData object. * * @private * * @param {Integer} width * @param {Integer} height * @return {ImageData} */ Filters._createImageData = function (width, height) { Filters._tmpCanvas = document.createElement('canvas'); Filters._tmpCtx = Filters._tmpCanvas.getContext('2d'); return this._tmpCtx.createImageData(width, height); }; /** * Applys a filter function to a canvas. * * The difference between this and the actual filter functions defined below * is that the filter functions generally modify the pixel buffer but do * not actually put that data back to the canvas (where it would actually * update what is visible). By contrast this method does make the changes * actually visible in the canvas. * * The apply method is the method that callers of this module would generally * use. It has been separated from the actual filters to support an advanced * use case of creating a filter chain that executes without actually updating * the canvas in between everystep. * * @param {HTMLCanvasElement} canvas [description] * @param {function(ImageData,Object)} func [description] * @param {Object} filterParam [description] */ Filters.apply = function (canvas, func, filterParam) { var ctx = canvas.getContext('2d'); var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); //Filters can either return a new ImageData object, or just modify //the one they received. var newImageData = func(imageData, filterParam); if (newImageData instanceof ImageData) { ctx.putImageData(newImageData, 0, 0, 0, 0, canvas.width, canvas.height); } else { ctx.putImageData(imageData, 0, 0, 0, 0, canvas.width, canvas.height); } }; /* * Filters */ /** * Converts the image to black and white pixels depending if they are above or * below the threshold defined by the level parameter. The parameter must be * between 0.0 (black) and 1.0 (white). If no level is specified, 0.5 is used. * * Borrowed from http://www.html5rocks.com/en/tutorials/canvas/imagefilters/ * * @param {Canvas} canvas * @param {Float} level */ Filters.threshold = function (canvas, level) { var pixels = Filters._toPixels(canvas); if (level === undefined) { level = 0.5; } var thresh = Math.floor(level * 255); for (var i = 0; i < pixels.length; i += 4) { var r = pixels[i]; var g = pixels[i + 1]; var b = pixels[i + 2]; var gray = (0.2126 * r + 0.7152 * g + 0.0722 * b); var val; if (gray >= thresh) { val = 255; } else { val = 0; } pixels[i] = pixels[i + 1] = pixels[i + 2] = val; } }; /** * Converts any colors in the image to grayscale equivalents. * No parameter is used. * * Borrowed from http://www.html5rocks.com/en/tutorials/canvas/imagefilters/ * * @param {Canvas} canvas */ Filters.gray = function (canvas) { var pixels = Filters._toPixels(canvas); for (var i = 0; i < pixels.length; i += 4) { var r = pixels[i]; var g = pixels[i + 1]; var b = pixels[i + 2]; // CIE luminance for RGB var gray = (0.2126 * r + 0.7152 * g + 0.0722 * b); pixels[i] = pixels[i + 1] = pixels[i + 2] = gray; } }; /** * Sets the alpha channel to entirely opaque. No parameter is used. * * @param {Canvas} canvas */ Filters.opaque = function (canvas) { var pixels = Filters._toPixels(canvas); for (var i = 0; i < pixels.length; i += 4) { pixels[i + 3] = 255; } return pixels; }; /** * Sets each pixel to its inverse value. No parameter is used. * @param {Canvas} canvas */ Filters.invert = function (canvas) { var pixels = Filters._toPixels(canvas); for (var i = 0; i < pixels.length; i += 4) { pixels[i] = 255 - pixels[i]; pixels[i + 1] = 255 - pixels[i + 1]; pixels[i + 2] = 255 - pixels[i + 2]; } }; /** * Limits each channel of the image to the number of colors specified as * the parameter. The parameter can be set to values between 2 and 255, but * results are most noticeable in the lower ranges. * * Adapted from java based processing implementation * * @param {Canvas} canvas * @param {Integer} level */ Filters.posterize = function (canvas, level) { var pixels = Filters._toPixels(canvas); if ((level < 2) || (level > 255)) { throw new Error( 'Level must be greater than 2 and less than 255 for posterize' ); } var levels1 = level - 1; for (var i = 0; i < pixels.length; i+=4) { var rlevel = pixels[i]; var glevel = pixels[i + 1]; var blevel = pixels[i + 2]; pixels[i] = (((rlevel * level) >> 8) * 255) / levels1; pixels[i + 1] = (((glevel * level) >> 8) * 255) / levels1; pixels[i + 2] = (((blevel * level) >> 8) * 255) / levels1; } }; /** * reduces the bright areas in an image * @param {Canvas} canvas * */ Filters.dilate = function (canvas) { var pixels = Filters._toPixels(canvas); var currIdx = 0; var maxIdx = pixels.length ? pixels.length/4 : 0; var out = new Int32Array(maxIdx); var currRowIdx, maxRowIdx, colOrig, colOut, currLum; var idxRight, idxLeft, idxUp, idxDown, colRight, colLeft, colUp, colDown, lumRight, lumLeft, lumUp, lumDown; while(currIdx < maxIdx) { currRowIdx = currIdx; maxRowIdx = currIdx + canvas.width; while (currIdx < maxRowIdx) { colOrig = colOut = Filters._getARGB(pixels, currIdx); idxLeft = currIdx - 1; idxRight = currIdx + 1; idxUp = currIdx - canvas.width; idxDown = currIdx + canvas.width; if (idxLeft < currRowIdx) { idxLeft = currIdx; } if (idxRight >= maxRowIdx) { idxRight = currIdx; } if (idxUp < 0){ idxUp = 0; } if (idxDown >= maxIdx) { idxDown = currIdx; } colUp = Filters._getARGB(pixels, idxUp); colLeft = Filters._getARGB(pixels, idxLeft); colDown = Filters._getARGB(pixels, idxDown); colRight = Filters._getARGB(pixels, idxRight); //compute luminance currLum = 77*(colOrig>>16&0xff) + 151*(colOrig>>8&0xff) + 28*(colOrig&0xff); lumLeft = 77*(colLeft>>16&0xff) + 151*(colLeft>>8&0xff) + 28*(colLeft&0xff); lumRight = 77*(colRight>>16&0xff) + 151*(colRight>>8&0xff) + 28*(colRight&0xff); lumUp = 77*(colUp>>16&0xff) + 151*(colUp>>8&0xff) + 28*(colUp&0xff); lumDown = 77*(colDown>>16&0xff) + 151*(colDown>>8&0xff) + 28*(colDown&0xff); if (lumLeft > currLum) { colOut = colLeft; currLum = lumLeft; } if (lumRight > currLum) { colOut = colRight; currLum = lumRight; } if (lumUp > currLum) { colOut = colUp; currLum = lumUp; } if (lumDown > currLum) { colOut = colDown; currLum = lumDown; } out[currIdx++]=colOut; } } Filters._setPixels(pixels, out); }; /** * increases the bright areas in an image * @param {Canvas} canvas * */ Filters.erode = function(canvas) { var pixels = Filters._toPixels(canvas); var currIdx = 0; var maxIdx = pixels.length ? pixels.length/4 : 0; var out = new Int32Array(maxIdx); var currRowIdx, maxRowIdx, colOrig, colOut, currLum; var idxRight, idxLeft, idxUp, idxDown, colRight, colLeft, colUp, colDown, lumRight, lumLeft, lumUp, lumDown; while(currIdx < maxIdx) { currRowIdx = currIdx; maxRowIdx = currIdx + canvas.width; while (currIdx < maxRowIdx) { colOrig = colOut = Filters._getARGB(pixels, currIdx); idxLeft = currIdx - 1; idxRight = currIdx + 1; idxUp = currIdx - canvas.width; idxDown = currIdx + canvas.width; if (idxLeft < currRowIdx) { idxLeft = currIdx; } if (idxRight >= maxRowIdx) { idxRight = currIdx; } if (idxUp < 0) { idxUp = 0; } if (idxDown >= maxIdx) { idxDown = currIdx; } colUp = Filters._getARGB(pixels, idxUp); colLeft = Filters._getARGB(pixels, idxLeft); colDown = Filters._getARGB(pixels, idxDown); colRight = Filters._getARGB(pixels, idxRight); //compute luminance currLum = 77*(colOrig>>16&0xff) + 151*(colOrig>>8&0xff) + 28*(colOrig&0xff); lumLeft = 77*(colLeft>>16&0xff) + 151*(colLeft>>8&0xff) + 28*(colLeft&0xff); lumRight = 77*(colRight>>16&0xff) + 151*(colRight>>8&0xff) + 28*(colRight&0xff); lumUp = 77*(colUp>>16&0xff) + 151*(colUp>>8&0xff) + 28*(colUp&0xff); lumDown = 77*(colDown>>16&0xff) + 151*(colDown>>8&0xff) + 28*(colDown&0xff); if (lumLeft < currLum) { colOut = colLeft; currLum = lumLeft; } if (lumRight < currLum) { colOut = colRight; currLum = lumRight; } if (lumUp < currLum) { colOut = colUp; currLum = lumUp; } if (lumDown < currLum) { colOut = colDown; currLum = lumDown; } out[currIdx++]=colOut; } } Filters._setPixels(pixels, out); }; // BLUR // internal kernel stuff for the gaussian blur filter var blurRadius; var blurKernelSize; var blurKernel; var blurMult; /* * Port of https://github.com/processing/processing/blob/ * master/core/src/processing/core/PImage.java#L1250 * * Optimized code for building the blur kernel. * further optimized blur code (approx. 15% for radius=20) * bigger speed gains for larger radii (~30%) * added support for various image types (ALPHA, RGB, ARGB) * [toxi 050728] */ function buildBlurKernel(r) { var radius = (r * 3.5)|0; radius = (radius < 1) ? 1 : ((radius < 248) ? radius : 248); if (blurRadius !== radius) { blurRadius = radius; blurKernelSize = 1 + blurRadius<<1; blurKernel = new Int32Array(blurKernelSize); blurMult = new Array(blurKernelSize); for(var l = 0; l < blurKernelSize; l++){ blurMult[l] = new Int32Array(256); } var bk,bki; var bm,bmi; for (var i = 1, radiusi = radius - 1; i < radius; i++) { blurKernel[radius+i] = blurKernel[radiusi] = bki = radiusi * radiusi; bm = blurMult[radius+i]; bmi = blurMult[radiusi--]; for (var j = 0; j < 256; j++){ bm[j] = bmi[j] = bki * j; } } bk = blurKernel[radius] = radius * radius; bm = blurMult[radius]; for (var k = 0; k < 256; k++){ bm[k] = bk * k; } } } // Port of https://github.com/processing/processing/blob/ // master/core/src/processing/core/PImage.java#L1433 function blurARGB(canvas, radius) { var pixels = Filters._toPixels(canvas); var width = canvas.width; var height = canvas.height; var numPackedPixels = width * height; var argb = new Int32Array(numPackedPixels); for (var j = 0; j < numPackedPixels; j++) { argb[j] = Filters._getARGB(pixels, j); } var sum, cr, cg, cb, ca; var read, ri, ym, ymi, bk0; var a2 = new Int32Array(numPackedPixels); var r2 = new Int32Array(numPackedPixels); var g2 = new Int32Array(numPackedPixels); var b2 = new Int32Array(numPackedPixels); var yi = 0; buildBlurKernel(radius); var x, y, i; var bm; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { cb = cg = cr = ca = sum = 0; read = x - blurRadius; if (read < 0) { bk0 = -read; read = 0; } else { if (read >= width) { break; } bk0 = 0; } for (i = bk0; i < blurKernelSize; i++) { if (read >= width) { break; } var c = argb[read + yi]; bm = blurMult[i]; ca += bm[(c & -16777216) >>> 24]; cr += bm[(c & 16711680) >> 16]; cg += bm[(c & 65280) >> 8]; cb += bm[c & 255]; sum += blurKernel[i]; read++; } ri = yi + x; a2[ri] = ca / sum; r2[ri] = cr / sum; g2[ri] = cg / sum; b2[ri] = cb / sum; } yi += width; } yi = 0; ym = -blurRadius; ymi = ym * width; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { cb = cg = cr = ca = sum = 0; if (ym < 0) { bk0 = ri = -ym; read = x; } else { if (ym >= height) { break; } bk0 = 0; ri = ym; read = x + ymi; } for (i = bk0; i < blurKernelSize; i++) { if (ri >= height) { break; } bm = blurMult[i]; ca += bm[a2[read]]; cr += bm[r2[read]]; cg += bm[g2[read]]; cb += bm[b2[read]]; sum += blurKernel[i]; ri++; read += width; } argb[x + yi] = (ca/sum)<<24 | (cr/sum)<<16 | (cg/sum)<<8 | (cb/sum); } yi += width; ymi += width; ym++; } Filters._setPixels(pixels, argb); } Filters.blur = function(canvas, radius){ blurARGB(canvas, radius); }; module.exports = Filters; },{}],24:[function(_dereq_,module,exports){ /** * @module IO * @submodule Input * @for p5 * @requires core */ /* globals Request: false */ /* globals Headers: false */ 'use strict'; var p5 = _dereq_('../core/core'); _dereq_('whatwg-fetch'); _dereq_('es6-promise').polyfill(); var fetchJsonp = _dereq_('fetch-jsonp'); _dereq_('../core/error_helpers'); /** * 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. JSONP is supported via a polyfill and you * can pass in as the second argument an object with definitions of the json * callback following the syntax specified here. * * @method loadJSON * @param {String} path name of the file or url to load * @param {Object} [jsonpOptions] options object for jsonp related settings * @param {String} [datatype] "json" or "jsonp" * @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 * @return {Object|Array} JSON data * @example * *

Calling loadJSON() inside preload() guarantees to complete the * operation before setup() and draw() are called.

* *
* // Examples use USGS Earthquake API: * // https://earthquake.usgs.gov/fdsnws/event/1/#methods * var earthquakes; * function preload() { * // Get the most recent earthquake in the database * var url = 'https://earthquake.usgs.gov/fdsnws/event/1/query?' + * 'format=geojson&limit=1&orderby=time'; * earthquakes = loadJSON(url); * } * * function setup() { * noLoop(); * } * * function draw() { * background(200); * // Get the magnitude and name of the earthquake out of the loaded JSON * var earthquakeMag = earthquakes.features[0].properties.mag; * var earthquakeName = earthquakes.features[0].properties.place; * ellipse(width/2, height/2, earthquakeMag * 10, earthquakeMag * 10); * textAlign(CENTER); * text(earthquakeName, 0, height - 30, width, 30); * } *
* * *

Outside of preload(), you may supply a callback function to handle the * object:

*
* function setup() { * noLoop(); * var url = 'https://earthquake.usgs.gov/fdsnws/event/1/query?' + * 'format=geojson&limit=1&orderby=time'; * loadJSON(url, drawEarthquake); * } * * function draw() { * background(200); * } * * function drawEarthquake(earthquakes) { * // Get the magnitude and name of the earthquake out of the loaded JSON * var earthquakeMag = earthquakes.features[0].properties.mag; * var earthquakeName = earthquakes.features[0].properties.place; * ellipse(width/2, height/2, earthquakeMag * 10, earthquakeMag * 10); * textAlign(CENTER); * text(earthquakeName, 0, height - 30, width, 30); * } *
* * @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; var errorCallback; var options; var ret = {}; // object needed for preload var t = 'json'; // check for explicit data type argument for (var i = 1; i < arguments.length; i++) { var arg = arguments[i]; if (typeof arg === 'string') { if (arg === 'jsonp' || arg === 'json') { t = arg; } } else if (typeof arg === 'function') { if(!callback){ callback = arg; }else{ errorCallback = arg; } } else if (typeof arg === 'object' && arg.hasOwnProperty('jsonpCallback')){ t = 'jsonp'; options = arg; } } var self = this; p5.prototype.httpDo(path, 'GET', options, t, function(resp){ for (var k in resp) { ret[k] = resp[k]; } if (typeof callback !== 'undefined') { callback(resp); } self._decrementPreload(); }, errorCallback); 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 {String[]} 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 () { var ret = []; var callback, errorCallback; for(var i=1; iReads 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} [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. * @param {function} [errorCallback] function to be executed if * there is an error, response is passed * in as first argument * @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; var errorCallback; var options = []; var header = false; var ext = path.substring(path.lastIndexOf('.')+1,path.length); var sep = ','; var separatorSet = false; if(ext === 'tsv'){ //Only need to check extension is tsv because csv is default sep = '\t'; } for (var i = 1; i < arguments.length; i++) { if (typeof (arguments[i]) === 'function') { if(typeof callback === 'undefined'){ callback = arguments[i]; } else if (typeof errorCallback === 'undefined') { errorCallback = 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(); var self = this; p5.prototype.httpDo(path, 'GET', 'text', function(resp){ 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 tokenBegin = function () { state.currentState = PRE_TOKEN; state.token = ''; }; var tokenEnd = function () { currentRecord.push(state.token); tokenBegin(); }; var recordBegin = function () { state.escaped = false; currentRecord = []; tokenBegin(); }; var recordEnd = function () { state.currentState = POST_RECORD; records.push(currentRecord); currentRecord = null; }; 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 (records[i].length === 1) { if (records[i][0] === 'undefined' || records[i][0] === '') { continue; } } row = new p5.TableRow(); row.arr = records[i]; row.obj = makeObject(records[i], t.columns); t.addRow(row); } if (typeof callback === 'function') { callback(t); } self._decrementPreload(); }, function(err){ // Error handling p5._friendlyFileLoadError(2, path); if(errorCallback){ errorCallback(err); }else{ throw err; } }); 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 * @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].getNum("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.prototype.loadXML = function() { var ret = {}; var callback, errorCallback; for(var i=1; ihttpDo(path, 'GET'). * * @method httpGet * @param {String} path name of the file or url to load * @param {String} [datatype] "json", "jsonp", "xml", or "text" * @param {Object} [data] param data passed sent with request * @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 * @example *
* // Examples use USGS Earthquake API: * // https://earthquake.usgs.gov/fdsnws/event/1/#methods * var earthquakes; * function preload() { * // Get the most recent earthquake in the database * var url = 'https://earthquake.usgs.gov/fdsnws/event/1/query?' + * 'format=geojson&limit=1&orderby=time'; * httpGet(url, "jsonp", false, function(response) { * // when the HTTP request completes, populate the variable that holds the * // earthquake data used in the visualization. * earthquakes = response; * }); * } * * function draw() { * if (!earthquakes) { * // Wait until the earthquake data has loaded before drawing. * return * } * background(200); * // Get the magnitude and name of the earthquake out of the loaded JSON * var earthquakeMag = earthquakes.features[0].properties.mag; * var earthquakeName = earthquakes.features[0].properties.place; * ellipse(width/2, height/2, earthquakeMag * 10, earthquakeMag * 10); * textAlign(CENTER); * text(earthquakeName, 0, height - 30, width, 30); * noLoop(); * } *
*/ p5.prototype.httpGet = function () { var args = Array.prototype.slice.call(arguments); args.splice(1, 0, '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. This is equivalent to * calling httpDo(path, 'POST'). * * @method httpPost * @param {String} path name of the file or url to load * @param {String} [datatype] "json", "jsonp", "xml", or "text" * @param {Object} [data] param data passed sent with request * @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 = Array.prototype.slice.call(arguments); args.splice(1, 0, '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.

* For more advanced use, you may also pass in the path as the first argument * and a object as the second argument, the signature follows the one specified * in the Fetch API specification. * * @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 {String} [datatype] "json", "jsonp", "xml", or "text" * @param {Object} [data] param data passed sent with request * @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 */ /** * @method httpDo * @param {String} path * @param {Object} options Request object options as documented in the * "fetch" API * reference * @param {function} [callback] * @param {function} [errorCallback] */ p5.prototype.httpDo = function () { var type = ''; var callback; var errorCallback; var request; var jsonpOptions = {}; var cbCount = 0; var contentType = 'text/plain'; // Trim the callbacks off the end to get an idea of how many arguments are passed for (var i = arguments.length-1; i > 0; i--){ if(typeof arguments[i] === 'function'){ cbCount++; }else{ break; } } // The number of arguments minus callbacks var argsCount = arguments.length - cbCount; if(argsCount === 2 && typeof arguments[0] === 'string' && typeof arguments[1] === 'object'){ // Intended for more advanced use, pass in Request parameters directly request = new Request(arguments[0], arguments[1]); callback = arguments[2]; errorCallback = arguments[3]; // do some sort of smart type checking if (type === '') { if (request.url.indexOf('json') !== -1) { type = 'json'; } else if (request.url.indexOf('xml') !== -1) { type = 'xml'; } else { type = 'text'; } } } else { // Provided with arguments var path = arguments[0]; var method = 'GET'; var data; for (var j = 1; j < arguments.length; j++) { var a = arguments[j]; if (typeof a === 'string') { if (a === 'GET' || a === 'POST' || a === 'PUT' || a === 'DELETE') { method = a; } else if(a === 'json' || a === 'jsonp' || a === 'xml' || a === 'text') { type = a; } else { data = a; } } else if (typeof a === 'number') { data = a.toString(); } else if (typeof a === 'object') { if(a.hasOwnProperty('jsonpCallback')){ for (var attr in a) { jsonpOptions[attr] = a[attr]; } }else{ data = JSON.stringify(a); contentType = 'application/json'; } } 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'; } } request = new Request(path, { method: method, mode: 'cors', body: data, headers: new Headers({ 'Content-Type': contentType }) }); } if(type === 'jsonp'){ fetchJsonp(arguments[0], jsonpOptions) .then(function(res){ if(res.ok){ return res.json(); } throw res; }).then(function(resp){ callback(resp); }).catch(function(err){ if (errorCallback) { errorCallback(err); } else { throw err; } }); }else{ fetch(request) .then(function(res){ if(res.ok){ if(type === 'json'){ return res.json(); }else{ return res.text(); } } throw res; }) .then(function(resp){ if (type === 'xml'){ var parser = new DOMParser(); resp = parser.parseFromString(resp, 'text/xml'); resp = parseXML(resp.documentElement); } callback(resp); }).catch(function(err, msg){ if (errorCallback) { errorCallback(err); } else { throw err; } }); } }; /** * @module IO * @submodule Output * @for p5 */ window.URL = window.URL || window.webkitURL; // private array of p5.PrintWriter objects p5.prototype._pWriters = []; 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.PrintWriter = function (filename, extension) { var self = this; this.name = filename; this.content = ''; //Changed to write because it was being overloaded by function below. this.write = 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 = {}; }; }; // object, filename, options --> saveJSON, saveStrings, // 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); } this.saveStrings(stringify.split('\n'), filename, 'json'); }; p5.prototype.saveJSONObject = p5.prototype.saveJSON; p5.prototype.saveJSONArray = p5.prototype.saveJSON; /** * 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(); }; // ======= // 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 ext; if(options === undefined){ ext = filename.substring(filename.lastIndexOf('.')+1,filename.length); }else{ ext = options; } var pWriter = this.createWriter(filename, ext); var header = table.columns; var sep = ','; // default to CSV if (ext === 'tsv') { sep = '\t'; } if (ext !== '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.write(header[h] + sep); } else { pWriter.write(header[h]); } } pWriter.write('\n'); } // 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.write(table.rows[i].arr[j] + sep); } else if (i < table.rows.length - 1) { pWriter.write(table.rows[i].arr[j]); } else { pWriter.write(table.rows[i].arr[j]); } } pWriter.write('\n'); } } // otherwise, make HTML else { pWriter.print(''); pWriter.print(''); var str = ' '); 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(' '); } 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(' '); } pWriter.print(' '); } pWriter.print('
' + e); pWriter.print('
' + htmlEntry); 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 = function(e) { destroyClickedElement(e); e.stopPropagation(); }; 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 {String[]} [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":9,"../core/error_helpers":12,"es6-promise":1,"fetch-jsonp":2,"whatwg-fetch":4}],25:[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 {p5.TableRow[]} [rows] An array of p5.TableRow objects */ p5.Table = function (rows) { /** * @property columns {String[]} */ this.columns = []; /** * @property rows {p5.TableRow[]} */ 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 {p5.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 {p5.TableRow[]} 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 {p5.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 {p5.TableRow[]} 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 {p5.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 {p5.TableRow[]} 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; },{"../core/core":9}],26:[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); 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]); } else { ret = parseFloat(this.arr[column]); } 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; },{"../core/core":9}],27:[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 * @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].getNum("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 {p5.XML} 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 {String[]} 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 {p5.XML[]} 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 {p5.XML} 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 {String[]} 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 getNum * @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.getNum("id")); * } * * // Sketch prints: * // 0 *
*/ p5.XML.prototype.getNum = 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; },{"../core/core":9}],28:[function(_dereq_,module,exports){ module.exports = { degreesToRadians: function(x) { return 2 * Math.PI * x / 360; }, radiansToDegrees: function(x) { return 360 * x / (2 * Math.PI); } }; },{}],29:[function(_dereq_,module,exports){ /** * @requires constants * @todo see methods below needing further implementation. * future consideration: implement SIMD optimizations * when browser compatibility becomes available * https://developer.mozilla.org/en-US/docs/Web/JavaScript/ * Reference/Global_Objects/SIMD */ 'use strict'; var p5 = _dereq_('../core/core'); var polarGeometry = _dereq_('../math/polargeometry'); var constants = _dereq_('../core/constants'); var GLMAT_ARRAY_TYPE = ( typeof Float32Array !== 'undefined') ? Float32Array : Array; /** * A class to describe a 4x4 matrix * for model and view matrix manipulation in the p5js webgl renderer. * class p5.Matrix * @constructor * @param {Array} [mat4] array literal of our 4x4 matrix */ p5.Matrix = function() { var args = new Array(arguments.length); for (var i = 0; i < args.length; ++i) { args[i] = arguments[i]; } // This is default behavior when object // instantiated using createMatrix() // @todo implement createMatrix() in core/math.js if(args[0] instanceof p5) { // save reference to p5 if passed in this.p5 = args[0]; if(args[1] === 'mat3'){ this.mat3 = args[2] || new GLMAT_ARRAY_TYPE([ 1, 0, 0, 0, 1, 0, 0, 0, 1 ]); } else { this.mat4 = args[1] || new GLMAT_ARRAY_TYPE([ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ]); } // default behavior when object // instantiated using new p5.Matrix() } else { if(args[0] === 'mat3'){ this.mat3 = args[1] || new GLMAT_ARRAY_TYPE([ 1, 0, 0, 0, 1, 0, 0, 0, 1 ]); } else { this.mat4 = args[0] || new GLMAT_ARRAY_TYPE([ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ]); } } return this; }; /** * Sets the x, y, and z component of the vector using two or three separate * variables, the data from a p5.Matrix, or the values from a float array. * * @param {p5.Matrix|Float32Array|Array} [inMatrix] the input p5.Matrix or * an Array of length 16 * @chainable */ p5.Matrix.prototype.set = function (inMatrix) { if (inMatrix instanceof p5.Matrix) { this.mat4 = inMatrix.mat4; return this; } else if (inMatrix instanceof GLMAT_ARRAY_TYPE) { this.mat4 = inMatrix; return this; } return this; }; /** * Gets a copy of the vector, returns a p5.Matrix object. * * @return {p5.Matrix} the copy of the p5.Matrix object */ p5.Matrix.prototype.get = function () { return new p5.Matrix(this.mat4); }; /** * return a copy of a matrix * @return {p5.Matrix} the result matrix */ p5.Matrix.prototype.copy = function(){ var copied = new p5.Matrix(); copied.mat4[0] = this.mat4[0]; copied.mat4[1] = this.mat4[1]; copied.mat4[2] = this.mat4[2]; copied.mat4[3] = this.mat4[3]; copied.mat4[4] = this.mat4[4]; copied.mat4[5] = this.mat4[5]; copied.mat4[6] = this.mat4[6]; copied.mat4[7] = this.mat4[7]; copied.mat4[8] = this.mat4[8]; copied.mat4[9] = this.mat4[9]; copied.mat4[10] = this.mat4[10]; copied.mat4[11] = this.mat4[11]; copied.mat4[12] = this.mat4[12]; copied.mat4[13] = this.mat4[13]; copied.mat4[14] = this.mat4[14]; copied.mat4[15] = this.mat4[15]; return copied; }; /** * return an identity matrix * @return {p5.Matrix} the result matrix */ p5.Matrix.identity = function(){ return new p5.Matrix(); }; /** * transpose according to a given matrix * @param {p5.Matrix|Float32Array|Array} a the matrix to be based on to transpose * @chainable */ p5.Matrix.prototype.transpose = function(a){ var a01, a02, a03, a12, a13, a23; if(a instanceof p5.Matrix){ a01 = a.mat4[1]; a02 = a.mat4[2]; a03 = a.mat4[3]; a12 = a.mat4[6]; a13 = a.mat4[7]; a23 = a.mat4[11]; this.mat4[0] = a.mat4[0]; this.mat4[1] = a.mat4[4]; this.mat4[2] = a.mat4[8]; this.mat4[3] = a.mat4[12]; this.mat4[4] = a01; this.mat4[5] = a.mat4[5]; this.mat4[6] = a.mat4[9]; this.mat4[7] = a.mat4[13]; this.mat4[8] = a02; this.mat4[9] = a12; this.mat4[10] = a.mat4[10]; this.mat4[11] = a.mat4[14]; this.mat4[12] = a03; this.mat4[13] = a13; this.mat4[14] = a23; this.mat4[15] = a.mat4[15]; }else if(a instanceof GLMAT_ARRAY_TYPE){ a01 = a[1]; a02 = a[2]; a03 = a[3]; a12 = a[6]; a13 = a[7]; a23 = a[11]; this.mat4[0] = a[0]; this.mat4[1] = a[4]; this.mat4[2] = a[8]; this.mat4[3] = a[12]; this.mat4[4] = a01; this.mat4[5] = a[5]; this.mat4[6] = a[9]; this.mat4[7] = a[13]; this.mat4[8] = a02; this.mat4[9] = a12; this.mat4[10] = a[10]; this.mat4[11] = a[14]; this.mat4[12] = a03; this.mat4[13] = a13; this.mat4[14] = a23; this.mat4[15] = a[15]; } return this; }; /** * invert matrix according to a give matrix * @param {p5.Matrix|Float32Array|Array} a the matrix to be based on to invert * @chainable */ p5.Matrix.prototype.invert = function(a){ var a00, a01, a02, a03, a10, a11, a12, a13, a20, a21, a22, a23, a30, a31, a32, a33; if(a instanceof p5.Matrix){ a00 = a.mat4[0]; a01 = a.mat4[1]; a02 = a.mat4[2]; a03 = a.mat4[3]; a10 = a.mat4[4]; a11 = a.mat4[5]; a12 = a.mat4[6]; a13 = a.mat4[7]; a20 = a.mat4[8]; a21 = a.mat4[9]; a22 = a.mat4[10]; a23 = a.mat4[11]; a30 = a.mat4[12]; a31 = a.mat4[13]; a32 = a.mat4[14]; a33 = a.mat4[15]; }else if(a instanceof GLMAT_ARRAY_TYPE){ a00 = a[0]; a01 = a[1]; a02 = a[2]; a03 = a[3]; a10 = a[4]; a11 = a[5]; a12 = a[6]; a13 = a[7]; a20 = a[8]; a21 = a[9]; a22 = a[10]; a23 = a[11]; a30 = a[12]; a31 = a[13]; a32 = a[14]; a33 = a[15]; } var b00 = a00 * a11 - a01 * a10, b01 = a00 * a12 - a02 * a10, b02 = a00 * a13 - a03 * a10, b03 = a01 * a12 - a02 * a11, b04 = a01 * a13 - a03 * a11, b05 = a02 * a13 - a03 * a12, b06 = a20 * a31 - a21 * a30, b07 = a20 * a32 - a22 * a30, b08 = a20 * a33 - a23 * a30, b09 = a21 * a32 - a22 * a31, b10 = a21 * a33 - a23 * a31, b11 = a22 * a33 - a23 * a32, // Calculate the determinant det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; if (!det) { return null; } det = 1.0 / det; this.mat4[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det; this.mat4[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det; this.mat4[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det; this.mat4[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det; this.mat4[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det; this.mat4[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det; this.mat4[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det; this.mat4[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det; this.mat4[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det; this.mat4[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det; this.mat4[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det; this.mat4[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det; this.mat4[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det; this.mat4[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det; this.mat4[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det; this.mat4[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det; return this; }; /** * Inverts a 3x3 matrix * @chainable */ p5.Matrix.prototype.invert3x3 = function (){ var a00 = this.mat3[0], a01 = this.mat3[1], a02 = this.mat3[2], a10 = this.mat3[3], a11 = this.mat3[4], a12 = this.mat3[5], a20 = this.mat3[6], a21 = this.mat3[7], a22 = this.mat3[8], b01 = a22 * a11 - a12 * a21, b11 = -a22 * a10 + a12 * a20, b21 = a21 * a10 - a11 * a20, // Calculate the determinant det = a00 * b01 + a01 * b11 + a02 * b21; if (!det) { return null; } det = 1.0 / det; this.mat3[0] = b01 * det; this.mat3[1] = (-a22 * a01 + a02 * a21) * det; this.mat3[2] = (a12 * a01 - a02 * a11) * det; this.mat3[3] = b11 * det; this.mat3[4] = (a22 * a00 - a02 * a20) * det; this.mat3[5] = (-a12 * a00 + a02 * a10) * det; this.mat3[6] = b21 * det; this.mat3[7] = (-a21 * a00 + a01 * a20) * det; this.mat3[8] = (a11 * a00 - a01 * a10) * det; return this; }; /** * transposes a 3x3 p5.Matrix by a mat3 * @param {[Number]} mat3 1-dimensional array * @chainable */ p5.Matrix.prototype.transpose3x3 = function (mat3){ var a01 = mat3[1], a02 = mat3[2], a12 = mat3[5]; this.mat3[1] = mat3[3]; this.mat3[2] = mat3[6]; this.mat3[3] = a01; this.mat3[5] = mat3[7]; this.mat3[6] = a02; this.mat3[7] = a12; return this; }; /** * converts a 4x4 matrix to its 3x3 inverse tranform * commonly used in MVMatrix to NMatrix conversions. * @param {p5.Matrix} mat4 the matrix to be based on to invert * @chainable * @todo finish implementation */ p5.Matrix.prototype.inverseTranspose = function (matrix){ if(this.mat3 === undefined){ console.error('sorry, this function only works with mat3'); } else { //convert mat4 -> mat3 this.mat3[0] = matrix.mat4[0]; this.mat3[1] = matrix.mat4[1]; this.mat3[2] = matrix.mat4[2]; this.mat3[3] = matrix.mat4[4]; this.mat3[4] = matrix.mat4[5]; this.mat3[5] = matrix.mat4[6]; this.mat3[6] = matrix.mat4[8]; this.mat3[7] = matrix.mat4[9]; this.mat3[8] = matrix.mat4[10]; } this.invert3x3().transpose3x3(this.mat3); return this; }; /** * inspired by Toji's mat4 determinant * @return {Number} Determinant of our 4x4 matrix */ p5.Matrix.prototype.determinant = function(){ var d00 = (this.mat4[0] * this.mat4[5]) - (this.mat4[1] * this.mat4[4]), d01 = (this.mat4[0] * this.mat4[6]) - (this.mat4[2] * this.mat4[4]), d02 = (this.mat4[0] * this.mat4[7]) - (this.mat4[3] * this.mat4[4]), d03 = (this.mat4[1] * this.mat4[6]) - (this.mat4[2] * this.mat4[5]), d04 = (this.mat4[1] * this.mat4[7]) - (this.mat4[3] * this.mat4[5]), d05 = (this.mat4[2] * this.mat4[7]) - (this.mat4[3] * this.mat4[6]), d06 = (this.mat4[8] * this.mat4[13]) - (this.mat4[9] * this.mat4[12]), d07 = (this.mat4[8] * this.mat4[14]) - (this.mat4[10] * this.mat4[12]), d08 = (this.mat4[8] * this.mat4[15]) - (this.mat4[11] * this.mat4[12]), d09 = (this.mat4[9] * this.mat4[14]) - (this.mat4[10] * this.mat4[13]), d10 = (this.mat4[9] * this.mat4[15]) - (this.mat4[11] * this.mat4[13]), d11 = (this.mat4[10] * this.mat4[15]) - (this.mat4[11] * this.mat4[14]); // Calculate the determinant return d00 * d11 - d01 * d10 + d02 * d09 + d03 * d08 - d04 * d07 + d05 * d06; }; /** * multiply two mat4s * @param {p5.Matrix|Float32Array|Array} multMatrix The matrix * we want to multiply by * @chainable */ p5.Matrix.prototype.mult = function(multMatrix){ var _dest = new GLMAT_ARRAY_TYPE(16); var _src = new GLMAT_ARRAY_TYPE(16); if(multMatrix instanceof p5.Matrix) { _src = multMatrix.mat4; } else if(multMatrix instanceof GLMAT_ARRAY_TYPE){ _src = multMatrix; } // each row is used for the multiplier var b0 = this.mat4[0], b1 = this.mat4[1], b2 = this.mat4[2], b3 = this.mat4[3]; _dest[0] = b0*_src[0] + b1*_src[4] + b2*_src[8] + b3*_src[12]; _dest[1] = b0*_src[1] + b1*_src[5] + b2*_src[9] + b3*_src[13]; _dest[2] = b0*_src[2] + b1*_src[6] + b2*_src[10] + b3*_src[14]; _dest[3] = b0*_src[3] + b1*_src[7] + b2*_src[11] + b3*_src[15]; b0 = this.mat4[4]; b1 = this.mat4[5]; b2 = this.mat4[6]; b3 = this.mat4[7]; _dest[4] = b0*_src[0] + b1*_src[4] + b2*_src[8] + b3*_src[12]; _dest[5] = b0*_src[1] + b1*_src[5] + b2*_src[9] + b3*_src[13]; _dest[6] = b0*_src[2] + b1*_src[6] + b2*_src[10] + b3*_src[14]; _dest[7] = b0*_src[3] + b1*_src[7] + b2*_src[11] + b3*_src[15]; b0 = this.mat4[8]; b1 = this.mat4[9]; b2 = this.mat4[10]; b3 = this.mat4[11]; _dest[8] = b0*_src[0] + b1*_src[4] + b2*_src[8] + b3*_src[12]; _dest[9] = b0*_src[1] + b1*_src[5] + b2*_src[9] + b3*_src[13]; _dest[10] = b0*_src[2] + b1*_src[6] + b2*_src[10] + b3*_src[14]; _dest[11] = b0*_src[3] + b1*_src[7] + b2*_src[11] + b3*_src[15]; b0 = this.mat4[12]; b1 = this.mat4[13]; b2 = this.mat4[14]; b3 = this.mat4[15]; _dest[12] = b0*_src[0] + b1*_src[4] + b2*_src[8] + b3*_src[12]; _dest[13] = b0*_src[1] + b1*_src[5] + b2*_src[9] + b3*_src[13]; _dest[14] = b0*_src[2] + b1*_src[6] + b2*_src[10] + b3*_src[14]; _dest[15] = b0*_src[3] + b1*_src[7] + b2*_src[11] + b3*_src[15]; this.mat4 = _dest; return this; }; /** * scales a p5.Matrix by scalars or a vector * @param {p5.Vector|Float32Array|Array} s vector to scale by * @chainable */ p5.Matrix.prototype.scale = function() { var x,y,z; var args = new Array(arguments.length); for (var i = 0; i < args.length; ++i) { args[i] = arguments[i]; } //if our 1st arg is a type p5.Vector if (args[0] instanceof p5.Vector){ x = args[0].x; y = args[0].y; z = args[0].z; } //otherwise if it's an array else if (args[0] instanceof Array){ x = args[0][0]; y = args[0][1]; z = args[0][2]; } var _dest = new GLMAT_ARRAY_TYPE(16); _dest[0] = this.mat4[0] * x; _dest[1] = this.mat4[1] * x; _dest[2] = this.mat4[2] * x; _dest[3] = this.mat4[3] * x; _dest[4] = this.mat4[4] * y; _dest[5] = this.mat4[5] * y; _dest[6] = this.mat4[6] * y; _dest[7] = this.mat4[7] * y; _dest[8] = this.mat4[8] * z; _dest[9] = this.mat4[9] * z; _dest[10] = this.mat4[10] * z; _dest[11] = this.mat4[11] * z; _dest[12] = this.mat4[12]; _dest[13] = this.mat4[13]; _dest[14] = this.mat4[14]; _dest[15] = this.mat4[15]; this.mat4 = _dest; return this; }; /** * rotate our Matrix around an axis by the given angle. * @param {Number} a The angle of rotation in radians * @param {p5.Vector|Array} axis the axis(es) to rotate around * @chainable * inspired by Toji's gl-matrix lib, mat4 rotation */ p5.Matrix.prototype.rotate = function(a, axis){ var x, y, z, _a, len; if (this.p5) { if (this.p5._angleMode === constants.DEGREES) { _a = polarGeometry.degreesToRadians(a); } } else { _a = a; } if (axis instanceof p5.Vector) { x = axis.x; y = axis.y; z = axis.z; } else if (axis instanceof Array) { x = axis[0]; y = axis[1]; z = axis[2]; } len = Math.sqrt(x * x + y * y + z * z); x *= (1/len); y *= (1/len); z *= (1/len); var a00 = this.mat4[0]; var a01 = this.mat4[1]; var a02 = this.mat4[2]; var a03 = this.mat4[3]; var a10 = this.mat4[4]; var a11 = this.mat4[5]; var a12 = this.mat4[6]; var a13 = this.mat4[7]; var a20 = this.mat4[8]; var a21 = this.mat4[9]; var a22 = this.mat4[10]; var a23 = this.mat4[11]; //sin,cos, and tan of respective angle var sA = Math.sin(_a); var cA = Math.cos(_a); var tA = 1 - cA; // Construct the elements of the rotation matrix var b00 = x * x * tA + cA; var b01 = y * x * tA + z * sA; var b02 = z * x * tA - y * sA; var b10 = x * y * tA - z * sA; var b11 = y * y * tA + cA; var b12 = z * y * tA + x * sA; var b20 = x * z * tA + y * sA; var b21 = y * z * tA - x * sA; var b22 = z * z * tA + cA; // rotation-specific matrix multiplication this.mat4[0] = a00 * b00 + a10 * b01 + a20 * b02; this.mat4[1] = a01 * b00 + a11 * b01 + a21 * b02; this.mat4[2] = a02 * b00 + a12 * b01 + a22 * b02; this.mat4[3] = a03 * b00 + a13 * b01 + a23 * b02; this.mat4[4] = a00 * b10 + a10 * b11 + a20 * b12; this.mat4[5] = a01 * b10 + a11 * b11 + a21 * b12; this.mat4[6] = a02 * b10 + a12 * b11 + a22 * b12; this.mat4[7] = a03 * b10 + a13 * b11 + a23 * b12; this.mat4[8] = a00 * b20 + a10 * b21 + a20 * b22; this.mat4[9] = a01 * b20 + a11 * b21 + a21 * b22; this.mat4[10] = a02 * b20 + a12 * b21 + a22 * b22; this.mat4[11] = a03 * b20 + a13 * b21 + a23 * b22; return this; }; /** * @todo finish implementing this method! * translates * @param {Number[]} v vector to translate by * @chainable */ p5.Matrix.prototype.translate = function(v){ var x = v[0], y = v[1], z = v[2] || 0; this.mat4[12] = this.mat4[0] * x +this.mat4[4] * y +this.mat4[8] * z +this.mat4[12]; this.mat4[13] = this.mat4[1] * x +this.mat4[5] * y +this.mat4[9] * z +this.mat4[13]; this.mat4[14] = this.mat4[2] * x +this.mat4[6] * y +this.mat4[10] * z +this.mat4[14]; this.mat4[15] = this.mat4[3] * x +this.mat4[7] * y +this.mat4[11] * z +this.mat4[15]; }; p5.Matrix.prototype.rotateX = function(a){ this.rotate(a, [1,0,0]); }; p5.Matrix.prototype.rotateY = function(a){ this.rotate(a, [0,1,0]); }; p5.Matrix.prototype.rotateZ = function(a){ this.rotate(a, [0,0,1]); }; /** * sets the perspective matrix * @param {Number} fovy [description] * @param {Number} aspect [description] * @param {Number} near near clipping plane * @param {Number} far far clipping plane * @chainable */ p5.Matrix.prototype.perspective = function(fovy,aspect,near,far){ var f = 1.0 / Math.tan(fovy / 2), nf = 1 / (near - far); this.mat4[0] = f / aspect; this.mat4[1] = 0; this.mat4[2] = 0; this.mat4[3] = 0; this.mat4[4] = 0; this.mat4[5] = f; this.mat4[6] = 0; this.mat4[7] = 0; this.mat4[8] = 0; this.mat4[9] = 0; this.mat4[10] = (far + near) * nf; this.mat4[11] = -1; this.mat4[12] = 0; this.mat4[13] = 0; this.mat4[14] = (2 * far * near) * nf; this.mat4[15] = 0; return this; }; /** * sets the ortho matrix * @param {Number} left [description] * @param {Number} right [description] * @param {Number} bottom [description] * @param {Number} top [description] * @param {Number} near near clipping plane * @param {Number} far far clipping plane * @chainable */ p5.Matrix.prototype.ortho = function(left,right,bottom,top,near,far){ var lr = 1 / (left - right), bt = 1 / (bottom - top), nf = 1 / (near - far); this.mat4[0] = -2 * lr; this.mat4[1] = 0; this.mat4[2] = 0; this.mat4[3] = 0; this.mat4[4] = 0; this.mat4[5] = -2 * bt; this.mat4[6] = 0; this.mat4[7] = 0; this.mat4[8] = 0; this.mat4[9] = 0; this.mat4[10] = 2 * nf; this.mat4[11] = 0; this.mat4[12] = (left + right) * lr; this.mat4[13] = (top + bottom) * bt; this.mat4[14] = (far + near) * nf; this.mat4[15] = 1; return this; }; /** * PRIVATE */ // matrix methods adapted from: // https://developer.mozilla.org/en-US/docs/Web/WebGL/ // gluPerspective // // function _makePerspective(fovy, aspect, znear, zfar){ // var ymax = znear * Math.tan(fovy * Math.PI / 360.0); // var ymin = -ymax; // var xmin = ymin * aspect; // var xmax = ymax * aspect; // return _makeFrustum(xmin, xmax, ymin, ymax, znear, zfar); // } //// //// glFrustum //// //function _makeFrustum(left, right, bottom, top, znear, zfar){ // var X = 2*znear/(right-left); // var Y = 2*znear/(top-bottom); // var A = (right+left)/(right-left); // var B = (top+bottom)/(top-bottom); // var C = -(zfar+znear)/(zfar-znear); // var D = -2*zfar*znear/(zfar-znear); // var frustrumMatrix =[ // X, 0, A, 0, // 0, Y, B, 0, // 0, 0, C, D, // 0, 0, -1, 0 //]; //return frustrumMatrix; // } // function _setMVPMatrices(){ ////an identity matrix ////@TODO use the p5.Matrix class to abstract away our MV matrices and ///other math //var _mvMatrix = //[ // 1.0,0.0,0.0,0.0, // 0.0,1.0,0.0,0.0, // 0.0,0.0,1.0,0.0, // 0.0,0.0,0.0,1.0 //]; module.exports = p5.Matrix; },{"../core/constants":8,"../core/core":9,"../math/polargeometry":28}],30:[function(_dereq_,module,exports){ 'use strict'; var p5 = _dereq_('../core/core'); var shader = _dereq_('./shader'); _dereq_('../core/p5.Renderer'); _dereq_('./p5.Matrix'); var uMVMatrixStack = []; //@TODO should implement public method //to override these attributes var attributes = { alpha: true, depth: true, stencil: true, antialias: false, premultipliedAlpha: false, preserveDrawingBuffer: false }; /** * 3D graphics class * @class p5.RendererGL * @constructor * @extends p5.Renderer * @todo extend class to include public method for offscreen * rendering (FBO). * */ p5.RendererGL = function(elt, pInst, isMainCanvas) { p5.Renderer.call(this, elt, pInst, isMainCanvas); this._initContext(); this.isP3D = true; //lets us know we're in 3d mode this.GL = this.drawingContext; //lights this.ambientLightCount = 0; this.directionalLightCount = 0; this.pointLightCount = 0; //camera this._curCamera = null; /** * model view, projection, & normal * matrices */ this.uMVMatrix = new p5.Matrix(); this.uPMatrix = new p5.Matrix(); this.uNMatrix = new p5.Matrix('mat3'); //Geometry & Material hashes this.gHash = {}; this.mHash = {}; //Imediate Mode //default drawing is done in Retained Mode this.isImmediateDrawing = false; this.immediateMode = {}; this.curFillColor = [0.5,0.5,0.5,1.0]; this.curStrokeColor = [0.5,0.5,0.5,1.0]; this.pointSize = 5.0;//default point/stroke return this; }; p5.RendererGL.prototype = Object.create(p5.Renderer.prototype); ////////////////////////////////////////////// // Setting ////////////////////////////////////////////// p5.RendererGL.prototype._initContext = function() { try { this.drawingContext = this.canvas.getContext('webgl', attributes) || this.canvas.getContext('experimental-webgl', attributes); if (this.drawingContext === null) { throw new Error('Error creating webgl context'); } else { console.log('p5.RendererGL: enabled webgl context'); var gl = this.drawingContext; gl.enable(gl.DEPTH_TEST); gl.depthFunc(gl.LEQUAL); gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight); } } catch (er) { throw new Error(er); } }; //detect if user didn't set the camera //then call this function below p5.RendererGL.prototype._setDefaultCamera = function(){ if(this._curCamera === null){ var _w = this.width; var _h = this.height; this.uPMatrix = p5.Matrix.identity(); var cameraZ = (this.height / 2) / Math.tan(Math.PI * 30 / 180); this.uPMatrix.perspective(60 / 180 * Math.PI, _w / _h, cameraZ * 0.1, cameraZ * 10); this._curCamera = 'default'; } }; p5.RendererGL.prototype._update = function() { this.uMVMatrix = p5.Matrix.identity(); this.translate(0, 0, -(this.height / 2) / Math.tan(Math.PI * 30 / 180)); this.ambientLightCount = 0; this.directionalLightCount = 0; this.pointLightCount = 0; }; /** * [background description] */ p5.RendererGL.prototype.background = function() { var gl = this.GL; var _col = this._pInst.color.apply(this._pInst, arguments); var _r = (_col.levels[0]) / 255; var _g = (_col.levels[1]) / 255; var _b = (_col.levels[2]) / 255; var _a = (_col.levels[3]) / 255; gl.clearColor(_r, _g, _b, _a); gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); }; //@TODO implement this // p5.RendererGL.prototype.clear = function() { //@TODO // }; ////////////////////////////////////////////// // SHADER ////////////////////////////////////////////// /** * [_initShaders description] * @param {string} vertId [description] * @param {string} fragId [description] * @return {Object} the shader program */ p5.RendererGL.prototype._initShaders = function(vertId, fragId, isImmediateMode) { var gl = this.GL; //set up our default shaders by: // 1. create the shader, // 2. load the shader source, // 3. compile the shader var _vertShader = gl.createShader(gl.VERTEX_SHADER); //load in our default vertex shader gl.shaderSource(_vertShader, shader[vertId]); gl.compileShader(_vertShader); // if our vertex shader failed compilation? if (!gl.getShaderParameter(_vertShader, gl.COMPILE_STATUS)) { alert('Yikes! An error occurred compiling the shaders:' + gl.getShaderInfoLog(_vertShader)); return null; } var _fragShader = gl.createShader(gl.FRAGMENT_SHADER); //load in our material frag shader gl.shaderSource(_fragShader, shader[fragId]); gl.compileShader(_fragShader); // if our frag shader failed compilation? if (!gl.getShaderParameter(_fragShader, gl.COMPILE_STATUS)) { alert('Darn! An error occurred compiling the shaders:' + gl.getShaderInfoLog(_fragShader)); return null; } var shaderProgram = gl.createProgram(); gl.attachShader(shaderProgram, _vertShader); gl.attachShader(shaderProgram, _fragShader); gl.linkProgram(shaderProgram); if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { alert('Snap! Error linking shader program'); } //END SHADERS SETUP this._getLocation(shaderProgram, isImmediateMode); return shaderProgram; }; p5.RendererGL.prototype._getLocation = function(shaderProgram, isImmediateMode) { var gl = this.GL; gl.useProgram(shaderProgram); //projection Matrix uniform shaderProgram.uPMatrixUniform = gl.getUniformLocation(shaderProgram, 'uProjectionMatrix'); //model view Matrix uniform shaderProgram.uMVMatrixUniform = gl.getUniformLocation(shaderProgram, 'uModelViewMatrix'); //@TODO: figure out a better way instead of if statement if(isImmediateMode === undefined){ //normal Matrix uniform shaderProgram.uNMatrixUniform = gl.getUniformLocation(shaderProgram, 'uNormalMatrix'); shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, 'uSampler'); } }; /** * Sets a shader uniform given a shaderProgram and uniform string * @param {String} shaderKey key to material Hash. * @param {String} uniform location in shader. * @param {Number} data data to bind uniform. Float data type. * @chainable * @todo currently this function sets uniform1f data. * Should generalize function to accept any uniform * data type. */ p5.RendererGL.prototype._setUniform1f = function(shaderKey,uniform,data) { var gl = this.GL; var shaderProgram = this.mHash[shaderKey]; gl.useProgram(shaderProgram); shaderProgram[uniform] = gl.getUniformLocation(shaderProgram, uniform); gl.uniform1f(shaderProgram[uniform], data); return this; }; p5.RendererGL.prototype._setMatrixUniforms = function(shaderKey) { var gl = this.GL; var shaderProgram = this.mHash[shaderKey]; gl.useProgram(shaderProgram); gl.uniformMatrix4fv( shaderProgram.uPMatrixUniform, false, this.uPMatrix.mat4); gl.uniformMatrix4fv( shaderProgram.uMVMatrixUniform, false, this.uMVMatrix.mat4); this.uNMatrix.inverseTranspose(this.uMVMatrix); gl.uniformMatrix3fv( shaderProgram.uNMatrixUniform, false, this.uNMatrix.mat3); }; ////////////////////////////////////////////// // GET CURRENT | for shader and color ////////////////////////////////////////////// p5.RendererGL.prototype._getShader = function(vertId, fragId, isImmediateMode) { var mId = vertId + '|' + fragId; //create it and put it into hashTable if(!this.materialInHash(mId)){ var shaderProgram = this._initShaders(vertId, fragId, isImmediateMode); this.mHash[mId] = shaderProgram; } this.curShaderId = mId; return this.mHash[this.curShaderId]; }; p5.RendererGL.prototype._getCurShaderId = function(){ //if the shader ID is not yet defined if(this.drawMode !== 'fill' && this.curShaderId === undefined){ //default shader: normalMaterial() var mId = 'normalVert|normalFrag'; var shaderProgram = this._initShaders('normalVert', 'normalFrag'); this.mHash[mId] = shaderProgram; this.curShaderId = mId; } else if(this.isImmediateDrawing && this.drawMode === 'fill'){ // note that this._getShader will check if the shader already exists // by looking up the shader id (composed of vertexShaderId|fragmentShaderId) // in the material hash. If the material isn't found in the hash, it // creates a new one using this._initShaders--however, we'd like // use the cached version as often as possible, so we defer to this._getShader // here instead of calling this._initShaders directly. this._getShader('immediateVert', 'vertexColorFrag', true); } return this.curShaderId; }; ////////////////////////////////////////////// // COLOR ////////////////////////////////////////////// /** * Basic fill material for geometry with a given color * @method fill * @param {Number|Array|String|p5.Color} v1 gray value, * red or hue value (depending on the current color mode), * or color Array, or CSS color string * @param {Number} [v2] optional: green or saturation value * @param {Number} [v3] optional: blue or brightness value * @param {Number} [a] optional: opacity * @chainable * @example *
* * function setup(){ * createCanvas(100, 100, WEBGL); * } * * function draw(){ * background(0); * fill(250, 0, 0); * rotateX(frameCount * 0.01); * rotateY(frameCount * 0.01); * rotateZ(frameCount * 0.01); * box(200, 200, 200); * } * *
* * @alt * red canvas * */ p5.RendererGL.prototype.fill = function(v1, v2, v3, a) { var gl = this.GL; var shaderProgram; //see material.js for more info on color blending in webgl var colors = this._applyColorBlend.apply(this, arguments); this.curFillColor = colors; this.drawMode = 'fill'; if(this.isImmediateDrawing){ shaderProgram = this._getShader('immediateVert','vertexColorFrag'); gl.useProgram(shaderProgram); } else { shaderProgram = this._getShader('normalVert', 'basicFrag'); gl.useProgram(shaderProgram); //RetainedMode uses a webgl uniform to pass color vals //in ImmediateMode, we want access to each vertex so therefore //we cannot use a uniform. shaderProgram.uMaterialColor = gl.getUniformLocation( shaderProgram, 'uMaterialColor' ); gl.uniform4f( shaderProgram.uMaterialColor, colors[0], colors[1], colors[2], colors[3]); } return this; }; p5.RendererGL.prototype.stroke = function(r, g, b, a) { var color = this._pInst.color.apply(this._pInst, arguments); var colorNormalized = color._array; this.curStrokeColor = colorNormalized; this.drawMode = 'stroke'; return this; }; //@TODO p5.RendererGL.prototype._strokeCheck = function(){ if(this.drawMode === 'stroke'){ throw new Error( 'stroke for shapes in 3D not yet implemented, use fill for now :(' ); } }; /** * [strokeWeight description] * @param {Number} pointSize stroke point size * @chainable * @todo strokeWeight currently works on points only. * implement on all wireframes and strokes. */ p5.RendererGL.prototype.strokeWeight = function(pointSize) { this.pointSize = pointSize; return this; }; ////////////////////////////////////////////// // HASH | for material and geometry ////////////////////////////////////////////// p5.RendererGL.prototype.geometryInHash = function(gId){ return this.gHash[gId] !== undefined; }; p5.RendererGL.prototype.materialInHash = function(mId){ return this.mHash[mId] !== undefined; }; /** * [resize description] * @param {Number} w [description] * @param {Number} h [description] */ p5.RendererGL.prototype.resize = function(w,h) { var gl = this.GL; p5.Renderer.prototype.resize.call(this, w, h); gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight); // If we're using the default camera, update the aspect ratio if(this._curCamera === 'default') { this._curCamera = null; this._setDefaultCamera(); } }; /** * clears color and depth buffers * with r,g,b,a * @param {Number} r normalized red val. * @param {Number} g normalized green val. * @param {Number} b normalized blue val. * @param {Number} a normalized alpha val. */ p5.RendererGL.prototype.clear = function() { var gl = this.GL; gl.clearColor(arguments[0], arguments[1], arguments[2], arguments[3]); gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); }; /** * [translate description] * @param {Number} x [description] * @param {Number} y [description] * @param {Number} z [description] * @chainable * @todo implement handle for components or vector as args */ p5.RendererGL.prototype.translate = function(x, y, z) { this.uMVMatrix.translate([x,-y,z]); return this; }; /** * Scales the Model View Matrix by a vector * @param {Number | p5.Vector | Array} x [description] * @param {Number} [y] y-axis scalar * @param {Number} [z] z-axis scalar * @chainable */ p5.RendererGL.prototype.scale = function(x,y,z) { this.uMVMatrix.scale([x,y,z]); return this; }; p5.RendererGL.prototype.rotate = function(rad, axis){ this.uMVMatrix.rotate(rad, axis); return this; }; p5.RendererGL.prototype.rotateX = function(rad) { this.rotate(rad, [1,0,0]); return this; }; p5.RendererGL.prototype.rotateY = function(rad) { this.rotate(rad, [0,1,0]); return this; }; p5.RendererGL.prototype.rotateZ = function(rad) { this.rotate(rad, [0,0,1]); return this; }; /** * pushes a copy of the model view matrix onto the * MV Matrix stack. */ p5.RendererGL.prototype.push = function() { uMVMatrixStack.push(this.uMVMatrix.copy()); }; /** * [pop description] */ p5.RendererGL.prototype.pop = function() { if (uMVMatrixStack.length === 0) { throw new Error('Invalid popMatrix!'); } this.uMVMatrix = uMVMatrixStack.pop(); }; p5.RendererGL.prototype.resetMatrix = function() { this.uMVMatrix = p5.Matrix.identity(); this.translate(0, 0, -800); return this; }; // Text/Typography // @TODO: p5.RendererGL.prototype._applyTextProperties = function() { //@TODO finish implementation console.error('text commands not yet implemented in webgl'); }; module.exports = p5.RendererGL; },{"../core/core":9,"../core/p5.Renderer":16,"./p5.Matrix":29,"./shader":31}],31:[function(_dereq_,module,exports){ module.exports = { immediateVert: "attribute vec3 aPosition;\nattribute vec4 aVertexColor;\n\nuniform mat4 uModelViewMatrix;\nuniform mat4 uProjectionMatrix;\nuniform float uResolution;\nuniform float uPointSize;\n\nvarying vec4 vColor;\nvoid main(void) {\n vec4 positionVec4 = vec4(aPosition * vec3(1.0, -1.0, 1.0), 1.0);\n gl_Position = uProjectionMatrix * uModelViewMatrix * positionVec4;\n vColor = aVertexColor;\n gl_PointSize = uPointSize;\n}\n", vertexColorVert: "attribute vec3 aPosition;\nattribute vec4 aVertexColor;\n\nuniform mat4 uModelViewMatrix;\nuniform mat4 uProjectionMatrix;\n\nvarying vec4 vColor;\n\nvoid main(void) {\n vec4 positionVec4 = vec4(aPosition * vec3(1.0, -1.0, 1.0), 1.0);\n gl_Position = uProjectionMatrix * uModelViewMatrix * positionVec4;\n vColor = aVertexColor;\n}\n", vertexColorFrag: "precision mediump float;\nvarying vec4 vColor;\nvoid main(void) {\n gl_FragColor = vColor;\n}", normalVert: "attribute vec3 aPosition;\nattribute vec3 aNormal;\nattribute vec2 aTexCoord;\n\nuniform mat4 uModelViewMatrix;\nuniform mat4 uProjectionMatrix;\nuniform mat3 uNormalMatrix;\n\nvarying vec3 vVertexNormal;\nvarying highp vec2 vVertTexCoord;\n\nvoid main(void) {\n vec4 positionVec4 = vec4(aPosition * vec3(1.0, -1.0, 1.0), 1.0);\n gl_Position = uProjectionMatrix * uModelViewMatrix * positionVec4;\n vVertexNormal = vec3( uNormalMatrix * aNormal );\n vVertTexCoord = aTexCoord;\n}\n", normalFrag: "precision mediump float;\nvarying vec3 vVertexNormal;\nvoid main(void) {\n gl_FragColor = vec4(vVertexNormal, 1.0);\n}", basicFrag: "precision mediump float;\nvarying vec3 vVertexNormal;\nuniform vec4 uMaterialColor;\nvoid main(void) {\n gl_FragColor = uMaterialColor;\n}", lightVert: "attribute vec3 aPosition;\nattribute vec3 aNormal;\nattribute vec2 aTexCoord;\n\nuniform mat4 uModelViewMatrix;\nuniform mat4 uProjectionMatrix;\nuniform mat3 uNormalMatrix;\nuniform int uAmbientLightCount;\nuniform int uDirectionalLightCount;\nuniform int uPointLightCount;\n\nuniform vec3 uAmbientColor[8];\nuniform vec3 uLightingDirection[8];\nuniform vec3 uDirectionalColor[8];\nuniform vec3 uPointLightLocation[8];\nuniform vec3 uPointLightColor[8];\nuniform bool uSpecular;\n\nvarying vec3 vVertexNormal;\nvarying vec2 vVertTexCoord;\nvarying vec3 vLightWeighting;\n\nvec3 ambientLightFactor = vec3(0.0, 0.0, 0.0);\nvec3 directionalLightFactor = vec3(0.0, 0.0, 0.0);\nvec3 pointLightFactor = vec3(0.0, 0.0, 0.0);\nvec3 pointLightFactor2 = vec3(0.0, 0.0, 0.0);\n\nvoid main(void){\n\n vec4 positionVec4 = vec4(aPosition, 1.0);\n gl_Position = uProjectionMatrix * uModelViewMatrix * positionVec4;\n\n vec3 vertexNormal = vec3( uNormalMatrix * aNormal );\n vVertexNormal = vertexNormal;\n vVertTexCoord = aTexCoord;\n\n vec4 mvPosition = uModelViewMatrix * vec4(aPosition, 1.0);\n vec3 eyeDirection = normalize(-mvPosition.xyz);\n\n float shininess = 32.0;\n float specularFactor = 2.0;\n float diffuseFactor = 0.3;\n\n for(int i = 0; i < 8; i++){\n if(uAmbientLightCount == i) break;\n ambientLightFactor += uAmbientColor[i];\n }\n\n for(int j = 0; j < 8; j++){\n if(uDirectionalLightCount == j) break;\n vec3 dir = uLightingDirection[j];\n float directionalLightWeighting = max(dot(vertexNormal, dir), 0.0);\n directionalLightFactor += uDirectionalColor[j] * directionalLightWeighting;\n }\n\n for(int k = 0; k < 8; k++){\n if(uPointLightCount == k) break;\n vec3 loc = uPointLightLocation[k];\n vec3 lightDirection = normalize(loc - mvPosition.xyz);\n\n float directionalLightWeighting = max(dot(vertexNormal, lightDirection), 0.0);\n pointLightFactor += uPointLightColor[k] * directionalLightWeighting;\n\n //factor2 for specular\n vec3 reflectionDirection = reflect(-lightDirection, vertexNormal);\n float specularLightWeighting = pow(max(dot(reflectionDirection, eyeDirection), 0.0), shininess);\n\n pointLightFactor2 += uPointLightColor[k] * (specularFactor * specularLightWeighting\n + directionalLightWeighting * diffuseFactor);\n }\n\n if(!uSpecular){\n vLightWeighting = ambientLightFactor + directionalLightFactor + pointLightFactor;\n }else{\n vLightWeighting = ambientLightFactor + directionalLightFactor + pointLightFactor2;\n }\n\n}\n", lightTextureFrag: "precision mediump float;\n\nuniform vec4 uMaterialColor;\nuniform sampler2D uSampler;\nuniform bool isTexture;\n\nvarying vec3 vLightWeighting;\nvarying highp vec2 vVertTexCoord;\n\nvoid main(void) {\n if(!isTexture){\n gl_FragColor = vec4(vec3(uMaterialColor.rgb * vLightWeighting), uMaterialColor.a);\n }else{\n vec4 textureColor = texture2D(uSampler, vVertTexCoord);\n if(vLightWeighting == vec3(0., 0., 0.)){\n gl_FragColor = textureColor;\n }else{\n gl_FragColor = vec4(vec3(textureColor.rgb * vLightWeighting), textureColor.a);\n }\n }\n}" }; },{}]},{},[9,14,15,17,26,25,27,8,11,24,18,5,6,10,22,20,21,13])(27) });