Project 06: Abstract Clock

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.

yeeeeeeeeeeeeet

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
}

Leave a Reply