├── .gitattributes
├── Back Prop
├── color nn - BP.js
├── index.html
└── libs
│ ├── matrix.js
│ ├── nn.js
│ ├── p5.dom.js
│ ├── p5.dom.min.js
│ ├── p5.js
│ ├── p5.min.js
│ ├── p5.sound.js
│ └── p5.sound.min.js
└── Genetic Algorithm
├── color nn - GA.js
├── index.html
└── libs
├── p5.dom.js
├── p5.dom.min.js
├── p5.js
├── p5.min.js
├── p5.sound.js
└── p5.sound.min.js
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/Back Prop/color nn - BP.js:
--------------------------------------------------------------------------------
1 | // for red, green, and blue color values
2 | let nn;
3 | var r, g, b, diameter, choices = 2;
4 | var button = [[180,300],[540,300]]
5 | let inputs, outputs, targets;
6 |
7 | function setup() {
8 | nn = new NeuralNetwork(3, 20, 2,.5);
9 |
10 | canv = createCanvas(700,500);
11 | canv.parent("sketcher");
12 |
13 | ColorRandomizer(false,[0,0]);
14 |
15 |
16 | diameter = 200;
17 | }
18 |
19 | function draw() {
20 | background(0);
21 |
22 | textSize(30);
23 | textAlign(CENTER, CENTER);
24 | fill(255);
25 | text("Does White or Black look better over this color?", 350,50);
26 |
27 | DrawDisplayer(0);
28 | DrawDisplayer(1);
29 |
30 | fill(255);
31 | ellipse(button[outputs[0] > outputs[1] ? 0 : 1][0], 150, 50, 50);
32 |
33 | }
34 |
35 | function DrawDisplayer(loc)
36 | {
37 | fill(r, g, b);
38 | ellipse(button[loc][0], button[loc][1], diameter, diameter);
39 | fill(loc*255);
40 | textSize(48);
41 | textAlign(CENTER, CENTER);
42 | var t = (loc==0) ? "BLACK" : "WHITE";
43 | text(t,button[loc][0], button[loc][1]);
44 | }
45 |
46 | function ColorRandomizer(tr,t)
47 | {
48 | if(tr)
49 | {
50 | // For training
51 | targets = t;
52 | nn.train(inputs, targets);
53 |
54 | // TIPS FROM USING THE TOY NN LIB
55 | // I think the backprop order of operation may need a bit of work? for I can't get the toy NN to converge without overfitting it seems
56 | // however I am out of time & not good enough in JS syntax to add to the high level JS syntax in the lib. xD
57 | // so I suggest using the nn.predict() function on only a small dataset perhaps to get the highest possible accuracy?
58 | // feel free to submit your github request here for the lib! https://github.com/CodingTrain/Toy-Neural-Network-JS
59 | outputs = nn.predict(inputs);
60 | }
61 | GrabRandomColAndGuess();
62 | }
63 |
64 | function GrabRandomColAndGuess()
65 | {
66 | // Pick colors randomly
67 | r = random(255);
68 | g = random(255);
69 | b = random(255);
70 |
71 | inputs = [r, g, b];
72 | // for guessing
73 | outputs = nn.predict(inputs);
74 | }
75 |
76 | function keyPressed() {
77 | if (keyCode === RIGHT_ARROW) {
78 | GrabRandomColAndGuess();
79 | }
80 | }
81 | // When the user clicks the mouse
82 | function mousePressed() {
83 | // Check if mouse is inside the circle
84 | var d = [];
85 |
86 | for ( var i = 0; i < choices; i++)
87 | {
88 | d[i] = dist(mouseX, mouseY, button[i][0], button[i][1]);
89 | }
90 |
91 | if (d[0] < diameter/2) {
92 | // Pick new random color values
93 | ColorRandomizer(true,[1,0]);
94 | }
95 | else if (d[1] < diameter/2) {
96 | // Pick new random color values
97 | ColorRandomizer(true,[0,1]);
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/Back Prop/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
Press "Right Arrow" to test a new random color!
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/Back Prop/libs/matrix.js:
--------------------------------------------------------------------------------
1 | // let m = new Matrix(3,2);
2 |
3 |
4 | class Matrix {
5 | constructor(rows, cols) {
6 | this.rows = rows;
7 | this.cols = cols;
8 | this.data = Array(this.rows).fill().map(() => Array(this.cols).fill(0));
9 | }
10 |
11 | static fromArray(arr) {
12 | return new Matrix(arr.length, 1).map((e, i) => arr[i]);
13 | }
14 |
15 | static subtract(a, b) {
16 | if(a.rows!==b.rows || a.cols!==b.cols){
17 | console.log('Columns and Rows of A must match Columns and Rows of B.');
18 | return;
19 | }
20 |
21 | // Return a new Matrix a-b
22 | return new Matrix(a.rows, a.cols)
23 | .map((_, i, j) => a.data[i][j] - b.data[i][j]);
24 | }
25 |
26 | toArray() {
27 | let arr = [];
28 | for (let i = 0; i < this.rows; i++) {
29 | for (let j = 0; j < this.cols; j++) {
30 | arr.push(this.data[i][j]);
31 | }
32 | }
33 | return arr;
34 | }
35 |
36 | randomize() {
37 | return this.map(e => Math.random() * 2 - 1);
38 | }
39 |
40 | add(n) {
41 | if (n instanceof Matrix) {
42 | if(this.rows!==n.rows || this.cols!==n.cols){
43 | console.log('Columns and Rows of A must match Columns and Rows of B.');
44 | return;
45 | }
46 | return this.map((e, i, j) => e + n.data[i][j]);
47 | } else {
48 | return this.map(e => e + n);
49 | }
50 | }
51 |
52 | static transpose(matrix) {
53 | return new Matrix(matrix.cols, matrix.rows)
54 | .map((_, i, j) => matrix.data[j][i]);
55 | }
56 |
57 | static multiply(a, b) {
58 | // Matrix product
59 | if (a.cols !== b.rows) {
60 | console.log('Columns of A must match rows of B.')
61 | return;
62 | }
63 |
64 | return new Matrix(a.rows, b.cols)
65 | .map((e, i, j) => {
66 | // Dot product of values in col
67 | let sum = 0;
68 | for (let k = 0; k < a.cols; k++) {
69 | sum += a.data[i][k] * b.data[k][j];
70 | }
71 | return sum;
72 | });
73 | }
74 |
75 | multiply(n) {
76 | if (n instanceof Matrix) {
77 | if(this.rows!==n.rows || this.cols!==n.cols){
78 | console.log('Columns and Rows of A must match Columns and Rows of B.');
79 | return;
80 | }
81 |
82 | // hadamard product
83 | return this.map((e, i, j) => e * n.data[i][j]);
84 | } else {
85 | // Scalar product
86 | return this.map(e => e * n);
87 | }
88 | }
89 |
90 | map(func) {
91 | // Apply a function to every element of matrix
92 | for (let i = 0; i < this.rows; i++) {
93 | for (let j = 0; j < this.cols; j++) {
94 | let val = this.data[i][j];
95 | this.data[i][j] = func(val, i, j);
96 | }
97 | }
98 | return this;
99 | }
100 |
101 | static map(matrix, func) {
102 | // Apply a function to every element of matrix
103 | return new Matrix(matrix.rows, matrix.cols)
104 | .map((e, i, j) => func(matrix.data[i][j], i, j));
105 | }
106 |
107 | print() {
108 | console.table(this.data);
109 | return this;
110 | }
111 |
112 | serialize() {
113 | return JSON.stringify(this);
114 | }
115 |
116 | static deserialize(data) {
117 | if(typeof data == 'string')
118 | {
119 | data = JSON.parse(data);
120 | }
121 | let matrix = new Matrix(data.rows, data.cols);
122 | matrix.data = data.data;
123 | return matrix;
124 | }
125 | }
126 |
127 | if (typeof module !== 'undefined') {
128 | module.exports = Matrix;
129 | }
--------------------------------------------------------------------------------
/Back Prop/libs/nn.js:
--------------------------------------------------------------------------------
1 | // Other techniques for learning
2 |
3 | class ActivationFunction{
4 | constructor(func, dfunc){
5 | this.func = func;
6 | this.dfunc = dfunc;
7 | }
8 | }
9 |
10 | let sigmoid = new ActivationFunction(
11 | x => 1 / (1 + Math.exp(-x)),
12 | y => y * (1- y)
13 | );
14 |
15 | let tanh = new ActivationFunction(
16 | x => Math.tanh(x),
17 | y => 1-(y*y)
18 | );
19 |
20 | // returns a softmax probability distribution
21 | function softmax(vec) {
22 | VEC = [];
23 |
24 | //
25 | for (var i = 0; i < vec.length; i++) {
26 | VEC[i] = Math.pow(Math.E, vec[i]) / Summation(vec);
27 | }
28 |
29 | return VEC
30 | }
31 |
32 | // returns the summation of input vector
33 | function Summation(vec) {
34 | final = 0;
35 |
36 | for (var i = 0; i < vec.length; i++) {
37 | final += Math.pow(Math.E, (vec[i]))
38 | }
39 |
40 | return final;
41 | }
42 |
43 | class NeuralNetwork {
44 | constructor(input_nodes, hidden_nodes, output_nodes, learn_rate) {
45 | this.input_nodes = input_nodes;
46 | this.hidden_nodes = hidden_nodes;
47 | this.output_nodes = output_nodes;
48 |
49 | this.weights_ih = new Matrix(this.hidden_nodes, this.input_nodes);
50 | this.weights_ho = new Matrix(this.output_nodes, this.hidden_nodes);
51 | this.weights_ih.randomize();
52 | this.weights_ho.randomize();
53 |
54 | this.bias_h = new Matrix(this.hidden_nodes, 1);
55 | this.bias_o = new Matrix(this.output_nodes, 1);
56 | this.bias_h.randomize();
57 | this.bias_o.randomize();
58 | this.setLearningRate(learn_rate);
59 |
60 | this.setActivationFunction();
61 |
62 | }
63 |
64 | predict(input_array) {
65 |
66 | // Generating the Hidden Outputs
67 | let inputs = Matrix.fromArray(input_array);
68 | let hidden = Matrix.multiply(this.weights_ih, inputs);
69 | hidden.add(this.bias_h);
70 | // activation function!
71 | hidden.map(this.activation_function.func);
72 |
73 | // Generating the output's output!
74 | let output = Matrix.multiply(this.weights_ho, hidden);
75 | output.add(this.bias_o);
76 | output.map(this.activation_function.func);
77 |
78 | // Sending back to the caller!
79 | return output.toArray();
80 | }
81 |
82 | setLearningRate(learning_rate) {
83 | this.learning_rate = learning_rate;
84 | }
85 |
86 | setActivationFunction(func = sigmoid) {
87 | this.activation_function = func;
88 | }
89 |
90 | train(input_array, target_array) {
91 | // Generating the Hidden Outputs
92 | let inputs = Matrix.fromArray(input_array);
93 | let hidden = Matrix.multiply(this.weights_ih, inputs);
94 | hidden.add(this.bias_h);
95 | // activation function!
96 | hidden.map(this.activation_function.func);
97 |
98 | // Generating the output's output!
99 | let outputs = Matrix.multiply(this.weights_ho, hidden);
100 | outputs.add(this.bias_o);
101 | outputs.map(this.activation_function.func);
102 |
103 | // Convert array to matrix object
104 | let targets = Matrix.fromArray(target_array);
105 |
106 | // Calculate the error
107 | // ERROR = TARGETS - OUTPUTS
108 | let output_errors = Matrix.subtract(targets, outputs);
109 |
110 | // let gradient = outputs * (1 - outputs);
111 | // Calculate gradient
112 | let gradients = Matrix.map(outputs, this.activation_function.dfunc);
113 | gradients.multiply(output_errors);
114 | gradients.multiply(this.learning_rate);
115 |
116 |
117 | // Calculate deltas
118 | let hidden_T = Matrix.transpose(hidden);
119 | let weight_ho_deltas = Matrix.multiply(gradients, hidden_T);
120 |
121 | // Adjust the weights by deltas
122 | this.weights_ho.add(weight_ho_deltas);
123 | // Adjust the bias by its deltas (which is just the gradients)
124 | this.bias_o.add(gradients);
125 |
126 | // Calculate the hidden layer errors
127 | let who_t = Matrix.transpose(this.weights_ho);
128 | let hidden_errors = Matrix.multiply(who_t, output_errors);
129 |
130 | // Calculate hidden gradient
131 | let hidden_gradient = Matrix.map(hidden, this.activation_function.dfunc);
132 | hidden_gradient.multiply(hidden_errors);
133 | hidden_gradient.multiply(this.learning_rate);
134 |
135 | // Calcuate input->hidden deltas
136 | let inputs_T = Matrix.transpose(inputs);
137 | let weight_ih_deltas = Matrix.multiply(hidden_gradient, inputs_T);
138 |
139 | this.weights_ih.add(weight_ih_deltas);
140 | // Adjust the bias by its deltas (which is just the gradients)
141 | this.bias_h.add(hidden_gradient);
142 |
143 | // outputs.print();
144 | // targets.print();
145 | // error.print();
146 | }
147 |
148 | serialize() {
149 | return JSON.stringify(this);
150 | }
151 |
152 | static deserialize(data) {
153 | if(typeof data == 'string')
154 | {
155 | data = JSON.parse(data);
156 | }
157 | let nn = new NeuralNetwork(data.input_nodes, data.hidden_nodes, data.output_nodes);
158 | nn.weights_ih = Matrix.deserialize(data.weights_ih);
159 | nn.weights_ho = Matrix.deserialize(data.weights_ho);
160 | nn.bias_h = Matrix.deserialize(data.bias_h);
161 | nn.bias_o = Matrix.deserialize(data.bias_o);
162 | nn.learning_rate = data.learning_rate;
163 | return nn;
164 | }
165 |
166 | }
167 |
--------------------------------------------------------------------------------
/Back Prop/libs/p5.dom.js:
--------------------------------------------------------------------------------
1 | /*! p5.dom.js v0.3.4 Jan 19, 2017 */
2 | /**
3 | * The web is much more than just canvas and p5.dom makes it easy to interact
4 | * with other HTML5 objects, including text, hyperlink, image, input, video,
5 | * audio, and webcam.
6 | * There is a set of creation methods, DOM manipulation methods, and
7 | * an extended p5.Element that supports a range of HTML elements. See the
8 | *
9 | * beyond the canvas tutorial for a full overview of how this addon works.
10 | *
11 | *
Methods and properties shown in black are part of the p5.js core, items in
12 | * blue are part of the p5.dom library. You will need to include an extra file
13 | * in order to access the blue functions. See the
14 | * using a library
15 | * section for information on how to include this library. p5.dom comes with
16 | * p5 complete or you can download the single file
17 | *
18 | * here.
19 | * See tutorial: beyond the canvas
20 | * for more info on how to use this libary.
21 | *
22 | * @module p5.dom
23 | * @submodule p5.dom
24 | * @for p5.dom
25 | * @main
26 | */
27 |
28 | (function(root, factory) {
29 | if (typeof define === 'function' && define.amd)
30 | define('p5.dom', ['p5'], function(p5) {
31 | factory(p5);
32 | });
33 | else if (typeof exports === 'object') factory(require('../p5'));
34 | else factory(root['p5']);
35 | })(this, function(p5) {
36 | // =============================================================================
37 | // p5 additions
38 | // =============================================================================
39 |
40 | /**
41 | * Searches the page for an element with the given ID, class, or tag name (using the '#' or '.'
42 | * prefixes to specify an ID or class respectively, and none for a tag) and returns it as
43 | * a p5.Element. If a class or tag name is given with more than 1 element,
44 | * only the first element will be returned.
45 | * The DOM node itself can be accessed with .elt.
46 | * Returns null if none found. You can also specify a container to search within.
47 | *
48 | * @method select
49 | * @param {String} name id, class, or tag name of element to search for
50 | * @param {String} [container] id, p5.Element, or HTML element to search within
51 | * @return {Object|p5.Element|Null} p5.Element containing node found
52 | * @example
53 | *
54 | * function setup() {
55 | * createCanvas(100, 100);
56 | * //translates canvas 50px down
57 | * select('canvas').position(100, 100);
58 | * }
59 | *
60 | *
61 | * // these are all valid calls to select()
62 | * var a = select('#moo');
63 | * var b = select('#blah', '#myContainer');
64 | * var c = select('#foo', b);
65 | * var d = document.getElementById('beep');
66 | * var e = select('p', d);
67 | * [a, b, c, d, e]; // unused
68 | *
69 | *
70 | */
71 | p5.prototype.select = function(e, p) {
72 | p5._validateParameters('select', arguments);
73 | var res = null;
74 | var container = getContainer(p);
75 | if (e[0] === '.') {
76 | e = e.slice(1);
77 | res = container.getElementsByClassName(e);
78 | if (res.length) {
79 | res = res[0];
80 | } else {
81 | res = null;
82 | }
83 | } else if (e[0] === '#') {
84 | e = e.slice(1);
85 | res = container.getElementById(e);
86 | } else {
87 | res = container.getElementsByTagName(e);
88 | if (res.length) {
89 | res = res[0];
90 | } else {
91 | res = null;
92 | }
93 | }
94 | if (res) {
95 | return this._wrapElement(res);
96 | } else {
97 | return null;
98 | }
99 | };
100 |
101 | /**
102 | * Searches the page for elements with the given class or tag name (using the '.' prefix
103 | * to specify a class and no prefix for a tag) and returns them as p5.Elements
104 | * in an array.
105 | * The DOM node itself can be accessed with .elt.
106 | * Returns an empty array if none found.
107 | * You can also specify a container to search within.
108 | *
109 | * @method selectAll
110 | * @param {String} name class or tag name of elements to search for
111 | * @param {String} [container] id, p5.Element, or HTML element to search within
112 | * @return {Array} Array of p5.Elements containing nodes found
113 | * @example
114 | *
115 | * function setup() {
116 | * createButton('btn');
117 | * createButton('2nd btn');
118 | * createButton('3rd btn');
119 | * var buttons = selectAll('button');
120 | *
121 | * for (var i = 0; i < buttons.length; i++) {
122 | * buttons[i].size(100, 100);
123 | * }
124 | * }
125 | *
126 | *
127 | * // these are all valid calls to selectAll()
128 | * var a = selectAll('.moo');
129 | * a = selectAll('div');
130 | * a = selectAll('button', '#myContainer');
131 | *
132 | * var d = select('#container');
133 | * a = selectAll('p', d);
134 | *
135 | * var f = document.getElementById('beep');
136 | * a = select('.blah', f);
137 | *
138 | * a; // unused
139 | *
140 | *
141 | */
142 | p5.prototype.selectAll = function(e, p) {
143 | p5._validateParameters('selectAll', arguments);
144 | var arr = [];
145 | var res;
146 | var container = getContainer(p);
147 | if (e[0] === '.') {
148 | e = e.slice(1);
149 | res = container.getElementsByClassName(e);
150 | } else {
151 | res = container.getElementsByTagName(e);
152 | }
153 | if (res) {
154 | for (var j = 0; j < res.length; j++) {
155 | var obj = this._wrapElement(res[j]);
156 | arr.push(obj);
157 | }
158 | }
159 | return arr;
160 | };
161 |
162 | /**
163 | * Helper function for select and selectAll
164 | */
165 | function getContainer(p) {
166 | var container = document;
167 | if (typeof p === 'string' && p[0] === '#') {
168 | p = p.slice(1);
169 | container = document.getElementById(p) || document;
170 | } else if (p instanceof p5.Element) {
171 | container = p.elt;
172 | } else if (p instanceof HTMLElement) {
173 | container = p;
174 | }
175 | return container;
176 | }
177 |
178 | /**
179 | * Helper function for getElement and getElements.
180 | */
181 | p5.prototype._wrapElement = function(elt) {
182 | var children = Array.prototype.slice.call(elt.children);
183 | if (elt.tagName === 'INPUT' && elt.type === 'checkbox') {
184 | var converted = new p5.Element(elt);
185 | converted.checked = function() {
186 | if (arguments.length === 0) {
187 | return this.elt.checked;
188 | } else if (arguments[0]) {
189 | this.elt.checked = true;
190 | } else {
191 | this.elt.checked = false;
192 | }
193 | return this;
194 | };
195 | return converted;
196 | } else if (elt.tagName === 'VIDEO' || elt.tagName === 'AUDIO') {
197 | return new p5.MediaElement(elt);
198 | } else if (elt.tagName === 'SELECT') {
199 | return this.createSelect(new p5.Element(elt));
200 | } else if (
201 | children.length > 0 &&
202 | children.every(function(c) {
203 | return c.tagName === 'INPUT' || c.tagName === 'LABEL';
204 | })
205 | ) {
206 | return this.createRadio(new p5.Element(elt));
207 | } else {
208 | return new p5.Element(elt);
209 | }
210 | };
211 |
212 | /**
213 | * Removes all elements created by p5, except any canvas / graphics
214 | * elements created by createCanvas or createGraphics.
215 | * Event handlers are removed, and element is removed from the DOM.
216 | * @method removeElements
217 | * @example
218 | *
219 | * function setup() {
220 | * createCanvas(100, 100);
221 | * createDiv('this is some text');
222 | * createP('this is a paragraph');
223 | * }
224 | * function mousePressed() {
225 | * removeElements(); // this will remove the div and p, not canvas
226 | * }
227 | *
228 | *
229 | */
230 | p5.prototype.removeElements = function(e) {
231 | p5._validateParameters('removeElements', arguments);
232 | for (var i = 0; i < this._elements.length; i++) {
233 | if (!(this._elements[i].elt instanceof HTMLCanvasElement)) {
234 | this._elements[i].remove();
235 | }
236 | }
237 | };
238 |
239 | /**
240 | * Helpers for create methods.
241 | */
242 | function addElement(elt, pInst, media) {
243 | var node = pInst._userNode ? pInst._userNode : document.body;
244 | node.appendChild(elt);
245 | var c = media ? new p5.MediaElement(elt) : new p5.Element(elt);
246 | pInst._elements.push(c);
247 | return c;
248 | }
249 |
250 | /**
251 | * Creates a <div></div> element in the DOM with given inner HTML.
252 | * Appends to the container node if one is specified, otherwise
253 | * appends to body.
254 | *
255 | * @method createDiv
256 | * @param {String} [html] inner HTML for element created
257 | * @return {Object|p5.Element} pointer to p5.Element holding created node
258 | * @example
259 | *
260 | * createDiv('this is some text');
261 | *
262 | */
263 |
264 | /**
265 | * Creates a <p></p> element in the DOM with given inner HTML. Used
266 | * for paragraph length text.
267 | * Appends to the container node if one is specified, otherwise
268 | * appends to body.
269 | *
270 | * @method createP
271 | * @param {String} [html] inner HTML for element created
272 | * @return {Object|p5.Element} pointer to p5.Element holding created node
273 | * @example
274 | *
275 | * createP('this is some text');
276 | *
277 | */
278 |
279 | /**
280 | * Creates a <span></span> element in the DOM with given inner HTML.
281 | * Appends to the container node if one is specified, otherwise
282 | * appends to body.
283 | *
284 | * @method createSpan
285 | * @param {String} [html] inner HTML for element created
286 | * @return {Object|p5.Element} pointer to p5.Element holding created node
287 | * @example
288 | *
289 | * createSpan('this is some text');
290 | *
291 | */
292 | var tags = ['div', 'p', 'span'];
293 | tags.forEach(function(tag) {
294 | var method = 'create' + tag.charAt(0).toUpperCase() + tag.slice(1);
295 | p5.prototype[method] = function(html) {
296 | var elt = document.createElement(tag);
297 | elt.innerHTML = typeof html === undefined ? '' : html;
298 | return addElement(elt, this);
299 | };
300 | });
301 |
302 | /**
303 | * Creates an <img> element in the DOM with given src and
304 | * alternate text.
305 | * Appends to the container node if one is specified, otherwise
306 | * appends to body.
307 | *
308 | * @method createImg
309 | * @param {String} src src path or url for image
310 | * @param {String} [alt] alternate text to be used if image does not load
311 | * @param {Function} [successCallback] callback to be called once image data is loaded
312 | * @return {Object|p5.Element} pointer to p5.Element holding created node
313 | * @example
314 | *
315 | * createImg('http://p5js.org/img/asterisk-01.png');
316 | *
317 | */
318 | p5.prototype.createImg = function() {
319 | p5._validateParameters('createImg', arguments);
320 | var elt = document.createElement('img');
321 | var args = arguments;
322 | var self;
323 | var setAttrs = function() {
324 | self.width = elt.offsetWidth || elt.width;
325 | self.height = elt.offsetHeight || elt.height;
326 | if (args.length > 1 && typeof args[1] === 'function') {
327 | self.fn = args[1];
328 | self.fn();
329 | } else if (args.length > 1 && typeof args[2] === 'function') {
330 | self.fn = args[2];
331 | self.fn();
332 | }
333 | };
334 | elt.src = args[0];
335 | if (args.length > 1 && typeof args[1] === 'string') {
336 | elt.alt = args[1];
337 | }
338 | elt.onload = function() {
339 | setAttrs();
340 | };
341 | self = addElement(elt, this);
342 | return self;
343 | };
344 |
345 | /**
346 | * Creates an <a></a> element in the DOM for including a hyperlink.
347 | * Appends to the container node if one is specified, otherwise
348 | * appends to body.
349 | *
350 | * @method createA
351 | * @param {String} href url of page to link to
352 | * @param {String} html inner html of link element to display
353 | * @param {String} [target] target where new link should open,
354 | * could be _blank, _self, _parent, _top.
355 | * @return {Object|p5.Element} pointer to p5.Element holding created node
356 | * @example
357 | *
358 | * createA('http://p5js.org/', 'this is a link');
359 | *
360 | */
361 | p5.prototype.createA = function(href, html, target) {
362 | p5._validateParameters('createA', arguments);
363 | var elt = document.createElement('a');
364 | elt.href = href;
365 | elt.innerHTML = html;
366 | if (target) elt.target = target;
367 | return addElement(elt, this);
368 | };
369 |
370 | /** INPUT **/
371 |
372 | /**
373 | * Creates a slider <input></input> element in the DOM.
374 | * Use .size() to set the display length of the slider.
375 | * Appends to the container node if one is specified, otherwise
376 | * appends to body.
377 | *
378 | * @method createSlider
379 | * @param {Number} min minimum value of the slider
380 | * @param {Number} max maximum value of the slider
381 | * @param {Number} [value] default value of the slider
382 | * @param {Number} [step] step size for each tick of the slider (if step is set to 0, the slider will move continuously from the minimum to the maximum value)
383 | * @return {Object|p5.Element} pointer to p5.Element holding created node
384 | * @example
385 | *
386 | * var slider;
387 | * function setup() {
388 | * slider = createSlider(0, 255, 100);
389 | * slider.position(10, 10);
390 | * slider.style('width', '80px');
391 | * }
392 | *
393 | * function draw() {
394 | * var val = slider.value();
395 | * background(val);
396 | * }
397 | *
398 | *
399 | *
400 | * var slider;
401 | * function setup() {
402 | * colorMode(HSB);
403 | * slider = createSlider(0, 360, 60, 40);
404 | * slider.position(10, 10);
405 | * slider.style('width', '80px');
406 | * }
407 | *
408 | * function draw() {
409 | * var val = slider.value();
410 | * background(val, 100, 100, 1);
411 | * }
412 | *
413 | */
414 | p5.prototype.createSlider = function(min, max, value, step) {
415 | p5._validateParameters('createSlider', arguments);
416 | var elt = document.createElement('input');
417 | elt.type = 'range';
418 | elt.min = min;
419 | elt.max = max;
420 | if (step === 0) {
421 | elt.step = 0.000000000000000001; // smallest valid step
422 | } else if (step) {
423 | elt.step = step;
424 | }
425 | if (typeof value === 'number') elt.value = value;
426 | return addElement(elt, this);
427 | };
428 |
429 | /**
430 | * Creates a <button></button> element in the DOM.
431 | * Use .size() to set the display size of the button.
432 | * Use .mousePressed() to specify behavior on press.
433 | * Appends to the container node if one is specified, otherwise
434 | * appends to body.
435 | *
436 | * @method createButton
437 | * @param {String} label label displayed on the button
438 | * @param {String} [value] value of the button
439 | * @return {Object|p5.Element} pointer to p5.Element holding created node
440 | * @example
441 | *
442 | * var button;
443 | * function setup() {
444 | * createCanvas(100, 100);
445 | * background(0);
446 | * button = createButton('click me');
447 | * button.position(19, 19);
448 | * button.mousePressed(changeBG);
449 | * }
450 | *
451 | * function changeBG() {
452 | * var val = random(255);
453 | * background(val);
454 | * }
455 | *
456 | */
457 | p5.prototype.createButton = function(label, value) {
458 | p5._validateParameters('createButton', arguments);
459 | var elt = document.createElement('button');
460 | elt.innerHTML = label;
461 | if (value) elt.value = value;
462 | return addElement(elt, this);
463 | };
464 |
465 | /**
466 | * Creates a checkbox <input></input> element in the DOM.
467 | * Calling .checked() on a checkbox returns if it is checked or not
468 | *
469 | * @method createCheckbox
470 | * @param {String} [label] label displayed after checkbox
471 | * @param {boolean} [value] value of the checkbox; checked is true, unchecked is false
472 | * @return {Object|p5.Element} pointer to p5.Element holding created node
473 | * @example
474 | *
475 | * var checkbox;
476 | *
477 | * function setup() {
478 | * checkbox = createCheckbox('label', false);
479 | * checkbox.changed(myCheckedEvent);
480 | * }
481 | *
482 | * function myCheckedEvent() {
483 | * if (this.checked()) {
484 | * console.log('Checking!');
485 | * } else {
486 | * console.log('Unchecking!');
487 | * }
488 | * }
489 | *
490 | */
491 | p5.prototype.createCheckbox = function() {
492 | p5._validateParameters('createCheckbox', arguments);
493 | var elt = document.createElement('div');
494 | var checkbox = document.createElement('input');
495 | checkbox.type = 'checkbox';
496 | elt.appendChild(checkbox);
497 | //checkbox must be wrapped in p5.Element before label so that label appears after
498 | var self = addElement(elt, this);
499 | self.checked = function() {
500 | var cb = self.elt.getElementsByTagName('input')[0];
501 | if (cb) {
502 | if (arguments.length === 0) {
503 | return cb.checked;
504 | } else if (arguments[0]) {
505 | cb.checked = true;
506 | } else {
507 | cb.checked = false;
508 | }
509 | }
510 | return self;
511 | };
512 | this.value = function(val) {
513 | self.value = val;
514 | return this;
515 | };
516 | if (arguments[0]) {
517 | var ran = Math.random()
518 | .toString(36)
519 | .slice(2);
520 | var label = document.createElement('label');
521 | checkbox.setAttribute('id', ran);
522 | label.htmlFor = ran;
523 | self.value(arguments[0]);
524 | label.appendChild(document.createTextNode(arguments[0]));
525 | elt.appendChild(label);
526 | }
527 | if (arguments[1]) {
528 | checkbox.checked = true;
529 | }
530 | return self;
531 | };
532 |
533 | /**
534 | * Creates a dropdown menu <select></select> element in the DOM.
535 | * It also helps to assign select-box methods to p5.Element when selecting existing select box
536 | * @method createSelect
537 | * @param {boolean} [multiple] true if dropdown should support multiple selections
538 | * @return {p5.Element}
539 | * @example
540 | *
541 | * var sel;
542 | *
543 | * function setup() {
544 | * textAlign(CENTER);
545 | * background(200);
546 | * sel = createSelect();
547 | * sel.position(10, 10);
548 | * sel.option('pear');
549 | * sel.option('kiwi');
550 | * sel.option('grape');
551 | * sel.changed(mySelectEvent);
552 | * }
553 | *
554 | * function mySelectEvent() {
555 | * var item = sel.value();
556 | * background(200);
557 | * text('it is a' + item + '!', 50, 50);
558 | * }
559 | *
560 | */
561 | /**
562 | * @method createSelect
563 | * @param {Object} existing DOM select element
564 | * @return {p5.Element}
565 | */
566 |
567 | p5.prototype.createSelect = function() {
568 | p5._validateParameters('createSelect', arguments);
569 | var elt, self;
570 | var arg = arguments[0];
571 | if (typeof arg === 'object' && arg.elt.nodeName === 'SELECT') {
572 | self = arg;
573 | elt = this.elt = arg.elt;
574 | } else {
575 | elt = document.createElement('select');
576 | if (arg && typeof arg === 'boolean') {
577 | elt.setAttribute('multiple', 'true');
578 | }
579 | self = addElement(elt, this);
580 | }
581 | self.option = function(name, value) {
582 | var index;
583 | //see if there is already an option with this name
584 | for (var i = 0; i < this.elt.length; i++) {
585 | if (this.elt[i].innerHTML === name) {
586 | index = i;
587 | break;
588 | }
589 | }
590 | //if there is an option with this name we will modify it
591 | if (index !== undefined) {
592 | //if the user passed in false then delete that option
593 | if (value === false) {
594 | this.elt.remove(index);
595 | } else {
596 | //otherwise if the name and value are the same then change both
597 | if (this.elt[index].innerHTML === this.elt[index].value) {
598 | this.elt[index].innerHTML = this.elt[index].value = value;
599 | //otherwise just change the value
600 | } else {
601 | this.elt[index].value = value;
602 | }
603 | }
604 | } else {
605 | //if it doesn't exist make it
606 | var opt = document.createElement('option');
607 | opt.innerHTML = name;
608 | if (arguments.length > 1) opt.value = value;
609 | else opt.value = name;
610 | elt.appendChild(opt);
611 | }
612 | };
613 | self.selected = function(value) {
614 | var arr = [],
615 | i;
616 | if (arguments.length > 0) {
617 | for (i = 0; i < this.elt.length; i++) {
618 | if (value.toString() === this.elt[i].value) {
619 | this.elt.selectedIndex = i;
620 | }
621 | }
622 | return this;
623 | } else {
624 | if (this.elt.getAttribute('multiple')) {
625 | for (i = 0; i < this.elt.selectedOptions.length; i++) {
626 | arr.push(this.elt.selectedOptions[i].value);
627 | }
628 | return arr;
629 | } else {
630 | return this.elt.value;
631 | }
632 | }
633 | };
634 | return self;
635 | };
636 |
637 | /**
638 | * Creates a radio button <input></input> element in the DOM.
639 | * The .option() method can be used to set options for the radio after it is
640 | * created. The .value() method will return the currently selected option.
641 | *
642 | * @method createRadio
643 | * @param {String} [divId] the id and name of the created div and input field respectively
644 | * @return {Object|p5.Element} pointer to p5.Element holding created node
645 | * @example
646 | *
647 | * var radio;
648 | *
649 | * function setup() {
650 | * radio = createRadio();
651 | * radio.option('black');
652 | * radio.option('white');
653 | * radio.option('gray');
654 | * radio.style('width', '60px');
655 | * textAlign(CENTER);
656 | * fill(255, 0, 0);
657 | * }
658 | *
659 | * function draw() {
660 | * var val = radio.value();
661 | * background(val);
662 | * text(val, width / 2, height / 2);
663 | * }
664 | *
665 | *
666 | * var radio;
667 | *
668 | * function setup() {
669 | * radio = createRadio();
670 | * radio.option('apple', 1);
671 | * radio.option('bread', 2);
672 | * radio.option('juice', 3);
673 | * radio.style('width', '60px');
674 | * textAlign(CENTER);
675 | * }
676 | *
677 | * function draw() {
678 | * background(200);
679 | * var val = radio.value();
680 | * if (val) {
681 | * text('item cost is $' + val, width / 2, height / 2);
682 | * }
683 | * }
684 | *
685 | */
686 | p5.prototype.createRadio = function(existing_radios) {
687 | p5._validateParameters('createRadio', arguments);
688 | // do some prep by counting number of radios on page
689 | var radios = document.querySelectorAll('input[type=radio]');
690 | var count = 0;
691 | if (radios.length > 1) {
692 | var length = radios.length;
693 | var prev = radios[0].name;
694 | var current = radios[1].name;
695 | count = 1;
696 | for (var i = 1; i < length; i++) {
697 | current = radios[i].name;
698 | if (prev !== current) {
699 | count++;
700 | }
701 | prev = current;
702 | }
703 | } else if (radios.length === 1) {
704 | count = 1;
705 | }
706 | // see if we got an existing set of radios from callee
707 | var elt, self;
708 | if (typeof existing_radios === 'object') {
709 | // use existing elements
710 | self = existing_radios;
711 | elt = this.elt = existing_radios.elt;
712 | } else {
713 | // create a set of radio buttons
714 | elt = document.createElement('div');
715 | self = addElement(elt, this);
716 | }
717 | // setup member functions
718 | self._getInputChildrenArray = function() {
719 | return Array.prototype.slice.call(this.elt.children).filter(function(c) {
720 | return c.tagName === 'INPUT';
721 | });
722 | };
723 |
724 | var times = -1;
725 | self.option = function(name, value) {
726 | var opt = document.createElement('input');
727 | opt.type = 'radio';
728 | opt.innerHTML = name;
729 | if (value) opt.value = value;
730 | else opt.value = name;
731 | opt.setAttribute('name', 'defaultradio' + count);
732 | elt.appendChild(opt);
733 | if (name) {
734 | times++;
735 | var label = document.createElement('label');
736 | opt.setAttribute('id', 'defaultradio' + count + '-' + times);
737 | label.htmlFor = 'defaultradio' + count + '-' + times;
738 | label.appendChild(document.createTextNode(name));
739 | elt.appendChild(label);
740 | }
741 | return opt;
742 | };
743 | self.selected = function(value) {
744 | var i;
745 | var inputChildren = self._getInputChildrenArray();
746 | if (value) {
747 | for (i = 0; i < inputChildren.length; i++) {
748 | if (inputChildren[i].value === value) inputChildren[i].checked = true;
749 | }
750 | return this;
751 | } else {
752 | for (i = 0; i < inputChildren.length; i++) {
753 | if (inputChildren[i].checked === true) return inputChildren[i].value;
754 | }
755 | }
756 | };
757 | self.value = function(value) {
758 | var i;
759 | var inputChildren = self._getInputChildrenArray();
760 | if (value) {
761 | for (i = 0; i < inputChildren.length; i++) {
762 | if (inputChildren[i].value === value) inputChildren[i].checked = true;
763 | }
764 | return this;
765 | } else {
766 | for (i = 0; i < inputChildren.length; i++) {
767 | if (inputChildren[i].checked === true) return inputChildren[i].value;
768 | }
769 | return '';
770 | }
771 | };
772 | return self;
773 | };
774 |
775 | /**
776 | * Creates an <input></input> element in the DOM for text input.
777 | * Use .size() to set the display length of the box.
778 | * Appends to the container node if one is specified, otherwise
779 | * appends to body.
780 | *
781 | * @method createInput
782 | * @param {String} [value] default value of the input box
783 | * @param {String} [type] type of text, ie text, password etc. Defaults to text
784 | * @return {Object|p5.Element} pointer to p5.Element holding created node
785 | * @example
786 | *
787 | * function setup() {
788 | * var inp = createInput('');
789 | * inp.input(myInputEvent);
790 | * }
791 | *
792 | * function myInputEvent() {
793 | * console.log('you are typing: ', this.value());
794 | * }
795 | *
796 | */
797 | p5.prototype.createInput = function(value, type) {
798 | p5._validateParameters('createInput', arguments);
799 | var elt = document.createElement('input');
800 | elt.type = type ? type : 'text';
801 | if (value) elt.value = value;
802 | return addElement(elt, this);
803 | };
804 |
805 | /**
806 | * Creates an <input></input> element in the DOM of type 'file'.
807 | * This allows users to select local files for use in a sketch.
808 | *
809 | * @method createFileInput
810 | * @param {Function} [callback] callback function for when a file loaded
811 | * @param {String} [multiple] optional to allow multiple files selected
812 | * @return {Object|p5.Element} pointer to p5.Element holding created DOM element
813 | * @example
814 | * var input;
815 | * var img;
816 | *
817 | * function setup() {
818 | * input = createFileInput(handleFile);
819 | * input.position(0, 0);
820 | * }
821 | *
822 | * function draw() {
823 | * if (img) {
824 | * image(img, 0, 0, width, height);
825 | * }
826 | * }
827 | *
828 | * function handleFile(file) {
829 | * print(file);
830 | * if (file.type === 'image') {
831 | * img = createImg(file.data);
832 | * img.hide();
833 | * }
834 | * }
835 | */
836 | p5.prototype.createFileInput = function(callback, multiple) {
837 | p5._validateParameters('createFileInput', arguments);
838 | // Function to handle when a file is selected
839 | // We're simplifying life and assuming that we always
840 | // want to load every selected file
841 | function handleFileSelect(evt) {
842 | function makeLoader(theFile) {
843 | // Making a p5.File object
844 | var p5file = new p5.File(theFile);
845 | return function(e) {
846 | p5file.data = e.target.result;
847 | callback(p5file);
848 | };
849 | }
850 | // These are the files
851 | var files = evt.target.files;
852 | // Load each one and trigger a callback
853 | for (var i = 0; i < files.length; i++) {
854 | var f = files[i];
855 | var reader = new FileReader();
856 |
857 | reader.onload = makeLoader(f);
858 |
859 | // Text or data?
860 | // This should likely be improved
861 | if (f.type.indexOf('text') > -1) {
862 | reader.readAsText(f);
863 | } else {
864 | reader.readAsDataURL(f);
865 | }
866 | }
867 | }
868 | // Is the file stuff supported?
869 | if (window.File && window.FileReader && window.FileList && window.Blob) {
870 | // Yup, we're ok and make an input file selector
871 | var elt = document.createElement('input');
872 | elt.type = 'file';
873 |
874 | // If we get a second argument that evaluates to true
875 | // then we are looking for multiple files
876 | if (multiple) {
877 | // Anything gets the job done
878 | elt.multiple = 'multiple';
879 | }
880 |
881 | // Now let's handle when a file was selected
882 | elt.addEventListener('change', handleFileSelect, false);
883 | return addElement(elt, this);
884 | } else {
885 | console.log(
886 | 'The File APIs are not fully supported in this browser. Cannot create element.'
887 | );
888 | }
889 | };
890 |
891 | /** VIDEO STUFF **/
892 |
893 | function createMedia(pInst, type, src, callback) {
894 | var elt = document.createElement(type);
895 |
896 | // allow src to be empty
897 | src = src || '';
898 | if (typeof src === 'string') {
899 | src = [src];
900 | }
901 | for (var i = 0; i < src.length; i++) {
902 | var source = document.createElement('source');
903 | source.src = src[i];
904 | elt.appendChild(source);
905 | }
906 | if (typeof callback !== 'undefined') {
907 | var callbackHandler = function() {
908 | callback();
909 | elt.removeEventListener('canplaythrough', callbackHandler);
910 | };
911 | elt.addEventListener('canplaythrough', callbackHandler);
912 | }
913 |
914 | var c = addElement(elt, pInst, true);
915 | c.loadedmetadata = false;
916 | // set width and height onload metadata
917 | elt.addEventListener('loadedmetadata', function() {
918 | c.width = elt.videoWidth;
919 | c.height = elt.videoHeight;
920 | // set elt width and height if not set
921 | if (c.elt.width === 0) c.elt.width = elt.videoWidth;
922 | if (c.elt.height === 0) c.elt.height = elt.videoHeight;
923 | c.loadedmetadata = true;
924 | });
925 |
926 | return c;
927 | }
928 | /**
929 | * Creates an HTML5 <video> element in the DOM for simple playback
930 | * of audio/video. Shown by default, can be hidden with .hide()
931 | * and drawn into canvas using video(). Appends to the container
932 | * node if one is specified, otherwise appends to body. The first parameter
933 | * can be either a single string path to a video file, or an array of string
934 | * paths to different formats of the same video. This is useful for ensuring
935 | * that your video can play across different browsers, as each supports
936 | * different formats. See this
937 | * page for further information about supported formats.
938 | *
939 | * @method createVideo
940 | * @param {String|Array} src path to a video file, or array of paths for
941 | * supporting different browsers
942 | * @param {Object} [callback] callback function to be called upon
943 | * 'canplaythrough' event fire, that is, when the
944 | * browser can play the media, and estimates that
945 | * enough data has been loaded to play the media
946 | * up to its end without having to stop for
947 | * further buffering of content
948 | * @return {p5.MediaElement|p5.Element} pointer to video p5.Element
949 | * @example
950 | *
951 | * var vid;
952 | * function setup() {
953 | * vid = createVideo(['small.mp4', 'small.ogv', 'small.webm'], vidLoad);
954 | * }
955 | *
956 | * // This function is called when the video loads
957 | * function vidLoad() {
958 | * vid.play();
959 | * }
960 | *
961 | */
962 | p5.prototype.createVideo = function(src, callback) {
963 | p5._validateParameters('createVideo', arguments);
964 | return createMedia(this, 'video', src, callback);
965 | };
966 |
967 | /** AUDIO STUFF **/
968 |
969 | /**
970 | * Creates a hidden HTML5 <audio> element in the DOM for simple audio
971 | * playback. Appends to the container node if one is specified,
972 | * otherwise appends to body. The first parameter
973 | * can be either a single string path to a audio file, or an array of string
974 | * paths to different formats of the same audio. This is useful for ensuring
975 | * that your audio can play across different browsers, as each supports
976 | * different formats. See this
977 | * page for further information about supported formats.
978 | *
979 | * @method createAudio
980 | * @param {String|String[]} [src] path to an audio file, or array of paths
981 | * for supporting different browsers
982 | * @param {Object} [callback] callback function to be called upon
983 | * 'canplaythrough' event fire, that is, when the
984 | * browser can play the media, and estimates that
985 | * enough data has been loaded to play the media
986 | * up to its end without having to stop for
987 | * further buffering of content
988 | * @return {p5.MediaElement|p5.Element} pointer to audio p5.Element /**
989 | * @example
990 | *
991 | * var ele;
992 | * function setup() {
993 | * ele = createAudio('assets/beat.mp3');
994 | *
995 | * // here we set the element to autoplay
996 | * // The element will play as soon
997 | * // as it is able to do so.
998 | * ele.autoplay(true);
999 | * }
1000 | *
1001 | */
1002 | p5.prototype.createAudio = function(src, callback) {
1003 | p5._validateParameters('createAudio', arguments);
1004 | return createMedia(this, 'audio', src, callback);
1005 | };
1006 |
1007 | /** CAMERA STUFF **/
1008 |
1009 | p5.prototype.VIDEO = 'video';
1010 | p5.prototype.AUDIO = 'audio';
1011 |
1012 | // from: https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia
1013 | // Older browsers might not implement mediaDevices at all, so we set an empty object first
1014 | if (navigator.mediaDevices === undefined) {
1015 | navigator.mediaDevices = {};
1016 | }
1017 |
1018 | // Some browsers partially implement mediaDevices. We can't just assign an object
1019 | // with getUserMedia as it would overwrite existing properties.
1020 | // Here, we will just add the getUserMedia property if it's missing.
1021 | if (navigator.mediaDevices.getUserMedia === undefined) {
1022 | navigator.mediaDevices.getUserMedia = function(constraints) {
1023 | // First get ahold of the legacy getUserMedia, if present
1024 | var getUserMedia =
1025 | navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
1026 |
1027 | // Some browsers just don't implement it - return a rejected promise with an error
1028 | // to keep a consistent interface
1029 | if (!getUserMedia) {
1030 | return Promise.reject(
1031 | new Error('getUserMedia is not implemented in this browser')
1032 | );
1033 | }
1034 |
1035 | // Otherwise, wrap the call to the old navigator.getUserMedia with a Promise
1036 | return new Promise(function(resolve, reject) {
1037 | getUserMedia.call(navigator, constraints, resolve, reject);
1038 | });
1039 | };
1040 | }
1041 |
1042 | /**
1043 | * Creates a new <video> element that contains the audio/video feed
1044 | * from a webcam. This can be drawn onto the canvas using video().
1045 | * More specific properties of the feed can be passing in a Constraints object.
1046 | * See the
1047 | * W3C
1048 | * spec for possible properties. Note that not all of these are supported
1049 | * by all browsers.
1050 | * Security note: A new browser security specification requires that getUserMedia,
1051 | * which is behind createCapture(), only works when you're running the code locally,
1052 | * or on HTTPS. Learn more here
1053 | * and here.
1054 | *
1055 | * @method createCapture
1056 | * @param {String|Constant|Object} type type of capture, either VIDEO or
1057 | * AUDIO if none specified, default both,
1058 | * or a Constraints object
1059 | * @param {Function} callback function to be called once
1060 | * stream has loaded
1061 | * @return {Object|p5.Element} capture video p5.Element
1062 | * @example
1063 | *
1064 | * var capture;
1065 | *
1066 | * function setup() {
1067 | * createCanvas(480, 120);
1068 | * capture = createCapture(VIDEO);
1069 | * }
1070 | *
1071 | * function draw() {
1072 | * image(capture, 0, 0, width, width * capture.height / capture.width);
1073 | * filter(INVERT);
1074 | * }
1075 | *
1076 | *
1077 | * function setup() {
1078 | * createCanvas(480, 120);
1079 | * var constraints = {
1080 | * video: {
1081 | * mandatory: {
1082 | * minWidth: 1280,
1083 | * minHeight: 720
1084 | * },
1085 | * optional: [{ maxFrameRate: 10 }]
1086 | * },
1087 | * audio: true
1088 | * };
1089 | * createCapture(constraints, function(stream) {
1090 | * console.log(stream);
1091 | * });
1092 | * }
1093 | *
1094 | */
1095 | p5.prototype.createCapture = function() {
1096 | p5._validateParameters('createCapture', arguments);
1097 | var useVideo = true;
1098 | var useAudio = true;
1099 | var constraints;
1100 | var cb;
1101 | for (var i = 0; i < arguments.length; i++) {
1102 | if (arguments[i] === p5.prototype.VIDEO) {
1103 | useAudio = false;
1104 | } else if (arguments[i] === p5.prototype.AUDIO) {
1105 | useVideo = false;
1106 | } else if (typeof arguments[i] === 'object') {
1107 | constraints = arguments[i];
1108 | } else if (typeof arguments[i] === 'function') {
1109 | cb = arguments[i];
1110 | }
1111 | }
1112 | if (navigator.getUserMedia) {
1113 | var elt = document.createElement('video');
1114 |
1115 | if (!constraints) {
1116 | constraints = { video: useVideo, audio: useAudio };
1117 | }
1118 |
1119 | navigator.mediaDevices.getUserMedia(constraints).then(
1120 | function(stream) {
1121 | try {
1122 | if ('srcObject' in elt) {
1123 | elt.srcObject = stream;
1124 | } else {
1125 | elt.src = window.URL.createObjectURL(stream);
1126 | }
1127 | } catch (err) {
1128 | elt.src = stream;
1129 | }
1130 | if (cb) {
1131 | cb(stream);
1132 | }
1133 | },
1134 | function(e) {
1135 | console.log(e);
1136 | }
1137 | );
1138 | } else {
1139 | throw 'getUserMedia not supported in this browser';
1140 | }
1141 | var c = addElement(elt, this, true);
1142 | c.loadedmetadata = false;
1143 | // set width and height onload metadata
1144 | elt.addEventListener('loadedmetadata', function() {
1145 | elt.play();
1146 | if (elt.width) {
1147 | c.width = elt.videoWidth = elt.width;
1148 | c.height = elt.videoHeight = elt.height;
1149 | } else {
1150 | c.width = c.elt.width = elt.videoWidth;
1151 | c.height = c.elt.height = elt.videoHeight;
1152 | }
1153 | c.loadedmetadata = true;
1154 | });
1155 | return c;
1156 | };
1157 |
1158 | /**
1159 | * Creates element with given tag in the DOM with given content.
1160 | * Appends to the container node if one is specified, otherwise
1161 | * appends to body.
1162 | *
1163 | * @method createElement
1164 | * @param {String} tag tag for the new element
1165 | * @param {String} [content] html content to be inserted into the element
1166 | * @return {Object|p5.Element} pointer to p5.Element holding created node
1167 | * @example
1168 | *
1169 | * createElement('h2', 'im an h2 p5.element!');
1170 | *
1171 | */
1172 | p5.prototype.createElement = function(tag, content) {
1173 | p5._validateParameters('createElement', arguments);
1174 | var elt = document.createElement(tag);
1175 | if (typeof content !== 'undefined') {
1176 | elt.innerHTML = content;
1177 | }
1178 | return addElement(elt, this);
1179 | };
1180 |
1181 | // =============================================================================
1182 | // p5.Element additions
1183 | // =============================================================================
1184 | /**
1185 | *
1186 | * Adds specified class to the element.
1187 | *
1188 | * @for p5.Element
1189 | * @method addClass
1190 | * @param {String} class name of class to add
1191 | * @return {Object|p5.Element}
1192 | * @example
1193 | *
1194 | * var div = createDiv('div');
1195 | * div.addClass('myClass');
1196 | *
1197 | */
1198 | p5.Element.prototype.addClass = function(c) {
1199 | if (this.elt.className) {
1200 | // PEND don't add class more than once
1201 | //var regex = new RegExp('[^a-zA-Z\d:]?'+c+'[^a-zA-Z\d:]?');
1202 | //if (this.elt.className.search(/[^a-zA-Z\d:]?hi[^a-zA-Z\d:]?/) === -1) {
1203 | this.elt.className = this.elt.className + ' ' + c;
1204 | //}
1205 | } else {
1206 | this.elt.className = c;
1207 | }
1208 | return this;
1209 | };
1210 |
1211 | /**
1212 | *
1213 | * Removes specified class from the element.
1214 | *
1215 | * @method removeClass
1216 | * @param {String} class name of class to remove
1217 | * @return {Object|p5.Element} * @example
1218 | *
1219 | * // In this example, a class is set when the div is created
1220 | * // and removed when mouse is pressed. This could link up
1221 | * // with a CSS style rule to toggle style properties.
1222 | *
1223 | * var div;
1224 | *
1225 | * function setup() {
1226 | * div = createDiv('div');
1227 | * div.addClass('myClass');
1228 | * }
1229 | *
1230 | * function mousePressed() {
1231 | * div.removeClass('myClass');
1232 | * }
1233 | *
1234 | */
1235 | p5.Element.prototype.removeClass = function(c) {
1236 | var regex = new RegExp('(?:^|\\s)' + c + '(?!\\S)');
1237 | this.elt.className = this.elt.className.replace(regex, '');
1238 | this.elt.className = this.elt.className.replace(/^\s+|\s+$/g, ''); //prettify (optional)
1239 | return this;
1240 | };
1241 |
1242 | /**
1243 | *
1244 | * Attaches the element as a child to the parent specified.
1245 | * Accepts either a string ID, DOM node, or p5.Element.
1246 | * If no argument is specified, an array of children DOM nodes is returned.
1247 | *
1248 | * @method child
1249 | * @param {String|Object|p5.Element} [child] the ID, DOM node, or p5.Element
1250 | * to add to the current element
1251 | * @return {p5.Element}
1252 | * @example
1253 | *
1254 | * var div0 = createDiv('this is the parent');
1255 | * var div1 = createDiv('this is the child');
1256 | * div0.child(div1); // use p5.Element
1257 | *
1258 | *
1259 | * var div0 = createDiv('this is the parent');
1260 | * var div1 = createDiv('this is the child');
1261 | * div1.id('apples');
1262 | * div0.child('apples'); // use id
1263 | *
1264 | *
1265 | * var div0 = createDiv('this is the parent');
1266 | * var elt = document.getElementById('myChildDiv');
1267 | * div0.child(elt); // use element from page
1268 | *
1269 | */
1270 | p5.Element.prototype.child = function(c) {
1271 | if (typeof c === 'undefined') {
1272 | return this.elt.childNodes;
1273 | }
1274 | if (typeof c === 'string') {
1275 | if (c[0] === '#') {
1276 | c = c.substring(1);
1277 | }
1278 | c = document.getElementById(c);
1279 | } else if (c instanceof p5.Element) {
1280 | c = c.elt;
1281 | }
1282 | this.elt.appendChild(c);
1283 | return this;
1284 | };
1285 |
1286 | /**
1287 | * Centers a p5 Element either vertically, horizontally,
1288 | * or both, relative to its parent or according to
1289 | * the body if the Element has no parent. If no argument is passed
1290 | * the Element is aligned both vertically and horizontally.
1291 | *
1292 | * @method center
1293 | * @param {String} [align] passing 'vertical', 'horizontal' aligns element accordingly
1294 | * @return {Object|p5.Element} pointer to p5.Element
1295 | * @example
1296 | *
1297 | * function setup() {
1298 | * var div = createDiv('').size(10, 10);
1299 | * div.style('background-color', 'orange');
1300 | * div.center();
1301 | * }
1302 | *
1303 | */
1304 | p5.Element.prototype.center = function(align) {
1305 | var style = this.elt.style.display;
1306 | var hidden = this.elt.style.display === 'none';
1307 | var parentHidden = this.parent().style.display === 'none';
1308 | var pos = { x: this.elt.offsetLeft, y: this.elt.offsetTop };
1309 |
1310 | if (hidden) this.show();
1311 |
1312 | this.elt.style.display = 'block';
1313 | this.position(0, 0);
1314 |
1315 | if (parentHidden) this.parent().style.display = 'block';
1316 |
1317 | var wOffset = Math.abs(this.parent().offsetWidth - this.elt.offsetWidth);
1318 | var hOffset = Math.abs(this.parent().offsetHeight - this.elt.offsetHeight);
1319 | var y = pos.y;
1320 | var x = pos.x;
1321 |
1322 | if (align === 'both' || align === undefined) {
1323 | this.position(wOffset / 2, hOffset / 2);
1324 | } else if (align === 'horizontal') {
1325 | this.position(wOffset / 2, y);
1326 | } else if (align === 'vertical') {
1327 | this.position(x, hOffset / 2);
1328 | }
1329 |
1330 | this.style('display', style);
1331 |
1332 | if (hidden) this.hide();
1333 |
1334 | if (parentHidden) this.parent().style.display = 'none';
1335 |
1336 | return this;
1337 | };
1338 |
1339 | /**
1340 | *
1341 | * If an argument is given, sets the inner HTML of the element,
1342 | * replacing any existing html. If true is included as a second
1343 | * argument, html is appended instead of replacing existing html.
1344 | * If no arguments are given, returns
1345 | * the inner HTML of the element.
1346 | *
1347 | * @for p5.Element
1348 | * @method html
1349 | * @param {String} [html] the HTML to be placed inside the element
1350 | * @param {boolean} [append] whether to append HTML to existing
1351 | * @return {Object|p5.Element|String}
1352 | * @example
1353 | *
1354 | * var div = createDiv('').size(100, 100);
1355 | * div.html('hi');
1356 | *
1357 | *
1358 | * var div = createDiv('Hello ').size(100, 100);
1359 | * div.html('World', true);
1360 | *
1361 | */
1362 | p5.Element.prototype.html = function() {
1363 | if (arguments.length === 0) {
1364 | return this.elt.innerHTML;
1365 | } else if (arguments[1]) {
1366 | this.elt.innerHTML += arguments[0];
1367 | return this;
1368 | } else {
1369 | this.elt.innerHTML = arguments[0];
1370 | return this;
1371 | }
1372 | };
1373 |
1374 | /**
1375 | *
1376 | * Sets the position of the element relative to (0, 0) of the
1377 | * window. Essentially, sets position:absolute and left and top
1378 | * properties of style. If no arguments given returns the x and y position
1379 | * of the element in an object.
1380 | *
1381 | * @method position
1382 | * @param {Number} [x] x-position relative to upper left of window
1383 | * @param {Number} [y] y-position relative to upper left of window
1384 | * @return {Object|p5.Element}
1385 | * @example
1386 | *
1387 | * function setup() {
1388 | * var cnv = createCanvas(100, 100);
1389 | * // positions canvas 50px to the right and 100px
1390 | * // below upper left corner of the window
1391 | * cnv.position(50, 100);
1392 | * }
1393 | *
1394 | */
1395 | p5.Element.prototype.position = function() {
1396 | if (arguments.length === 0) {
1397 | return { x: this.elt.offsetLeft, y: this.elt.offsetTop };
1398 | } else {
1399 | this.elt.style.position = 'absolute';
1400 | this.elt.style.left = arguments[0] + 'px';
1401 | this.elt.style.top = arguments[1] + 'px';
1402 | this.x = arguments[0];
1403 | this.y = arguments[1];
1404 | return this;
1405 | }
1406 | };
1407 |
1408 | /* Helper method called by p5.Element.style() */
1409 | p5.Element.prototype._translate = function() {
1410 | this.elt.style.position = 'absolute';
1411 | // save out initial non-translate transform styling
1412 | var transform = '';
1413 | if (this.elt.style.transform) {
1414 | transform = this.elt.style.transform.replace(/translate3d\(.*\)/g, '');
1415 | transform = transform.replace(/translate[X-Z]?\(.*\)/g, '');
1416 | }
1417 | if (arguments.length === 2) {
1418 | this.elt.style.transform =
1419 | 'translate(' + arguments[0] + 'px, ' + arguments[1] + 'px)';
1420 | } else if (arguments.length > 2) {
1421 | this.elt.style.transform =
1422 | 'translate3d(' +
1423 | arguments[0] +
1424 | 'px,' +
1425 | arguments[1] +
1426 | 'px,' +
1427 | arguments[2] +
1428 | 'px)';
1429 | if (arguments.length === 3) {
1430 | this.elt.parentElement.style.perspective = '1000px';
1431 | } else {
1432 | this.elt.parentElement.style.perspective = arguments[3] + 'px';
1433 | }
1434 | }
1435 | // add any extra transform styling back on end
1436 | this.elt.style.transform += transform;
1437 | return this;
1438 | };
1439 |
1440 | /* Helper method called by p5.Element.style() */
1441 | p5.Element.prototype._rotate = function() {
1442 | // save out initial non-rotate transform styling
1443 | var transform = '';
1444 | if (this.elt.style.transform) {
1445 | transform = this.elt.style.transform.replace(/rotate3d\(.*\)/g, '');
1446 | transform = transform.replace(/rotate[X-Z]?\(.*\)/g, '');
1447 | }
1448 |
1449 | if (arguments.length === 1) {
1450 | this.elt.style.transform = 'rotate(' + arguments[0] + 'deg)';
1451 | } else if (arguments.length === 2) {
1452 | this.elt.style.transform =
1453 | 'rotate(' + arguments[0] + 'deg, ' + arguments[1] + 'deg)';
1454 | } else if (arguments.length === 3) {
1455 | this.elt.style.transform = 'rotateX(' + arguments[0] + 'deg)';
1456 | this.elt.style.transform += 'rotateY(' + arguments[1] + 'deg)';
1457 | this.elt.style.transform += 'rotateZ(' + arguments[2] + 'deg)';
1458 | }
1459 | // add remaining transform back on
1460 | this.elt.style.transform += transform;
1461 | return this;
1462 | };
1463 |
1464 | /**
1465 | * Sets the given style (css) property (1st arg) of the element with the
1466 | * given value (2nd arg). If a single argument is given, .style()
1467 | * returns the value of the given property; however, if the single argument
1468 | * is given in css syntax ('text-align:center'), .style() sets the css
1469 | * appropriatly. .style() also handles 2d and 3d css transforms. If
1470 | * the 1st arg is 'rotate', 'translate', or 'position', the following arguments
1471 | * accept Numbers as values. ('translate', 10, 100, 50);
1472 | *
1473 | * @method style
1474 | * @param {String} property property to be set
1475 | * @param {String|Number|p5.Color} [value] value to assign to property (only String|Number for rotate/translate)
1476 | * @param {String|Number|p5.Color} [value2] position can take a 2nd value
1477 | * @param {String|Number|p5.Color} [value3] translate can take a 2nd & 3rd value
1478 | * @return {String|Object|p5.Element} value of property, if no value is specified
1479 | * or p5.Element
1480 | * @example
1481 | *
1482 | * var myDiv = createDiv('I like pandas.');
1483 | * myDiv.style('font-size', '18px');
1484 | * myDiv.style('color', '#ff0000');
1485 | *
1486 | *
1487 | * var col = color(25, 23, 200, 50);
1488 | * var button = createButton('button');
1489 | * button.style('background-color', col);
1490 | * button.position(10, 10);
1491 | *
1492 | *
1493 | * var myDiv = createDiv('I like lizards.');
1494 | * myDiv.style('position', 20, 20);
1495 | * myDiv.style('rotate', 45);
1496 | *
1497 | *
1498 | * var myDiv;
1499 | * function setup() {
1500 | * background(200);
1501 | * myDiv = createDiv('I like gray.');
1502 | * myDiv.position(20, 20);
1503 | * }
1504 | *
1505 | * function draw() {
1506 | * myDiv.style('font-size', mouseX + 'px');
1507 | * }
1508 | *
1509 | */
1510 | p5.Element.prototype.style = function(prop, val) {
1511 | var self = this;
1512 |
1513 | if (val instanceof p5.Color) {
1514 | val =
1515 | 'rgba(' +
1516 | val.levels[0] +
1517 | ',' +
1518 | val.levels[1] +
1519 | ',' +
1520 | val.levels[2] +
1521 | ',' +
1522 | val.levels[3] / 255 +
1523 | ')';
1524 | }
1525 |
1526 | if (typeof val === 'undefined') {
1527 | if (prop.indexOf(':') === -1) {
1528 | var styles = window.getComputedStyle(self.elt);
1529 | var style = styles.getPropertyValue(prop);
1530 | return style;
1531 | } else {
1532 | var attrs = prop.split(';');
1533 | for (var i = 0; i < attrs.length; i++) {
1534 | var parts = attrs[i].split(':');
1535 | if (parts[0] && parts[1]) {
1536 | this.elt.style[parts[0].trim()] = parts[1].trim();
1537 | }
1538 | }
1539 | }
1540 | } else {
1541 | if (prop === 'rotate' || prop === 'translate' || prop === 'position') {
1542 | var trans = Array.prototype.shift.apply(arguments);
1543 | var f = this[trans] || this['_' + trans];
1544 | f.apply(this, arguments);
1545 | } else {
1546 | this.elt.style[prop] = val;
1547 | if (
1548 | prop === 'width' ||
1549 | prop === 'height' ||
1550 | prop === 'left' ||
1551 | prop === 'top'
1552 | ) {
1553 | var numVal = val.replace(/\D+/g, '');
1554 | this[prop] = parseInt(numVal, 10); // pend: is this necessary?
1555 | }
1556 | }
1557 | }
1558 | return this;
1559 | };
1560 |
1561 | /**
1562 | *
1563 | * Adds a new attribute or changes the value of an existing attribute
1564 | * on the specified element. If no value is specified, returns the
1565 | * value of the given attribute, or null if attribute is not set.
1566 | *
1567 | * @method attribute
1568 | * @param {String} attr attribute to set
1569 | * @param {String} [value] value to assign to attribute
1570 | * @return {String|Object|p5.Element} value of attribute, if no value is
1571 | * specified or p5.Element
1572 | * @example
1573 | *
1574 | * var myDiv = createDiv('I like pandas.');
1575 | * myDiv.attribute('align', 'center');
1576 | *
1577 | */
1578 | p5.Element.prototype.attribute = function(attr, value) {
1579 | //handling for checkboxes and radios to ensure options get
1580 | //attributes not divs
1581 | if (
1582 | this.elt.firstChild != null &&
1583 | (this.elt.firstChild.type === 'checkbox' ||
1584 | this.elt.firstChild.type === 'radio')
1585 | ) {
1586 | if (typeof value === 'undefined') {
1587 | return this.elt.firstChild.getAttribute(attr);
1588 | } else {
1589 | for (var i = 0; i < this.elt.childNodes.length; i++) {
1590 | this.elt.childNodes[i].setAttribute(attr, value);
1591 | }
1592 | }
1593 | } else if (typeof value === 'undefined') {
1594 | return this.elt.getAttribute(attr);
1595 | } else {
1596 | this.elt.setAttribute(attr, value);
1597 | return this;
1598 | }
1599 | };
1600 |
1601 | /**
1602 | *
1603 | * Removes an attribute on the specified element.
1604 | *
1605 | * @method removeAttribute
1606 | * @param {String} attr attribute to remove
1607 | * @return {Object|p5.Element}
1608 | *
1609 | * @example
1610 | *
1611 | * var button;
1612 | * var checkbox;
1613 | *
1614 | * function setup() {
1615 | * checkbox = createCheckbox('enable', true);
1616 | * checkbox.changed(enableButton);
1617 | * button = createButton('button');
1618 | * button.position(10, 10);
1619 | * }
1620 | *
1621 | * function enableButton() {
1622 | * if (this.checked()) {
1623 | * // Re-enable the button
1624 | * button.removeAttribute('disabled');
1625 | * } else {
1626 | * // Disable the button
1627 | * button.attribute('disabled', '');
1628 | * }
1629 | * }
1630 | *
1631 | */
1632 | p5.Element.prototype.removeAttribute = function(attr) {
1633 | if (
1634 | this.elt.firstChild != null &&
1635 | (this.elt.firstChild.type === 'checkbox' ||
1636 | this.elt.firstChild.type === 'radio')
1637 | ) {
1638 | for (var i = 0; i < this.elt.childNodes.length; i++) {
1639 | this.elt.childNodes[i].removeAttribute(attr);
1640 | }
1641 | }
1642 | this.elt.removeAttribute(attr);
1643 | return this;
1644 | };
1645 |
1646 | /**
1647 | * Either returns the value of the element if no arguments
1648 | * given, or sets the value of the element.
1649 | *
1650 | * @method value
1651 | * @param {String|Number} [value]
1652 | * @return {String|Object|p5.Element} value of element if no value is specified or p5.Element
1653 | * @example
1654 | *
1655 | * // gets the value
1656 | * var inp;
1657 | * function setup() {
1658 | * inp = createInput('');
1659 | * }
1660 | *
1661 | * function mousePressed() {
1662 | * print(inp.value());
1663 | * }
1664 | *
1665 | *
1666 | * // sets the value
1667 | * var inp;
1668 | * function setup() {
1669 | * inp = createInput('myValue');
1670 | * }
1671 | *
1672 | * function mousePressed() {
1673 | * inp.value('myValue');
1674 | * }
1675 | *
1676 | */
1677 | p5.Element.prototype.value = function() {
1678 | if (arguments.length > 0) {
1679 | this.elt.value = arguments[0];
1680 | return this;
1681 | } else {
1682 | if (this.elt.type === 'range') {
1683 | return parseFloat(this.elt.value);
1684 | } else return this.elt.value;
1685 | }
1686 | };
1687 |
1688 | /**
1689 | *
1690 | * Shows the current element. Essentially, setting display:block for the style.
1691 | *
1692 | * @method show
1693 | * @return {Object|p5.Element}
1694 | * @example
1695 | *
1696 | * var div = createDiv('div');
1697 | * div.style('display', 'none');
1698 | * div.show(); // turns display to block
1699 | *
1700 | */
1701 | p5.Element.prototype.show = function() {
1702 | this.elt.style.display = 'block';
1703 | return this;
1704 | };
1705 |
1706 | /**
1707 | * Hides the current element. Essentially, setting display:none for the style.
1708 | *
1709 | * @method hide
1710 | * @return {Object|p5.Element}
1711 | * @example
1712 | *
1713 | * var div = createDiv('this is a div');
1714 | * div.hide();
1715 | *
1716 | */
1717 | p5.Element.prototype.hide = function() {
1718 | this.elt.style.display = 'none';
1719 | return this;
1720 | };
1721 |
1722 | /**
1723 | *
1724 | * Sets the width and height of the element. AUTO can be used to
1725 | * only adjust one dimension. If no arguments given returns the width and height
1726 | * of the element in an object.
1727 | *
1728 | * @method size
1729 | * @param {Number|Constant} [w] width of the element, either AUTO, or a number
1730 | * @param {Number|Constant} [h] height of the element, either AUTO, or a number
1731 | * @return {Object|p5.Element}
1732 | * @example
1733 | *
1734 | * var div = createDiv('this is a div');
1735 | * div.size(100, 100);
1736 | *
1737 | */
1738 | p5.Element.prototype.size = function(w, h) {
1739 | if (arguments.length === 0) {
1740 | return { width: this.elt.offsetWidth, height: this.elt.offsetHeight };
1741 | } else {
1742 | var aW = w;
1743 | var aH = h;
1744 | var AUTO = p5.prototype.AUTO;
1745 | if (aW !== AUTO || aH !== AUTO) {
1746 | if (aW === AUTO) {
1747 | aW = h * this.width / this.height;
1748 | } else if (aH === AUTO) {
1749 | aH = w * this.height / this.width;
1750 | }
1751 | // set diff for cnv vs normal div
1752 | if (this.elt instanceof HTMLCanvasElement) {
1753 | var j = {};
1754 | var k = this.elt.getContext('2d');
1755 | var prop;
1756 | for (prop in k) {
1757 | j[prop] = k[prop];
1758 | }
1759 | this.elt.setAttribute('width', aW * this._pInst._pixelDensity);
1760 | this.elt.setAttribute('height', aH * this._pInst._pixelDensity);
1761 | this.elt.setAttribute(
1762 | 'style',
1763 | 'width:' + aW + 'px; height:' + aH + 'px'
1764 | );
1765 | this._pInst.scale(
1766 | this._pInst._pixelDensity,
1767 | this._pInst._pixelDensity
1768 | );
1769 | for (prop in j) {
1770 | this.elt.getContext('2d')[prop] = j[prop];
1771 | }
1772 | } else {
1773 | this.elt.style.width = aW + 'px';
1774 | this.elt.style.height = aH + 'px';
1775 | this.elt.width = aW;
1776 | this.elt.height = aH;
1777 | this.width = aW;
1778 | this.height = aH;
1779 | }
1780 |
1781 | this.width = this.elt.offsetWidth;
1782 | this.height = this.elt.offsetHeight;
1783 |
1784 | if (this._pInst) {
1785 | // main canvas associated with p5 instance
1786 | if (this._pInst._curElement.elt === this.elt) {
1787 | this._pInst._setProperty('width', this.elt.offsetWidth);
1788 | this._pInst._setProperty('height', this.elt.offsetHeight);
1789 | }
1790 | }
1791 | }
1792 | return this;
1793 | }
1794 | };
1795 |
1796 | /**
1797 | * Removes the element and deregisters all listeners.
1798 | * @method remove
1799 | * @example
1800 | *
1801 | * var myDiv = createDiv('this is some text');
1802 | * myDiv.remove();
1803 | *
1804 | */
1805 | p5.Element.prototype.remove = function() {
1806 | // deregister events
1807 | for (var ev in this._events) {
1808 | this.elt.removeEventListener(ev, this._events[ev]);
1809 | }
1810 | if (this.elt.parentNode) {
1811 | this.elt.parentNode.removeChild(this.elt);
1812 | }
1813 | delete this;
1814 | };
1815 |
1816 | // =============================================================================
1817 | // p5.MediaElement additions
1818 | // =============================================================================
1819 |
1820 | /**
1821 | * Extends p5.Element to handle audio and video. In addition to the methods
1822 | * of p5.Element, it also contains methods for controlling media. It is not
1823 | * called directly, but p5.MediaElements are created by calling createVideo,
1824 | * createAudio, and createCapture.
1825 | *
1826 | * @class p5.MediaElement
1827 | * @constructor
1828 | * @param {String} elt DOM node that is wrapped
1829 | */
1830 | p5.MediaElement = function(elt, pInst) {
1831 | p5.Element.call(this, elt, pInst);
1832 |
1833 | var self = this;
1834 | this.elt.crossOrigin = 'anonymous';
1835 |
1836 | this._prevTime = 0;
1837 | this._cueIDCounter = 0;
1838 | this._cues = [];
1839 | this._pixelDensity = 1;
1840 | this._modified = false;
1841 |
1842 | /**
1843 | * Path to the media element source.
1844 | *
1845 | * @property src
1846 | * @return {String} src
1847 | * @example
1848 | *
1849 | * var ele;
1850 | *
1851 | * function setup() {
1852 | * background(250);
1853 | *
1854 | * //p5.MediaElement objects are usually created
1855 | * //by calling the createAudio(), createVideo(),
1856 | * //and createCapture() functions.
1857 | *
1858 | * //In this example we create
1859 | * //a new p5.MediaElement via createAudio().
1860 | * ele = createAudio('assets/beat.mp3');
1861 | *
1862 | * //We'll set up our example so that
1863 | * //when you click on the text,
1864 | * //an alert box displays the MediaElement's
1865 | * //src field.
1866 | * textAlign(CENTER);
1867 | * text('Click Me!', width / 2, height / 2);
1868 | * }
1869 | *
1870 | * function mouseClicked() {
1871 | * //here we test if the mouse is over the
1872 | * //canvas element when it's clicked
1873 | * if (mouseX >= 0 && mouseX <= width && mouseY >= 0 && mouseY <= height) {
1874 | * //Show our p5.MediaElement's src field
1875 | * alert(ele.src);
1876 | * }
1877 | * }
1878 | *
1879 | */
1880 | Object.defineProperty(self, 'src', {
1881 | get: function() {
1882 | var firstChildSrc = self.elt.children[0].src;
1883 | var srcVal = self.elt.src === window.location.href ? '' : self.elt.src;
1884 | var ret =
1885 | firstChildSrc === window.location.href ? srcVal : firstChildSrc;
1886 | return ret;
1887 | },
1888 | set: function(newValue) {
1889 | for (var i = 0; i < self.elt.children.length; i++) {
1890 | self.elt.removeChild(self.elt.children[i]);
1891 | }
1892 | var source = document.createElement('source');
1893 | source.src = newValue;
1894 | elt.appendChild(source);
1895 | self.elt.src = newValue;
1896 | self.modified = true;
1897 | }
1898 | });
1899 |
1900 | // private _onended callback, set by the method: onended(callback)
1901 | self._onended = function() {};
1902 | self.elt.onended = function() {
1903 | self._onended(self);
1904 | };
1905 | };
1906 | p5.MediaElement.prototype = Object.create(p5.Element.prototype);
1907 |
1908 | /**
1909 | * Play an HTML5 media element.
1910 | *
1911 | * @method play
1912 | * @return {Object|p5.Element}
1913 | * @example
1914 | *
1915 | * var ele;
1916 | *
1917 | * function setup() {
1918 | * //p5.MediaElement objects are usually created
1919 | * //by calling the createAudio(), createVideo(),
1920 | * //and createCapture() functions.
1921 | *
1922 | * //In this example we create
1923 | * //a new p5.MediaElement via createAudio().
1924 | * ele = createAudio('assets/beat.mp3');
1925 | *
1926 | * background(250);
1927 | * textAlign(CENTER);
1928 | * text('Click to Play!', width / 2, height / 2);
1929 | * }
1930 | *
1931 | * function mouseClicked() {
1932 | * //here we test if the mouse is over the
1933 | * //canvas element when it's clicked
1934 | * if (mouseX >= 0 && mouseX <= width && mouseY >= 0 && mouseY <= height) {
1935 | * //Here we call the play() function on
1936 | * //the p5.MediaElement we created above.
1937 | * //This will start the audio sample.
1938 | * ele.play();
1939 | *
1940 | * background(200);
1941 | * text('You clicked Play!', width / 2, height / 2);
1942 | * }
1943 | * }
1944 | *
1945 | */
1946 | p5.MediaElement.prototype.play = function() {
1947 | if (this.elt.currentTime === this.elt.duration) {
1948 | this.elt.currentTime = 0;
1949 | }
1950 |
1951 | if (this.elt.readyState > 1) {
1952 | this.elt.play();
1953 | } else {
1954 | // in Chrome, playback cannot resume after being stopped and must reload
1955 | this.elt.load();
1956 | this.elt.play();
1957 | }
1958 | return this;
1959 | };
1960 |
1961 | /**
1962 | * Stops an HTML5 media element (sets current time to zero).
1963 | *
1964 | * @method stop
1965 | * @return {Object|p5.Element}
1966 | * @example
1967 | *
1968 | * //This example both starts
1969 | * //and stops a sound sample
1970 | * //when the user clicks the canvas
1971 | *
1972 | * //We will store the p5.MediaElement
1973 | * //object in here
1974 | * var ele;
1975 | *
1976 | * //while our audio is playing,
1977 | * //this will be set to true
1978 | * var sampleIsPlaying = false;
1979 | *
1980 | * function setup() {
1981 | * //Here we create a p5.MediaElement object
1982 | * //using the createAudio() function.
1983 | * ele = createAudio('assets/beat.mp3');
1984 | * background(200);
1985 | * textAlign(CENTER);
1986 | * text('Click to play!', width / 2, height / 2);
1987 | * }
1988 | *
1989 | * function mouseClicked() {
1990 | * //here we test if the mouse is over the
1991 | * //canvas element when it's clicked
1992 | * if (mouseX >= 0 && mouseX <= width && mouseY >= 0 && mouseY <= height) {
1993 | * background(200);
1994 | *
1995 | * if (sampleIsPlaying) {
1996 | * //if the sample is currently playing
1997 | * //calling the stop() function on
1998 | * //our p5.MediaElement will stop
1999 | * //it and reset its current
2000 | * //time to 0 (i.e. it will start
2001 | * //at the beginning the next time
2002 | * //you play it)
2003 | * ele.stop();
2004 | *
2005 | * sampleIsPlaying = false;
2006 | * text('Click to play!', width / 2, height / 2);
2007 | * } else {
2008 | * //loop our sound element until we
2009 | * //call ele.stop() on it.
2010 | * ele.loop();
2011 | *
2012 | * sampleIsPlaying = true;
2013 | * text('Click to stop!', width / 2, height / 2);
2014 | * }
2015 | * }
2016 | * }
2017 | *
2018 | */
2019 | p5.MediaElement.prototype.stop = function() {
2020 | this.elt.pause();
2021 | this.elt.currentTime = 0;
2022 | return this;
2023 | };
2024 |
2025 | /**
2026 | * Pauses an HTML5 media element.
2027 | *
2028 | * @method pause
2029 | * @return {Object|p5.Element}
2030 | * @example
2031 | *
2032 | * //This example both starts
2033 | * //and pauses a sound sample
2034 | * //when the user clicks the canvas
2035 | *
2036 | * //We will store the p5.MediaElement
2037 | * //object in here
2038 | * var ele;
2039 | *
2040 | * //while our audio is playing,
2041 | * //this will be set to true
2042 | * var sampleIsPlaying = false;
2043 | *
2044 | * function setup() {
2045 | * //Here we create a p5.MediaElement object
2046 | * //using the createAudio() function.
2047 | * ele = createAudio('assets/lucky_dragons.mp3');
2048 | * background(200);
2049 | * textAlign(CENTER);
2050 | * text('Click to play!', width / 2, height / 2);
2051 | * }
2052 | *
2053 | * function mouseClicked() {
2054 | * //here we test if the mouse is over the
2055 | * //canvas element when it's clicked
2056 | * if (mouseX >= 0 && mouseX <= width && mouseY >= 0 && mouseY <= height) {
2057 | * background(200);
2058 | *
2059 | * if (sampleIsPlaying) {
2060 | * //Calling pause() on our
2061 | * //p5.MediaElement will stop it
2062 | * //playing, but when we call the
2063 | * //loop() or play() functions
2064 | * //the sample will start from
2065 | * //where we paused it.
2066 | * ele.pause();
2067 | *
2068 | * sampleIsPlaying = false;
2069 | * text('Click to resume!', width / 2, height / 2);
2070 | * } else {
2071 | * //loop our sound element until we
2072 | * //call ele.pause() on it.
2073 | * ele.loop();
2074 | *
2075 | * sampleIsPlaying = true;
2076 | * text('Click to pause!', width / 2, height / 2);
2077 | * }
2078 | * }
2079 | * }
2080 | *
2081 | */
2082 | p5.MediaElement.prototype.pause = function() {
2083 | this.elt.pause();
2084 | return this;
2085 | };
2086 |
2087 | /**
2088 | * Set 'loop' to true for an HTML5 media element, and starts playing.
2089 | *
2090 | * @method loop
2091 | * @return {Object|p5.Element}
2092 | * @example
2093 | *
2094 | * //Clicking the canvas will loop
2095 | * //the audio sample until the user
2096 | * //clicks again to stop it
2097 | *
2098 | * //We will store the p5.MediaElement
2099 | * //object in here
2100 | * var ele;
2101 | *
2102 | * //while our audio is playing,
2103 | * //this will be set to true
2104 | * var sampleIsLooping = false;
2105 | *
2106 | * function setup() {
2107 | * //Here we create a p5.MediaElement object
2108 | * //using the createAudio() function.
2109 | * ele = createAudio('assets/lucky_dragons.mp3');
2110 | * background(200);
2111 | * textAlign(CENTER);
2112 | * text('Click to loop!', width / 2, height / 2);
2113 | * }
2114 | *
2115 | * function mouseClicked() {
2116 | * //here we test if the mouse is over the
2117 | * //canvas element when it's clicked
2118 | * if (mouseX >= 0 && mouseX <= width && mouseY >= 0 && mouseY <= height) {
2119 | * background(200);
2120 | *
2121 | * if (!sampleIsLooping) {
2122 | * //loop our sound element until we
2123 | * //call ele.stop() on it.
2124 | * ele.loop();
2125 | *
2126 | * sampleIsLooping = true;
2127 | * text('Click to stop!', width / 2, height / 2);
2128 | * } else {
2129 | * ele.stop();
2130 | *
2131 | * sampleIsLooping = false;
2132 | * text('Click to loop!', width / 2, height / 2);
2133 | * }
2134 | * }
2135 | * }
2136 | *
2137 | */
2138 | p5.MediaElement.prototype.loop = function() {
2139 | this.elt.setAttribute('loop', true);
2140 | this.play();
2141 | return this;
2142 | };
2143 | /**
2144 | * Set 'loop' to false for an HTML5 media element. Element will stop
2145 | * when it reaches the end.
2146 | *
2147 | * @method noLoop
2148 | * @return {Object|p5.Element}
2149 | * @example
2150 | *
2151 | * //This example both starts
2152 | * //and stops loop of sound sample
2153 | * //when the user clicks the canvas
2154 | *
2155 | * //We will store the p5.MediaElement
2156 | * //object in here
2157 | * var ele;
2158 | * //while our audio is playing,
2159 | * //this will be set to true
2160 | * var sampleIsPlaying = false;
2161 | *
2162 | * function setup() {
2163 | * //Here we create a p5.MediaElement object
2164 | * //using the createAudio() function.
2165 | * ele = createAudio('assets/beat.mp3');
2166 | * background(200);
2167 | * textAlign(CENTER);
2168 | * text('Click to play!', width / 2, height / 2);
2169 | * }
2170 | *
2171 | * function mouseClicked() {
2172 | * //here we test if the mouse is over the
2173 | * //canvas element when it's clicked
2174 | * if (mouseX >= 0 && mouseX <= width && mouseY >= 0 && mouseY <= height) {
2175 | * background(200);
2176 | *
2177 | * if (sampleIsPlaying) {
2178 | * ele.noLoop();
2179 | * text('No more Loops!', width / 2, height / 2);
2180 | * } else {
2181 | * ele.loop();
2182 | * sampleIsPlaying = true;
2183 | * text('Click to stop looping!', width / 2, height / 2);
2184 | * }
2185 | * }
2186 | * }
2187 | *
2188 | *
2189 | */
2190 | p5.MediaElement.prototype.noLoop = function() {
2191 | this.elt.setAttribute('loop', false);
2192 | return this;
2193 | };
2194 |
2195 | /**
2196 | * Set HTML5 media element to autoplay or not.
2197 | *
2198 | * @method autoplay
2199 | * @param {Boolean} autoplay whether the element should autoplay
2200 | * @return {Object|p5.Element}
2201 | */
2202 | p5.MediaElement.prototype.autoplay = function(val) {
2203 | this.elt.setAttribute('autoplay', val);
2204 | return this;
2205 | };
2206 |
2207 | /**
2208 | * Sets volume for this HTML5 media element. If no argument is given,
2209 | * returns the current volume.
2210 | *
2211 | * @param {Number} [val] volume between 0.0 and 1.0
2212 | * @return {Number|p5.MediaElement} current volume or p5.MediaElement
2213 | * @method volume
2214 | *
2215 | * @example
2216 | *
2217 | * var ele;
2218 | * function setup() {
2219 | * // p5.MediaElement objects are usually created
2220 | * // by calling the createAudio(), createVideo(),
2221 | * // and createCapture() functions.
2222 | * // In this example we create
2223 | * // a new p5.MediaElement via createAudio().
2224 | * ele = createAudio('assets/lucky_dragons.mp3');
2225 | * background(250);
2226 | * textAlign(CENTER);
2227 | * text('Click to Play!', width / 2, height / 2);
2228 | * }
2229 | * function mouseClicked() {
2230 | * // Here we call the volume() function
2231 | * // on the sound element to set its volume
2232 | * // Volume must be between 0.0 and 1.0
2233 | * ele.volume(0.2);
2234 | * ele.play();
2235 | * background(200);
2236 | * text('You clicked Play!', width / 2, height / 2);
2237 | * }
2238 | *
2239 | *
2240 | * var audio;
2241 | * var counter = 0;
2242 | *
2243 | * function loaded() {
2244 | * audio.play();
2245 | * }
2246 | *
2247 | * function setup() {
2248 | * audio = createAudio('assets/lucky_dragons.mp3', loaded);
2249 | * textAlign(CENTER);
2250 | * }
2251 | *
2252 | * function draw() {
2253 | * if (counter === 0) {
2254 | * background(0, 255, 0);
2255 | * text('volume(0.9)', width / 2, height / 2);
2256 | * } else if (counter === 1) {
2257 | * background(255, 255, 0);
2258 | * text('volume(0.5)', width / 2, height / 2);
2259 | * } else if (counter === 2) {
2260 | * background(255, 0, 0);
2261 | * text('volume(0.1)', width / 2, height / 2);
2262 | * }
2263 | * }
2264 | *
2265 | * function mousePressed() {
2266 | * counter++;
2267 | * if (counter === 0) {
2268 | * audio.volume(0.9);
2269 | * } else if (counter === 1) {
2270 | * audio.volume(0.5);
2271 | * } else if (counter === 2) {
2272 | * audio.volume(0.1);
2273 | * } else {
2274 | * counter = 0;
2275 | * audio.volume(0.9);
2276 | * }
2277 | * }
2278 | *
2279 | *
2280 | */
2281 | p5.MediaElement.prototype.volume = function(val) {
2282 | if (typeof val === 'undefined') {
2283 | return this.elt.volume;
2284 | } else {
2285 | this.elt.volume = val;
2286 | }
2287 | };
2288 |
2289 | /**
2290 | * If no arguments are given, returns the current playback speed of the
2291 | * element. The speed parameter sets the speed where 2.0 will play the
2292 | * element twice as fast, 0.5 will play at half the speed, and -1 will play
2293 | * the element in normal speed in reverse.(Note that not all browsers support
2294 | * backward playback and even if they do, playback might not be smooth.)
2295 | *
2296 | * @method speed
2297 | * @param {Number} [speed] speed multiplier for element playback
2298 | * @return {Number|Object|p5.MediaElement} current playback speed or p5.MediaElement
2299 | * @example
2300 | *
2301 | * //Clicking the canvas will loop
2302 | * //the audio sample until the user
2303 | * //clicks again to stop it
2304 | *
2305 | * //We will store the p5.MediaElement
2306 | * //object in here
2307 | * var ele;
2308 | * var button;
2309 | *
2310 | * function setup() {
2311 | * createCanvas(710, 400);
2312 | * //Here we create a p5.MediaElement object
2313 | * //using the createAudio() function.
2314 | * ele = createAudio('assets/beat.mp3');
2315 | * ele.loop();
2316 | * background(200);
2317 | *
2318 | * button = createButton('2x speed');
2319 | * button.position(100, 68);
2320 | * button.mousePressed(twice_speed);
2321 | *
2322 | * button = createButton('half speed');
2323 | * button.position(200, 68);
2324 | * button.mousePressed(half_speed);
2325 | *
2326 | * button = createButton('reverse play');
2327 | * button.position(300, 68);
2328 | * button.mousePressed(reverse_speed);
2329 | *
2330 | * button = createButton('STOP');
2331 | * button.position(400, 68);
2332 | * button.mousePressed(stop_song);
2333 | *
2334 | * button = createButton('PLAY!');
2335 | * button.position(500, 68);
2336 | * button.mousePressed(play_speed);
2337 | * }
2338 | *
2339 | * function twice_speed() {
2340 | * ele.speed(2);
2341 | * }
2342 | *
2343 | * function half_speed() {
2344 | * ele.speed(0.5);
2345 | * }
2346 | *
2347 | * function reverse_speed() {
2348 | * ele.speed(-1);
2349 | * }
2350 | *
2351 | * function stop_song() {
2352 | * ele.stop();
2353 | * }
2354 | *
2355 | * function play_speed() {
2356 | * ele.play();
2357 | * }
2358 | *
2359 | */
2360 | p5.MediaElement.prototype.speed = function(val) {
2361 | if (typeof val === 'undefined') {
2362 | return this.elt.playbackRate;
2363 | } else {
2364 | this.elt.playbackRate = val;
2365 | }
2366 | };
2367 |
2368 | /**
2369 | * If no arguments are given, returns the current time of the element.
2370 | * If an argument is given the current time of the element is set to it.
2371 | *
2372 | * @method time
2373 | * @param {Number} [time] time to jump to (in seconds)
2374 | * @return {Number|Object|p5.MediaElement} current time (in seconds)
2375 | * or p5.MediaElement
2376 | * @example
2377 | *
2378 | * var ele;
2379 | * var beginning = true;
2380 | * function setup() {
2381 | * //p5.MediaElement objects are usually created
2382 | * //by calling the createAudio(), createVideo(),
2383 | * //and createCapture() functions.
2384 | *
2385 | * //In this example we create
2386 | * //a new p5.MediaElement via createAudio().
2387 | * ele = createAudio('assets/lucky_dragons.mp3');
2388 | * background(250);
2389 | * textAlign(CENTER);
2390 | * text('start at beginning', width / 2, height / 2);
2391 | * }
2392 | *
2393 | * // this function fires with click anywhere
2394 | * function mousePressed() {
2395 | * if (beginning === true) {
2396 | * // here we start the sound at the beginning
2397 | * // time(0) is not necessary here
2398 | * // as this produces the same result as
2399 | * // play()
2400 | * ele.play().time(0);
2401 | * background(200);
2402 | * text('jump 2 sec in', width / 2, height / 2);
2403 | * beginning = false;
2404 | * } else {
2405 | * // here we jump 2 seconds into the sound
2406 | * ele.play().time(2);
2407 | * background(250);
2408 | * text('start at beginning', width / 2, height / 2);
2409 | * beginning = true;
2410 | * }
2411 | * }
2412 | *
2413 | */
2414 |
2415 | p5.MediaElement.prototype.time = function(val) {
2416 | if (typeof val === 'undefined') {
2417 | return this.elt.currentTime;
2418 | } else {
2419 | this.elt.currentTime = val;
2420 | }
2421 | };
2422 |
2423 | /**
2424 | * Returns the duration of the HTML5 media element.
2425 | *
2426 | * @method duration
2427 | * @return {Number} duration
2428 | *
2429 | * @example
2430 | *
2431 | * var ele;
2432 | * function setup() {
2433 | * //p5.MediaElement objects are usually created
2434 | * //by calling the createAudio(), createVideo(),
2435 | * //and createCapture() functions.
2436 | * //In this example we create
2437 | * //a new p5.MediaElement via createAudio().
2438 | * ele = createAudio('assets/doorbell.mp3');
2439 | * background(250);
2440 | * textAlign(CENTER);
2441 | * text('Click to know the duration!', 10, 25, 70, 80);
2442 | * }
2443 | * function mouseClicked() {
2444 | * ele.play();
2445 | * background(200);
2446 | * //ele.duration dislpays the duration
2447 | * text(ele.duration() + ' seconds', width / 2, height / 2);
2448 | * }
2449 | *
2450 | */
2451 | p5.MediaElement.prototype.duration = function() {
2452 | return this.elt.duration;
2453 | };
2454 | p5.MediaElement.prototype.pixels = [];
2455 | p5.MediaElement.prototype.loadPixels = function() {
2456 | if (!this.canvas) {
2457 | this.canvas = document.createElement('canvas');
2458 | this.drawingContext = this.canvas.getContext('2d');
2459 | }
2460 | if (this.loadedmetadata) {
2461 | // wait for metadata for w/h
2462 | if (this.canvas.width !== this.elt.width) {
2463 | this.canvas.width = this.elt.width;
2464 | this.canvas.height = this.elt.height;
2465 | this.width = this.canvas.width;
2466 | this.height = this.canvas.height;
2467 | }
2468 | this.drawingContext.drawImage(
2469 | this.elt,
2470 | 0,
2471 | 0,
2472 | this.canvas.width,
2473 | this.canvas.height
2474 | );
2475 | p5.Renderer2D.prototype.loadPixels.call(this);
2476 | }
2477 | this.setModified(true);
2478 | return this;
2479 | };
2480 | p5.MediaElement.prototype.updatePixels = function(x, y, w, h) {
2481 | if (this.loadedmetadata) {
2482 | // wait for metadata
2483 | p5.Renderer2D.prototype.updatePixels.call(this, x, y, w, h);
2484 | }
2485 | this.setModified(true);
2486 | return this;
2487 | };
2488 | p5.MediaElement.prototype.get = function(x, y, w, h) {
2489 | if (this.loadedmetadata) {
2490 | // wait for metadata
2491 | return p5.Renderer2D.prototype.get.call(this, x, y, w, h);
2492 | } else if (typeof x === 'undefined') {
2493 | return new p5.Image(1, 1);
2494 | } else if (w > 1) {
2495 | return new p5.Image(x, y, w, h);
2496 | } else {
2497 | return [0, 0, 0, 255];
2498 | }
2499 | };
2500 | p5.MediaElement.prototype.set = function(x, y, imgOrCol) {
2501 | if (this.loadedmetadata) {
2502 | // wait for metadata
2503 | p5.Renderer2D.prototype.set.call(this, x, y, imgOrCol);
2504 | this.setModified(true);
2505 | }
2506 | };
2507 | p5.MediaElement.prototype.copy = function() {
2508 | p5.Renderer2D.prototype.copy.apply(this, arguments);
2509 | };
2510 | p5.MediaElement.prototype.mask = function() {
2511 | this.loadPixels();
2512 | this.setModified(true);
2513 | p5.Image.prototype.mask.apply(this, arguments);
2514 | };
2515 | /**
2516 | * helper method for web GL mode to figure out if the element
2517 | * has been modified and might need to be re-uploaded to texture
2518 | * memory between frames.
2519 | * @method isModified
2520 | * @private
2521 | * @return {boolean} a boolean indicating whether or not the
2522 | * image has been updated or modified since last texture upload.
2523 | */
2524 | p5.MediaElement.prototype.isModified = function() {
2525 | return this._modified;
2526 | };
2527 | /**
2528 | * helper method for web GL mode to indicate that an element has been
2529 | * changed or unchanged since last upload. gl texture upload will
2530 | * set this value to false after uploading the texture; or might set
2531 | * it to true if metadata has become available but there is no actual
2532 | * texture data available yet..
2533 | * @method setModified
2534 | * @param {boolean} val sets whether or not the element has been
2535 | * modified.
2536 | * @private
2537 | */
2538 | p5.MediaElement.prototype.setModified = function(value) {
2539 | this._modified = value;
2540 | };
2541 | /**
2542 | * Schedule an event to be called when the audio or video
2543 | * element reaches the end. If the element is looping,
2544 | * this will not be called. The element is passed in
2545 | * as the argument to the onended callback.
2546 | *
2547 | * @method onended
2548 | * @param {Function} callback function to call when the
2549 | * soundfile has ended. The
2550 | * media element will be passed
2551 | * in as the argument to the
2552 | * callback.
2553 | * @return {Object|p5.MediaElement}
2554 | * @example
2555 | *
2556 | * function setup() {
2557 | * var audioEl = createAudio('assets/beat.mp3');
2558 | * audioEl.showControls();
2559 | * audioEl.onended(sayDone);
2560 | * }
2561 | *
2562 | * function sayDone(elt) {
2563 | * alert('done playing ' + elt.src);
2564 | * }
2565 | *
2566 | */
2567 | p5.MediaElement.prototype.onended = function(callback) {
2568 | this._onended = callback;
2569 | return this;
2570 | };
2571 |
2572 | /*** CONNECT TO WEB AUDIO API / p5.sound.js ***/
2573 |
2574 | /**
2575 | * Send the audio output of this element to a specified audioNode or
2576 | * p5.sound object. If no element is provided, connects to p5's master
2577 | * output. That connection is established when this method is first called.
2578 | * All connections are removed by the .disconnect() method.
2579 | *
2580 | * This method is meant to be used with the p5.sound.js addon library.
2581 | *
2582 | * @method connect
2583 | * @param {AudioNode|Object} audioNode AudioNode from the Web Audio API,
2584 | * or an object from the p5.sound library
2585 | */
2586 | p5.MediaElement.prototype.connect = function(obj) {
2587 | var audioContext, masterOutput;
2588 |
2589 | // if p5.sound exists, same audio context
2590 | if (typeof p5.prototype.getAudioContext === 'function') {
2591 | audioContext = p5.prototype.getAudioContext();
2592 | masterOutput = p5.soundOut.input;
2593 | } else {
2594 | try {
2595 | audioContext = obj.context;
2596 | masterOutput = audioContext.destination;
2597 | } catch (e) {
2598 | throw 'connect() is meant to be used with Web Audio API or p5.sound.js';
2599 | }
2600 | }
2601 |
2602 | // create a Web Audio MediaElementAudioSourceNode if none already exists
2603 | if (!this.audioSourceNode) {
2604 | this.audioSourceNode = audioContext.createMediaElementSource(this.elt);
2605 |
2606 | // connect to master output when this method is first called
2607 | this.audioSourceNode.connect(masterOutput);
2608 | }
2609 |
2610 | // connect to object if provided
2611 | if (obj) {
2612 | if (obj.input) {
2613 | this.audioSourceNode.connect(obj.input);
2614 | } else {
2615 | this.audioSourceNode.connect(obj);
2616 | }
2617 | } else {
2618 | // otherwise connect to master output of p5.sound / AudioContext
2619 | this.audioSourceNode.connect(masterOutput);
2620 | }
2621 | };
2622 |
2623 | /**
2624 | * Disconnect all Web Audio routing, including to master output.
2625 | * This is useful if you want to re-route the output through
2626 | * audio effects, for example.
2627 | *
2628 | * @method disconnect
2629 | */
2630 | p5.MediaElement.prototype.disconnect = function() {
2631 | if (this.audioSourceNode) {
2632 | this.audioSourceNode.disconnect();
2633 | } else {
2634 | throw 'nothing to disconnect';
2635 | }
2636 | };
2637 |
2638 | /*** SHOW / HIDE CONTROLS ***/
2639 |
2640 | /**
2641 | * Show the default MediaElement controls, as determined by the web browser.
2642 | *
2643 | * @method showControls
2644 | * @example
2645 | *
2646 | * var ele;
2647 | * function setup() {
2648 | * //p5.MediaElement objects are usually created
2649 | * //by calling the createAudio(), createVideo(),
2650 | * //and createCapture() functions.
2651 | * //In this example we create
2652 | * //a new p5.MediaElement via createAudio()
2653 | * ele = createAudio('assets/lucky_dragons.mp3');
2654 | * background(200);
2655 | * textAlign(CENTER);
2656 | * text('Click to Show Controls!', 10, 25, 70, 80);
2657 | * }
2658 | * function mousePressed() {
2659 | * ele.showControls();
2660 | * background(200);
2661 | * text('Controls Shown', width / 2, height / 2);
2662 | * }
2663 | *
2664 | */
2665 | p5.MediaElement.prototype.showControls = function() {
2666 | // must set style for the element to show on the page
2667 | this.elt.style['text-align'] = 'inherit';
2668 | this.elt.controls = true;
2669 | };
2670 |
2671 | /**
2672 | * Hide the default mediaElement controls.
2673 | * @method hideControls
2674 | * @example
2675 | *
2676 | * var ele;
2677 | * function setup() {
2678 | * //p5.MediaElement objects are usually created
2679 | * //by calling the createAudio(), createVideo(),
2680 | * //and createCapture() functions.
2681 | * //In this example we create
2682 | * //a new p5.MediaElement via createAudio()
2683 | * ele = createAudio('assets/lucky_dragons.mp3');
2684 | * ele.showControls();
2685 | * background(200);
2686 | * textAlign(CENTER);
2687 | * text('Click to hide Controls!', 10, 25, 70, 80);
2688 | * }
2689 | * function mousePressed() {
2690 | * ele.hideControls();
2691 | * background(200);
2692 | * text('Controls hidden', width / 2, height / 2);
2693 | * }
2694 | *
2695 | */
2696 | p5.MediaElement.prototype.hideControls = function() {
2697 | this.elt.controls = false;
2698 | };
2699 |
2700 | /*** SCHEDULE EVENTS ***/
2701 |
2702 | // Cue inspired by JavaScript setTimeout, and the
2703 | // Tone.js Transport Timeline Event, MIT License Yotam Mann 2015 tonejs.org
2704 | var Cue = function(callback, time, id, val) {
2705 | this.callback = callback;
2706 | this.time = time;
2707 | this.id = id;
2708 | this.val = val;
2709 | };
2710 |
2711 | /**
2712 | * Schedule events to trigger every time a MediaElement
2713 | * (audio/video) reaches a playback cue point.
2714 | *
2715 | * Accepts a callback function, a time (in seconds) at which to trigger
2716 | * the callback, and an optional parameter for the callback.
2717 | *
2718 | * Time will be passed as the first parameter to the callback function,
2719 | * and param will be the second parameter.
2720 | *
2721 | *
2722 | * @method addCue
2723 | * @param {Number} time Time in seconds, relative to this media
2724 | * element's playback. For example, to trigger
2725 | * an event every time playback reaches two
2726 | * seconds, pass in the number 2. This will be
2727 | * passed as the first parameter to
2728 | * the callback function.
2729 | * @param {Function} callback Name of a function that will be
2730 | * called at the given time. The callback will
2731 | * receive time and (optionally) param as its
2732 | * two parameters.
2733 | * @param {Object} [value] An object to be passed as the
2734 | * second parameter to the
2735 | * callback function.
2736 | * @return {Number} id ID of this cue,
2737 | * useful for removeCue(id)
2738 | * @example
2739 | *
2740 | * function setup() {
2741 | * background(255, 255, 255);
2742 | *
2743 | * var audioEl = createAudio('assets/beat.mp3');
2744 | * audioEl.showControls();
2745 | *
2746 | * // schedule three calls to changeBackground
2747 | * audioEl.addCue(0.5, changeBackground, color(255, 0, 0));
2748 | * audioEl.addCue(1.0, changeBackground, color(0, 255, 0));
2749 | * audioEl.addCue(2.5, changeBackground, color(0, 0, 255));
2750 | * audioEl.addCue(3.0, changeBackground, color(0, 255, 255));
2751 | * audioEl.addCue(4.2, changeBackground, color(255, 255, 0));
2752 | * audioEl.addCue(5.0, changeBackground, color(255, 255, 0));
2753 | * }
2754 | *
2755 | * function changeBackground(val) {
2756 | * background(val);
2757 | * }
2758 | *
2759 | */
2760 | p5.MediaElement.prototype.addCue = function(time, callback, val) {
2761 | var id = this._cueIDCounter++;
2762 |
2763 | var cue = new Cue(callback, time, id, val);
2764 | this._cues.push(cue);
2765 |
2766 | if (!this.elt.ontimeupdate) {
2767 | this.elt.ontimeupdate = this._onTimeUpdate.bind(this);
2768 | }
2769 |
2770 | return id;
2771 | };
2772 |
2773 | /**
2774 | * Remove a callback based on its ID. The ID is returned by the
2775 | * addCue method.
2776 | * @method removeCue
2777 | * @param {Number} id ID of the cue, as returned by addCue
2778 | * @example
2779 | *
2780 | * var audioEl, id1, id2;
2781 | * function setup() {
2782 | * background(255, 255, 255);
2783 | * audioEl = createAudio('assets/beat.mp3');
2784 | * audioEl.showControls();
2785 | * // schedule five calls to changeBackground
2786 | * id1 = audioEl.addCue(0.5, changeBackground, color(255, 0, 0));
2787 | * audioEl.addCue(1.0, changeBackground, color(0, 255, 0));
2788 | * audioEl.addCue(2.5, changeBackground, color(0, 0, 255));
2789 | * audioEl.addCue(3.0, changeBackground, color(0, 255, 255));
2790 | * id2 = audioEl.addCue(4.2, changeBackground, color(255, 255, 0));
2791 | * text('Click to remove first and last Cue!', 10, 25, 70, 80);
2792 | * }
2793 | * function mousePressed() {
2794 | * audioEl.removeCue(id1);
2795 | * audioEl.removeCue(id2);
2796 | * }
2797 | * function changeBackground(val) {
2798 | * background(val);
2799 | * }
2800 | *
2801 | */
2802 | p5.MediaElement.prototype.removeCue = function(id) {
2803 | for (var i = 0; i < this._cues.length; i++) {
2804 | if (this._cues[i].id === id) {
2805 | console.log(id);
2806 | this._cues.splice(i, 1);
2807 | }
2808 | }
2809 |
2810 | if (this._cues.length === 0) {
2811 | this.elt.ontimeupdate = null;
2812 | }
2813 | };
2814 |
2815 | /**
2816 | * Remove all of the callbacks that had originally been scheduled
2817 | * via the addCue method.
2818 | * @method clearCues
2819 | * @param {Number} id ID of the cue, as returned by addCue
2820 | * @example
2821 | *
2822 | * var audioEl;
2823 | * function setup() {
2824 | * background(255, 255, 255);
2825 | * audioEl = createAudio('assets/beat.mp3');
2826 | * //Show the default MediaElement controls, as determined by the web browser
2827 | * audioEl.showControls();
2828 | * // schedule calls to changeBackground
2829 | * background(200);
2830 | * text('Click to change Cue!', 10, 25, 70, 80);
2831 | * audioEl.addCue(0.5, changeBackground, color(255, 0, 0));
2832 | * audioEl.addCue(1.0, changeBackground, color(0, 255, 0));
2833 | * audioEl.addCue(2.5, changeBackground, color(0, 0, 255));
2834 | * audioEl.addCue(3.0, changeBackground, color(0, 255, 255));
2835 | * audioEl.addCue(4.2, changeBackground, color(255, 255, 0));
2836 | * }
2837 | * function mousePressed() {
2838 | * // here we clear the scheduled callbacks
2839 | * audioEl.clearCues();
2840 | * // then we add some more callbacks
2841 | * audioEl.addCue(1, changeBackground, color(2, 2, 2));
2842 | * audioEl.addCue(3, changeBackground, color(255, 255, 0));
2843 | * }
2844 | * function changeBackground(val) {
2845 | * background(val);
2846 | * }
2847 | *
2848 | */
2849 | p5.MediaElement.prototype.clearCues = function() {
2850 | this._cues = [];
2851 | this.elt.ontimeupdate = null;
2852 | };
2853 |
2854 | // private method that checks for cues to be fired if events
2855 | // have been scheduled using addCue(callback, time).
2856 | p5.MediaElement.prototype._onTimeUpdate = function() {
2857 | var playbackTime = this.time();
2858 |
2859 | for (var i = 0; i < this._cues.length; i++) {
2860 | var callbackTime = this._cues[i].time;
2861 | var val = this._cues[i].val;
2862 |
2863 | if (this._prevTime < callbackTime && callbackTime <= playbackTime) {
2864 | // pass the scheduled callbackTime as parameter to the callback
2865 | this._cues[i].callback(val);
2866 | }
2867 | }
2868 |
2869 | this._prevTime = playbackTime;
2870 | };
2871 |
2872 | // =============================================================================
2873 | // p5.File
2874 | // =============================================================================
2875 |
2876 | /**
2877 | * Base class for a file
2878 | * Using this for createFileInput
2879 | *
2880 | * @class p5.File
2881 | * @constructor
2882 | * @param {File} file File that is wrapped
2883 | */
2884 | p5.File = function(file, pInst) {
2885 | /**
2886 | * Underlying File object. All normal File methods can be called on this.
2887 | *
2888 | * @property file
2889 | */
2890 | this.file = file;
2891 |
2892 | this._pInst = pInst;
2893 |
2894 | // Splitting out the file type into two components
2895 | // This makes determining if image or text etc simpler
2896 | var typeList = file.type.split('/');
2897 | /**
2898 | * File type (image, text, etc.)
2899 | *
2900 | * @property type
2901 | */
2902 | this.type = typeList[0];
2903 | /**
2904 | * File subtype (usually the file extension jpg, png, xml, etc.)
2905 | *
2906 | * @property subtype
2907 | */
2908 | this.subtype = typeList[1];
2909 | /**
2910 | * File name
2911 | *
2912 | * @property name
2913 | */
2914 | this.name = file.name;
2915 | /**
2916 | * File size
2917 | *
2918 | * @property size
2919 | */
2920 | this.size = file.size;
2921 |
2922 | /**
2923 | * URL string containing image data.
2924 | *
2925 | * @property data
2926 | */
2927 | this.data = undefined;
2928 | };
2929 | });
2930 |
--------------------------------------------------------------------------------
/Back Prop/libs/p5.dom.min.js:
--------------------------------------------------------------------------------
1 | /*! p5.js v0.6.0 January 19, 2018 */ !function(a,b){"function"==typeof define&&define.amd?define("p5.dom",["p5"],function(a){b(a)}):b("object"==typeof exports?require("../p5"):a.p5)}(this,function(a){function b(b){var c=document;return"string"==typeof b&&"#"===b[0]?(b=b.slice(1),c=document.getElementById(b)||document):b instanceof a.Element?c=b.elt:b instanceof HTMLElement&&(c=b),c}function c(b,c,d){(c._userNode?c._userNode:document.body).appendChild(b);var e=d?new a.MediaElement(b):new a.Element(b);return c._elements.push(e),e}function d(a,b,d,e){var f=document.createElement(b);"string"==typeof(d=d||"")&&(d=[d]);for(var g=0;g0&&c.every(function(a){return"INPUT"===a.tagName||"LABEL"===a.tagName})?this.createRadio(new a.Element(b)):new a.Element(b)},a.prototype.removeElements=function(b){a._validateParameters("removeElements",arguments);for(var c=0;c1&&"function"==typeof e[1]?(b.fn=e[1],b.fn()):e.length>1&&"function"==typeof e[2]&&(b.fn=e[2],b.fn())};return d.src=e[0],e.length>1&&"string"==typeof e[1]&&(d.alt=e[1]),d.onload=function(){f()},b=c(d,this)},a.prototype.createA=function(b,d,e){a._validateParameters("createA",arguments);var f=document.createElement("a");return f.href=b,f.innerHTML=d,e&&(f.target=e),c(f,this)},a.prototype.createSlider=function(b,d,e,f){a._validateParameters("createSlider",arguments);var g=document.createElement("input");return g.type="range",g.min=b,g.max=d,0===f?g.step=1e-18:f&&(g.step=f),"number"==typeof e&&(g.value=e),c(g,this)},a.prototype.createButton=function(b,d){a._validateParameters("createButton",arguments);var e=document.createElement("button");return e.innerHTML=b,d&&(e.value=d),c(e,this)},a.prototype.createCheckbox=function(){a._validateParameters("createCheckbox",arguments);var b=document.createElement("div"),d=document.createElement("input");d.type="checkbox",b.appendChild(d);var e=c(b,this);if(e.checked=function(){var a=e.elt.getElementsByTagName("input")[0];if(a){if(0===arguments.length)return a.checked;arguments[0]?a.checked=!0:a.checked=!1}return e},this.value=function(a){return e.value=a,this},arguments[0]){var f=Math.random().toString(36).slice(2),g=document.createElement("label");d.setAttribute("id",f),g.htmlFor=f,e.value(arguments[0]),g.appendChild(document.createTextNode(arguments[0])),b.appendChild(g)}return arguments[1]&&(d.checked=!0),e},a.prototype.createSelect=function(){a._validateParameters("createSelect",arguments);var b,d,e=arguments[0];return"object"==typeof e&&"SELECT"===e.elt.nodeName?(d=e,b=this.elt=e.elt):(b=document.createElement("select"),e&&"boolean"==typeof e&&b.setAttribute("multiple","true"),d=c(b,this)),d.option=function(a,c){for(var d,e=0;e1?f.value=c:f.value=a,b.appendChild(f)}},d.selected=function(a){var b,c=[];if(arguments.length>0){for(b=0;b1){var f=d.length,g=d[0].name,h=d[1].name;e=1;for(var i=1;i-1?h.readAsText(g):h.readAsDataURL(g)}}if(a._validateParameters("createFileInput",arguments),window.File&&window.FileReader&&window.FileList&&window.Blob){var f=document.createElement("input");return f.type="file",d&&(f.multiple="multiple"),f.addEventListener("change",e,!1),c(f,this)}console.log("The File APIs are not fully supported in this browser. Cannot create element.")},a.prototype.createVideo=function(b,c){return a._validateParameters("createVideo",arguments),d(this,"video",b,c)},a.prototype.createAudio=function(b,c){return a._validateParameters("createAudio",arguments),d(this,"audio",b,c)},a.prototype.VIDEO="video",a.prototype.AUDIO="audio",void 0===navigator.mediaDevices&&(navigator.mediaDevices={}),void 0===navigator.mediaDevices.getUserMedia&&(navigator.mediaDevices.getUserMedia=function(a){var b=navigator.webkitGetUserMedia||navigator.mozGetUserMedia;return b?new Promise(function(c,d){b.call(navigator,a,c,d)}):Promise.reject(new Error("getUserMedia is not implemented in this browser"))}),a.prototype.createCapture=function(){a._validateParameters("createCapture",arguments);for(var b,d,e=!0,f=!0,g=0;g2&&(this.elt.style.transform="translate3d("+arguments[0]+"px,"+arguments[1]+"px,"+arguments[2]+"px)",3===arguments.length?this.elt.parentElement.style.perspective="1000px":this.elt.parentElement.style.perspective=arguments[3]+"px"),this.elt.style.transform+=a,this},a.Element.prototype._rotate=function(){var a="";return this.elt.style.transform&&(a=this.elt.style.transform.replace(/rotate3d\(.*\)/g,""),a=a.replace(/rotate[X-Z]?\(.*\)/g,"")),1===arguments.length?this.elt.style.transform="rotate("+arguments[0]+"deg)":2===arguments.length?this.elt.style.transform="rotate("+arguments[0]+"deg, "+arguments[1]+"deg)":3===arguments.length&&(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+=a,this},a.Element.prototype.style=function(b,c){var d=this;if(c instanceof a.Color&&(c="rgba("+c.levels[0]+","+c.levels[1]+","+c.levels[2]+","+c.levels[3]/255+")"),void 0===c){if(-1===b.indexOf(":"))return window.getComputedStyle(d.elt).getPropertyValue(b);for(var e=b.split(";"),f=0;f0?(this.elt.value=arguments[0],this):"range"===this.elt.type?parseFloat(this.elt.value):this.elt.value},a.Element.prototype.show=function(){return this.elt.style.display="block",this},a.Element.prototype.hide=function(){return this.elt.style.display="none",this},a.Element.prototype.size=function(b,c){if(0===arguments.length)return{width:this.elt.offsetWidth,height:this.elt.offsetHeight};var d=b,e=c,f=a.prototype.AUTO;if(d!==f||e!==f){if(d===f?d=c*this.width/this.height:e===f&&(e=b*this.height/this.width),this.elt instanceof HTMLCanvasElement){var g,h={},i=this.elt.getContext("2d");for(g in i)h[g]=i[g];this.elt.setAttribute("width",d*this._pInst._pixelDensity),this.elt.setAttribute("height",e*this._pInst._pixelDensity),this.elt.setAttribute("style","width:"+d+"px; height:"+e+"px"),this._pInst.scale(this._pInst._pixelDensity,this._pInst._pixelDensity);for(g in h)this.elt.getContext("2d")[g]=h[g]}else this.elt.style.width=d+"px",this.elt.style.height=e+"px",this.elt.width=d,this.elt.height=e,this.width=d,this.height=e;this.width=this.elt.offsetWidth,this.height=this.elt.offsetHeight,this._pInst&&this._pInst._curElement.elt===this.elt&&(this._pInst._setProperty("width",this.elt.offsetWidth),this._pInst._setProperty("height",this.elt.offsetHeight))}return this},a.Element.prototype.remove=function(){for(var a in this._events)this.elt.removeEventListener(a,this._events[a]);this.elt.parentNode&&this.elt.parentNode.removeChild(this.elt)},a.MediaElement=function(b,c){a.Element.call(this,b,c);var d=this;this.elt.crossOrigin="anonymous",this._prevTime=0,this._cueIDCounter=0,this._cues=[],this._pixelDensity=1,this._modified=!1,Object.defineProperty(d,"src",{get:function(){var a=d.elt.children[0].src,b=d.elt.src===window.location.href?"":d.elt.src;return a===window.location.href?b:a},set:function(a){for(var c=0;c1?this.elt.play():(this.elt.load(),this.elt.play()),this},a.MediaElement.prototype.stop=function(){return this.elt.pause(),this.elt.currentTime=0,this},a.MediaElement.prototype.pause=function(){return this.elt.pause(),this},a.MediaElement.prototype.loop=function(){return this.elt.setAttribute("loop",!0),this.play(),this},a.MediaElement.prototype.noLoop=function(){return this.elt.setAttribute("loop",!1),this},a.MediaElement.prototype.autoplay=function(a){return this.elt.setAttribute("autoplay",a),this},a.MediaElement.prototype.volume=function(a){if(void 0===a)return this.elt.volume;this.elt.volume=a},a.MediaElement.prototype.speed=function(a){if(void 0===a)return this.elt.playbackRate;this.elt.playbackRate=a},a.MediaElement.prototype.time=function(a){if(void 0===a)return this.elt.currentTime;this.elt.currentTime=a},a.MediaElement.prototype.duration=function(){return this.elt.duration},a.MediaElement.prototype.pixels=[],a.MediaElement.prototype.loadPixels=function(){return this.canvas||(this.canvas=document.createElement("canvas"),this.drawingContext=this.canvas.getContext("2d")),this.loadedmetadata&&(this.canvas.width!==this.elt.width&&(this.canvas.width=this.elt.width,this.canvas.height=this.elt.height,this.width=this.canvas.width,this.height=this.canvas.height),this.drawingContext.drawImage(this.elt,0,0,this.canvas.width,this.canvas.height),a.Renderer2D.prototype.loadPixels.call(this)),this.setModified(!0),this},a.MediaElement.prototype.updatePixels=function(b,c,d,e){return this.loadedmetadata&&a.Renderer2D.prototype.updatePixels.call(this,b,c,d,e),this.setModified(!0),this},a.MediaElement.prototype.get=function(b,c,d,e){return this.loadedmetadata?a.Renderer2D.prototype.get.call(this,b,c,d,e):void 0===b?new a.Image(1,1):d>1?new a.Image(b,c,d,e):[0,0,0,255]},a.MediaElement.prototype.set=function(b,c,d){this.loadedmetadata&&(a.Renderer2D.prototype.set.call(this,b,c,d),this.setModified(!0))},a.MediaElement.prototype.copy=function(){a.Renderer2D.prototype.copy.apply(this,arguments)},a.MediaElement.prototype.mask=function(){this.loadPixels(),this.setModified(!0),a.Image.prototype.mask.apply(this,arguments)},a.MediaElement.prototype.isModified=function(){return this._modified},a.MediaElement.prototype.setModified=function(a){this._modified=a},a.MediaElement.prototype.onended=function(a){return this._onended=a,this},a.MediaElement.prototype.connect=function(b){var c,d;if("function"==typeof a.prototype.getAudioContext)c=a.prototype.getAudioContext(),d=a.soundOut.input;else try{c=b.context,d=c.destination}catch(a){throw"connect() is meant to be used with Web Audio API or p5.sound.js"}this.audioSourceNode||(this.audioSourceNode=c.createMediaElementSource(this.elt),this.audioSourceNode.connect(d)),b?b.input?this.audioSourceNode.connect(b.input):this.audioSourceNode.connect(b):this.audioSourceNode.connect(d)},a.MediaElement.prototype.disconnect=function(){if(!this.audioSourceNode)throw"nothing to disconnect";this.audioSourceNode.disconnect()},a.MediaElement.prototype.showControls=function(){this.elt.style["text-align"]="inherit",this.elt.controls=!0},a.MediaElement.prototype.hideControls=function(){this.elt.controls=!1};var e=function(a,b,c,d){this.callback=a,this.time=b,this.id=c,this.val=d};a.MediaElement.prototype.addCue=function(a,b,c){var d=this._cueIDCounter++,f=new e(b,a,d,c);return this._cues.push(f),this.elt.ontimeupdate||(this.elt.ontimeupdate=this._onTimeUpdate.bind(this)),d},a.MediaElement.prototype.removeCue=function(a){for(var b=0;b softmax(G)[1] ? 0 : 1;
56 |
57 | // Add a score to the predictor
58 | if(gg==theAnswer)
59 | {
60 | scores[i]+=softmax(G)[gg];
61 | }
62 |
63 | // Returns a proper guess, 0 or 1, left or right, black or white depending on how each voted
64 | Guesses[gg]++;
65 | }
66 |
67 | }
68 |
69 | function IniNetwork()
70 | {
71 | var allWeights = [[[[]]],[[[]]]];
72 |
73 | // These are defining our layers that will hold weights, specifically the hidden Layer & Output layer.
74 | allWeights[0] = [[],[],[]];
75 | allWeights[1] = [[],[]];
76 |
77 | // Let's populate the hidden layer with weights!
78 | for ( var j = 0; j < allWeights[0].length; j++)
79 | {
80 | for ( var i = 0; i < 4; i++)
81 | {
82 | // the weights are set here randomly
83 | allWeights[0][j][i] = random();
84 | }
85 | }
86 |
87 | // Let's populate the output layer with weights!
88 | for ( var j = 0; j < allWeights[1].length; j++)
89 | {
90 | for ( var i = 0; i < 4; i++)
91 | {
92 | // the weights are set here randomly
93 | allWeights[1][j][i] = random();
94 | }
95 | }
96 |
97 | return allWeights;
98 | }
99 |
100 | //
101 | function draw() {
102 | background(0);
103 |
104 | textSize(30);
105 | textAlign(CENTER, CENTER);
106 | fill(255);
107 | text("Does White or Black look better over this color?", 350,50);
108 |
109 | textAlign(LEFT, CENTER);
110 | textSize(12);
111 | text("Gen: "+generation+"\nColorsLeft: " + colorsL, 16,95);
112 |
113 | // Plop stores the fitness of all predictors, but this information needs to be presented in a better way
114 | var plop = "";
115 | for ( var i = 0; i < scores.length; i++)
116 | {
117 | plop+=(Math.round(scores[i]*10))/10 + ((i!=scores.length-1) ? "," : "" );
118 | }
119 | textAlign(CENTER, CENTER);
120 | textSize(6);
121 | text(plop, 350,455);
122 |
123 | DrawDisplayer(0);
124 | DrawDisplayer(1);
125 |
126 | fill(255);
127 | ellipse(button[Guesses[0] > Guesses[1] ? 0 : 1][0], 150, 50, 50);
128 | }
129 |
130 | //
131 | function DrawDisplayer(loc)
132 | {
133 | fill(r, g, b);
134 | ellipse(button[loc][0], button[loc][1], diameter, diameter);
135 | fill(loc*255);
136 | textSize(48);
137 | textAlign(CENTER, CENTER);
138 | var t = (loc==0) ? "BLACK\n"+Guesses[0] : "WHITE\n"+Guesses[1];
139 | text(t,button[loc][0], button[loc][1]);
140 | }
141 |
142 | //
143 | function reLU(x)
144 | {
145 | if(x<0)
146 | {
147 | return 0;
148 | }
149 | else
150 | {
151 | return x;
152 | }
153 | }
154 |
155 | //
156 | function InpEncoder(x)
157 | {
158 | return x/255;
159 | }
160 |
161 | //
162 | function softmax(vec) {
163 | VEC = [];
164 |
165 | //
166 | for (var i = 0; i < vec.length; i++) {
167 | VEC[i] = Math.pow(Math.E, vec[i]) / Summation(vec);
168 | }
169 |
170 | return VEC
171 | }
172 |
173 | //
174 | function Summation(vec) {
175 | final = 0;
176 |
177 | for (var i = 0; i < vec.length; i++) {
178 | final += Math.pow(Math.E, (vec[i]))
179 | }
180 |
181 | return final;
182 | }
183 |
184 | //
185 | function Handler()
186 | {
187 | ColorRandomizer();
188 | GetGuess();
189 | colorsL--;
190 |
191 | // New gen if out of colors
192 | if(colorsL <= 0)
193 | {
194 | newGen();
195 | }
196 | }
197 |
198 | // YOURE GOING TO WANT TO PRETTY MUCH THROW THIS FUNCTION INTO THE TRASH & WRITE A GENETIC ALGORITHM THAT IS A LOT LESS LAZIER!
199 | // I highly reccomend Daniel Shiffmans Nature of Code Chapter 9 on Genetic Algorithms, its very thorough! https://www.youtube.com/watch?v=9zfeTw-uFCw&list=PLRqwX-V7Uu6bJM3VgzjNV5YxVxUwzALHV
200 | function newGen()
201 | {
202 |
203 | var topParent = 0, secParent = 0;
204 | var track = [];
205 |
206 | // this loop will give us first place
207 | for ( var i = 0; i < scores.length; i++)
208 | {
209 | if (scores[i] > topParent)
210 | {
211 | topParent = scores[i];
212 | track[0] = i;
213 | }
214 | }
215 |
216 | // this loop will give us second place
217 | for ( var i = 0; i < scores.length; i++)
218 | {
219 | if (scores[i] > secParent && i!=track[0])
220 | {
221 | secParent = scores[i];
222 | track[1] = i;
223 | }
224 | }
225 |
226 | //
227 | if(track[0] == null)
228 | {
229 | track[0] = Math.floor(random(0,scores.length));
230 | }
231 | if(track[1] == null)
232 | {
233 | track[1] = Math.floor(random(0,scores.length));
234 | }
235 |
236 | // THIS IS INCREDIBLY LAZY
237 | // mate first & second place for entire generation (33)
238 | for ( var i = 0; i < predictor[1]; i++)
239 | {
240 | print(colorPredictor, colorPredictor[track[0]],colorPredictor[track[1]]);
241 | colorPredictor[i] = MateTime(colorPredictor[track[0]],colorPredictor[track[1]]);
242 | }
243 |
244 | // repeat all
245 |
246 | generation++;
247 | colorsL = colorsPer[1];
248 |
249 | //
250 | for ( var i = 0; i < scores.length; i++)
251 | {
252 | scores[i] = 0;
253 | }
254 | }
255 |
256 | function MateTime(a,b)
257 | {
258 | // mate top 2, but sometimes randomly not top 2
259 |
260 | var allWeights = [[[[]]],[[[]]]];
261 |
262 | allWeights[0] = [[],[],[]];
263 | allWeights[1] = [[],[]];
264 |
265 | // Let's populate the hidden layer with weights!
266 | for ( var j = 0; j < allWeights[0].length; j++)
267 | {
268 | for ( var i = 0; i < 4; i++)
269 | {
270 | var coin = random();
271 | // the weights are set here randomly
272 | allWeights[0][j][i] = ((coin>.5) ? a[0][j][i] : b[0][j][i]) + random(-.5,.5);
273 | }
274 | }
275 |
276 | // Let's populate the output layer with weights!
277 | for ( var j = 0; j < allWeights[1].length; j++)
278 | {
279 | for ( var i = 0; i < 4; i++)
280 | {
281 | var coin = random();
282 | // the weights are set here randomly
283 | allWeights[1][j][i] = ((coin>.5) ? a[1][j][i] : b[1][j][i]) + random(-.5,.5);
284 | }
285 | }
286 |
287 |
288 |
289 | return allWeights;
290 |
291 | }
292 |
293 | //
294 | function keyPressed() {
295 | if (keyCode === RIGHT_ARROW) {
296 | ColorRandomizer();
297 | GetGuess();
298 |
299 | }
300 | }
301 |
302 |
303 | //
304 | function ColorRandomizer()
305 | {
306 | // Pick colors randomly
307 | r = random(255);
308 | g = random(255);
309 | b = random(255);
310 | }
311 |
312 | // When the user clicks the mouse
313 | function mousePressed() {
314 | // Check if mouse is inside the circle
315 | var d = [];
316 |
317 | for ( var i = 0; i < choices; i++)
318 | {
319 | d[i] = dist(mouseX, mouseY, button[i][0], button[i][1]);
320 | }
321 |
322 | if (d[0] < diameter/2) {
323 | print("FUNCTION FOR BLACK");
324 | theAnswer = 0;
325 |
326 | // Pick new random color values
327 | Handler();
328 | }
329 | else if (d[1] < diameter/2) {
330 | print("FUNCTION FOR WHITE");
331 | theAnswer = 1;
332 | // Pick new random color values
333 | Handler();
334 | }
335 | }
336 |
--------------------------------------------------------------------------------
/Genetic Algorithm/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
Press "Right Arrow" to test a new random color!
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/Genetic Algorithm/libs/p5.dom.min.js:
--------------------------------------------------------------------------------
1 | /*! p5.js v0.6.0 January 19, 2018 */ !function(a,b){"function"==typeof define&&define.amd?define("p5.dom",["p5"],function(a){b(a)}):b("object"==typeof exports?require("../p5"):a.p5)}(this,function(a){function b(b){var c=document;return"string"==typeof b&&"#"===b[0]?(b=b.slice(1),c=document.getElementById(b)||document):b instanceof a.Element?c=b.elt:b instanceof HTMLElement&&(c=b),c}function c(b,c,d){(c._userNode?c._userNode:document.body).appendChild(b);var e=d?new a.MediaElement(b):new a.Element(b);return c._elements.push(e),e}function d(a,b,d,e){var f=document.createElement(b);"string"==typeof(d=d||"")&&(d=[d]);for(var g=0;g0&&c.every(function(a){return"INPUT"===a.tagName||"LABEL"===a.tagName})?this.createRadio(new a.Element(b)):new a.Element(b)},a.prototype.removeElements=function(b){a._validateParameters("removeElements",arguments);for(var c=0;c1&&"function"==typeof e[1]?(b.fn=e[1],b.fn()):e.length>1&&"function"==typeof e[2]&&(b.fn=e[2],b.fn())};return d.src=e[0],e.length>1&&"string"==typeof e[1]&&(d.alt=e[1]),d.onload=function(){f()},b=c(d,this)},a.prototype.createA=function(b,d,e){a._validateParameters("createA",arguments);var f=document.createElement("a");return f.href=b,f.innerHTML=d,e&&(f.target=e),c(f,this)},a.prototype.createSlider=function(b,d,e,f){a._validateParameters("createSlider",arguments);var g=document.createElement("input");return g.type="range",g.min=b,g.max=d,0===f?g.step=1e-18:f&&(g.step=f),"number"==typeof e&&(g.value=e),c(g,this)},a.prototype.createButton=function(b,d){a._validateParameters("createButton",arguments);var e=document.createElement("button");return e.innerHTML=b,d&&(e.value=d),c(e,this)},a.prototype.createCheckbox=function(){a._validateParameters("createCheckbox",arguments);var b=document.createElement("div"),d=document.createElement("input");d.type="checkbox",b.appendChild(d);var e=c(b,this);if(e.checked=function(){var a=e.elt.getElementsByTagName("input")[0];if(a){if(0===arguments.length)return a.checked;arguments[0]?a.checked=!0:a.checked=!1}return e},this.value=function(a){return e.value=a,this},arguments[0]){var f=Math.random().toString(36).slice(2),g=document.createElement("label");d.setAttribute("id",f),g.htmlFor=f,e.value(arguments[0]),g.appendChild(document.createTextNode(arguments[0])),b.appendChild(g)}return arguments[1]&&(d.checked=!0),e},a.prototype.createSelect=function(){a._validateParameters("createSelect",arguments);var b,d,e=arguments[0];return"object"==typeof e&&"SELECT"===e.elt.nodeName?(d=e,b=this.elt=e.elt):(b=document.createElement("select"),e&&"boolean"==typeof e&&b.setAttribute("multiple","true"),d=c(b,this)),d.option=function(a,c){for(var d,e=0;e1?f.value=c:f.value=a,b.appendChild(f)}},d.selected=function(a){var b,c=[];if(arguments.length>0){for(b=0;b1){var f=d.length,g=d[0].name,h=d[1].name;e=1;for(var i=1;i-1?h.readAsText(g):h.readAsDataURL(g)}}if(a._validateParameters("createFileInput",arguments),window.File&&window.FileReader&&window.FileList&&window.Blob){var f=document.createElement("input");return f.type="file",d&&(f.multiple="multiple"),f.addEventListener("change",e,!1),c(f,this)}console.log("The File APIs are not fully supported in this browser. Cannot create element.")},a.prototype.createVideo=function(b,c){return a._validateParameters("createVideo",arguments),d(this,"video",b,c)},a.prototype.createAudio=function(b,c){return a._validateParameters("createAudio",arguments),d(this,"audio",b,c)},a.prototype.VIDEO="video",a.prototype.AUDIO="audio",void 0===navigator.mediaDevices&&(navigator.mediaDevices={}),void 0===navigator.mediaDevices.getUserMedia&&(navigator.mediaDevices.getUserMedia=function(a){var b=navigator.webkitGetUserMedia||navigator.mozGetUserMedia;return b?new Promise(function(c,d){b.call(navigator,a,c,d)}):Promise.reject(new Error("getUserMedia is not implemented in this browser"))}),a.prototype.createCapture=function(){a._validateParameters("createCapture",arguments);for(var b,d,e=!0,f=!0,g=0;g2&&(this.elt.style.transform="translate3d("+arguments[0]+"px,"+arguments[1]+"px,"+arguments[2]+"px)",3===arguments.length?this.elt.parentElement.style.perspective="1000px":this.elt.parentElement.style.perspective=arguments[3]+"px"),this.elt.style.transform+=a,this},a.Element.prototype._rotate=function(){var a="";return this.elt.style.transform&&(a=this.elt.style.transform.replace(/rotate3d\(.*\)/g,""),a=a.replace(/rotate[X-Z]?\(.*\)/g,"")),1===arguments.length?this.elt.style.transform="rotate("+arguments[0]+"deg)":2===arguments.length?this.elt.style.transform="rotate("+arguments[0]+"deg, "+arguments[1]+"deg)":3===arguments.length&&(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+=a,this},a.Element.prototype.style=function(b,c){var d=this;if(c instanceof a.Color&&(c="rgba("+c.levels[0]+","+c.levels[1]+","+c.levels[2]+","+c.levels[3]/255+")"),void 0===c){if(-1===b.indexOf(":"))return window.getComputedStyle(d.elt).getPropertyValue(b);for(var e=b.split(";"),f=0;f0?(this.elt.value=arguments[0],this):"range"===this.elt.type?parseFloat(this.elt.value):this.elt.value},a.Element.prototype.show=function(){return this.elt.style.display="block",this},a.Element.prototype.hide=function(){return this.elt.style.display="none",this},a.Element.prototype.size=function(b,c){if(0===arguments.length)return{width:this.elt.offsetWidth,height:this.elt.offsetHeight};var d=b,e=c,f=a.prototype.AUTO;if(d!==f||e!==f){if(d===f?d=c*this.width/this.height:e===f&&(e=b*this.height/this.width),this.elt instanceof HTMLCanvasElement){var g,h={},i=this.elt.getContext("2d");for(g in i)h[g]=i[g];this.elt.setAttribute("width",d*this._pInst._pixelDensity),this.elt.setAttribute("height",e*this._pInst._pixelDensity),this.elt.setAttribute("style","width:"+d+"px; height:"+e+"px"),this._pInst.scale(this._pInst._pixelDensity,this._pInst._pixelDensity);for(g in h)this.elt.getContext("2d")[g]=h[g]}else this.elt.style.width=d+"px",this.elt.style.height=e+"px",this.elt.width=d,this.elt.height=e,this.width=d,this.height=e;this.width=this.elt.offsetWidth,this.height=this.elt.offsetHeight,this._pInst&&this._pInst._curElement.elt===this.elt&&(this._pInst._setProperty("width",this.elt.offsetWidth),this._pInst._setProperty("height",this.elt.offsetHeight))}return this},a.Element.prototype.remove=function(){for(var a in this._events)this.elt.removeEventListener(a,this._events[a]);this.elt.parentNode&&this.elt.parentNode.removeChild(this.elt)},a.MediaElement=function(b,c){a.Element.call(this,b,c);var d=this;this.elt.crossOrigin="anonymous",this._prevTime=0,this._cueIDCounter=0,this._cues=[],this._pixelDensity=1,this._modified=!1,Object.defineProperty(d,"src",{get:function(){var a=d.elt.children[0].src,b=d.elt.src===window.location.href?"":d.elt.src;return a===window.location.href?b:a},set:function(a){for(var c=0;c1?this.elt.play():(this.elt.load(),this.elt.play()),this},a.MediaElement.prototype.stop=function(){return this.elt.pause(),this.elt.currentTime=0,this},a.MediaElement.prototype.pause=function(){return this.elt.pause(),this},a.MediaElement.prototype.loop=function(){return this.elt.setAttribute("loop",!0),this.play(),this},a.MediaElement.prototype.noLoop=function(){return this.elt.setAttribute("loop",!1),this},a.MediaElement.prototype.autoplay=function(a){return this.elt.setAttribute("autoplay",a),this},a.MediaElement.prototype.volume=function(a){if(void 0===a)return this.elt.volume;this.elt.volume=a},a.MediaElement.prototype.speed=function(a){if(void 0===a)return this.elt.playbackRate;this.elt.playbackRate=a},a.MediaElement.prototype.time=function(a){if(void 0===a)return this.elt.currentTime;this.elt.currentTime=a},a.MediaElement.prototype.duration=function(){return this.elt.duration},a.MediaElement.prototype.pixels=[],a.MediaElement.prototype.loadPixels=function(){return this.canvas||(this.canvas=document.createElement("canvas"),this.drawingContext=this.canvas.getContext("2d")),this.loadedmetadata&&(this.canvas.width!==this.elt.width&&(this.canvas.width=this.elt.width,this.canvas.height=this.elt.height,this.width=this.canvas.width,this.height=this.canvas.height),this.drawingContext.drawImage(this.elt,0,0,this.canvas.width,this.canvas.height),a.Renderer2D.prototype.loadPixels.call(this)),this.setModified(!0),this},a.MediaElement.prototype.updatePixels=function(b,c,d,e){return this.loadedmetadata&&a.Renderer2D.prototype.updatePixels.call(this,b,c,d,e),this.setModified(!0),this},a.MediaElement.prototype.get=function(b,c,d,e){return this.loadedmetadata?a.Renderer2D.prototype.get.call(this,b,c,d,e):void 0===b?new a.Image(1,1):d>1?new a.Image(b,c,d,e):[0,0,0,255]},a.MediaElement.prototype.set=function(b,c,d){this.loadedmetadata&&(a.Renderer2D.prototype.set.call(this,b,c,d),this.setModified(!0))},a.MediaElement.prototype.copy=function(){a.Renderer2D.prototype.copy.apply(this,arguments)},a.MediaElement.prototype.mask=function(){this.loadPixels(),this.setModified(!0),a.Image.prototype.mask.apply(this,arguments)},a.MediaElement.prototype.isModified=function(){return this._modified},a.MediaElement.prototype.setModified=function(a){this._modified=a},a.MediaElement.prototype.onended=function(a){return this._onended=a,this},a.MediaElement.prototype.connect=function(b){var c,d;if("function"==typeof a.prototype.getAudioContext)c=a.prototype.getAudioContext(),d=a.soundOut.input;else try{c=b.context,d=c.destination}catch(a){throw"connect() is meant to be used with Web Audio API or p5.sound.js"}this.audioSourceNode||(this.audioSourceNode=c.createMediaElementSource(this.elt),this.audioSourceNode.connect(d)),b?b.input?this.audioSourceNode.connect(b.input):this.audioSourceNode.connect(b):this.audioSourceNode.connect(d)},a.MediaElement.prototype.disconnect=function(){if(!this.audioSourceNode)throw"nothing to disconnect";this.audioSourceNode.disconnect()},a.MediaElement.prototype.showControls=function(){this.elt.style["text-align"]="inherit",this.elt.controls=!0},a.MediaElement.prototype.hideControls=function(){this.elt.controls=!1};var e=function(a,b,c,d){this.callback=a,this.time=b,this.id=c,this.val=d};a.MediaElement.prototype.addCue=function(a,b,c){var d=this._cueIDCounter++,f=new e(b,a,d,c);return this._cues.push(f),this.elt.ontimeupdate||(this.elt.ontimeupdate=this._onTimeUpdate.bind(this)),d},a.MediaElement.prototype.removeCue=function(a){for(var b=0;b