xxxxxxxxxx
413
/*
Slider sets "exploded view-ed-ness"
"a" toggles alpha on dial
"f" toggles full screen
mouse around to pan/zoom/orbit
A rough 3D model of a Day/Date movement like the Seiko NH36
Makes use of pre-rendered assets with alpha masks.
Using texture + plane in WEBGL mode is much faster than image();
hour and minute hands are not rigged up yet.
Day and date dials are on autopilot every three seconds (with an offset between)
Getting some time in on P5 3D lighting, primitives, and orbit control (all pretty basic);
*/
let d = 800;
let w = d,
h = d,
r = d/2,
w2 = w/2,
h2 = h/2;
let dayRing,
dateRing,
dial,
dialShadow; //buffers for weekday and date rings, dial
let hourHand,
minuteHand,
secondHand;
let weekIndex = 0, //inconsistent term week or "day of week"
dayIndex = 0,
weekRotation = 0,
weekRotationTarget = 0,
dayRotation = 0,
dayRotationTarget = 0,
rotationRate = 0.1;
let sRotation = 0; //will hold second hand position, the only animated part of this sketch
let dialAlpha = 255,
dialAlphaTarget = 255,
dialAlphaRate = 0.1;
let explode;
function setup() {
createCanvas(w, h, WEBGL);
dial = createGraphics(w, h);
drawDial(dial);
dayRing = createGraphics(w, h);
drawDayRing(dayRing);
dateRing = createGraphics(w, h);
drawDateRing(dateRing);
hourHand = createGraphics(r/6, r*0.6*2);
drawHourHand(hourHand);
minuteHand = createGraphics(r/8, r*0.85*2);
drawMinuteHand(minuteHand);
secondHand = createGraphics(r/8, r*0.85*2);
drawSecondHand(secondHand);
setInterval(function() {
weekIndex += 2;
setTimeout(function() {
dayIndex+=1;
}, 400)
}, 2000);
setInterval(function() {
sRotation = millis()/1000/60 * TWO_PI;
}, 125);
explode = createSlider(1, 1000, 1);
explode.size(500);
explode.position(0,0);
//debugMode();
//noLoop();
}
function draw() {
//background(220);
let f = explode.value();
//let f = 1;
clear();
orbitControl();
ambientLight(200,200,200);
directionalLight(255, 255, 255, 1, 1, -1);
translate(0,0,-300);
//rotateX(0.1);
//rotateY(0.8);
imageMode(CENTER);
tint(255);
push();
translate(0,0,-(0+0.2*f));
renderMovement();
pop();
//place day ring
dayRotationTarget = -dayIndex * TWO_PI/31;
dayRotation += rotationRate * (dayRotationTarget - dayRotation)
push();
rotate(dayRotation);
texture(dateRing);
//image(dateRing, 0, 0, 0);
noStroke();
translate(0,0,(2+0.3*f));
plane(w, h);
pop();
//place date ring
weekRotationTarget = -weekIndex * TWO_PI/14;
weekRotation += rotationRate * (weekRotationTarget - weekRotation)
push();
rotate(weekRotation);
texture(dayRing);
//image(dayRing, 0, 0, 0);
translate(0,0,(2+0.4*f));
noStroke();
plane(w, h);
pop();
//place dial
push();
dialAlpha += dialAlphaRate*(dialAlphaTarget - dialAlpha);
tint(255, dialAlpha);
texture(dial);
translate(0,0,5+0.5*f);
//image(dialShadow, r*0.65, 14, 0);
//image(dial, 0, 0, 0);
noStroke();
plane(w, h);
pop();
//place markers:
push();
translate(0,0,(0+0.8*f));
renderMarkers();
pop();
//hour hand
push();
translate(0,0,(20+0.9*f));
texture(hourHand);
//image(hourHand, 0, -100);
rotateZ(-1);
plane(hourHand.width, hourHand.height);
pop();
//minute hand
push();
translate(0,0,(30+1.0*f));
texture(minuteHand);
//image(hourHand, 0, -100);
rotateZ(1);
plane(minuteHand.width, minuteHand.height);
pop();
//second hand
push();
translate(0,0,(40+1.1*f));
texture(secondHand);
//image(hourHand, 0, -100);
rotateZ(sRotation);
plane(secondHand.width, secondHand.height);
pop();
noStroke();
textSize(30);
strokeWeight(1);
//text(nf(frameRate(), 2,1), 30,30);
//print(nf(frameRate(), 2,1));
}
function drawHourHand(g) {
g.fill(0);
g.noStroke();
g.ellipseMode(CENTER);
g.push();
g.h2 = g.height/2;
for (let i=0; i<2; i++) {
g.ellipse(g.width/2,g.width/2,g.width,g.width);
g.rect(0,g.width/2, g.width, g.h2-g.width/2);
g.ellipse(g.width/2,g.h2,g.width,g.width);
g.scale(0.7);
g.translate(14,15,0);
g.fill(255);
}
g.pop();
g.blendMode(REMOVE);
g.ellipse(g.width/2, g.h2, r/8, r/8);
}
function drawMinuteHand(g) {
g.fill(0);
g.noStroke();
g.ellipseMode(CENTER);
g.push();
g.h2 = g.height/2;
for (let i=0; i<2; i++) {
g.ellipse(g.width/2,g.width/2,g.width,g.width);
g.rect(0,g.width/2, g.width, g.h2-g.width/2);
g.ellipse(g.width/2,g.h2,g.width,g.width);
g.scale(0.7);
g.translate(11,12,0);
g.fill(255);
}
g.pop();
g.stroke(0);
g.strokeWeight(10);
g.line(0,80, g.width/2, 60);
g.line(g.width/2, 60, g.width, 80);
g.blendMode(REMOVE);
g.ellipse(g.width/2, g.h2, r/16, r/16);
}
function drawSecondHand(g) {
let c1 = color(190, 32, 32);
g.stroke(c1);
g.strokeWeight(10);
g.h2 = g.height/2;
g.line(g.width/2,0,g.width/2,g.h2+80);
g.fill(c1);
g.ellipse(g.width/2, g.h2, 25, 25);
g.fill(255);
g.ellipse(g.width/2, 80, 35, 35);
g.blendMode(REMOVE);
g.fill(0);
g.noStroke();
g.ellipse(g.width/2, g.h2, 10, 10);
}
function renderMovement() {
//build a rudimentary movement
noStroke();
fill(128);
push();
translate(0,0,-20);
rotateX(PI/2);
cylinder(r, 30, 64);
translate(0,30,0);
fill(100);
cylinder(r/16, 40);
fill(64);
cylinder(r/24, 60);
fill(32);
cylinder(r/100, 80);
pop();
}
function renderMarkers() {
push();
translate(0,0,6);
push();
scale(0.5,1,0.2);
//fill(200,200,200, dialAlpha);
translate(-r/7,-r*0.8,0);
specularMaterial(200, dialAlpha);
shininess(1);
box(r/4);
translate(2*r/7, 0, 0);
box(r/4);
pop();
push();
rotateZ(-PI/2);
scale(0.5,1,0.2);
translate(0, -r*0.8, 0);
//fill(200,200,200, dialAlpha);
specularMaterial(200, dialAlpha);
shininess(1);
box(r/4);
pop();
for (let i=0; i<12; i++) {
if (i!==0 && i!=3 && i!==9) {
push();
rotateZ(TWO_PI/12 * i);
translate(0,-r*0.8, 5);
rotateX(PI/2);
specularMaterial(200, dialAlpha);
shininess(1);
cylinder(40, 10, 64);
pop();
}
}
pop();
}
function drawDial(g) {
dialShadow = createGraphics(300,100); //temp buffer to draw a blurred shadow in the date window
dialShadow.noFill(0);
dialShadow.stroke(0, 128);
dialShadow.strokeWeight(12);
dialShadow.rect(0,0, r*0.65, r/6); //todo: calculate height as proportion
dialShadow.filter(BLUR, 12);
let salmon = color(196,156,127);
let deepBlue = color(63,73,95);
g.fill(deepBlue);
g.noStroke();
g.ellipseMode(CENTER);
g.ellipse(w2, h2, d, d);
g.push();
g.strokeWeight(3);
g.stroke(255);
g.translate(w2, h2);
for (let i=0; i<60; i++) {
if (i%5==0) g.line(0, -w2+5, 0, -w2+40);
else g.line(0, -w2+5, 0, -w2+20)
g.rotate(TWO_PI/60);
}
g.pop();
g.rectMode(CENTER);
g.strokeWeight(20);
g.stroke(128);
g.rect(0.8*w, h2-2, r*0.65, r/6); //todo: calculate height as proportion
g.blendMode(REMOVE);
g.fill(0);
g.noStroke();
g.ellipse(w2,h2,r/5, r/5);
g.rect(0.8*w, h2-2, r*0.65, r/6); //todo: calculate height as proportion
}
function drawDayRing(g) {
let dayNames = ["MON", "LUN", "TUE", "MAR",
"WED", "MIE", "THU", "JUE",
"FRI", "VIE", "SAT", "SAB",
"SUN", "DOM"];
g.strokeWeight(d*0.23);
g.stroke(255);
g.noFill();
let p = 0.43;
g.ellipse(w2, h2, d*p, d*p);
g.push();
g.translate(w2, h2);
g.strokeWeight(1);
g.stroke(0);
g.fill(0);
g.textAlign(CENTER, CENTER);
g.textSize(60);
g.push();
let c1 = color(64, 64, 255);
let c2 = color(255, 64, 64);
for (let i=0; i<14; i++) {
if (i<=9) {
g.stroke(0);
g.fill(0);
}
if (i==10 || i==11) {
g.stroke(c1);
g.fill(c1);
}
if (i==12 || i==13) {
g.stroke(c2);
g.fill(c2);
}
g.text(dayNames[i], r*p*1.1, 0);
g.rotate(TWO_PI/14);
}
g.pop();
g.pop();
}
function drawDateRing(g) {
g.strokeWeight(d*0.13);
g.stroke(255);
g.noFill();
let p = 0.8;
g.ellipse(w2, h2, d*p, d*p);
g.push();
g.translate(w2, h2);
//translate(r, 0);
g.strokeWeight(1);
g.stroke(0);
g.fill(0);
g.textAlign(CENTER, CENTER);
for (let i=1; i<=31; i++) {
if (i<10) g.textSize(80);
else g.textSize(70);
g.text(i, r*p, 0);
g.rotate(TWO_PI/31);
}
g.pop();
}
function keyPressed() {
if (key=='w') {
weekIndex += 1;
}
if (key=='d') {
dayIndex += 1;
}
if (key=='a') {
if (dialAlphaTarget == 0) dialAlphaTarget = 255;
else (dialAlphaTarget = 0);
}
if (key=='r') {
resetMatrix();
}
if (key=='f') {
let fs = fullscreen();
fullscreen(!fs);
}
}