├── LICENSE ├── README.md ├── clocks ├── clock-01.js ├── clock-02.js ├── clock-03.js ├── clock-04.js ├── clock-05.js ├── clock-06.js ├── clock-07.js ├── clock-08.js ├── clock-09.js ├── clock-10.js ├── clock-11.js └── clock-12.js ├── css └── main.css ├── index.html ├── libraries ├── p5.dom.js ├── p5.js └── p5.sound.js ├── resources ├── font.js └── font_shapes.js └── sketch.js /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Daniel Shiffman 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 12 o'clocks 2 | 3 | This repository hosts a collaborative p5.js port of John Maeda's 12 o'clocks, 4 | originally written for Mac OS 9 and released in 1997. Due to the Power PC 5 | hardware that Mac OS 9 ran on, it is very hard to emulate the original artwork 6 | and without access to a Mac of the era it is difficult to view. Therefore, we 7 | are collaborating to bring John Maeda's 12 o'clocks to the web in p5.js. The 8 | current version is available to be viewed at this repository's [GitHub Pages][0] 9 | 10 | ## Resources 11 | 12 | Possibly the most useful resource for this is a post from Golan Levin on the 13 | [12 o'clocks][1]. Golan Levin has created some very useful GIFs of all 12 of 14 | the clocks in action. 15 | 16 | There is also a useful [video][2] on vimeo from John Maeda including each of 17 | the 12'oclocks in acton (From around 4:00 through to 6:20). 18 | 19 | If you can emulate Mac OS 9 or have a working Mac of the era, the software is 20 | still available from John Maeda's [website][3] 21 | 22 | ## Contributing 23 | 24 | It would be great if you want to help out and recreate the 12 o'clocks, or if 25 | you have any more useful resources for the project. If you are new to git, you 26 | can check out [Git and GitHub for Poets][2], otherwise feel free to fork this 27 | repository and create a Pull Request with your changes. Need help? Feel free 28 | to raise an issue if there's anything we can help you with! 29 | 30 | Each clock has its own sketch file in the clocks folder, numbered 01 to 12 31 | based on the position of the clock in the grid. 32 | 33 | 34 | [0]: https://codingtrain.github.io/12oclocks 35 | [1]: http://cmuems.com/2016/60212/lectures/lecture-09-09b-clocks/maedas-clocks/ 36 | [2]: https://vimeo.com/124707805 37 | [3]: https://maedastudio.com/2004/rbooks2k/twelve.html 38 | -------------------------------------------------------------------------------- /clocks/clock-01.js: -------------------------------------------------------------------------------- 1 | // This is the pendulum clock 2 | var clock01 = function(sketch) { 3 | sketch.setup = function() { 4 | 5 | } 6 | 7 | sketch.draw = function() { 8 | sketch.background(0); 9 | 10 | let angle = sketch.map(sketch.millis() % 2000, 0, 2000, 0, sketch.PI * 2); 11 | 12 | sketch.translate(sketch.width/2, 0); 13 | 14 | sketch.push(); 15 | { 16 | sketch.rotate(sketch.sin(angle) * 0.5); 17 | sketch.translate(0, sketch.height - 50); 18 | 19 | sketch.fill(255); 20 | drawClock(sketch.hour(), sketch.minute()); 21 | } 22 | sketch.pop(); 23 | 24 | sketch.push(); 25 | { 26 | sketch.rotate(-sketch.sin(angle) * 0.5); 27 | sketch.translate(0, sketch.height - 50); 28 | 29 | sketch.fill(128); 30 | drawPeriod(sketch.hour()); 31 | } 32 | sketch.pop(); 33 | } 34 | 35 | function drawClock(hour, minute) { 36 | hour %= 12; 37 | if (hour == 0) { 38 | hour = 12; 39 | } 40 | 41 | if (hour >= 10) { 42 | drawCharacter(sketch.floor(hour / 10), -60, 0); 43 | } 44 | drawCharacter(hour % 10, -30, 0); 45 | 46 | drawCharacter(':', 0, 0); 47 | 48 | drawCharacter(sketch.floor(minute / 10), 30, 0); 49 | drawCharacter(minute % 10, 60, 0); 50 | } 51 | 52 | function drawPeriod(hour) { 53 | if (hour < 12) { 54 | drawCharacter('A', -15, 0); 55 | } else { 56 | drawCharacter('P', -15, 0); 57 | } 58 | drawCharacter('M', 15, 0); 59 | } 60 | 61 | function drawCharacter(character, x, y) { 62 | let dots = font[character]; 63 | for (let row = 0; row < dots.length; row++) { 64 | for (let col = 0; col < dots[row].length; col++) { 65 | if (dots[row][col] === 1) 66 | sketch.ellipse(x + 5 * col - 10, y + 5 * row - 15, 5, 5); 67 | } 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /clocks/clock-02.js: -------------------------------------------------------------------------------- 1 | // This is the overlaid circles clock 2 | var clock02 = function(sketch) { 3 | sketch.setup = function() { 4 | 5 | } 6 | 7 | sketch.draw = function() { 8 | let strokeWeight = 2; 9 | let radius = 15; 10 | sketch.background(0); 11 | sketch.fill(255); 12 | 13 | // set up current working area 14 | // the clock is 11 cells wide by 7 cells tall 15 | if(sketch.width / sketch.height > 11/7) 16 | { 17 | // restrict based on height 18 | // make the sketch have 10 cells of height (7 for the clock, 3 for edge padding) 19 | radius = sketch.height / 10; 20 | } 21 | else{ 22 | // restrict based on width 23 | // make the sketch have 14 cells of width (11 for the clock, 3 for edge padding) 24 | radius = sketch.width / 14; 25 | } 26 | if(radius < 15) 27 | strokeWeight = 1; 28 | 29 | let centerX = sketch.width/2 + radius/2; 30 | let centerY = sketch.height/2 + radius/2; 31 | sketch.strokeWeight(strokeWeight); 32 | sketch.ellipseMode(sketch.CENTER); 33 | DrawHour(sketch, sketch.hour(), centerX, centerY, radius, strokeWeight); 34 | DrawMinute(sketch, sketch.minute(), centerX, centerY, radius, strokeWeight); 35 | DrawSecond(sketch, sketch.second(), centerX, centerY, radius, strokeWeight); 36 | } 37 | /** 38 | * Draw the hour value. If the hour is less than 10, then the value will be centered. 39 | * 40 | * @param {p5} sketch Current sketch 41 | * @param {number} hour Target value 42 | * @param {number} centerX Current center offset for x 43 | * @param {number} centerY Current center offset for y 44 | * @param {number} cellWidth Width for a single dot to occupy 45 | * @param {number} strokeWidth Current strokeWidth, used for padding 46 | */ 47 | function DrawHour(sketch, hour, centerX, centerY, cellWidth, strokeWidth) 48 | { 49 | hour = hour % 12 === 0 ? 12 : hour % 12;; 50 | let ones = hour%10; 51 | sketch.stroke(255,0,0); 52 | sketch.noFill(); 53 | if(hour>=10) 54 | { 55 | let tens = Math.floor(hour/10); 56 | DrawCharacter(sketch, tens, centerX - (cellWidth * 5.5), centerY - Math.floor((cellWidth * 7)/2), cellWidth, cellWidth); 57 | DrawCharacter(sketch, ones, centerX + (cellWidth/2), centerY - Math.floor((cellWidth * 7)/2), cellWidth, cellWidth); 58 | } 59 | else 60 | { 61 | DrawCharacter(sketch, ones, Math.floor((centerX - (cellWidth * 5 / 2))), centerY - Math.floor((cellWidth * 7)/2), cellWidth, cellWidth); 62 | } 63 | } 64 | 65 | /** 66 | * Draw the minute value. 67 | * 68 | * @param {p5} sketch Current sketch 69 | * @param {number} minute Target value 70 | * @param {number} centerX Current center offset for x 71 | * @param {number} centerY Current center offset for y 72 | * @param {number} cellWidth Width for a single dot to occupy 73 | * @param {number} strokeWidth Current strokeWidth, used for padding 74 | */ 75 | function DrawMinute(sketch, minute, centerX, centerY, cellWidth, strokeWidth) 76 | { 77 | let ones = minute%10; 78 | let tens = Math.floor(minute/10); 79 | sketch.stroke(255,255,0); 80 | sketch.noFill(); 81 | 82 | DrawCharacter(sketch, tens, centerX - (cellWidth * 5.5), centerY - Math.floor((cellWidth * 7)/2), cellWidth, cellWidth-(strokeWidth*3)); 83 | DrawCharacter(sketch, ones, centerX + (cellWidth/2), centerY - Math.floor((cellWidth * 7)/2), cellWidth, cellWidth-(strokeWidth*3)); 84 | } 85 | 86 | /** 87 | * Draw the minute value. 88 | * 89 | * @param {p5} sketch Current sketch 90 | * @param {number} second Target value 91 | * @param {number} centerX Current center offset for x 92 | * @param {number} centerY Current center offset for y 93 | * @param {number} cellWidth Width for a single dot to occupy 94 | * @param {number} strokeWidth Current strokeWidth, used for padding 95 | */ 96 | function DrawSecond(sketch, second, centerX, centerY, cellWidth, strokeWidth) 97 | { 98 | let ones = second%10; 99 | let tens = Math.floor(second/10); 100 | sketch.stroke(0,0,255); 101 | sketch.noFill(); 102 | 103 | DrawCharacter(sketch, tens, centerX - (cellWidth * 5.5), centerY - Math.floor((cellWidth * 7)/2), cellWidth, cellWidth -(strokeWidth*6)); 104 | DrawCharacter(sketch, ones, centerX + (cellWidth/2), centerY - Math.floor((cellWidth * 7)/2), cellWidth, cellWidth -(strokeWidth*6)); 105 | } 106 | /** 107 | * 108 | * 109 | * @param {p5} sketch Current sketch 110 | * @param {number|string} char The SINGLE CHARACTER to draw 111 | * @param {number} offsetX Current offset for x 112 | * @param {number} offsetY Current offset for y 113 | * @param {number} cellWidth Width for a single dot to occupy 114 | * @param {number} radius Current strokeWidth, used for padding 115 | */ 116 | function DrawCharacter(sketch, char, offsetX, offsetY, cellWidth, radius) 117 | { 118 | let dots = font[char]; 119 | for(let y = 0; y < dots.length; y++) 120 | for(let x = 0; x < dots[y].length; x++) 121 | if(dots[y][x] === 1) 122 | sketch.ellipse(x*cellWidth + offsetX, y*cellWidth + offsetY, radius); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /clocks/clock-03.js: -------------------------------------------------------------------------------- 1 | // This is the double circle clock 2 | var clock03 = function(sketch) { 3 | // "radial grid" parameters 4 | const radial_step = 360/(6*6); // 6 characters of (5 width + 1 "blank" between each) 5 | 6 | sketch.setup = function() { 7 | sketch.angleMode(sketch.DEGREES); 8 | sketch.noStroke(); 9 | 10 | } 11 | 12 | sketch.draw = function() { 13 | // disc divided in 10 parts (top line empty, 7 others for the digit and the two smaller diameters empty) 14 | let diameter_step = sketch.min(sketch.height, sketch.width) / 10; 15 | // angle of rotation of the display 16 | let angle = sketch.map(sketch.millis() % 60000, 0, 60000, 0, 360); 17 | 18 | // Get current time 19 | let h = sketch.hour() % 12 === 0 ? 12 : sketch.hour() % 12; 20 | let m = sketch.minute(); 21 | // convert to string and add space before minutes and hours if they are less than two characters 22 | if (h < 10) { 23 | h = ' '+h; 24 | } 25 | if (m < 10) { 26 | m = '0'+m; 27 | } 28 | 29 | // final text to display, for example "12:34 " 30 | let text = h+':'+m+' '; 31 | 32 | // draw 33 | sketch.background(0); 34 | for (let i = 6; i >= 0; i--) { // i loop all 7 lines of a character in reverse 35 | for (let j = 0; j < 5; j++) { // j loop all 5 columns of a character 36 | for (let k = 0; k < 6; k++) { // k loop all characters of the text 37 | sketch.fill(255); 38 | // skip arcs that are not needed 39 | if (text[k] == ' ' || font[text[k]][6-i][j] == 0) { 40 | continue; 41 | } 42 | 43 | // a little padding to the arc to reduce gaps 44 | let hasRightNeighbour = (j+1 < 5 && font[text[k]][6-i][j+1] == 1); 45 | let padding = hasRightNeighbour ? 1 : 0; 46 | 47 | // display left arc, layer by layer for each columns so only a small "square" is left to be seen 48 | sketch.arc(0, sketch.height/2, // arc centered on the left center of the screen 49 | diameter_step*(i+3), diameter_step*(i+3), // with a diameter corresponding to the line i of the caracter 50 | radial_step*(angle + k*6 + j), // starting at the current rotation + character position k + column j of the caracter 51 | radial_step*(angle + k*6 + j+1)+padding); // ending +1 further 52 | //...and right arc 53 | sketch.arc(sketch.width, sketch.height/2, diameter_step*(i+3), diameter_step*(i+3), radial_step*(angle+k*6+j), radial_step*(angle+k*6+j+1)+padding); 54 | } 55 | sketch.fill(0); 56 | } 57 | sketch.ellipse(0, sketch.height/2, diameter_step*(i+2) - 1); 58 | sketch.ellipse(sketch.width, sketch.height/2, diameter_step*(i+2) - 1); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /clocks/clock-04.js: -------------------------------------------------------------------------------- 1 | // This is the pixelated clock with colourized history 2 | var clock04 = function(sketch) { 3 | let clock = []; 4 | let baseWidth = 400; 5 | 6 | let numTimes = 25; 7 | 8 | let charWidthBase = 5; 9 | let charHeightBase = 5; 10 | let skewBase = 1.5; 11 | 12 | let idx = numTimes - 1; 13 | 14 | for (let i = 0; i < numTimes; i++) { 15 | clock.push(null); 16 | } 17 | 18 | sketch.draw = function() { 19 | catchUp(); 20 | 21 | sketch.background(0); 22 | 23 | let scale = sketch.width / baseWidth; 24 | 25 | let charWidth = charWidthBase * scale; 26 | let charHeight = charHeightBase * scale; 27 | let skew = skewBase * scale; 28 | 29 | sketch.colorMode(sketch.HSB); 30 | 31 | 32 | // Translate so the clock ends up in the right spot 33 | let translateX = (sketch.width/2 - 25.5 * charWidth) + 2 * numTimes * charWidth; 34 | let translateY = (sketch.height/2 - 7 * charHeight) - 2 * numTimes * charHeight; 35 | sketch.translate(translateX, translateY); 36 | for (let i = idx + 1; i <= idx + clock.length; i++) { 37 | let j = i % clock.length; 38 | if (clock[j]) { 39 | let last = j === idx; 40 | drawClock(clock[j], charWidth, charHeight, skew, last); 41 | } 42 | sketch.translate(-2 * charWidth, 2 * charHeight); 43 | } 44 | } 45 | 46 | function drawClock(t, charWidth, charHeight, skew, last) { 47 | sketch.push() 48 | let timeString = t.h + ":" + pad(t.m) + ":" + pad(t.s); 49 | 50 | // If hour is a single digit; 51 | // translate by 1 char before starting 52 | if (t.h < 10) { 53 | sketch.translate(6 * charWidth, 6 * skew); 54 | } 55 | 56 | if (last) { 57 | sketch.stroke(0, 0, 100); 58 | } else { 59 | sketch.stroke(t.hue, 100, 100); 60 | } 61 | sketch.strokeWeight(1.25); 62 | sketch.noFill(); 63 | for(let i = 0; i < timeString.length; i++) { 64 | drawCharacter(timeString[i], charWidth, charHeight, skew); 65 | sketch.translate(6 * charWidth, 5.5 * skew); 66 | } 67 | sketch.pop(); 68 | } 69 | 70 | function drawCharacter(c, charWidth, charHeight, skew) { 71 | let cMatrix = font[c]; 72 | let x = 0; 73 | let y = 0; 74 | for (let r = 0; r < cMatrix.length; r++) { 75 | let row = cMatrix[r]; 76 | let firstY = y; 77 | for (let col = 0; col < row.length; col++) { 78 | if (row[col] == 1) { 79 | sketch.beginShape(); 80 | sketch.vertex(x, y); 81 | sketch.vertex(x + charWidth, y + skew); 82 | sketch.vertex(x + charWidth, y + charHeight + skew); 83 | sketch.vertex(x, y + charHeight); 84 | sketch.endShape(sketch.CLOSE); 85 | } 86 | x += charWidth; 87 | y += skew; 88 | } 89 | y = firstY + charHeight; 90 | x = 0; 91 | } 92 | } 93 | 94 | function pad(num) { 95 | let n = num.toString(); 96 | if (n.length == 2) { 97 | return n; 98 | } else { 99 | return "0"+n; 100 | } 101 | } 102 | 103 | function getTime() { 104 | let rightNow = { 105 | h: sketch.hour() % 12 === 0 ? 12 : sketch.hour() % 12, 106 | m: sketch.minute(), 107 | s: sketch.second(), 108 | }; 109 | return rightNow; 110 | } 111 | 112 | function tick(t) { 113 | let newT; 114 | if (t === null) { 115 | newT = getTime(); 116 | newT.hue = 5 * Math.floor(sketch.random(360 / 5)); 117 | } else { 118 | // Returns time t + 1 second 119 | // And assigns the correct hue 120 | newT = { 121 | s: t.s + 1, 122 | m: t.m, 123 | h: t.h, 124 | hue: (t.hue + 5) % 360 125 | }; 126 | } 127 | 128 | if (newT.s == 60) { 129 | newT.s = 0; 130 | newT.m += 1; 131 | } 132 | 133 | if (newT.m == 60) { 134 | newT.m = 0; 135 | if(newT.h === 12) { 136 | newT.h = 1; 137 | } else { 138 | newT.h = newT.h + 1; 139 | } 140 | } 141 | 142 | return newT; 143 | } 144 | 145 | function timeEquals(time1, time2) { 146 | if (time1 === time2) { 147 | return true; 148 | } else if(!time1 || !time2) { 149 | return false; 150 | } else { 151 | return time1.s === time2.s && time1.m === time2.m && time1.h === time2.h; 152 | } 153 | } 154 | 155 | function catchUp() { 156 | let time = getTime(); 157 | let lastTime = clock[idx]; 158 | // TODO: This could run up to 12 * 60 * 60 times... if someone leaves this 159 | // open for more than 24 hours. Testing seems to suggest this is not that 160 | // problematic. 161 | while (!timeEquals(time, lastTime)) { 162 | lastTime = tick(lastTime); 163 | idx = (idx + 1) % numTimes; 164 | clock[idx] = lastTime; 165 | } 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /clocks/clock-05.js: -------------------------------------------------------------------------------- 1 | // This is the overlapping rotated numbers clock 2 | 3 | var clock05 = function(sketch) { 4 | sketch.setup = function() { 5 | 6 | } 7 | 8 | sketch.draw = function() { 9 | let h = sketch.hour() % 12 === 0 ? 12 : sketch.hour() % 12; 10 | let m = sketch.minute(); 11 | let s = sketch.second(); 12 | 13 | sketch.background(150); 14 | sketch.translate(sketch.width/2,sketch.height/2); 15 | 16 | // --- draw hours --- 17 | let size = sketch.height/14; 18 | sketch.fill(0); 19 | 20 | sketch.push(); 21 | sketch.rotate(2*sketch.PI*h/12); 22 | if (h >= 10) { 23 | sketch.translate(-3*size, 0); 24 | drawCharacter(sketch.floor(h/10), size); 25 | sketch.translate(6*size, 0); 26 | drawCharacter(h%10, size); 27 | } else { 28 | drawCharacter(h, size); 29 | } 30 | sketch.pop(); 31 | 32 | // the minutes and seconds are an additional 180 degrees rotated (look at original) 33 | sketch.rotate(sketch.PI); 34 | 35 | // --- draw minutes --- 36 | size = size * 0.75; 37 | sketch.fill(255); 38 | 39 | sketch.push(); 40 | sketch.rotate(2*sketch.PI*m/60-sketch.PI/2); 41 | if (m >= 10) { 42 | sketch.translate(-3*size, 0); 43 | drawCharacter(sketch.floor(m/10), size); 44 | sketch.translate(6*size, 0); 45 | drawCharacter(m%10, size); 46 | } else { 47 | drawCharacter(m, size); 48 | } 49 | sketch.pop(); 50 | 51 | // --- draw minutes --- 52 | size = size / 0.75 * 0.5; 53 | sketch.fill(231,0,0); 54 | 55 | sketch.push(); 56 | sketch.rotate(2*sketch.PI*s/60-sketch.PI/2); 57 | if (s >= 10) { 58 | sketch.translate(-3*size, 0); 59 | drawCharacter(sketch.floor(s/10), size); 60 | sketch.translate(6*size, 0); 61 | drawCharacter(s%10, size); 62 | } else { 63 | drawCharacter(s, size); 64 | } 65 | sketch.pop(); 66 | } 67 | 68 | function drawCharacter(character, size) { 69 | let dots = font[character]; 70 | 71 | sketch.push(); 72 | sketch.noStroke(); 73 | for (let row = 0; row < dots.length; row++) { 74 | for (let col = 0; col < dots[row].length; col++) { 75 | if (dots[row][col] === 1) { 76 | if (joined(row, col + 1) && joined(row+1,col)) { 77 | sketch.rect(size * col - size*2.5, size * row - size*3.5, size + joined(row, col + 1), size); 78 | sketch.rect(size * col - size*2.5, size * row - size*3.5, size, size + joined(row + 1, col)); 79 | } else { 80 | sketch.rect(size * col - size*2.5, size * row - size*3.5, size + joined(row, col + 1), size + joined(row + 1, col)); 81 | } 82 | } 83 | } 84 | } 85 | sketch.pop(); 86 | 87 | function joined(row, col) { 88 | if (row < 0 || row >= dots.length || col < 0 || col >= dots[row].length) { 89 | return false; 90 | } 91 | return dots[row][col] === 1; 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /clocks/clock-06.js: -------------------------------------------------------------------------------- 1 | // This is the Conway's Game of Life clock 2 | 3 | // In this clock, the current clock text is used to seed an 4 | // instance of Conway's Game of Life. 5 | // See wikipedia https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life 6 | 7 | var clock06 = function(sketch) { 8 | sketch.setup = function() { 9 | 10 | } 11 | 12 | sketch.draw = function() { 13 | if(sketch.GameOfLife === null || sketch.GameOfLife === undefined) 14 | { 15 | sketch.GameOfLife = { 16 | Speed:50, 17 | NextStep:-1, 18 | Instance: new GameOfLife(63, 30) 19 | }; 20 | } 21 | 22 | // The simulation runs slower than the canvas draw speed. 23 | // So, we should only redraw the clock if the simulation has updated. 24 | if(sketch.GameOfLife.NextStep < sketch.millis()) 25 | { 26 | // Update next run step 27 | sketch.GameOfLife.NextStep = sketch.millis() + sketch.GameOfLife.Speed; 28 | 29 | let grid = sketch.GameOfLife.Instance.GetGrid(); 30 | let gridHeight = grid.length; 31 | let gridWidth = grid[0].length; 32 | 33 | // Places the clock text in the center of the grid. 34 | // 47 and 7 are magic numbers, for the width and height of the clock text. Doesn't change. 35 | let clockOffsetX = Math.floor((gridWidth - 47) / 2); 36 | let clockOffsetY = Math.floor((gridHeight - 7) / 2); 37 | 38 | var clockText = MakeClockText(sketch.hour(), sketch.minute(), sketch.second(), clockOffsetX, clockOffsetY); 39 | // Apply clock text to the simulation 40 | sketch.GameOfLife.Instance.EnableCells(clockText, true); 41 | // Step the simulation forward 42 | sketch.GameOfLife.Instance.Update(); 43 | // Clear clock text from the simulation, for drawing purposes. 44 | sketch.GameOfLife.Instance.EnableCells(clockText, false); 45 | 46 | 47 | 48 | let cellWidth = sketch.width / gridWidth; 49 | let cellHeight = sketch.height / gridHeight; 50 | let cellSize = Math.min(cellWidth, cellHeight); 51 | // used for centering to the sketch 52 | let paddingX = (sketch.width - Math.floor(cellSize * gridWidth)) / 2; 53 | let paddingY = (sketch.height - Math.floor(cellSize * gridHeight)) / 2; 54 | 55 | // begin drawing 56 | sketch.background(31); 57 | sketch.fill(150,150,150); 58 | sketch.noStroke(); 59 | // draw the current grid 60 | for(let y = 0; y < grid.length; y++) 61 | { 62 | for(let x = 0; x < grid[0].length; x++) 63 | { 64 | if(grid[y][x] === true) 65 | { 66 | 67 | sketch.rect(paddingX + Math.floor(cellSize*x + 1), 68 | paddingY + Math.floor(cellSize*y + 1), 69 | Math.floor(cellSize - 2), 70 | Math.floor(cellSize - 2)); 71 | } 72 | } 73 | } 74 | 75 | // draw the clock text 76 | sketch.fill(255,255,1); 77 | sketch.ellipseMode(sketch.CORNER) 78 | for(let i = 0; i < clockText.length; i++) 79 | { 80 | sketch.ellipse(paddingX + Math.floor(clockText[i].x * cellSize), 81 | paddingY + Math.floor(clockText[i].y * cellSize), 82 | Math.floor((cellSize)-1), 83 | Math.floor((cellSize)-1)); 84 | } 85 | } 86 | } 87 | 88 | function MakeClockText(hour, minute, second, offsetX, offsetY) 89 | { 90 | let vectors = new Array(); 91 | //hours 92 | hour = hour % 12 === 0 ? 12 : hour % 12; 93 | if (hour >= 10) { 94 | vectors = vectors.concat(MakeCharacterText(Math.floor(hour/10), offsetX, offsetY)); 95 | } 96 | offsetX += 5 + 1; 97 | vectors = vectors.concat(MakeCharacterText(hour % 10, offsetX, offsetY)); 98 | 99 | //colon 100 | offsetX += 5 + 1; 101 | vectors = vectors.concat(MakeCharacterText(":", offsetX, offsetY)); 102 | 103 | //minutes 104 | offsetX += 5 + 1; 105 | vectors = vectors.concat(MakeCharacterText(Math.floor(minute/10), offsetX, offsetY)); 106 | offsetX += 5 + 1; 107 | vectors = vectors.concat(MakeCharacterText(minute % 10, offsetX, offsetY)); 108 | 109 | //colon 110 | offsetX += 5 + 1; 111 | vectors = vectors.concat(MakeCharacterText(":", offsetX, offsetY)); 112 | 113 | //seconds 114 | offsetX += 5 + 1; 115 | vectors = vectors.concat(MakeCharacterText(Math.floor(second/10), offsetX, offsetY)); 116 | offsetX += 5 + 1; 117 | vectors = vectors.concat(MakeCharacterText(second % 10, offsetX, offsetY)); 118 | 119 | return vectors; 120 | } 121 | 122 | function MakeCharacterText( char, offsetX, offsetY) 123 | { 124 | let vectors = new Array(); 125 | let dots = font[char]; 126 | 127 | for(let y=0; y < dots.length; y++) 128 | for(let x=0; x= 0 && 206 | x < this.CellWidth && 207 | y >= 0 && 208 | y < this.CellHeight) 209 | { 210 | row_min = Math.max(0, x - 1); 211 | row_max = Math.min(this.CellWidth-1, x + 1); 212 | col_min = Math.max(0, y - 1); 213 | col_max = Math.min(this.CellHeight-1, y + 1); 214 | 215 | for (nY = col_min; nY <= col_max; nY++) 216 | { 217 | for (nX = row_min; nX <= row_max; nX++) 218 | { 219 | if ((nX !== x || nY !== y) && grid[nY][nX] === true) 220 | { 221 | liveNeighbors++; 222 | } 223 | } 224 | } 225 | } 226 | 227 | return liveNeighbors; 228 | }; 229 | 230 | 231 | /** 232 | * Change state of target cells 233 | * @param {p5.Vector[]} cells Cells to be changed 234 | * @param {boolean} isLive Whether to set as alive, or dead 235 | */ 236 | GameOfLife.prototype.EnableCells = function (cells, isLive) { 237 | for(let i = 0; i < cells.length; i++) 238 | { 239 | if(cells[i].x >= 0 && 240 | cells[i].x < this.CellWidth && 241 | cells[i].y >= 0 && 242 | cells[i].y < this.CellHeight) 243 | { 244 | this.gridFront[cells[i].y][cells[i].x] = isLive; 245 | } 246 | } 247 | }; 248 | 249 | return GameOfLife; 250 | }()); -------------------------------------------------------------------------------- /clocks/clock-07.js: -------------------------------------------------------------------------------- 1 | // This is the time as a clock-hand 2 | var clock07 = function(sketch) { 3 | 4 | sketch.setup = function() { 5 | sketch.setFrameRate(1); 6 | } 7 | 8 | sketch.draw = function() { 9 | sketch.background(0); 10 | sketch.fill(255); 11 | sketch.textAlign(sketch.CENTER, sketch.CENTER); 12 | 13 | let min_dimension = Math.min(sketch.height, sketch.width); 14 | 15 | let hSpacing = min_dimension / 4; 16 | let spacing = (min_dimension / 2 - hSpacing) / 5; 17 | let h = sketch.hour(); 18 | let m = sketch.minute(); 19 | let s = sketch.second(); 20 | let mod = h % 12 === 0 ? 12 : h % 12; 21 | let angle = sketch.map(s, 0, 60, -sketch.PI, sketch.PI); 22 | let letterSqueeze = 0.6; 23 | 24 | let hIndicatorSize = min_dimension / 100; 25 | 26 | sketch.translate(sketch.width / 2, sketch.height / 2); 27 | sketch.rotate(angle); 28 | 29 | // draw AM PM 30 | sketch.textSize(hIndicatorSize); 31 | sketch.fill(100); 32 | 33 | sketch.translate(-hIndicatorSize * 3, 0); 34 | if (h < 12) { 35 | drawCharacter('A', hIndicatorSize, 1); 36 | } else { 37 | drawCharacter('P', hIndicatorSize, 1); 38 | } 39 | sketch.translate(hIndicatorSize * 6, 0); 40 | drawCharacter('M', hIndicatorSize, 1); 41 | sketch.translate(-hIndicatorSize * 3, 0); 42 | sketch.translate(0, hSpacing); 43 | 44 | // draw H 45 | hIndicatorSize *= .75; 46 | sketch.fill(255); 47 | if (mod < 10) { 48 | sketch.translate(0, spacing); 49 | drawCharacter(mod, hIndicatorSize, letterSqueeze); 50 | sketch.translate(0, spacing); 51 | } else { 52 | drawCharacter(sketch.floor(mod / 10), hIndicatorSize, letterSqueeze); 53 | sketch.translate(0, spacing); 54 | drawCharacter(mod % 10, hIndicatorSize, letterSqueeze); 55 | sketch.translate(0, spacing); 56 | } 57 | 58 | // draw ':' 59 | sketch.fill(255, 0, 0); 60 | if((Date.now() % 1000) < 500) 61 | drawCharacter(":", hIndicatorSize, letterSqueeze); 62 | sketch.translate(0, spacing); 63 | 64 | // draw M 65 | sketch.fill(255); 66 | if (m < 10) { 67 | drawCharacter(0, hIndicatorSize, letterSqueeze); 68 | sketch.translate(0, spacing); 69 | drawCharacter(m, hIndicatorSize, letterSqueeze); 70 | } else { 71 | drawCharacter(sketch.floor(m / 10), hIndicatorSize, letterSqueeze); 72 | sketch.translate(0, spacing); 73 | drawCharacter(m % 10, hIndicatorSize, letterSqueeze); 74 | } 75 | } 76 | 77 | function drawCharacter(character, size, letterSqueeze) { 78 | let dots = font[character]; 79 | sketch.push(); 80 | sketch.noStroke(); 81 | for (let row = 0; row < dots.length; row++) { 82 | for (let col = 0; col < dots[row].length; col++) { 83 | if (dots[row][col] === 1) { 84 | if (joined(row, col + 1) && joined(row+1,col)) { 85 | sketch.rect(size * col - size*2.5, (size * row - size*2.5) * letterSqueeze, size + joined(row, col + 1), size * letterSqueeze); 86 | sketch.rect(size * col - size*2.5, (size * row - size*2.5) * letterSqueeze, size, size * letterSqueeze + joined(row + 1, col)); 87 | } else { 88 | sketch.rect(size * col - size*2.5, (size * row - size*2.5) * letterSqueeze, size + joined(row, col + 1), size * letterSqueeze + joined(row + 1, col)); 89 | } 90 | 91 | } 92 | } 93 | } 94 | sketch.pop(); 95 | function joined(row, col) { 96 | if (row < 0 || row >= dots.length || col < 0 || col >= dots[row].length) { 97 | return false; 98 | } 99 | return dots[row][col] === 1; 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /clocks/clock-08.js: -------------------------------------------------------------------------------- 1 | // This is the zoom and fade clock 2 | var clock08 = function(sketch) { 3 | 4 | // Angle to use in sin calculation 5 | let angle = 0.0; 6 | // Amount to increment angle by 7 | const delta = 0.025; 8 | 9 | sketch.setup = function() { 10 | } 11 | 12 | sketch.draw = function() { 13 | 14 | let h = sketch.hour() % 12 === 0 ? 12 : sketch.hour() % 12; 15 | let m = sketch.minute(); 16 | 17 | sketch.background(0); 18 | 19 | sketch.translate(sketch.width/2,sketch.height/2); 20 | 21 | // Map sin to scale 22 | let hScale = sketch.map(Math.sin(angle), -1, 1, 2, 10); 23 | let mScale = sketch.map(Math.sin(angle), -1, 1, 10, 2); 24 | 25 | // Hours 26 | sketch.translate(-sketch.width/4,0) 27 | sketch.fill(sketch.map(Math.sin(angle), -1, 1, 100, 255)); 28 | sketch.push(); 29 | if (h >= 10) { 30 | drawCharacter(sketch.floor(h/10), hScale); 31 | sketch.translate(6*hScale, 0); 32 | drawCharacter(h%10, hScale); 33 | } else { 34 | drawCharacter(h%10, hScale); 35 | } 36 | sketch.pop(); 37 | 38 | // Minutes 39 | sketch.translate(sketch.width/2,0) 40 | sketch.fill(sketch.map(Math.sin(angle), -1, 1, 255, 100)); 41 | sketch.push(); 42 | if (m >= 10) { 43 | sketch.translate(-6*mScale, 0); 44 | drawCharacter(sketch.floor(m/10), mScale); 45 | sketch.translate(6*mScale, 0); 46 | drawCharacter(m%10, mScale); 47 | } else { 48 | sketch.translate(-6*mScale, 0); 49 | drawCharacter(0, mScale); 50 | sketch.translate(6*mScale, 0); 51 | drawCharacter(m%10, mScale); 52 | } 53 | sketch.pop(); 54 | 55 | // Increase angle 56 | angle += delta; 57 | } 58 | 59 | function drawCharacter(character, size) { 60 | let dots = font[character]; 61 | sketch.push(); 62 | sketch.noStroke(); 63 | for (let row = 0; row < dots.length; row++) { 64 | for (let col = 0; col < dots[row].length; col++) { 65 | if (dots[row][col] === 1) { 66 | if (joined(row, col + 1) && joined(row+1,col)) { 67 | sketch.rect(size * col - size*2.5, size * row - size*3.5, size + joined(row, col + 1), size); 68 | sketch.rect(size * col - size*2.5, size * row - size*3.5, size, size + joined(row + 1, col)); 69 | } else { 70 | sketch.rect(size * col - size*2.5, size * row - size*3.5, size + joined(row, col + 1), size + joined(row + 1, col)); 71 | } 72 | } 73 | } 74 | } 75 | sketch.pop(); 76 | 77 | function joined(row, col) { 78 | if (row < 0 || row >= dots.length || col < 0 || col >= dots[row].length) { 79 | return false; 80 | } 81 | return dots[row][col] === 1; 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /clocks/clock-09.js: -------------------------------------------------------------------------------- 1 | // This is the clocks-as-pixels clock 2 | var clock09 = function(sketch) { 3 | sketch.setup = function() { 4 | sketch.angleMode(sketch.DEGREES); 5 | sketch.ellipseMode(sketch.CENTER); 6 | }; 7 | sketch.draw = function() { 8 | sketch.background(0); 9 | sketch.fill(177); 10 | sketch.textAlign(sketch.CENTER, sketch.CENTER); 11 | drawTime(get12Hour(sketch.hour()).toString(), sketch.minute().toString(),sketch.second.toString()); 12 | // drawTime("4","48", "0"); // Testing hour & minute as shown at http://cmuems.com/2016/60212/wp-content/uploads/2016/09/maeda-all.gif 13 | }; 14 | 15 | // Copied from https://github.com/CodingTrain/12oclocks/pull/2/ by @FantasyTeddy 16 | // size parameter/argument added by @haideralipunjabi 17 | function drawCharacter(character, x, y, size) { 18 | let dots = font[character]; 19 | for (let row = 0; row < dots.length; row++) { 20 | for (let col = 0; col < dots[row].length; col++) { 21 | if (dots[row][col] === 1) 22 | drawClock(x + (5*size) * col - (10*size), y + (5 * size) * row - (15 * size), 5*size, 5*size); 23 | } 24 | } 25 | } 26 | 27 | // By @haideralipunjabi 28 | function drawTime(hour, minute, second){ 29 | // Prefixing 0 before minute < 10 30 | if(minute.length === 1) minute = "0" + minute; 31 | 32 | // Calculating multiplying factor to size of each dot, so that clock fills whole space. 33 | // Calculated by divinding availavle space(width) by required space (no. of cols * size of dot(5) ) 34 | // No. of cols = (No. of characters * No. of cols of each character) + Spaces in between characters + cols to leave before and after clock 35 | // Here, No. of characters = 5, No. of cols of each character = 5, Spaces in between characters = 4, cols to leave before and after = 6 (3 before & 3 after) 36 | 37 | size = sketch.width / (35 * 5); 38 | 39 | // Spacing between characters (Size of dot * calculated size) 40 | 41 | spacing = 5 * size; 42 | 43 | // For each character in hour, draw that character 44 | 45 | for (let i = 0; i < hour.length; i++){ 46 | drawCharacter(hour[hour.length -1 - i], sketch.width/2 - (spacing * (i + 1) * 6), sketch.height/2,size); 47 | } 48 | // Draw the : in between hour and minute 49 | drawCharacter(":", sketch.width/2, sketch.height/2,size); 50 | 51 | // For each character in minute, draw that character 52 | for (let i = 0; i < minute.length; i++){ 53 | drawCharacter(minute[i], sketch.width/2 + (spacing * (i+1) * 6) , sketch.height/2,size); 54 | } 55 | } 56 | function drawClock(x, y, w, h) 57 | { 58 | let hr = get12Hour(sketch.hour()); 59 | let mn = sketch.minute(); 60 | let sc = sketch.second(); 61 | let secondAngle = sketch.map(sc, 0, 60, 0, 360) - 90; 62 | let minuteAngle = sketch.map(mn, 0, 60, 0, 360) -90; 63 | let hourAngle = sketch.map(hr % 12, 0, 12, 0, 360) -90; 64 | sketch.stroke(200); 65 | sketch.strokeWeight(w * 0.05); 66 | // Draw the inner cirle 67 | sketch.fill(255); 68 | sketch.ellipse(x,y,w * 0.95,h * 0.95); 69 | // Draw the arms / arcs 70 | sketch.push(); 71 | sketch.noStroke(); 72 | sketch.fill(0); 73 | sketch.translate(x,y); 74 | // Draw the hour arm 75 | sketch.push(); 76 | sketch.rotate(hourAngle); 77 | sketch.triangle(0,0,0.2 * w,-0.02 * w, 0.2 * w,0.02 * w); 78 | sketch.pop(); 79 | // Draw the minute arm 80 | sketch.push(); 81 | sketch.rotate(minuteAngle); 82 | sketch.triangle(0,0,0.45 * w,-0.02 * w,0.45 * w,0.02 * w); 83 | sketch.pop(); 84 | // Draw the second arm 85 | sketch.push(); 86 | sketch.noFill(); 87 | sketch.stroke(255,0,0); 88 | sketch.strokeWeight(w * 0.05); 89 | sketch.arc(0,0,w * 0.8,w * 0.8,secondAngle - 5, secondAngle + 5); 90 | sketch.pop(); 91 | sketch.pop(); 92 | } 93 | function get12Hour(hour) 94 | { 95 | if(hour === 0) return 12; 96 | return hour > 12 ? hour-12 : hour; 97 | } 98 | }; 99 | -------------------------------------------------------------------------------- /clocks/clock-10.js: -------------------------------------------------------------------------------- 1 | // This is the rotated grid clock 2 | var clock10 = function(sketch) { 3 | 4 | let colorRed, colorDarkRed, colorWhite, colorDefault; 5 | 6 | const relativeScale = 0.85; 7 | const xSpacing = 17; 8 | const ySpacing = 8; 9 | 10 | let scale; 11 | 12 | sketch.setup = function() { 13 | sketch.angleMode(sketch.DEGREES); 14 | sketch.strokeWeight(1.1); 15 | }; 16 | 17 | sketch.draw = function() { 18 | colorRed = sketch.color(255, 0, 0); 19 | colorDarkRed = sketch.color(150, 0, 0); 20 | colorWhite = sketch.color(255, 255, 255); 21 | colorDefault = sketch.color(90, 90, 90); 22 | 23 | scale = sketch.height / (10 * ySpacing) * relativeScale; 24 | 25 | sketch.background(0); 26 | sketch.fill(224); 27 | sketch.textAlign(sketch.CENTER, sketch.CENTER); 28 | 29 | sketch.push(); 30 | sketch.rotate(10); 31 | sketch.translate(scale * 30, 0); 32 | drawGrid(); 33 | drawHour(); 34 | drawMinute(); 35 | drawSecond() 36 | sketch.pop(); 37 | }; 38 | 39 | // drawGrid function draws the calendar 40 | function drawGrid() { 41 | sketch.push(); 42 | sketch.noStroke(); 43 | sketch.fill(colorDefault); 44 | 45 | for (let i = 0; i < 6; i++) { 46 | for (let j = 0; j < 10; j++) { 47 | drawDate(i*10 + j, scale, drawShape, true); 48 | sketch.translate(0, ySpacing * scale); 49 | } 50 | sketch.translate(xSpacing * scale, -10 * ySpacing * scale); 51 | } 52 | 53 | sketch.pop(); 54 | } 55 | 56 | function drawHour() { 57 | sketch.push(); 58 | sketch.noStroke(); 59 | sketch.fill(colorWhite); 60 | 61 | let h = sketch.hour12(); 62 | translateTo(h); 63 | drawDate(h, scale, drawShape, false); 64 | 65 | sketch.pop(); 66 | } 67 | 68 | function drawMinute() { 69 | sketch.push(); 70 | sketch.noFill(); 71 | sketch.stroke(colorWhite); 72 | 73 | let m = sketch.minute(); 74 | translateTo(m); 75 | drawDate(m, scale, drawShape, true); 76 | 77 | sketch.pop(); 78 | } 79 | 80 | function drawSecond() { 81 | sketch.push(); 82 | sketch.noFill(); 83 | sketch.stroke(colorDarkRed); 84 | 85 | let s = sketch.second(); 86 | translateTo(s); 87 | sketch.translate(-5.5 * scale, -3.5 * scale); 88 | 89 | drawDate(s, 2*scale, drawShape, true); 90 | drawDate(s, 2*scale, drawDiagonal, true); 91 | 92 | sketch.translate(-scale, scale); 93 | sketch.noStroke(); 94 | sketch.fill(colorRed); 95 | 96 | drawDate(s, 2*scale, drawShape, true); 97 | 98 | sketch.pop(); 99 | } 100 | 101 | // Translate to a specific number on the grid 102 | function translateTo(number) { 103 | let n1 = Math.floor(number / 10); 104 | let n2 = number % 10; 105 | sketch.translate((n1 * xSpacing) * scale, (n2 * ySpacing) * scale); 106 | } 107 | 108 | // drawDate draws each date in the grid/calendar 109 | function drawDate(number, scale, drawFunction, leadingZero) { 110 | if(leadingZero) 111 | number = (number < 10 ? "0" : "") + number; 112 | else 113 | number = (number < 10 ? " " : "") + number; 114 | 115 | sketch.push() 116 | 117 | if(number.charAt(0) !== " ") 118 | drawFunction(number.charAt(0), scale); 119 | 120 | sketch.translate(6 * scale, 0); 121 | drawFunction(number.charAt(1), scale); 122 | sketch.pop(); 123 | } 124 | 125 | // Draws the shape of a character 126 | function drawShape(character, scale) { 127 | fontShapes.draw(sketch, character, scale); 128 | } 129 | 130 | // Draws the diagonal lines of a character 131 | function drawDiagonal(character, scale) { 132 | let char = fontShapes[character.charAt(0)]; 133 | 134 | for (let s = 0; s < char.length; s++) 135 | for (let v = 0; v < char[s].length; v++) 136 | sketch.line( 137 | char[s][v][0] * scale, char[s][v][1] * scale, 138 | (char[s][v][0] - 0.5) * scale, (char[s][v][1] + 0.5) * scale 139 | ); 140 | } 141 | 142 | } 143 | -------------------------------------------------------------------------------- /clocks/clock-11.js: -------------------------------------------------------------------------------- 1 | // This is the 3D rotating pixel clock 2 | var clock11 = function(sketch) { 3 | // Parameter of the ellipse path. 4 | // All values are relative to the canvas size; 5 | const ellipseX = 0.5; 6 | const ellipseY = 0.4; 7 | const ellipseHeight = 0.25; 8 | const ellipseWidth = 0.7; 9 | // Size of one dot 10 | const pointSize = 3; 11 | // Offset of the dots 12 | const xOffset = 3.5; 13 | const yOffset = 5; 14 | // Duration of one cycle in milliseconds 15 | const cycleDuration = 5000; 16 | // Keeps track of the offset on the ellipse path. 17 | // Values are between 0 and 1. 18 | let alpha = 0; 19 | 20 | sketch.setup = function() { 21 | sketch.angleMode(sketch.DEGREES); 22 | } 23 | 24 | sketch.draw = function() { 25 | // Clear screen 26 | sketch.background(150); 27 | 28 | // scale the points depending on the canvas 29 | sketch.strokeWeight(pointSize * sketch.height / 200); 30 | 31 | // Calculate the current offset 32 | alpha = (sketch.millis() % cycleDuration) / cycleDuration; 33 | 34 | // First draw the back half 35 | for(let i = 0; i < 11*5; i++) { 36 | drawColumn(i, false); 37 | } 38 | 39 | // Then draw the front half on top 40 | for(let i = 0; i < 11*5; i++) { 41 | drawColumn(i, true); 42 | } 43 | } 44 | 45 | /** 46 | * Draws one column of the global dot matrix. Also perform a simple 47 | * depth test. Depending on the value of 'drawFront' the front or the back 48 | * half is going to be discard. 49 | * 50 | * @param int globalColumn The index of column of the global dot matrix 51 | * that is going to be drawn. 52 | * @param bool drawFront Whether to draw the front or the back 53 | * half of the dot matrix. 54 | */ 55 | function drawColumn(globalColumn, drawFront) { 56 | // Grab the current character 57 | let character = getTime().charAt(Math.floor(globalColumn / 5)); 58 | // If the character is empty then skip it 59 | if(character == ' ') 60 | return; 61 | 62 | // get the dot matrix of the character 63 | let charMatrix = font[character]; 64 | // get the local column index 65 | let column = globalColumn % 5; 66 | 67 | // shortcuts 68 | let height = sketch.height; 69 | let width = sketch.width; 70 | 71 | // if the aspect ration is to high, scale by height 72 | if(width/height > 2) 73 | width = height * 1.75; 74 | 75 | // calculate the center of the ellipse 76 | let centerX = ellipseX * sketch.width; 77 | let centerY = ellipseY * sketch.height; 78 | 79 | // calculate the position on a normatized circle. 80 | let relX = sketch.cos(alpha*360 - globalColumn * xOffset); 81 | let relY = -sketch.sin(alpha*360 - globalColumn * xOffset); 82 | 83 | // Check if the column is at the back or front 84 | // and discard depending on 'bool drawFront' 85 | if(drawFront == (relY < 0)) 86 | return; 87 | 88 | // calculate the current grayscale 89 | sketch.stroke(255 * sketch.map(relY, -1, 1, 0, 1)); 90 | 91 | sketch.push(); 92 | // set the position of the first dot 93 | sketch.translate( 94 | relX * width * ellipseWidth/2 + centerX, 95 | relY * height * ellipseHeight/2 + centerY 96 | ); 97 | 98 | // draw the current column 99 | for(let i=0; i<7; i++) { 100 | if(charMatrix[i][column] == 1) { 101 | sketch.point(0, i * yOffset * sketch.height / 200); 102 | } 103 | } 104 | 105 | sketch.pop() 106 | } 107 | 108 | /** 109 | * Outputs the time at the correct format. The length of the string 110 | * is always 11. To ensure the length, a leading whitespace is added 111 | * if nedded. 112 | * 113 | * @return String The current time as a string. 114 | */ 115 | function getTime() { 116 | let h = sketch.hour() % 12; 117 | let m = sketch.minute(); 118 | let s = sketch.second(); 119 | let isAM = sketch.hour() < 12; 120 | 121 | h = "" + (h == 0 ? 12 : h); 122 | h = (h < 10 ? " " : "") + h; 123 | m = (m < 10 ? "0" : "") + m; 124 | s = (s < 10 ? "0" : "") + s; 125 | 126 | return h + ":" + m + ":" + s + " " + (isAM ? "A" : "P") + "M"; 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /clocks/clock-12.js: -------------------------------------------------------------------------------- 1 | // This is the cube clock 2 | var clock12 = function(sketch) { 3 | // color of the top face 4 | const topColor = [255, 0, 0, 127]; 5 | // color of the front faces 6 | const frontColor = [255, 255, 255, 127]; 7 | // color of the back faces 8 | const backColor = [200, 200, 200, 127]; 9 | // scale of the cube 10 | let scale = 1.1; 11 | 12 | // The axes of the three different coordinate systems. 13 | // The normalized x axis (1, 0) will be mapped to the first vector; 14 | // the normalized y axis (0, 1) will be mapped to the second vector. 15 | sketch.angleMode(sketch.DEGREES); 16 | const axes = { 17 | top_bottom: [ 18 | sketch.createVector(1, 0).rotate(30), 19 | sketch.createVector(1, 0).rotate(150) // vec(0,1).rot(60) doesn't work :/ 20 | ], 21 | left_right: [ 22 | sketch.createVector(1, 0).rotate(-30), 23 | sketch.createVector(0, 1).mult(6 / 7) 24 | ], 25 | front_back: [ 26 | sketch.createVector(1, 0).rotate(30), 27 | sketch.createVector(0, 1).mult(6 / 7) 28 | ] 29 | }; 30 | 31 | // Some constants for calculation 32 | const EDGE_LENGTH = 77; 33 | const ZERO = () => sketch.createVector(0, 0); 34 | const UP_VECTOR = axes.front_back[1].copy().mult(-EDGE_LENGTH); 35 | const LEFT_VECTOR = axes.top_bottom[1].copy().mult(EDGE_LENGTH); 36 | const RIGHT_VECTOR = axes.top_bottom[0].copy().mult(EDGE_LENGTH); 37 | 38 | // Origin of the 6 coordinate systems 39 | const origin = { 40 | top: ZERO().add(UP_VECTOR), 41 | bottom: ZERO(), 42 | 43 | left: ZERO().add(LEFT_VECTOR).add(UP_VECTOR), 44 | right: ZERO().add(LEFT_VECTOR).add(RIGHT_VECTOR).add(UP_VECTOR), 45 | 46 | front: ZERO().add(LEFT_VECTOR).add(UP_VECTOR), 47 | back: ZERO().add(UP_VECTOR) 48 | }; 49 | 50 | 51 | sketch.setup = function() { 52 | sketch.noStroke() 53 | } 54 | 55 | sketch.draw = function() { 56 | scale = sketch.min(sketch.height, sketch.width) / 200; 57 | // clear screen 58 | sketch.background(0); 59 | 60 | // grab time 61 | let h = sketch.hour() % 12; 62 | let m = sketch.minute(); 63 | let s = sketch.second(); 64 | 65 | // format time 66 | h = "" + (h == 0 ? 12 : h); 67 | m = (m < 10 ? "0" : "") + m; 68 | s = (s < 10 ? "0" : "") + s; 69 | 70 | // draw the faces in the right order with the corresponding coordinate system 71 | drawNumber(s, backColor, axes.top_bottom, origin.bottom); 72 | drawNumber(h, backColor, axes.front_back, origin.back); 73 | drawNumber(m, backColor, axes.left_right, origin.left); 74 | drawNumber(m, frontColor, axes.left_right, origin.right); 75 | drawNumber(h, frontColor, axes.front_back, origin.front); 76 | drawNumber(s, topColor, axes.top_bottom, origin.top); 77 | } 78 | 79 | /** 80 | * Draws the number with the given color at the given coordinate system. 81 | * The origin is relative to the center of the canvas. 82 | * 83 | * @param {String} number The number to be displayed. 84 | * @param {double[4]} color The rgba-color of the number. 85 | * @param {p5.Vector[2]} axes The axes of the targeted coordinate system. 86 | * @param {p5.Vector} origin The relative origin of the number. 87 | */ 88 | function drawNumber(number, color, axes, origin) { 89 | // set the fill color 90 | sketch.fill(color); 91 | 92 | sketch.push(); 93 | // Move the cube to the center and apply the corresponding 94 | // coordinate system and scale. 95 | sketch.translate(sketch.width * 0.5, sketch.height * 0.45); 96 | sketch.applyMatrix( 97 | axes[0].x, axes[0].y, 98 | axes[1].x, axes[1].y, 99 | origin.x * scale, origin.y * scale 100 | ); 101 | sketch.scale(scale * 7, scale * 11); 102 | 103 | // draw the first number 104 | fontShapes.draw(sketch, number.charAt(0)); 105 | 106 | // draw the second number if available. 107 | if (number.length > 1) { 108 | sketch.translate(6, 0); 109 | fontShapes.draw(sketch, number.charAt(1)); 110 | } 111 | 112 | sketch.pop(); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /css/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 0; 3 | margin: 0; 4 | background: #000; 5 | color: #FFF; 6 | font-family: arial; 7 | } 8 | canvas { 9 | vertical-align: top; 10 | width: 20vw !important; 11 | height: 20vh !important; 12 | width: calc(20vw - 2px) !important; 13 | height: calc(20vh - 2px) !important; 14 | min-width: 150px; 15 | min-height: 150px; 16 | } 17 | a { 18 | color: #fff !important; 19 | } 20 | #container { 21 | display: grid; 22 | grid-template-rows: repeat(4, 1fr); 23 | grid-template-columns: repeat(4, 1fr); 24 | width: 80vw; 25 | min-height: 80vh; 26 | margin: 10vh 10vw; 27 | } 28 | 29 | #container .clock { 30 | border: 1px solid #333; 31 | } 32 | 33 | #container .clock:hover { 34 | border-color: red; 35 | } 36 | 37 | #container .copyright { 38 | grid-column-start: 1; 39 | grid-column-end: 5; 40 | padding: 0 20px; 41 | text-align: right; 42 | } 43 | 44 | #container.single .clock { 45 | display: none; 46 | } 47 | 48 | #container.single .clock.selected { 49 | display: block; 50 | grid-column-start: 1; 51 | grid-column-end: 5; 52 | grid-row-start: 1; 53 | grid-row-end: 4; 54 | } 55 | 56 | #container.single .clock.selected canvas { 57 | width: 80vw !important; 58 | height: 60vh !important; 59 | width: calc(80vw - 2px) !important; 60 | height: calc(60vh - 2px) !important; 61 | } 62 | 63 | #container.single .copyright { 64 | grid-column-end: 3; 65 | } 66 | 67 | #container .contributors { 68 | display: none; 69 | padding: 0 20px; 70 | grid-column-start: 3; 71 | grid-column-end: 5; 72 | } 73 | 74 | #container.single .contributors { 75 | display: block; 76 | } 77 | 78 | ul { 79 | margin: 0; 80 | padding: 0; 81 | } 82 | 83 | li { 84 | display: inline; 85 | 86 | } 87 | 88 | li + li::before { 89 | content: ", "; 90 | } 91 | 92 | @media (max-width: 800px) { 93 | #container { 94 | grid-template-rows: repeat(7, 1fr); 95 | grid-template-columns: repeat(2, 1fr); 96 | } 97 | #container.single { 98 | grid-template-rows: 1fr 3fr 1fr; 99 | grid-template-columns: repeat(2, 1fr); 100 | } 101 | #container .copyright { 102 | grid-row-start: 1; 103 | grid-column-end: 3; 104 | } 105 | canvas { 106 | width: 40vw !important; 107 | } 108 | #container.single .clock.selected { 109 | grid-column-end: 3; 110 | grid-row-start: 2; 111 | grid-row-end: 3; 112 | } 113 | #container .contributors { 114 | grid-row-start: 3; 115 | grid-column-start: 1; 116 | grid-column-end: 3; 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 12 o'clocks 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 | 49 |
50 |

 

51 |

This clock was ported to p5.js by:

52 |
    53 | 54 |
55 |
56 |
57 | 58 | 59 | -------------------------------------------------------------------------------- /libraries/p5.dom.js: -------------------------------------------------------------------------------- 1 | /*! p5.dom.js v0.2.13 Oct 1, 2016 */ 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) { (factory(p5));}); 31 | else if (typeof exports === 'object') 32 | factory(require('../p5')); 33 | else 34 | 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 | *
68 | * 69 | */ 70 | p5.prototype.select = function (e, p) { 71 | var res = null; 72 | var container = getContainer(p); 73 | if (e[0] === '.'){ 74 | e = e.slice(1); 75 | res = container.getElementsByClassName(e); 76 | if (res.length) { 77 | res = res[0]; 78 | } else { 79 | res = null; 80 | } 81 | }else if (e[0] === '#'){ 82 | e = e.slice(1); 83 | res = container.getElementById(e); 84 | }else { 85 | res = container.getElementsByTagName(e); 86 | if (res.length) { 87 | res = res[0]; 88 | } else { 89 | res = null; 90 | } 91 | } 92 | if (res) { 93 | return wrapElement(res); 94 | } else { 95 | return null; 96 | } 97 | }; 98 | 99 | /** 100 | * Searches the page for elements with the given class or tag name (using the '.' prefix 101 | * to specify a class and no prefix for a tag) and returns them as p5.Elements 102 | * in an array. 103 | * The DOM node itself can be accessed with .elt. 104 | * Returns an empty array if none found. 105 | * You can also specify a container to search within. 106 | * 107 | * @method selectAll 108 | * @param {String} name class or tag name of elements to search for 109 | * @param {String} [container] id, p5.Element, or HTML element to search within 110 | * @return {Array} Array of p5.Elements containing nodes found 111 | * @example 112 | *
113 | * function setup() { 114 | * createButton('btn'); 115 | * createButton('2nd btn'); 116 | * createButton('3rd btn'); 117 | * var buttons = selectAll('button'); 118 | * 119 | * for (var i = 0; i < buttons.length; i++){ 120 | * buttons[i].size(100,100); 121 | * } 122 | * } 123 | *
124 | *
125 | * // these are all valid calls to selectAll() 126 | * var a = selectAll('.moo'); 127 | * var b = selectAll('div'); 128 | * var c = selectAll('button', '#myContainer'); 129 | * var d = select('#container'); 130 | * var e = selectAll('p', d); 131 | * var f = document.getElementById('beep'); 132 | * var g = select('.blah', f); 133 | *
134 | * 135 | */ 136 | p5.prototype.selectAll = function (e, p) { 137 | var arr = []; 138 | var res; 139 | var container = getContainer(p); 140 | if (e[0] === '.'){ 141 | e = e.slice(1); 142 | res = container.getElementsByClassName(e); 143 | } else { 144 | res = container.getElementsByTagName(e); 145 | } 146 | if (res) { 147 | for (var j = 0; j < res.length; j++) { 148 | var obj = wrapElement(res[j]); 149 | arr.push(obj); 150 | } 151 | } 152 | return arr; 153 | }; 154 | 155 | /** 156 | * Helper function for select and selectAll 157 | */ 158 | function getContainer(p) { 159 | var container = document; 160 | if (typeof p === 'string' && p[0] === '#'){ 161 | p = p.slice(1); 162 | container = document.getElementById(p) || document; 163 | } else if (p instanceof p5.Element){ 164 | container = p.elt; 165 | } else if (p instanceof HTMLElement){ 166 | container = p; 167 | } 168 | return container; 169 | } 170 | 171 | /** 172 | * Helper function for getElement and getElements. 173 | */ 174 | function wrapElement(elt) { 175 | if(elt.tagName === "INPUT" && elt.type === "checkbox") { 176 | var converted = new p5.Element(elt); 177 | converted.checked = function(){ 178 | if (arguments.length === 0){ 179 | return this.elt.checked; 180 | } else if(arguments[0]) { 181 | this.elt.checked = true; 182 | } else { 183 | this.elt.checked = false; 184 | } 185 | return this; 186 | }; 187 | return converted; 188 | } else if (elt.tagName === "VIDEO" || elt.tagName === "AUDIO") { 189 | return new p5.MediaElement(elt); 190 | } else { 191 | return new p5.Element(elt); 192 | } 193 | } 194 | 195 | /** 196 | * Removes all elements created by p5, except any canvas / graphics 197 | * elements created by createCanvas or createGraphics. 198 | * Event handlers are removed, and element is removed from the DOM. 199 | * @method removeElements 200 | * @example 201 | *
202 | * function setup() { 203 | * createCanvas(100, 100); 204 | * createDiv('this is some text'); 205 | * createP('this is a paragraph'); 206 | * } 207 | * function mousePressed() { 208 | * removeElements(); // this will remove the div and p, not canvas 209 | * } 210 | *
211 | * 212 | */ 213 | p5.prototype.removeElements = function (e) { 214 | for (var i=0; i 242 | * var myDiv; 243 | * function setup() { 244 | * myDiv = createDiv('this is some text'); 245 | * } 246 | * 247 | */ 248 | 249 | /** 250 | * Creates a <p></p> element in the DOM with given inner HTML. Used 251 | * for paragraph length text. 252 | * Appends to the container node if one is specified, otherwise 253 | * appends to body. 254 | * 255 | * @method createP 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 | * var myP; 261 | * function setup() { 262 | * myP = createP('this is some text'); 263 | * } 264 | *
265 | */ 266 | 267 | /** 268 | * Creates a <span></span> element in the DOM with given inner HTML. 269 | * Appends to the container node if one is specified, otherwise 270 | * appends to body. 271 | * 272 | * @method createSpan 273 | * @param {String} html inner HTML for element created 274 | * @return {Object/p5.Element} pointer to p5.Element holding created node 275 | * @example 276 | *
277 | * var mySpan; 278 | * function setup() { 279 | * mySpan = createSpan('this is some text'); 280 | * } 281 | *
282 | */ 283 | var tags = ['div', 'p', 'span']; 284 | tags.forEach(function(tag) { 285 | var method = 'create' + tag.charAt(0).toUpperCase() + tag.slice(1); 286 | p5.prototype[method] = function(html) { 287 | var elt = document.createElement(tag); 288 | elt.innerHTML = typeof html === undefined ? "" : html; 289 | return addElement(elt, this); 290 | } 291 | }); 292 | 293 | /** 294 | * Creates an <img /> element in the DOM with given src and 295 | * alternate text. 296 | * Appends to the container node if one is specified, otherwise 297 | * appends to body. 298 | * 299 | * @method createImg 300 | * @param {String} src src path or url for image 301 | * @param {String} [alt] alternate text to be used if image does not load 302 | * @param {Function} [successCallback] callback to be called once image data is loaded 303 | * @return {Object/p5.Element} pointer to p5.Element holding created node 304 | * @example 305 | *
306 | * var img; 307 | * function setup() { 308 | * img = createImg('http://p5js.org/img/asterisk-01.png'); 309 | * } 310 | *
311 | */ 312 | p5.prototype.createImg = function() { 313 | var elt = document.createElement('img'); 314 | var args = arguments; 315 | var self; 316 | var setAttrs = function(){ 317 | self.width = elt.offsetWidth; 318 | self.height = elt.offsetHeight; 319 | if (args.length > 1 && typeof args[1] === 'function'){ 320 | self.fn = args[1]; 321 | self.fn(); 322 | }else if (args.length > 1 && typeof args[2] === 'function'){ 323 | self.fn = args[2]; 324 | self.fn(); 325 | } 326 | }; 327 | elt.src = args[0]; 328 | if (args.length > 1 && typeof args[1] === 'string'){ 329 | elt.alt = args[1]; 330 | } 331 | elt.onload = function(){ 332 | setAttrs(); 333 | } 334 | self = addElement(elt, this); 335 | return self; 336 | }; 337 | 338 | /** 339 | * Creates an <a></a> element in the DOM for including a hyperlink. 340 | * Appends to the container node if one is specified, otherwise 341 | * appends to body. 342 | * 343 | * @method createA 344 | * @param {String} href url of page to link to 345 | * @param {String} html inner html of link element to display 346 | * @param {String} [target] target where new link should open, 347 | * could be _blank, _self, _parent, _top. 348 | * @return {Object/p5.Element} pointer to p5.Element holding created node 349 | * @example 350 | *
351 | * var myLink; 352 | * function setup() { 353 | * myLink = createA('http://p5js.org/', 'this is a link'); 354 | * } 355 | *
356 | */ 357 | p5.prototype.createA = function(href, html, target) { 358 | var elt = document.createElement('a'); 359 | elt.href = href; 360 | elt.innerHTML = html; 361 | if (target) elt.target = target; 362 | return addElement(elt, this); 363 | }; 364 | 365 | /** INPUT **/ 366 | 367 | 368 | /** 369 | * Creates a slider <input></input> element in the DOM. 370 | * Use .size() to set the display length of the slider. 371 | * Appends to the container node if one is specified, otherwise 372 | * appends to body. 373 | * 374 | * @method createSlider 375 | * @param {Number} min minimum value of the slider 376 | * @param {Number} max maximum value of the slider 377 | * @param {Number} [value] default value of the slider 378 | * @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) 379 | * @return {Object/p5.Element} pointer to p5.Element holding created node 380 | * @example 381 | *
382 | * var slider; 383 | * function setup() { 384 | * slider = createSlider(0, 255, 100); 385 | * slider.position(10, 10); 386 | * slider.style('width', '80px'); 387 | * } 388 | * 389 | * function draw() { 390 | * var val = slider.value(); 391 | * background(val); 392 | * } 393 | *
394 | * 395 | *
396 | * var slider; 397 | * function setup() { 398 | * colorMode(HSB); 399 | * slider = createSlider(0, 360, 60, 40); 400 | * slider.position(10, 10); 401 | * slider.style('width', '80px'); 402 | * } 403 | * 404 | * function draw() { 405 | * var val = slider.value(); 406 | * background(val, 100, 100, 1); 407 | * } 408 | *
409 | */ 410 | p5.prototype.createSlider = function(min, max, value, step) { 411 | var elt = document.createElement('input'); 412 | elt.type = 'range'; 413 | elt.min = min; 414 | elt.max = max; 415 | if (step === 0) { 416 | elt.step = .000000000000000001; // smallest valid step 417 | } else if (step) { 418 | elt.step = step; 419 | } 420 | if (typeof(value) === "number") elt.value = value; 421 | return addElement(elt, this); 422 | }; 423 | 424 | /** 425 | * Creates a <button></button> element in the DOM. 426 | * Use .size() to set the display size of the button. 427 | * Use .mousePressed() to specify behavior on press. 428 | * Appends to the container node if one is specified, otherwise 429 | * appends to body. 430 | * 431 | * @method createButton 432 | * @param {String} label label displayed on the button 433 | * @param {String} [value] value of the button 434 | * @return {Object/p5.Element} pointer to p5.Element holding created node 435 | * @example 436 | *
437 | * var button; 438 | * function setup() { 439 | * createCanvas(100, 100); 440 | * background(0); 441 | * button = createButton('click me'); 442 | * button.position(19, 19); 443 | * button.mousePressed(changeBG); 444 | * } 445 | * 446 | * function changeBG() { 447 | * var val = random(255); 448 | * background(val); 449 | * } 450 | *
451 | */ 452 | p5.prototype.createButton = function(label, value) { 453 | var elt = document.createElement('button'); 454 | elt.innerHTML = label; 455 | elt.value = value; 456 | if (value) elt.value = value; 457 | return addElement(elt, this); 458 | }; 459 | 460 | /** 461 | * Creates a checkbox <input></input> element in the DOM. 462 | * Calling .checked() on a checkbox returns if it is checked or not 463 | * 464 | * @method createCheckbox 465 | * @param {String} [label] label displayed after checkbox 466 | * @param {boolean} [value] value of the checkbox; checked is true, unchecked is false.Unchecked if no value given 467 | * @return {Object/p5.Element} pointer to p5.Element holding created node 468 | * @example 469 | *
470 | * var checkbox; 471 | * 472 | * function setup() { 473 | * checkbox = createCheckbox('label', false); 474 | * checkbox.changed(myCheckedEvent); 475 | * } 476 | * 477 | * function myCheckedEvent() { 478 | * if (this.checked()) { 479 | * console.log("Checking!"); 480 | * } else { 481 | * console.log("Unchecking!"); 482 | * } 483 | * } 484 | *
485 | */ 486 | p5.prototype.createCheckbox = function() { 487 | var elt = document.createElement('div'); 488 | var checkbox = document.createElement('input'); 489 | checkbox.type = 'checkbox'; 490 | elt.appendChild(checkbox); 491 | //checkbox must be wrapped in p5.Element before label so that label appears after 492 | var self = addElement(elt, this); 493 | self.checked = function(){ 494 | var cb = self.elt.getElementsByTagName('input')[0]; 495 | if (cb) { 496 | if (arguments.length === 0){ 497 | return cb.checked; 498 | }else if(arguments[0]){ 499 | cb.checked = true; 500 | }else{ 501 | cb.checked = false; 502 | } 503 | } 504 | return self; 505 | }; 506 | this.value = function(val){ 507 | self.value = val; 508 | return this; 509 | }; 510 | if (arguments[0]){ 511 | var ran = Math.random().toString(36).slice(2); 512 | var label = document.createElement('label'); 513 | checkbox.setAttribute('id', ran); 514 | label.htmlFor = ran; 515 | self.value(arguments[0]); 516 | label.appendChild(document.createTextNode(arguments[0])); 517 | elt.appendChild(label); 518 | } 519 | if (arguments[1]){ 520 | checkbox.checked = true; 521 | } 522 | return self; 523 | }; 524 | 525 | /** 526 | * Creates a dropdown menu <select></select> element in the DOM. 527 | * @method createSelect 528 | * @param {boolean} [multiple] [true if dropdown should support multiple selections] 529 | * @return {Object/p5.Element} pointer to p5.Element holding created node 530 | * @example 531 | *
532 | * var sel; 533 | * 534 | * function setup() { 535 | * textAlign(CENTER); 536 | * background(200); 537 | * sel = createSelect(); 538 | * sel.position(10, 10); 539 | * sel.option('pear'); 540 | * sel.option('kiwi'); 541 | * sel.option('grape'); 542 | * sel.changed(mySelectEvent); 543 | * } 544 | * 545 | * function mySelectEvent() { 546 | * var item = sel.value(); 547 | * background(200); 548 | * text("it's a "+item+"!", 50, 50); 549 | * } 550 | *
551 | */ 552 | p5.prototype.createSelect = function(mult) { 553 | var elt = document.createElement('select'); 554 | if (mult){ 555 | elt.setAttribute('multiple', 'true'); 556 | } 557 | var self = addElement(elt, this); 558 | self.option = function(name, value){ 559 | var opt = document.createElement('option'); 560 | opt.innerHTML = name; 561 | if (arguments.length > 1) 562 | opt.value = value; 563 | else 564 | opt.value = name; 565 | elt.appendChild(opt); 566 | }; 567 | self.selected = function(value){ 568 | var arr = []; 569 | if (arguments.length > 0){ 570 | for (var i = 0; i < this.elt.length; i++){ 571 | if (value.toString() === this.elt[i].value){ 572 | this.elt.selectedIndex = i; 573 | } 574 | } 575 | return this; 576 | }else{ 577 | if (mult){ 578 | for (var i = 0; i < this.elt.selectedOptions.length; i++){ 579 | arr.push(this.elt.selectedOptions[i].value); 580 | } 581 | return arr; 582 | }else{ 583 | return this.elt.value; 584 | } 585 | } 586 | }; 587 | return self; 588 | }; 589 | 590 | /** 591 | * Creates a radio button <input></input> element in the DOM. 592 | * The .option() method can be used to set options for the radio after it is 593 | * created. The .value() method will return the currently selected option. 594 | * 595 | * @method createRadio 596 | * @param {String} [divId] the id and name of the created div and input field respectively 597 | * @return {Object/p5.Element} pointer to p5.Element holding created node 598 | * @example 599 | *
600 | * var radio; 601 | * 602 | * function setup() { 603 | * radio = createRadio(); 604 | * radio.option("black"); 605 | * radio.option("white"); 606 | * radio.option("gray"); 607 | * radio.style('width', '60px'); 608 | * textAlign(CENTER); 609 | * fill(255, 0, 0); 610 | * } 611 | * 612 | * function draw() { 613 | * var val = radio.value(); 614 | * background(val); 615 | * text(val, width/2, height/2); 616 | * } 617 | *
618 | *
619 | * var radio; 620 | * 621 | * function setup() { 622 | * radio = createRadio(); 623 | * radio.option('apple', 1); 624 | * radio.option('bread', 2); 625 | * radio.option('juice', 3); 626 | * radio.style('width', '60px'); 627 | * textAlign(CENTER); 628 | * } 629 | * 630 | * function draw() { 631 | * background(200); 632 | * var val = radio.value(); 633 | * if (val) { 634 | * text('item cost is $'+val, width/2, height/2); 635 | * } 636 | * } 637 | *
638 | */ 639 | p5.prototype.createRadio = function() { 640 | var radios = document.querySelectorAll("input[type=radio]"); 641 | var count = 0; 642 | if(radios.length > 1){ 643 | var length = radios.length; 644 | var prev=radios[0].name; 645 | var current = radios[1].name; 646 | count = 1; 647 | for(var i = 1; i < length; i++) { 648 | current = radios[i].name; 649 | if(prev != current){ 650 | count++; 651 | } 652 | prev = current; 653 | } 654 | } 655 | else if (radios.length == 1){ 656 | count = 1; 657 | } 658 | var elt = document.createElement('div'); 659 | var self = addElement(elt, this); 660 | var times = -1; 661 | self.option = function(name, value){ 662 | var opt = document.createElement('input'); 663 | opt.type = 'radio'; 664 | opt.innerHTML = name; 665 | if (arguments.length > 1) 666 | opt.value = value; 667 | else 668 | opt.value = name; 669 | opt.setAttribute('name',"defaultradio"+count); 670 | elt.appendChild(opt); 671 | if (name){ 672 | times++; 673 | var ran = Math.random().toString(36).slice(2); 674 | var label = document.createElement('label'); 675 | opt.setAttribute('id', "defaultradio"+count+"-"+times); 676 | label.htmlFor = "defaultradio"+count+"-"+times; 677 | label.appendChild(document.createTextNode(name)); 678 | elt.appendChild(label); 679 | } 680 | return opt; 681 | }; 682 | self.selected = function(){ 683 | var length = this.elt.childNodes.length; 684 | if(arguments.length == 1) { 685 | for (var i = 0; i < length; i+=2){ 686 | if(this.elt.childNodes[i].value == arguments[0]) 687 | this.elt.childNodes[i].checked = true; 688 | } 689 | return this; 690 | } else { 691 | for (var i = 0; i < length; i+=2){ 692 | if(this.elt.childNodes[i].checked == true) 693 | return this.elt.childNodes[i].value; 694 | } 695 | } 696 | }; 697 | self.value = function(){ 698 | var length = this.elt.childNodes.length; 699 | if(arguments.length == 1) { 700 | for (var i = 0; i < length; i+=2){ 701 | if(this.elt.childNodes[i].value == arguments[0]) 702 | this.elt.childNodes[i].checked = true; 703 | } 704 | return this; 705 | } else { 706 | for (var i = 0; i < length; i+=2){ 707 | if(this.elt.childNodes[i].checked == true) 708 | return this.elt.childNodes[i].value; 709 | } 710 | return ""; 711 | } 712 | }; 713 | return self 714 | }; 715 | 716 | /** 717 | * Creates an <input></input> element in the DOM for text input. 718 | * Use .size() to set the display length of the box. 719 | * Appends to the container node if one is specified, otherwise 720 | * appends to body. 721 | * 722 | * @method createInput 723 | * @param {Number} [value] default value of the input box 724 | * @return {Object/p5.Element} pointer to p5.Element holding created node 725 | * @example 726 | *
727 | * function setup(){ 728 | * var inp = createInput(''); 729 | * inp.input(myInputEvent); 730 | * } 731 | * 732 | * function myInputEvent(){ 733 | * console.log('you are typing: ', this.value()); 734 | * } 735 | * 736 | *
737 | */ 738 | p5.prototype.createInput = function(value) { 739 | var elt = document.createElement('input'); 740 | elt.type = 'text'; 741 | if (value) elt.value = value; 742 | return addElement(elt, this); 743 | }; 744 | 745 | /** 746 | * Creates an <input></input> element in the DOM of type 'file'. 747 | * This allows users to select local files for use in a sketch. 748 | * 749 | * @method createFileInput 750 | * @param {Function} [callback] callback function for when a file loaded 751 | * @param {String} [multiple] optional to allow multiple files selected 752 | * @return {Object/p5.Element} pointer to p5.Element holding created DOM element 753 | */ 754 | p5.prototype.createFileInput = function(callback, multiple) { 755 | 756 | // Is the file stuff supported? 757 | if (window.File && window.FileReader && window.FileList && window.Blob) { 758 | // Yup, we're ok and make an input file selector 759 | var elt = document.createElement('input'); 760 | elt.type = 'file'; 761 | 762 | // If we get a second argument that evaluates to true 763 | // then we are looking for multiple files 764 | if (multiple) { 765 | // Anything gets the job done 766 | elt.multiple = 'multiple'; 767 | } 768 | 769 | // Function to handle when a file is selected 770 | // We're simplifying life and assuming that we always 771 | // want to load every selected file 772 | function handleFileSelect(evt) { 773 | // These are the files 774 | var files = evt.target.files; 775 | // Load each one and trigger a callback 776 | for (var i = 0; i < files.length; i++) { 777 | var f = files[i]; 778 | var reader = new FileReader(); 779 | function makeLoader(theFile) { 780 | // Making a p5.File object 781 | var p5file = new p5.File(theFile); 782 | return function(e) { 783 | p5file.data = e.target.result; 784 | callback(p5file); 785 | }; 786 | }; 787 | reader.onload = makeLoader(f); 788 | 789 | // Text or data? 790 | // This should likely be improved 791 | if (f.type.indexOf('text') > -1) { 792 | reader.readAsText(f); 793 | } else { 794 | reader.readAsDataURL(f); 795 | } 796 | } 797 | } 798 | 799 | // Now let's handle when a file was selected 800 | elt.addEventListener('change', handleFileSelect, false); 801 | return addElement(elt, this); 802 | } else { 803 | console.log('The File APIs are not fully supported in this browser. Cannot create element.'); 804 | } 805 | }; 806 | 807 | 808 | /** VIDEO STUFF **/ 809 | 810 | function createMedia(pInst, type, src, callback) { 811 | var elt = document.createElement(type); 812 | 813 | // allow src to be empty 814 | var src = src || ''; 815 | if (typeof src === 'string') { 816 | src = [src]; 817 | } 818 | for (var i=0; i