hilbert-clock/script.js
Christian Lawson-Perfect 2c7badf010 It's a proper clock! The hands fill up to one end, then empty, then it all starts again.
The period is 60 seconds for the small hand, and 60 minutes for the big hand, so it matches a standard clock.
2025-03-17 09:52:21 +00:00

93 lines
No EOL
2.4 KiB
JavaScript

let steps = 3;
console.clear();
const rules = {
"A": "+BF-AFA-FB+",
"B": "-AF+BFB+FA-",
};
steps = Math.min(steps,5); // make sure I don't type a huge number that crashes my PC
let s = "A";
for(let i=0;i<steps;i++) {
let ns = "";
for(let c of s) {
ns += rules[c] || c;
}
s = ns;
}
let [x,y] = [0,0];
const m = 1/(2**steps-1);
let dir = [m,0];
let d = 'M 0 0 ';
const width = 1.5 * 2**-(steps+1);
const dots = [];
for(let c of s) {
const [dx,dy] = dir;
switch(c) {
case '+':
dir = [-dy,dx];
break;
case '-':
dir = [dy,-dx];
break;
case 'F':
x += dx;
y += dy;
d += `l ${dx} ${dy}`;
}
}
d += 'l 0.000000001 0.000001'; // make sure the final dot is drawn
const length = (2**(2*steps)-1) * m;
const curves = Array.from(document.querySelectorAll('.curve'));
for(let curve of curves) {
curve.setAttribute('stroke-width',width);
curve.setAttribute('stroke-linecap','round');
curve.setAttribute('stroke-dasharray',`${length} ${length}`);
curve.setAttribute('d',d);
}
document.getElementById('dots').setAttribute('stroke-width',width*0.3);
document.getElementById('dots').setAttribute('d',d);
document.getElementById('dots').setAttribute('stroke-dasharray',`0 ${m}`);
function easeInOutSine(x) {
return -(Math.cos(Math.PI * x) - 1) / 2;
}
function easeLinear(t) {
return 1-2*Math.abs(t % 1 - 0.5);
}
function frame() {
const segments = 2**(2*steps)-1; // number of segments in the curve
const period = 1000*60/segments; // One period of the small hand is 60 seconds
let scale = segments**(curves.length-1);
curves.forEach((curve,i) => {
const now = new Date();
const midnight = new Date(now.getFullYear(),now.getMonth(),now.getDate());
const dt = (new Date() - midnight) / scale / period;
const m = dt % (2*segments);
const t = i == 0 ? m : (Math.floor(m) + easeInOutSine(m % 1)); // linear for big hand, eased for small hand
const a = t/segments; // between 0 and 2
const b = 2*(1-Math.abs(1-a/2));
const end = Math.min(b,1)*length;
const start = Math.max(0,b-1)*length;
//document.getElementById('debug').textContent = `${m.toFixed(4)} ${length} ${segments}`
curve.setAttribute('stroke-dasharray',`${end-start} ${2*length}`);
curve.setAttribute('stroke-dashoffset',-start);
curve.setAttribute('stroke-width',width/2**(i));
scale /= segments;
})
requestAnimationFrame(frame);
}
frame();