The idea here was to use a binary representation for a clock. Green is 1, red is 0. The clock hours with the back square as the MSB, while minutes have the back square as the LSB. If the back left square is green, that means it’s 8 o’clock, and if the back right square is green, that means it’s 8:01. The back building, tells whether it is afternoon or not. This is also the return of 3D space in my projects. Hooray.
function setup() {
createCanvas(600, 600);
background(220);
text("p5.js vers 0.9.0 test.", 10, 15);
}
/*
Idea: Create a small town that tells the time based on what's "open."
Doors will represent a binary pattern on each side of town.
A building in the back tells whether it is am or pm.
*/
function draw() {
background(0);
fill(255);
stroke("#FF00FF");
// fill the floor and make a road
drawCube(-10000, 200, 1, 20000, 0, 20000);
fill(128);
drawCube(-200, 200, 1, 400, 0, 20000);
// draw am pm tower
fill("#800080");
drawCube(-200, -400, 2400, 400, 600, 400);
fill(hour() < 12 ? "#800000" : "#008000");
drawCube(-100, -300, 2400, 200, 200, 0);
// get time and store it
let h = hour() % 12;
let m = minute();
var hStr = h.toString(2);
var mStr = m.toString(2);
// draw cubes and doors
for (var i = 0; i < 4; i++) {
fill("#800080");
drawCube(200, -400, 1800 - (i * 500), 800, 600, 400);
drawCube(-200, -400, 1800 - (i * 500), -800, 600, 400);
// hour squares
fill(hStr[i] == 1 ? "#008000" : "#800000");
drawCube(-200, (i * -100), 1900 - (i * 500), 100, 100, 0);
// minute squares
fill(mStr[i] == 1 ? "#008000" : "#800000");
drawCube(200, (i * -100), 1900 - (i * 500), -100, 100, 0);
if (i > 1) {
fill(mStr[i+2] == 1 ? "#008000" : "#800000");
drawCube(200, 200 + (i * -100), 1900 - (i * 500), -100, 100, 0);
}
}
}
// the return of the 3d projection matrix
// uses projection matrix seen in this video:
// https://www.youtube.com/watch?v=ih20l3pJoeU
// the video is mostly math about projecting 3d coordinates to 2d coordinates
function projMatrixMult(coords) {
// aspect ratio
var a = width / height;
// field of view
var fov = HALF_PI;
f = 1 / tan(fov / 2);
// range of view
var zNear = 0.1;
var zFar = 1000;
var q = zFar / (zFar - zNear);
if (coords.length != 3) {
print("Improper array size.");
return coords;
} else {
// this calculates the result of multiplying [x, y, z, 1]
// with a 4x4 projection matrix (not shown for lack of use without
// the math.js extension)
let projMat = [a * f * coords[0], f * coords[1],
coords[2] * q - zNear * q, coords[2]];
if (coords[2] != 0) {
projMat[0] /= projMat[3];
projMat[1] /= projMat[3];
projMat[2] /= projMat[3];
projMat[3] /= projMat[3];
return projMat;
} else {
print("z is equal to 0");
return coords;
}
}
}
// self explanatory
// note to self: add rotation parameters
// note to self: generalize for any number of points greater than 4 (later)
function drawCube(x, y, z, wid, hei, d) {
// push 3d coords to an array
let cubePoints3D = [];
cubePoints3D.push([x, y + hei, z]); // front bottom left 0
cubePoints3D.push([x, y, z]); // front top left 1
cubePoints3D.push([x + wid, y + hei, z]); // front bottom right 2
cubePoints3D.push([x + wid, y, z]); // front top right 3
cubePoints3D.push([x, y + hei, z + d]); // back bottom left 4
cubePoints3D.push([x, y, z + d]); // back top left 5
cubePoints3D.push([x + wid, y + hei, z + d]); // back bottom right 6
cubePoints3D.push([x + wid, y, z + d]); // back top right 7
// get projection and add to list of points
let cubeProj = [];
for (let i = 0; i < cubePoints3D.length; i++) {
cubeProj.push(projMatrixMult(cubePoints3D[i]));
}
// this scales the image to be on screen
for (let i = 0; i < cubeProj.length; i++) {
cubeProj[i][0] += 1;
cubeProj[i][1] += 1;
cubeProj[i][0] *= width / 2;
cubeProj[i][1] *= height / 2;
}
// need 6 quads for the 6 faces of the cube, draw the further quads first
// realistically, without camera movement or rotation, i can draw only
// 5 quads and still have it look find regardless of placement
// if i add rotation, i need to add conditionals for what is drawn first
// back face
quad(cubeProj[7][0], cubeProj[7][1], cubeProj[6][0], cubeProj[6][1],
cubeProj[4][0], cubeProj[4][1], cubeProj[5][0], cubeProj[5][1]);
// bottom
quad(cubeProj[6][0], cubeProj[6][1], cubeProj[4][0], cubeProj[4][1],
cubeProj[0][0], cubeProj[0][1], cubeProj[2][0], cubeProj[2][1]);
// top
quad(cubeProj[7][0], cubeProj[7][1], cubeProj[5][0], cubeProj[5][1],
cubeProj[1][0], cubeProj[1][1], cubeProj[3][0], cubeProj[3][1]);
if (x > 0) {
// left
quad(cubeProj[5][0], cubeProj[5][1], cubeProj[4][0], cubeProj[4][1],
cubeProj[0][0], cubeProj[0][1], cubeProj[1][0], cubeProj[1][1]);
// right
quad(cubeProj[7][0], cubeProj[7][1], cubeProj[6][0], cubeProj[6][1],
cubeProj[2][0], cubeProj[2][1], cubeProj[3][0], cubeProj[3][1]);
} else {
// right
quad(cubeProj[7][0], cubeProj[7][1], cubeProj[6][0], cubeProj[6][1],
cubeProj[2][0], cubeProj[2][1], cubeProj[3][0], cubeProj[3][1]);
// left
quad(cubeProj[5][0], cubeProj[5][1], cubeProj[4][0], cubeProj[4][1],
cubeProj[0][0], cubeProj[0][1], cubeProj[1][0], cubeProj[1][1]);
}
// front
quad(cubeProj[3][0], cubeProj[3][1], cubeProj[2][0], cubeProj[2][1],
cubeProj[0][0], cubeProj[0][1], cubeProj[1][0], cubeProj[1][1]);
// the lines are no longer necessary
}