Skip to content

Instantly share code, notes, and snippets.

@andrewchee
Created August 17, 2017 18:01
Show Gist options
  • Save andrewchee/8942797877353002933841168630e809 to your computer and use it in GitHub Desktop.
Save andrewchee/8942797877353002933841168630e809 to your computer and use it in GitHub Desktop.
Triangle Grid
/*! p5.dom.js v0.2.4 October 6, 2015 */
/**
* <p>The web is much more than just canvas and p5.dom makes it easy to interact
* with other HTML5 objects, including text, hyperlink, image, input, video,
* audio, and webcam.</p>
* <p>There is a set of creation methods, DOM manipulation methods, and
* an extended p5.Element that supports a range of HTML elements. See the
* <a href="https://github.com/processing/p5.js/wiki/Beyond-the-canvas">
* beyond the canvas tutorial</a> for a full overview of how this addon works.
*
* <p>Methods and properties shown in black are part of the p5.js core, items in
* blue are part of the p5.dom library. You will need to include an extra file
* in order to access the blue functions. See the
* <a href="http://p5js.org/libraries/#using-a-library">using a library</a>
* section for information on how to include this library. p5.dom comes with
* <a href="http://p5js.org/download">p5 complete</a> or you can download the single file
* <a href="https://raw.githubusercontent.com/lmccart/p5.js/master/lib/addons/p5.dom.js">
* here</a>.</p>
* <p>See <a href="https://github.com/processing/p5.js/wiki/Beyond-the-canvas">tutorial: beyond the canvas</a>
* for more info on how to use this libary.</a>
*
* @module p5.dom
* @submodule p5.dom
* @for p5.dom
* @main
*/
(function (root, factory) {
if (typeof define === 'function' && define.amd)
define('p5.dom', ['p5'], function (p5) { (factory(p5));});
else if (typeof exports === 'object')
factory(require('../p5'));
else
factory(root['p5']);
}(this, function (p5) {
// =============================================================================
// p5 additions
// =============================================================================
/**
* Searches the page for an element with the given ID, class, or tag name (using the '#' or '.'
* prefixes to specify an ID or class respectively, and none for a tag) and returns it as
* a p5.Element. If a class or tag name is given with more than 1 element,
* only the first element will be returned.
* The DOM node itself can be accessed with .elt.
* Returns null if none found.
*
* @method select
* @param {String} name id, class, or tag name of element to search for
* @return {Object/p5.Element|Null} p5.Element containing node found
* @example
* <div ><code class='norender'>
* function setup() {
* createCanvas(100,100);
* //translates canvas 50px down
* select('canvas').translate(0,50);
* }
* </code></div>
*
*/
p5.prototype.select = function (e) {
var res;
var str;
if (e[0] === '.'){
str = e.slice(1);
res = document.getElementsByClassName(str);
if (res) {
return wrapElement(res[0]);
}else {
return null;
}
}else if (e[0] === '#'){
str = e.slice(1);
res = document.getElementById(str);
if (res) {
return wrapElement(res);
}else {
return null;
}
}else{
res = document.getElementsByTagName(e);
if (res) {
return wrapElement(res[0]);
}else {
return null;
}
}
};
/**
* Searches the page for elements with the given class or tag name (using the '.' prefix
* to specify a class and no prefix for a tag) and returns them as p5.Elements
* in an array.
* The DOM node itself can be accessed with .elt.
* Returns null if none found.
*
* @method selectAll
* @param {String} name class or tag name of elements to search for
* @return {Array} Array of p5.Elements containing nodes found
* @example
* <div ><code class='norender'>
* function setup() {
* createButton('btn');
* createButton('2nd btn');
* createButton('3rd btn');
* var buttons = selectAll('button');
*
* for (var i = 0; i < buttons.length; i++){
* buttons[i].size(100,100);
* }
* }
* </code></div>
*
*/
p5.prototype.selectAll = function (e) {
var arr = [];
var res;
var str;
if (e[0] === '.'){
str = e.slice(1);
res = document.getElementsByClassName(str);
}else {
res = document.getElementsByTagName(e);
}
if (res) {
for (var j = 0; j < res.length; j++) {
var obj = wrapElement(res[j]);
arr.push(obj);
}
}
return arr;
};
/**
* Helper function for getElement and getElements.
*/
function wrapElement(elt) {
if (elt.tagName === "VIDEO" || elt.tagName === "AUDIO") {
return new p5.MediaElement(elt);
} else {
return new p5.Element(elt);
}
}
/**
* Removes all elements created by p5, except any canvas / graphics
* elements created by createCanvas or createGraphics.
* Event handlers are removed, and element is removed from the DOM.
* @method removeElements
* @example
* <div class='norender'><code>
* function setup() {
* createCanvas(100, 100);
* createDiv('this is some text');
* createP('this is a paragraph');
* }
* function mousePressed() {
* removeElements(); // this will remove the div and p, not canvas
* }
* </code></div>
*
*/
p5.prototype.removeElements = function (e) {
for (var i=0; i<this._elements.length; i++) {
if (!(this._elements[i].elt instanceof HTMLCanvasElement)) {
this._elements[i].remove();
}
}
};
/**
* Helpers for create methods.
*/
function addElement(elt, pInst, media) {
var node = pInst._userNode ? pInst._userNode : document.body;
node.appendChild(elt);
var c = media ? new p5.MediaElement(elt) : new p5.Element(elt);
pInst._elements.push(c);
return c;
}
/**
* Creates a &lt;div&gt;&lt;/div&gt; element in the DOM with given inner HTML.
* Appends to the container node if one is specified, otherwise
* appends to body.
*
* @method createDiv
* @param {String} html inner HTML for element created
* @return {Object/p5.Element} pointer to p5.Element holding created node
* @example
* <div class='norender'><code>
* var myDiv;
* function setup() {
* myDiv = createDiv('this is some text');
* }
* </code></div>
*/
/**
* Creates a &lt;p&gt;&lt;/p&gt; element in the DOM with given inner HTML. Used
* for paragraph length text.
* Appends to the container node if one is specified, otherwise
* appends to body.
*
* @method createP
* @param {String} html inner HTML for element created
* @return {Object/p5.Element} pointer to p5.Element holding created node
* @example
* <div class='norender'><code>
* var myP;
* function setup() {
* myP = createP('this is some text');
* }
* </code></div>
*/
/**
* Creates a &lt;span&gt;&lt;/span&gt; element in the DOM with given inner HTML.
* Appends to the container node if one is specified, otherwise
* appends to body.
*
* @method createSpan
* @param {String} html inner HTML for element created
* @return {Object/p5.Element} pointer to p5.Element holding created node
* @example
* <div class='norender'><code>
* var mySpan;
* function setup() {
* mySpan = createSpan('this is some text');
* }
* </code></div>
*/
var tags = ['div', 'p', 'span'];
tags.forEach(function(tag) {
var method = 'create' + tag.charAt(0).toUpperCase() + tag.slice(1);
p5.prototype[method] = function(html) {
var elt = document.createElement(tag);
elt.innerHTML = typeof html === undefined ? "" : html;
return addElement(elt, this);
}
});
/**
* Creates an &lt;img /&gt; element in the DOM with given src and
* alternate text.
* Appends to the container node if one is specified, otherwise
* appends to body.
*
* @method createImg
* @param {String} src src path or url for image
* @param {String} [alt] alternate text to be used if image does not load
* @param {Function} [successCallback] callback to be called once image data is loaded
* @return {Object/p5.Element} pointer to p5.Element holding created node
* @example
* <div class='norender'><code>
* var img;
* function setup() {
* img = createImg('http://p5js.org/img/asterisk-01.png');
* }
* </code></div>
*/
p5.prototype.createImg = function() {
var elt = document.createElement('img');
var args = arguments;
var self;
var setAttrs = function(){
self.width = elt.offsetWidth;
self.height = elt.offsetHeight;
if (args.length > 1 && typeof args[1] === 'function'){
self.fn = args[1];
self.fn();
}else if (args.length > 1 && typeof args[2] === 'function'){
self.fn = args[2];
self.fn();
}
};
elt.src = args[0];
if (args.length > 1 && typeof args[1] === 'string'){
elt.alt = args[1];
}
elt.onload = function(){
setAttrs();
}
self = addElement(elt, this);
return self;
};
/**
* Creates an &lt;a&gt;&lt;/a&gt; element in the DOM for including a hyperlink.
* Appends to the container node if one is specified, otherwise
* appends to body.
*
* @method createA
* @param {String} href url of page to link to
* @param {String} html inner html of link element to display
* @param {String} [target] target where new link should open,
* could be _blank, _self, _parent, _top.
* @return {Object/p5.Element} pointer to p5.Element holding created node
* @example
* <div class='norender'><code>
* var myLink;
* function setup() {
* myLink = createA('http://p5js.org/', 'this is a link');
* }
* </code></div>
*/
p5.prototype.createA = function(href, html, target) {
var elt = document.createElement('a');
elt.href = href;
elt.innerHTML = html;
if (target) elt.target = target;
return addElement(elt, this);
};
/** INPUT **/
/**
* Creates a slider &lt;input&gt;&lt;/input&gt; element in the DOM.
* Use .size() to set the display length of the slider.
* Appends to the container node if one is specified, otherwise
* appends to body.
*
* @method createSlider
* @param {Number} min minimum value of the slider
* @param {Number} max maximum value of the slider
* @param {Number} [value] default value of the slider
* @return {Object/p5.Element} pointer to p5.Element holding created node
* @example
* <div><code>
* var slider;
* function setup() {
* slider = createSlider(0, 255, 100);
* slider.position(10, 10);
* slider.style('width', '80px');
* }
*
* function draw() {
* var val = slider.value();
* background(val);
* }
* </code></div>
*/
p5.prototype.createSlider = function(min, max, value, step) {
var elt = document.createElement('input');
elt.type = 'range';
elt.min = min;
elt.max = max;
if (step) elt.step = step;
if (typeof(value) === "number") elt.value = value;
return addElement(elt, this);
};
/**
* Creates a &lt;button&gt;&lt;/button&gt; element in the DOM.
* Use .size() to set the display size of the button.
* Use .mousePressed() to specify behavior on press.
* Appends to the container node if one is specified, otherwise
* appends to body.
*
* @method createButton
* @param {String} label label displayed on the button
* @param {String} [value] value of the button
* @return {Object/p5.Element} pointer to p5.Element holding created node
* @example
* <div class='norender'><code>
* var button;
* function setup() {
* createCanvas(100, 100);
* background(0);
* button = createButton('click me');
* button.position(19, 19);
* button.mousePressed(changeBG);
* }
*
* function changeBG() {
* var val = random(255);
* background(val);
* }
* </code></div>
*/
p5.prototype.createButton = function(label, value) {
var elt = document.createElement('button');
elt.innerHTML = label;
elt.value = value;
if (value) elt.value = value;
return addElement(elt, this);
};
/**
* Creates a checkbox &lt;input&gt;&lt;/input&gt; element in the DOM.
* Calling .checked() on a checkbox returns if it is checked or not
*
* @method createCheckbox
* @param {String} [label] label displayed after checkbox
* @param {boolean} [value] value of the checkbox; checked is true, unchecked is false.Unchecked if no value given
* @return {Object/p5.Element} pointer to p5.Element holding created node
* @example
* <div class='norender'><code>
* var checkbox;
*
* function setup() {
* checkbox = createCheckbox('label', false);
* checkbox.changed(myCheckedEvent);
* }
*
* function myCheckedEvent() {
* if (this.checked()) {
* console.log("Unchecking!");
* } else {
* console.log("Checking!");
* }
*
* </code></div>
*/
p5.prototype.createCheckbox = function() {
var elt = document.createElement('input');
elt.type = 'checkbox';
//checkbox must be wrapped in p5.Element before label so that label appears after
var self = addElement(elt, this);
self.checked = function(){
if (arguments.length === 0){
return self.elt.checked;
}else if(arguments[0]){
self.elt.checked = true;
}else{
self.elt.checked = false;
}
return self;
};
this.value = function(val){
self.value = val;
return this;
};
if (arguments[0]){
var ran = Math.random().toString(36).slice(2);
var label = document.createElement('label');
elt.setAttribute('id', ran);
label.htmlFor = ran;
self.value(arguments[0]);
label.appendChild(document.createTextNode(arguments[0]));
addElement(label, this);
}
if (arguments[1]){
elt.checked = true;
}
return self;
};
/**
* Creates a dropdown menu &lt;select&gt;&lt;/select&gt; element in the DOM.
* @method createSelect
* @param {boolean} [multiple] [true if dropdown should support multiple selections]
* @return {Object/p5.Element} pointer to p5.Element holding created node
* @example
* <div class='norender'><code>
* var checkbox;
*
* function setup() {
* sel = createSelect();
* sel.option('pear');
* sel.option('apple');
* sel.selected('pear');
* sel.changed(mySelectEvent);
* }
*
* function mySelectEvent() {
* var selected = this.value();
* if (selected === 'pear') {
* console.log("it's a pear!");
* }
* }
*
* </code></div>
*
*/
p5.prototype.createSelect = function(mult) {
var elt = document.createElement('select');
if (mult){
elt.setAttribute('multiple', 'true');
}
var self = addElement(elt, this);
self.option = function(name, value){
var opt = document.createElement('option');
opt.innerHTML = name;
if (arguments.length > 1)
opt.value = value;
else
opt.value = name;
elt.appendChild(opt);
};
self.selected = function(value){
var arr = [];
if (arguments.length > 0){
for (var i = 0; i < this.elt.length; i++){
if (value.toString() === this.elt[i].value){
this.elt.selectedIndex = i;
}
}
return this;
}else{
if (mult){
for (var i = 0; i < this.elt.selectedOptions.length; i++){
arr.push(this.elt.selectedOptions[i].value);
}
return arr;
}else{
return this.elt.value;
}
}
};
return self;
};
/**
* Creates an &lt;input&gt;&lt;/input&gt; element in the DOM for text input.
* Use .size() to set the display length of the box.
* Appends to the container node if one is specified, otherwise
* appends to body.
*
* @method createInput
* @param {Number} [value] default value of the input box
* @return {Object/p5.Element} pointer to p5.Element holding created node
* @example
* <div class='norender'><code>
* function setup(){
* var inp = createInput('');
* inp.input(myInputEvent);
* }
*
* function myInputEvent(){
* console.log('you are typing: ', this.value());
* }
*
* </code></div>
*/
p5.prototype.createInput = function(value) {
var elt = document.createElement('input');
elt.type = 'text';
if (value) elt.value = value;
return addElement(elt, this);
};
/**
* Creates an &lt;input&gt;&lt;/input&gt; element in the DOM of type 'file'.
* This allows users to select local files for use in a sketch.
*
* @method createFileInput
* @param {Function} [callback] callback function for when a file loaded
* @param {String} [multiple] optional to allow multiple files selected
* @return {Object/p5.Element} pointer to p5.Element holding created DOM element
*/
p5.prototype.createFileInput = function(callback, multiple) {
// Is the file stuff supported?
if (window.File && window.FileReader && window.FileList && window.Blob) {
// Yup, we're ok and make an input file selector
var elt = document.createElement('input');
elt.type = 'file';
// If we get a second argument that evaluates to true
// then we are looking for multiple files
if (multiple) {
// Anything gets the job done
elt.multiple = 'multiple';
}
// Now let's handle when a file was selected
elt.addEventListener('change', handleFileSelect, false);
// Function to handle when a file is selected
// We're simplifying life and assuming that we always
// want to load every selected file
function handleFileSelect(evt) {
// These are the files
var files = evt.target.files;
// Load each one and trigger a callback
for (var i = 0; i < files.length; i++) {
var f = files[i];
var reader = new FileReader();
reader.onload = makeLoader(f);
function makeLoader(theFile) {
// Making a p5.File object
var p5file = new p5.File(theFile);
return function(e) {
p5file.data = e.target.result;
callback(p5file);
};
};
// Text or data?
// This should likely be improved
if (f.type.indexOf('text') > -1) {
reader.readAsText(f);
} else {
reader.readAsDataURL(f);
}
}
}
return addElement(elt, this);
} else {
console.log('The File APIs are not fully supported in this browser. Cannot create element.');
}
};
/** VIDEO STUFF **/
function createMedia(pInst, type, src, callback) {
var elt = document.createElement(type);
if (typeof src === 'string') {
src = [src];
}
for (var i=0; i<src.length; i++) {
var source = document.createElement('source');
source.src = src[i];
elt.appendChild(source);
}
if (typeof callback !== 'undefined') {
var callbackHandler = function() {
callback();
elt.removeEventListener('canplaythrough', callbackHandler);
}
elt.addEventListener('canplaythrough', callbackHandler);
}
var c = addElement(elt, pInst, true);
c.loadedmetadata = false;
// set width and height onload metadata
elt.addEventListener('loadedmetadata', function() {
c.width = elt.videoWidth;
c.height = elt.videoHeight;
c.loadedmetadata = true;
});
return c;
}
/**
* Creates an HTML5 &lt;video&gt; element in the DOM for simple playback
* of audio/video. Shown by default, can be hidden with .hide()
* and drawn into canvas using video(). Appends to the container
* node if one is specified, otherwise appends to body. The first parameter
* can be either a single string path to a video file, or an array of string
* paths to different formats of the same video. This is useful for ensuring
* that your video can play across different browsers, as each supports
* different formats. See <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Supported_media_formats">this
* page</a> for further information about supported formats.
*
* @method createVideo
* @param {String|Array} src path to a video file, or array of paths for
* supporting different browsers
* @param {Object} [callback] callback function to be called upon
* 'canplaythrough' event fire, that is, when the
* browser can play the media, and estimates that
* enough data has been loaded to play the media
* up to its end without having to stop for
* further buffering of content
* @return {Object/p5.Element} pointer to video p5.Element
*/
p5.prototype.createVideo = function(src, callback) {
return createMedia(this, 'video', src, callback);
};
/** AUDIO STUFF **/
/**
* Creates a hidden HTML5 &lt;audio&gt; element in the DOM for simple audio
* playback. Appends to the container node if one is specified,
* otherwise appends to body. The first parameter
* can be either a single string path to a audio file, or an array of string
* paths to different formats of the same audio. This is useful for ensuring
* that your audio can play across different browsers, as each supports
* different formats. See <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Supported_media_formats">this
* page for further information about supported formats.
*
* @method createAudio
* @param {String|Array} src path to an audio file, or array of paths for
* supporting different browsers
* @param {Object} [callback] callback function to be called upon
* 'canplaythrough' event fire, that is, when the
* browser can play the media, and estimates that
* enough data has been loaded to play the media
* up to its end without having to stop for
* further buffering of content
* @return {Object/p5.Element} pointer to audio p5.Element
*/
p5.prototype.createAudio = function(src, callback) {
return createMedia(this, 'audio', src, callback);
};
/** CAMERA STUFF **/
p5.prototype.VIDEO = 'video';
p5.prototype.AUDIO = 'audio';
navigator.getUserMedia = navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ||
navigator.msGetUserMedia;
/**
* Creates a new &lt;video&gt; element that contains the audio/video feed
* from a webcam. This can be drawn onto the canvas using video(). More
* specific properties of the stream can be passing in a Constraints object.
* See the
* <a href="http://w3c.github.io/mediacapture-main/getusermedia.html">W3C
* spec</a> for possible properties. Note that not all of these are supported
* by all browsers.
*
* @method createCapture
* @param {String|Constant|Object} type type of capture, either VIDEO or
* AUDIO if none specified, default both,
* or a Constraints boject
* @param {Function} callback function to be called once
* stream has loaded
* @return {Object/p5.Element} capture video p5.Element
* @example
* <div class='norender'><code>
* var capture;
*
* function setup() {
* createCanvas(480, 120);
* capture = createCapture(VIDEO);
* }
*
* function draw() {
* image(capture, 0, 0, width, width*capture.height/capture.width);
* filter(INVERT);
* }
* </code></div>
* <div class='norender'><code>
* function setup() {
* createCanvas(480, 120);
* var constraints = {
* video: {
* mandatory: {
* minWidth: 1280,
* minHeight: 720
* },
* optional: [
* { maxFrameRate: 10 }
* ]
* },
* audio: true
* };
* createCapture(constraints, function(stream) {
* console.log(stream);
* });
* }
* </code></div>
*/
p5.prototype.createCapture = function() {
var useVideo = true;
var useAudio = true;
var constraints;
var cb;
for (var i=0; i<arguments.length; i++) {
if (arguments[i] === p5.prototype.VIDEO) {
useAudio = false;
} else if (arguments[i] === p5.prototype.AUDIO) {
useVideo = false;
} else if (typeof arguments[i] === 'object') {
constraints = arguments[i];
} else if (typeof arguments[i] === 'function') {
cb = arguments[i];
}
}
if (navigator.getUserMedia) {
var elt = document.createElement('video');
if (!constraints) {
constraints = {video: useVideo, audio: useAudio};
}
navigator.getUserMedia(constraints, function(stream) {
elt.src = window.URL.createObjectURL(stream);
elt.play();
if (cb) {
cb(stream);
}
}, function(e) { console.log(e); });
} else {
throw 'getUserMedia not supported in this browser';
}
var c = addElement(elt, this, true);
c.loadedmetadata = false;
// set width and height onload metadata
elt.addEventListener('loadedmetadata', function() {
c.width = elt.videoWidth;
c.height = elt.videoHeight;
c.loadedmetadata = true;
});
return c;
};
/**
* Creates element with given tag in the DOM with given content.
* Appends to the container node if one is specified, otherwise
* appends to body.
*
* @method createElement
* @param {String} tag tag for the new element
* @param {String} [content] html content to be inserted into the element
* @return {Object/p5.Element} pointer to p5.Element holding created node
* @example
* <div class='norender'><code>
* var h2 = createElement('h2','im an h2 p5.element!');
* </code></div>
*/
p5.prototype.createElement = function(tag, content) {
var elt = document.createElement(tag);
if (typeof content !== 'undefined') {
elt.innerHTML = content;
}
return addElement(elt, this);
};
// =============================================================================
// p5.Element additions
// =============================================================================
/**
*
* Adds specified class to the element.
*
* @for p5.Element
* @method addClass
* @param {String} class name of class to add
* @return {Object/p5.Element}
* @example
* <div class='norender'><code>
* var div = createDiv('div');
* div.addClass('myClass');
* </code></div>
*/
p5.Element.prototype.addClass = function(c) {
if (this.elt.className) {
// PEND don't add class more than once
//var regex = new RegExp('[^a-zA-Z\d:]?'+c+'[^a-zA-Z\d:]?');
//if (this.elt.className.search(/[^a-zA-Z\d:]?hi[^a-zA-Z\d:]?/) === -1) {
this.elt.className = this.elt.className+' '+c;
//}
} else {
this.elt.className = c;
}
return this;
}
/**
*
* Removes specified class from the element.
*
* @method removeClass
* @param {String} class name of class to remove
* @return {Object/p5.Element}
*/
p5.Element.prototype.removeClass = function(c) {
var regex = new RegExp('(?:^|\\s)'+c+'(?!\\S)');
this.elt.className = this.elt.className.replace(regex, '');
this.elt.className = this.elt.className.replace(/^\s+|\s+$/g, ""); //prettify (optional)
return this;
}
/**
*
* Attaches the element as a child to the parent specified.
* Accepts either a string ID, DOM node, or p5.Element
*
* @method child
* @param {String|Object/p5.Element} child the ID, DOM node, or p5.Element
* to add to the current element
* @return {p5.Element}
* @example
* <div class='norender'><code>
* var div0 = createDiv('this is the parent');
* var div1 = createDiv('this is the child');
* div0.child(div1); // use p5.Element
* </code></div>
* <div class='norender'><code>
* var div0 = createDiv('this is the parent');
* var div1 = createDiv('this is the child');
* div1.id('apples');
* div0.child('apples'); // use id
* </code></div>
* <div class='norender'><code>
* var div0 = createDiv('this is the parent');
* var elt = document.getElementById('myChildDiv');
* div0.child(elt); // use element from page
* </code></div>
*/
p5.Element.prototype.child = function(c) {
if (typeof c === 'string') {
if (c[0] === '#') {
c = c.substring(1);
}
c = document.getElementById(c);
} else if (c instanceof p5.Element) {
c = c.elt;
}
this.elt.appendChild(c);
return this;
};
/**
* Centers a p5 Element either vertically, horizontally,
* or both, relative to its parent or according to
* the body if the Element has no parent. If no argument is passed
* the Element is aligned both vertically and horizontally.
*
* @param {String} align passing 'vertical', 'horizontal' aligns element accordingly
* @return {Object/p5.Element} pointer to p5.Element
* @example
* <div><code>
* function setup() {
* var div = createDiv('').size(10,10);
* div.style('background-color','orange');
* div.center();
*
* }
* </code></div>
*/
p5.Element.prototype.center = function(align) {
var style = this.elt.style.display;
var hidden = this.elt.style.display === 'none';
var parentHidden = this.parent().style.display === 'none';
var pos = { x : this.elt.offsetLeft, y : this.elt.offsetTop };
if (hidden) this.show();
this.elt.style.display = 'block';
this.position(0,0);
if (parentHidden) this.parent().style.display = 'block';
var wOffset = Math.abs(this.parent().offsetWidth - this.elt.offsetWidth);
var hOffset = Math.abs(this.parent().offsetHeight - this.elt.offsetHeight);
var y = pos.y;
var x = pos.x;
if (align === 'both' || align === undefined){
this.position(wOffset/2, hOffset/2);
}else if (align === 'horizontal'){
this.position(wOffset/2, y);
}else if (align === 'vertical'){
this.position(x, hOffset/2);
}
this.style('display', style);
if (hidden) this.hide();
if (parentHidden) this.parent().style.display = 'none';
return this;
};
/**
*
* If an argument is given, sets the inner HTML of the element,
* replacing any existing html. If no arguments are given, returns
* the inner HTML of the element.
*
* @for p5.Element
* @method html
* @param {String} [html] the HTML to be placed inside the element
* @return {Object/p5.Element|String}
* @example
* <div class='norender'><code>
* var div = createDiv('').size(100,100);
* div.style('background-color','orange');
* div.html('hi');
* </code></div>
*/
p5.Element.prototype.html = function(html) {
if (typeof html !== 'undefined') {
this.elt.innerHTML = html;
return this;
} else {
return this.elt.innerHTML;
}
};
/**
*
* Sets the position of the element relative to (0, 0) of the
* window. Essentially, sets position:absolute and left and top
* properties of style. If no arguments given returns the x and y position
* of the element in an object.
*
* @method position
* @param {Number} [x] x-position relative to upper left of window
* @param {Number} [y] y-position relative to upper left of window
* @return {Object/p5.Element}
* @example
* <div><code class='norender'>
* function setup() {
* var cnv = createCanvas(100, 100);
* // positions canvas 50px to the right and 100px
* // below upper left corner of the window
* cnv.position(50, 100);
* }
* </code></div>
*/
p5.Element.prototype.position = function() {
if (arguments.length === 0){
return { 'x' : this.elt.offsetLeft , 'y' : this.elt.offsetTop };
}else{
this.elt.style.position = 'absolute';
this.elt.style.left = arguments[0]+'px';
this.elt.style.top = arguments[1]+'px';
this.x = arguments[0];
this.y = arguments[1];
return this;
}
};
/**
* Translates an element with css transforms in either 2d (if 2 arguments given)
* or 3d (if 3 arguments given) space.
* @method translate
* @param {Number} x x-position in px
* @param {Number} y y-position in px
* @param {Number} [z] z-position in px
* @param {Number} [perspective] sets the perspective of the parent element in px,
* default value set to 1000px
* @return {Object/p5.Element}
* @example
* <div ><code class='norender'>
* function setup() {
* var cnv = createCanvas(100,100);
* //translates canvas 50px down
* cnv.translate(0,50);
* }
* </code></div>
*/
p5.Element.prototype.translate = function(){
this.elt.style.position = 'absolute';
if (arguments.length === 2){
var style = this.elt.style.transform.replace(/translate3d\(.*\)/g, '');
style = style.replace(/translate[X-Z]?\(.*\)/g, '');
this.elt.style.transform = 'translate('+arguments[0]+'px, '+arguments[1]+'px)';
this.elt.style.transform += style;
}else if (arguments.length === 3){
var style = this.elt.style.transform.replace(/translate3d\(.*\)/g, '');
style = style.replace(/translate[X-Z]?\(.*\)/g, '');
this.elt.style.transform = 'translate3d('+arguments[0]+'px,'+arguments[1]+'px,'+arguments[2]+'px)';
this.elt.style.transform += style;
this.elt.parentElement.style.perspective = '1000px';
}else if (arguments.length === 4){
var style = this.elt.style.transform.replace(/translate3d\(.*\)/g, '');
style = style.replace(/translate[X-Z]?\(.*\)/g, '');
this.elt.style.transform = 'translate3d('+arguments[0]+'px,'+arguments[1]+'px,'+arguments[2]+'px)';
this.elt.style.transform += style;
this.elt.parentElement.style.perspective = arguments[3]+'px';
}
return this;
};
/**
* Rotates an element with css transforms in either 2d (if 2 arguments given)
* or 3d (if 3 arguments given) space.
* @method rotate
* @param {Number} x amount of degrees to rotate the element along the x-axis in deg
* @param {Number} [y] amount of degrees to rotate the element along the y-axis in deg
* @param {Number} [z] amount of degrees to rotate the element along the z-axis in deg
* @return {Object/p5.Element}
* @example
* <div><code>
* var x = 0,
* y = 0,
* z = 0;
*
* function draw(){
* x+=.5 % 360;
* y+=.5 % 360;
* z+=.5 % 360;
* //rotates p5.js logo .5 degrees on every axis each frame.
* select('canvas').rotate(x,y,z);
* }
* </code></div>
*/
p5.Element.prototype.rotate = function(){
if (arguments.length === 1){
var style = this.elt.style.transform.replace(/rotate3d\(.*\)/g, '');
style = style.replace(/rotate[X-Z]?\(.*\)/g, '');
this.elt.style.transform = 'rotate('+arguments[0]+'deg)';
this.elt.style.transform += style;
}else if (arguments.length === 2){
var style = this.elt.style.transform.replace(/rotate3d\(.*\)/g, '');
style = style.replace(/rotate[X-Z]?\(.*\)/g, '');
this.elt.style.transform = 'rotate('+arguments[0]+'deg, '+arguments[1]+'deg)';
this.elt.style.transform += style;
}else if (arguments.length === 3){
var style = this.elt.style.transform.replace(/rotate3d\(.*\)/g, '');
style = style.replace(/rotate[X-Z]?\(.*\)/g, '');
this.elt.style.transform = 'rotateX('+arguments[0]+'deg)';
this.elt.style.transform += 'rotateY('+arguments[1]+'deg)';
this.elt.style.transform += 'rotateZ('+arguments[2]+'deg)';
this.elt.style.transform += style;
}
return this;
};
/**
* Sets the given style (css) property (1st arg) of the element with the
* given value (2nd arg). If a single argument is given, .style()
* returns the value of the given property; however, if the single argument
* is given in css syntax ('text-align:center'), .style() sets the css
* appropriatly. .style() also handles 2d and 3d css transforms. If
* the 1st arg is 'rotate', 'translate', or 'position', the following arguments
* accept Numbers as values. ('translate', 10, 100, 50);
*
* @method style
* @param {String} property property to be set
* @param {String|Number} [value] value to assign to property
* @param {String|Number} [value] value to assign to property (rotate/translate)
* @param {String|Number} [value] value to assign to property (rotate/translate)
* @param {String|Number} [value] value to assign to property (translate)
* @return {String|Object/p5.Element} value of property, if no value is specified
* or p5.Element
* @example
* <div><code class="norender">
* var myDiv = createDiv("I like pandas.");
* myDiv.style("color", "#ff0000");
* myDiv.style("font-size", "18px");
* </code></div>
*/
p5.Element.prototype.style = function(prop, val) {
var self = this;
if (typeof val === 'undefined') {
if (prop.indexOf(':') === -1) {
var styles = window.getComputedStyle(self.elt);
var style = styles.getPropertyValue(prop);
return style;
} else {
var attrs = prop.split(';');
for (var i = 0; i < attrs.length; i++) {
var parts = attrs[i].split(':');
if (parts[0] && parts[1]) {
this.elt.style[parts[0].trim()] = parts[1].trim();
}
}
}
} else {
if (prop === 'rotate'){
if (arguments.length === 2) {
var style = this.elt.style.transform.replace(/rotate3d\(.*\)/g, '');
style = style.replace(/rotate[X-Z]?\(.*\)/g, '');
this.elt.style.transform = 'rotate(' + arguments[0] + 'deg)';
this.elt.style.transform += style;
} else if (arguments.length === 3) {
var style = this.elt.style.transform.replace(/rotate3d\(.*\)/g, '');
style = style.replace(/rotate[X-Z]?\(.*\)/g, '');
this.elt.style.transform = 'rotate(' + arguments[0] + 'deg, ' + arguments[1] + 'deg)';
this.elt.style.transform += style;
} else if (arguments.length === 4) {
var style = this.elt.style.transform.replace(/rotate3d\(.*\)/g, '');
style = style.replace(/rotate[X-Z]?\(.*\)/g, '');
this.elt.style.transform = 'rotateX(' + arguments[0] + 'deg)';
this.elt.style.transform += 'rotateY(' + arguments[1] + 'deg)';
this.elt.style.transform += 'rotateZ(' + arguments[2] + 'deg)';
this.elt.style.transform += style;
}
} else if (prop === 'translate') {
if (arguments.length === 3) {
var style = this.elt.style.transform.replace(/translate3d\(.*\)/g, '');
style = style.replace(/translate[X-Z]?\(.*\)/g, '');
this.elt.style.transform = 'translate(' + arguments[0] + 'px, ' + arguments[1] + 'px)';
this.elt.style.transform += style;
} else if (arguments.length === 4) {
var style = this.elt.style.transform.replace(/translate3d\(.*\)/g, '');
style = style.replace(/translate[X-Z]?\(.*\)/g, '');
this.elt.style.transform = 'translate3d(' + arguments[0] + 'px,' + arguments[1] + 'px,' + arguments[2] + 'px)';
this.elt.style.transform += style;
this.elt.parentElement.style.perspective = '1000px';
} else if (arguments.length === 5) {
var style = this.elt.style.transform.replace(/translate3d\(.*\)/g, '');
style = style.replace(/translate[X-Z]?\(.*\)/g, '');
this.elt.style.transform = 'translate3d(' + arguments[0] + 'px,' + arguments[1] + 'px,' + arguments[2] + 'px)';
this.elt.style.transform += style;
this.elt.parentElement.style.perspective = arguments[3] + 'px';
}
} else if (prop === 'position') {
this.elt.style.left = arguments[1] + 'px';
this.elt.style.top = arguments[2] + 'px';
this.x = arguments[1];
this.y = arguments[2];
} else {
this.elt.style[prop] = val;
if (prop === 'width' || prop === 'height' || prop === 'left' || prop === 'top') {
var numVal = val.replace(/\D+/g, '');
this[prop] = parseInt(numVal, 10);
}
}
}
return this;
};
/**
*
* Adds a new attribute or changes the value of an existing attribute
* on the specified element. If no value is specified, returns the
* value of the given attribute, or null if attribute is not set.
*
* @method attribute
* @param {String} attr attribute to set
* @param {String} [value] value to assign to attribute
* @return {String|Object/p5.Element} value of attribute, if no value is
* specified or p5.Element
* @example
* <div class="norender"><code>
* var myDiv = createDiv("I like pandas.");
*myDiv.attribute("align", "center");
* </code></div>
*/
p5.Element.prototype.attribute = function(attr, value) {
if (typeof value === 'undefined') {
return this.elt.getAttribute(attr);
} else {
this.elt.setAttribute(attr, value);
return this;
}
};
/**
* Either returns the value of the element if no arguments
* given, or sets the value of the element.
*
* @method value
* @param {String|Number} [value]
* @return {String|Object/p5.Element} value of element if no value is specified or p5.Element
*/
p5.Element.prototype.value = function() {
if (arguments.length > 0) {
this.elt.value = arguments[0];
return this;
} else {
if (this.elt.type === 'range') {
return parseFloat(this.elt.value);
}
else return this.elt.value;
}
};
/**
*
* Shows the current element. Essentially, setting display:block for the style.
*
* @method show
* @return {Object/p5.Element}
*/
p5.Element.prototype.show = function() {
this.elt.style.display = 'block';
return this;
};
/**
* Hides the current element. Essentially, setting display:none for the style.
*
* @method hide
* @return {Object/p5.Element}
*/
p5.Element.prototype.hide = function() {
this.elt.style.display = 'none';
return this;
};
/**
*
* Sets the width and height of the element. AUTO can be used to
* only adjust one dimension. If no arguments given returns the width and height
* of the element in an object.
*
* @method size
* @param {Number} [w] width of the element
* @param {Number} [h] height of the element
* @return {Object/p5.Element}
*/
p5.Element.prototype.size = function(w, h) {
if (arguments.length === 0){
return { 'width' : this.elt.offsetWidth , 'height' : this.elt.offsetHeight };
}else{
var aW = w;
var aH = h;
var AUTO = p5.prototype.AUTO;
if (aW !== AUTO || aH !== AUTO) {
if (aW === AUTO) {
aW = h * this.width / this.height;
} else if (aH === AUTO) {
aH = w * this.height / this.width;
}
// set diff for cnv vs normal div
if (this.elt instanceof HTMLCanvasElement) {
var j = {};
var k = this.elt.getContext('2d');
for (var prop in k) {
j[prop] = k[prop];
}
this.elt.setAttribute('width', aW * this._pInst._pixelDensity);
this.elt.setAttribute('height', aH * this._pInst._pixelDensity);
this.elt.setAttribute('style', 'width:' + aW + 'px; height:' + aH + 'px');
this._pInst.scale(this._pInst._pixelDensity, this._pInst._pixelDensity);
for (var prop in j) {
this.elt.getContext('2d')[prop] = j[prop];
}
} else {
this.elt.style.width = aW+'px';
this.elt.style.height = aH+'px';
this.elt.width = aW;
this.elt.height = aH;
this.width = aW;
this.height = aH;
}
this.width = this.elt.offsetWidth;
this.height = this.elt.offsetHeight;
if (this._pInst) { // main canvas associated with p5 instance
if (this._pInst._curElement.elt === this.elt) {
this._pInst._setProperty('width', this.elt.offsetWidth);
this._pInst._setProperty('height', this.elt.offsetHeight);
}
}
}
return this;
}
};
/**
* Removes the element and deregisters all listeners.
* @method remove
* @example
* <div class='norender'><code>
* var myDiv = createDiv('this is some text');
* myDiv.remove();
* </code></div>
*/
p5.Element.prototype.remove = function() {
// deregister events
for (var ev in this._events) {
this.elt.removeEventListener(ev, this._events[ev]);
}
if (this.elt.parentNode) {
this.elt.parentNode.removeChild(this.elt);
}
delete(this);
};
// =============================================================================
// p5.MediaElement additions
// =============================================================================
/**
* Extends p5.Element to handle audio and video. In addition to the methods
* of p5.Element, it also contains methods for controlling media. It is not
* called directly, but p5.MediaElements are created by calling createVideo,
* createAudio, and createCapture.
*
* @class p5.MediaElement
* @constructor
* @param {String} elt DOM node that is wrapped
* @param {Object} [pInst] pointer to p5 instance
*/
p5.MediaElement = function(elt, pInst) {
p5.Element.call(this, elt, pInst);
this._prevTime = 0;
this._cueIDCounter = 0;
this._cues = [];
this.pixelDensity = 1;
};
p5.MediaElement.prototype = Object.create(p5.Element.prototype);
/**
* Play an HTML5 media element.
*
* @method play
* @return {Object/p5.Element}
*/
p5.MediaElement.prototype.play = function() {
if (this.elt.currentTime === this.elt.duration) {
this.elt.currentTime = 0;
}
if (this.elt.readyState > 1) {
this.elt.play();
} else {
// in Chrome, playback cannot resume after being stopped and must reload
this.elt.load();
this.elt.play();
}
return this;
};
/**
* Stops an HTML5 media element (sets current time to zero).
*
* @method stop
* @return {Object/p5.Element}
*/
p5.MediaElement.prototype.stop = function() {
this.elt.pause();
this.elt.currentTime = 0;
return this;
};
/**
* Pauses an HTML5 media element.
*
* @method pause
* @return {Object/p5.Element}
*/
p5.MediaElement.prototype.pause = function() {
this.elt.pause();
return this;
};
/**
* Set 'loop' to true for an HTML5 media element, and starts playing.
*
* @method loop
* @return {Object/p5.Element}
*/
p5.MediaElement.prototype.loop = function() {
this.elt.setAttribute('loop', true);
this.play();
return this;
};
/**
* Set 'loop' to false for an HTML5 media element. Element will stop
* when it reaches the end.
*
* @method noLoop
* @return {Object/p5.Element}
*/
p5.MediaElement.prototype.noLoop = function() {
this.elt.setAttribute('loop', false);
return this;
};
/**
* Set HTML5 media element to autoplay or not.
*
* @method autoplay
* @param {Boolean} autoplay whether the element should autoplay
* @return {Object/p5.Element}
*/
p5.MediaElement.prototype.autoplay = function(val) {
this.elt.setAttribute('autoplay', val);
return this;
};
/**
* Sets volume for this HTML5 media element. If no argument is given,
* returns the current volume.
*
* @param {Number} [val] volume between 0.0 and 1.0
* @return {Number|p5.MediaElement} current volume or p5.MediaElement
* @method volume
*/
p5.MediaElement.prototype.volume = function(val) {
if (typeof val === 'undefined') {
return this.elt.volume;
} else {
this.elt.volume = val;
}
};
/**
* If no arguments are given, returns the current time of the element.
* If an argument is given the current time of the element is set to it.
*
* @method time
* @param {Number} [time] time to jump to (in seconds)
* @return {Number|Object/p5.MediaElement} current time (in seconds)
* or p5.MediaElement
*/
p5.MediaElement.prototype.time = function(val) {
if (typeof val === 'undefined') {
return this.elt.currentTime;
} else {
this.elt.currentTime = val;
}
};
/**
* Returns the duration of the HTML5 media element.
*
* @method duration
* @return {Number} duration
*/
p5.MediaElement.prototype.duration = function() {
return this.elt.duration;
};
p5.MediaElement.prototype.pixels = [];
p5.MediaElement.prototype.loadPixels = function() {
if (this.loadedmetadata) { // wait for metadata for w/h
if (!this.canvas) {
this.canvas = document.createElement('canvas');
this.canvas.width = this.elt.clientWidth;
this.canvas.height = this.elt.clientHeight;
this.width = this.canvas.width;
this.height = this.canvas.height;
this.drawingContext = this.canvas.getContext('2d');
// Adding an extra check to see if size has changed
} else if (this.canvas.width !== this.elt.clientWidth) {
this.canvas.width = this.elt.clientWidth;
this.canvas.height = this.elt.clientHeight;
this.width = this.canvas.width;
this.height = this.canvas.height;
}
this.drawingContext.drawImage(this.elt, 0, 0, this.canvas.width, this.canvas.height);
p5.Renderer2D.prototype.loadPixels.call(this);
}
return this;
}
p5.MediaElement.prototype.updatePixels = function(x, y, w, h){
if (this.loadedmetadata) { // wait for metadata
p5.Renderer2D.prototype.updatePixels.call(this, x, y, w, h);
}
return this;
}
p5.MediaElement.prototype.get = function(x, y, w, h){
if (this.loadedmetadata) { // wait for metadata
return p5.Renderer2D.prototype.get.call(this, x, y, w, h);
} else return [0, 0, 0, 255];
};
p5.MediaElement.prototype.set = function(x, y, imgOrCol){
if (this.loadedmetadata) { // wait for metadata
p5.Renderer2D.prototype.set.call(this, x, y, imgOrCol);
}
};
/*** CONNECT TO WEB AUDIO API / p5.sound.js ***/
/**
* Send the audio output of this element to a specified audioNode or
* p5.sound object. If no element is provided, connects to p5's master
* output. That connection is established when this method is first called.
* All connections are removed by the .disconnect() method.
*
* This method is meant to be used with the p5.sound.js addon library.
*
* @method connect
* @param {AudioNode|p5.sound object} audioNode AudioNode from the Web Audio API,
* or an object from the p5.sound library
*/
p5.MediaElement.prototype.connect = function(obj) {
var audioContext, masterOutput;
// if p5.sound exists, same audio context
if (typeof p5.prototype.getAudioContext === 'function') {
audioContext = p5.prototype.getAudioContext();
masterOutput = p5.soundOut.input;
} else {
try {
audioContext = obj.context;
masterOutput = audioContext.destination
} catch(e) {
throw 'connect() is meant to be used with Web Audio API or p5.sound.js'
}
}
// create a Web Audio MediaElementAudioSourceNode if none already exists
if (!this.audioSourceNode) {
this.audioSourceNode = audioContext.createMediaElementSource(this.elt);
// connect to master output when this method is first called
this.audioSourceNode.connect(masterOutput);
}
// connect to object if provided
if (obj) {
if (obj.input) {
this.audioSourceNode.connect(obj.input);
} else {
this.audioSourceNode.connect(obj);
}
}
// otherwise connect to master output of p5.sound / AudioContext
else {
this.audioSourceNode.connect(masterOutput);
}
};
/**
* Disconnect all Web Audio routing, including to master output.
* This is useful if you want to re-route the output through
* audio effects, for example.
*
* @method disconnect
*/
p5.MediaElement.prototype.disconnect = function() {
if (this.audioSourceNode) {
this.audioSourceNode.disconnect();
} else {
throw 'nothing to disconnect';
}
};
/*** SHOW / HIDE CONTROLS ***/
/**
* Show the default MediaElement controls, as determined by the web browser.
*
* @method showControls
*/
p5.MediaElement.prototype.showControls = function() {
// must set style for the element to show on the page
this.elt.style['text-align'] = 'inherit';
this.elt.controls = true;
};
/**
* Hide the default mediaElement controls.
*
* @method hideControls
*/
p5.MediaElement.prototype.hideControls = function() {
this.elt.controls = false;
};
/*** SCHEDULE EVENTS ***/
/**
* Schedule events to trigger every time a MediaElement
* (audio/video) reaches a playback cue point.
*
* Accepts a callback function, a time (in seconds) at which to trigger
* the callback, and an optional parameter for the callback.
*
* Time will be passed as the first parameter to the callback function,
* and param will be the second parameter.
*
*
* @method addCue
* @param {Number} time Time in seconds, relative to this media
* element's playback. For example, to trigger
* an event every time playback reaches two
* seconds, pass in the number 2. This will be
* passed as the first parameter to
* the callback function.
* @param {Function} callback Name of a function that will be
* called at the given time. The callback will
* receive time and (optionally) param as its
* two parameters.
* @param {Object} [value] An object to be passed as the
* second parameter to the
* callback function.
* @return {Number} id ID of this cue,
* useful for removeCue(id)
* @example
* <div><code>
* function setup() {
* background(255,255,255);
*
* audioEl = createAudio('assets/beat.mp3');
* audioEl.showControls();
*
* // schedule three calls to changeBackground
* audioEl.addCue(0.5, changeBackground, color(255,0,0) );
* audioEl.addCue(1.0, changeBackground, color(0,255,0) );
* audioEl.addCue(2.5, changeBackground, color(0,0,255) );
* audioEl.addCue(3.0, changeBackground, color(0,255,255) );
* audioEl.addCue(4.2, changeBackground, color(255,255,0) );
* audioEl.addCue(5.0, changeBackground, color(255,255,0) );
* }
*
* function changeBackground(val) {
* background(val);
* }
* </code></div>
*/
p5.MediaElement.prototype.addCue = function(time, callback, val) {
var id = this._cueIDCounter++;
var cue = new Cue(callback, time, id, val);
this._cues.push(cue);
if (!this.elt.ontimeupdate) {
this.elt.ontimeupdate = this._onTimeUpdate.bind(this);
}
return id;
};
/**
* Remove a callback based on its ID. The ID is returned by the
* addCue method.
*
* @method removeCue
* @param {Number} id ID of the cue, as returned by addCue
*/
p5.MediaElement.prototype.removeCue = function(id) {
for (var i = 0; i < this._cues.length; i++) {
var cue = this._cues[i];
if (cue.id === id) {
this.cues.splice(i, 1);
}
}
if (this._cues.length === 0) {
this.elt.ontimeupdate = null
}
};
/**
* Remove all of the callbacks that had originally been scheduled
* via the addCue method.
*
* @method clearCues
*/
p5.MediaElement.prototype.clearCues = function() {
this._cues = [];
this.elt.ontimeupdate = null;
};
// private method that checks for cues to be fired if events
// have been scheduled using addCue(callback, time).
p5.MediaElement.prototype._onTimeUpdate = function() {
var playbackTime = this.time();
for (var i = 0 ; i < this._cues.length; i++) {
var callbackTime = this._cues[i].time;
var val = this._cues[i].val;
if (this._prevTime < callbackTime && callbackTime <= playbackTime) {
// pass the scheduled callbackTime as parameter to the callback
this._cues[i].callback(val);
}
}
this._prevTime = playbackTime;
};
// Cue inspired by JavaScript setTimeout, and the
// Tone.js Transport Timeline Event, MIT License Yotam Mann 2015 tonejs.org
var Cue = function(callback, time, id, val) {
this.callback = callback;
this.time = time;
this.id = id;
this.val = val;
};
// =============================================================================
// p5.File
// =============================================================================
/**
* Base class for a file
* Using this for createFileInput
*
* @class p5.File
* @constructor
* @param {File} file File that is wrapped
* @param {Object} [pInst] pointer to p5 instance
*/
p5.File = function(file, pInst) {
/**
* Underlying File object. All normal File methods can be called on this.
*
* @property file
*/
this.file = file;
this._pInst = pInst;
// Splitting out the file type into two components
// This makes determining if image or text etc simpler
var typeList = file.type.split('/');
/**
* File type (image, text, etc.)
*
* @property type
*/
this.type = typeList[0];
/**
* File subtype (usually the file extension jpg, png, xml, etc.)
*
* @property subtype
*/
this.subtype = typeList[1];
/**
* File name
*
* @property name
*/
this.name = file.name;
/**
* File size
*
* @property size
*/
this.size = file.size;
// Data not loaded yet
this.data = undefined;
};
}));
var u;
var u2;
var count;
var mods = [];
var darkbg = false;
function setup() {
createCanvas(windowWidth, windowHeight);
background('#7DF5D1');
u = 50;
u2 = (u/2)*sqrt(3);
var highCount = (height/u)+3;
var wideCount = (width/u2)+3;
count = int(highCount * wideCount);
var index = 0;
for (var xc = 0; xc < wideCount*2; xc++) {
for (var yc = 0; yc < highCount*2; yc++) {
mods[index++] = new Module((int(xc)*u2*2),int(yc)*u);
}
}
}
function draw() {
noStroke();
for (var i = 0; i <= count; i++) {
mods[i].draw1();
}
translate(0,u/2);
for (var i = 0; i <= count; i++) {
mods[i].draw3();
}
translate(u2,0);
for (var i = 0; i <= count; i++) {
mods[i].draw2();
}
translate(0,-u/2);
for (var i = 0; i <= count; i++) {
mods[i].draw4();
}
}
function mousePressed() {
for (var i = 0; i <= count; i++) {
mods[i].Pressed();
}
}
function Module(_x, _y) {
this.s = 25;
this.x1 = _x;
this.y1 = _y;
this.x2 = _x;
this.y2 = _y;
this.x3 = _x;
this.y3 = _y;
this.x4 = _x;
this.y4 = _y;
this.b1 = false;
this.b2 = false;
this.b3 = false;
this.b4 = false;
this.isOverTriangle1 = false;
this.isOverTriangle2 = false;
this.isOverTriangle3 = false;
this.isOverTriangle4 = false;
this.c1 = '#545861';
this.c2 = '#545861';
this.c3 = '#545861';
this.c4 = '#545861';
}
Module.prototype.draw1 = function() {
push();
translate(this.x1, this.y1);
rectMode(CENTER);
noStroke();
fill(this.c1);
triangle(-this.s,-this.s,-this.s,this.s,(sqrt(3)*this.s)-this.s,0);
this.px1 = mouseX;
this.py1 = mouseY;
this.ax1 = this.x1-this.s;
this.ay1 = this.y1-this.s;
this.bx1 = this.x1-this.s;
this.by1 = this.y1+this.s;
this.cx1 = this.x1+(sqrt(3)*this.s)-this.s;
this.cy1 = this.y1;
this.v01 = [this.cx1-this.ax1,this.cy1-this.ay1];
this.v11 = [this.bx1-this.ax1,this.by1-this.ay1];
this.v21 = [this.px1-this.ax1,this.py1-this.ay1];
this.dot001 = (this.v01[0]*this.v01[0]) + (this.v01[1]*this.v01[1]);
this.dot011 = (this.v01[0]*this.v11[0]) + (this.v01[1]*this.v11[1]);
this.dot021 = (this.v01[0]*this.v21[0]) + (this.v01[1]*this.v21[1]);
this.dot111 = (this.v11[0]*this.v11[0]) + (this.v11[1]*this.v11[1]);
this.dot121 = (this.v11[0]*this.v21[0]) + (this.v11[1]*this.v21[1]);
this.invDenom1 = 1/ (this.dot001 * this.dot111 - this.dot011 * this.dot011);
this.u1 = (this.dot111 * this.dot021 - this.dot011 * this.dot121) * this.invDenom1;
this.v1 = (this.dot001 * this.dot121 - this.dot011 * this.dot021) * this.invDenom1;
this.isOverTriangle1 = ((this.u1 >= 0) && (this.v1 >= 0) && (this.u1 + this.v1 < 1));
if(this.isOverTriangle1 === true)
{
fill('rgba(255, 255, 255, 0.2)');
triangle(-this.s,-this.s,-this.s,this.s,(sqrt(3)*this.s)-this.s,0);
} else {
noFill();
}
pop();
}
Module.prototype.draw2 = function() {
push();
translate(this.x2, this.y2);
rectMode(CENTER);
noStroke();
fill(this.c2);
triangle(-this.s,-this.s,-this.s,this.s,(sqrt(3)*this.s)-this.s,0);
this.px2 = mouseX;
this.py2 = mouseY;
this.nx2 = this.x2+u2;
this.ny2 = this.y2+(u/2);
this.ax2 = this.nx2-this.s;
this.ay2 = this.ny2-this.s;
this.bx2 = this.nx2-this.s;
this.by2 = this.ny2+this.s;
this.cx2 = this.nx2+(sqrt(3)*this.s)-this.s;
this.cy2 = this.ny2;
this.v02 = [this.cx2-this.ax2,this.cy2-this.ay2];
this.v12 = [this.bx2-this.ax2,this.by2-this.ay2];
this.v22 = [this.px2-this.ax2,this.py2-this.ay2];
this.dot002 = (this.v02[0]*this.v02[0]) + (this.v02[1]*this.v02[1]);
this.dot012 = (this.v02[0]*this.v12[0]) + (this.v02[1]*this.v12[1]);
this.dot022 = (this.v02[0]*this.v22[0]) + (this.v02[1]*this.v22[1]);
this.dot112 = (this.v12[0]*this.v12[0]) + (this.v12[1]*this.v12[1]);
this.dot122 = (this.v12[0]*this.v22[0]) + (this.v12[1]*this.v22[1]);
this.invDenom2 = 1/ (this.dot002 * this.dot112 - this.dot012 * this.dot012);
this.u2 = (this.dot112 * this.dot022 - this.dot012 * this.dot122) * this.invDenom2;
this.v2 = (this.dot002 * this.dot122 - this.dot012 * this.dot022) * this.invDenom2;
this.isOverTriangle2 = ((this.u2 >= 0) && (this.v2 >= 0) && (this.u2 + this.v2 < 1));
if(this.isOverTriangle2 === true)
{
fill('rgba(255, 255, 255, 0.2)');
triangle(-this.s,-this.s,-this.s,this.s,(sqrt(3)*this.s)-this.s,0);
} else {
noFill();
}
pop();
}
Module.prototype.draw3 = function() {
push();
translate(this.x3, this.y3);
rectMode(CENTER);
noStroke();
fill(this.c3);
triangle(-this.s,0,(sqrt(3)*this.s)-this.s,-this.s,(sqrt(3)*this.s)-this.s,this.s);
this.px3 = mouseX;
this.py3 = mouseY;
this.nx3 = this.x3;
this.ny3 = this.y3+(u/2);
this.ax3 = this.nx3-this.s;
this.ay3 = this.ny3;
this.bx3 = this.nx3+(sqrt(3)*this.s)-this.s;
this.by3 = this.ny3-this.s;
this.cx3 = this.nx3+(sqrt(3)*this.s)-this.s;
this.cy3 = this.ny3+this.s;
this.v03 = [this.cx3-this.ax3,this.cy3-this.ay3];
this.v13 = [this.bx3-this.ax3,this.by3-this.ay3];
this.v23 = [this.px3-this.ax3,this.py3-this.ay3];
this.dot003 = (this.v03[0]*this.v03[0]) + (this.v03[1]*this.v03[1]);
this.dot013 = (this.v03[0]*this.v13[0]) + (this.v03[1]*this.v13[1]);
this.dot023 = (this.v03[0]*this.v23[0]) + (this.v03[1]*this.v23[1]);
this.dot113 = (this.v13[0]*this.v13[0]) + (this.v13[1]*this.v13[1]);
this.dot123 = (this.v13[0]*this.v23[0]) + (this.v13[1]*this.v23[1]);
this.invDenom3 = 1/ (this.dot003 * this.dot113 - this.dot013 * this.dot013);
this.u3 = (this.dot113 * this.dot023 - this.dot013 * this.dot123) * this.invDenom3;
this.v3 = (this.dot003 * this.dot123 - this.dot013 * this.dot023) * this.invDenom3;
this.isOverTriangle3 = ((this.u3 >= 0) && (this.v3 >= 0) && (this.u3 + this.v3 < 1));
if(this.isOverTriangle3 === true)
{
fill('rgba(255, 255, 255, 0.2)');
triangle(-this.s,0,(sqrt(3)*this.s)-this.s,-this.s,(sqrt(3)*this.s)-this.s,this.s);
} else {
noFill();
}
pop();
}
Module.prototype.draw4 = function() {
push();
translate(this.x4, this.y4);
rectMode(CENTER);
noStroke();
fill(this.c4);
triangle(-this.s,0,(sqrt(3)*this.s)-this.s,-this.s,(sqrt(3)*this.s)-this.s,this.s);
this.px4 = mouseX;
this.py4 = mouseY;
this.nx4 = this.x4+u2;
this.ny4 = this.y4;
this.ax4 = this.nx4-this.s;
this.ay4 = this.ny4;
this.bx4 = this.nx4+(sqrt(3)*this.s)-this.s;
this.by4 = this.ny4-this.s;
this.cx4 = this.nx4+(sqrt(3)*this.s)-this.s;
this.cy4 = this.ny4+this.s;
this.v04 = [this.cx4-this.ax4,this.cy4-this.ay4];
this.v14 = [this.bx4-this.ax4,this.by4-this.ay4];
this.v24 = [this.px4-this.ax4,this.py4-this.ay4];
this.dot004 = (this.v04[0]*this.v04[0]) + (this.v04[1]*this.v04[1]);
this.dot014 = (this.v04[0]*this.v14[0]) + (this.v04[1]*this.v14[1]);
this.dot024 = (this.v04[0]*this.v24[0]) + (this.v04[1]*this.v24[1]);
this.dot114 = (this.v14[0]*this.v14[0]) + (this.v14[1]*this.v14[1]);
this.dot124 = (this.v14[0]*this.v24[0]) + (this.v14[1]*this.v24[1]);
this.invDenom4 = 1/ (this.dot004 * this.dot114 - this.dot014 * this.dot014);
this.u4 = (this.dot114 * this.dot024 - this.dot014 * this.dot124) * this.invDenom4;
this.v4 = (this.dot004 * this.dot124 - this.dot014 * this.dot024) * this.invDenom4;
this.isOverTriangle4 = ((this.u4 >= 0) && (this.v4 >= 0) && (this.u4 + this.v4 < 1));
if(this.isOverTriangle4 === true)
{
fill('rgba(255, 255, 255, 0.2)');
triangle(-this.s,0,(sqrt(3)*this.s)-this.s,-this.s,(sqrt(3)*this.s)-this.s,this.s);
} else {
noFill();
}
pop();
}
Module.prototype.Pressed = function() {
if (this.isOverTriangle1 === true){
if(this.b1 === false){
this.c1 = '#7DF5D1';
this.b1 = true;
} else {
this.c1 = '#545861';
this.b1 = false;
}
}
if (this.isOverTriangle2 === true){
if(this.b2 === false){
this.c2 = '#7DF5D1';
this.b2 = true;
} else {
this.c2 = '#545861';
this.b2 = false;
}
}
if (this.isOverTriangle3 === true){
if(this.b3 === false){
this.c3 = '#7DF5D1';
this.b3 = true;
} else {
this.c3 = '#545861';
this.b3 = false;
}
}
if (this.isOverTriangle4 === true){
if(this.b4 === false){
this.c4 = '#7DF5D1';
this.b4 = true;
} else {
this.c4 = '#545861';
this.b4 = false;
}
}
}
function windowResized() {
resizeCanvas(windowWidth, windowHeight);
}
<!--
See https://dl.dropboxusercontent.com/u/5222119/p5/022/index.html
-->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>22/100</title>
<script language="javascript" type="text/javascript" src="../p5.js"></script>
<script src="p5.dom.js" type="text/javascript"></script>
<script src="sketch.js" type="text/javascript"></script>
<style> body{padding:0; margin:0;} </style>
</head>
<body>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment