xxxxxxxxxx
// Fibonacci Clock Algorithm Visualization
// Based on responsive-design.js template
// Canvas dimensions
let canvasWidth = 400;
let drawHeight = 570;
let controlHeight = 80;
let canvasHeight = drawHeight + controlHeight;
let margin = 25;
let sliderLeftMargin = 105;
let defaultTextSize = 16;
// Container dimensions (calculated on resize)
let containerWidth;
let containerHeight = canvasHeight;
// Fibonacci clock cell values
const fibValues = [1, 1, 2, 3, 5];
// cell Size
cs = 75;
// Clock cells layout
const cells = [
{ value: 1, x: cs*2, y: 100, width: cs, height: cs}, // First 1-square (oneA)
{ value: 1, x: cs*2, y: 175, width: cs, height: cs }, // Second 1-square (oneB)
{ value: 2, x: 0, y: 100, width: cs*2, height: cs*2 }, // 2-square
{ value: 3, x: 0, y: 250, width: 225, height: 225 }, // 3-square
{ value: 5, x: 225, y: 100, width: 375, height: 375 } // 5-square
];
// Colors
const OFF_COLOR = "#333333"; // Dark gray for "off" squares
const HOUR_COLOR = "#FF4136"; // Red for hours
const MINUTE_COLOR = "#2ECC40"; // Green for minutes
const BOTH_COLOR = "#0074D9"; // Blue for both hours and minutes
const TEXT_COLOR = "#FFFFFF"; // White text
// UI controls
let hourSlider;
let minuteSlider;
let hours = 0;
let minutes = 0;
// State of each cell: 0=off, 1=hour, 2=minute, 3=both
let state = [0, 0, 0, 0, 0];
function setup() {
// Create a canvas to match the parent container's size
updateCanvasSize();
const canvas = createCanvas(containerWidth, containerHeight);
canvas.parent(document.querySelector('main'));
// Create hour slider (0-12)
hourSlider = createSlider(0, 12, 9);
hourSlider.position(sliderLeftMargin, drawHeight + 20);
hourSlider.size(canvasWidth - sliderLeftMargin - 15);
// Create minute slider (0-59)
minuteSlider = createSlider(0, 59, 39);
minuteSlider.position(sliderLeftMargin, drawHeight + 50);
minuteSlider.size(canvasWidth - sliderLeftMargin - 15);
}
function draw() {
// Draw background regions
stroke('silver');
strokeWeight(1);
fill('aliceblue');
rect(0, 0, canvasWidth, drawHeight);
fill('white');
rect(0, drawHeight, canvasWidth, controlHeight);
// Get values from sliders
hours = hourSlider.value();
if (hours === 0) hours = 12; // Convert 0 to 12 for display
minutes = minuteSlider.value();
// Calculate state array based on hours and minutes
state = fibTime(hours, minutes);
// Draw title
fill('black');
strokeWeight(0);
textSize(24);
textAlign(CENTER, TOP);
text('Fibonacci Clock Algorithm', canvasWidth / 2, 10);
// Draw fibonacci clock
drawFibonacciClock();
// Draw equation representations for hours and minutes
drawEquations();
// Draw state display
drawStateDisplay();
// Draw slider labels
fill('black');
textSize(16);
textAlign(LEFT, CENTER);
text('Hours: ' + hours, 10, drawHeight + 30);
text('Minutes: ' + minutes, 10, drawHeight + 60);
}
// Calculate the state of each cell based on hours and minutes
function fibTime(hours, minutes) {
const newState = [0, 0, 0, 0, 0];
// Calculate Fibonacci representation for hours
let remainingHours = hours;
let idx = fibValues.length - 1;
// Step through values in reverse order (largest first)
for (let i = idx; i >= 0; i--) {
if (remainingHours === 0 || i < 0) break;
if (remainingHours >= fibValues[i]) {
newState[i] += 1; // 1 represents hours
remainingHours -= fibValues[i];
}
}
// Calculate Fibonacci representation for minutes (in increments of 5)
let remainingMinutes = Math.floor(minutes / 5);
idx = fibValues.length - 1;
for (let i = idx; i >= 0; i--) {
if (remainingMinutes === 0 || i < 0) break;
if (remainingMinutes >= fibValues[i]) {
newState[i] += 2; // 2 represents minutes, 3 (1+2) represents both
remainingMinutes -= fibValues[i];
}
}
return newState;
}
// Draw the Fibonacci clock cells with appropriate colors based on state
function drawFibonacciClock() {
push();
translate(0,-50);
// Loop through each of the five cells
cells.forEach((cell, index) => {
// Determine color based on state
let fillColor;
switch (state[index]) {
case 1: fillColor = HOUR_COLOR; break; // Hour only
case 2: fillColor = MINUTE_COLOR; break; // Minute only
case 3: fillColor = BOTH_COLOR; break; // Both
default: fillColor = OFF_COLOR; // Off
}
// Draw cell
fill(fillColor);
stroke(0);
strokeWeight(2);
rect(cell.x, cell.y, cell.width, cell.height);
// Draw value in cell
fill(TEXT_COLOR);
noStroke();
textSize(cell.value === 1 ? 20 : 28);
textAlign(CENTER, CENTER);
text(cell.value, cell.x + cell.width / 2, cell.y + cell.height / 2);
});
pop();
}
// Display the state values
function drawStateDisplay() {
push();
// for moving the entire state display
translate(0,0);
fill('black');
textSize(18);
textAlign(LEFT, TOP);
text('Cell States (0=off, 1=hour, 2=minute, 3=both)', 20, drawHeight - 70);
// Display each value
textAlign(CENTER, TOP);
textSize(16);
for (let i = 0; i < state.length; i++) {
// Determine cell name
let cellName;
if (i === 0) cellName = "1A";
else if (i === 1) cellName = "1B";
else cellName = fibValues[i].toString();
// Draw cell labels
fill('black');
text(`${cellName}: ${state[i]}`, 50 + i * 75, drawHeight - 40);
}
pop();
}
// Draw equations showing how hours and minutes are calculated
function drawEquations() {
const hourComponents = [];
const minuteComponents = [];
// Collect components for each equation
for (let i = 0; i < state.length; i++) {
if (state[i] === 1 || state[i] === 3) {
hourComponents.push(fibValues[i]);
}
if (state[i] === 2 || state[i] === 3) {
minuteComponents.push(fibValues[i]);
}
}
textAlign(LEFT, TOP);
textSize(18);
// Draw hours equation
let x = 0;
let y = drawHeight - 150;
push()
translate(10, 20)
fill('black');
text("Hours: ", x, y);
x += 80;
// Draw hour components with appropriate colors
if (hourComponents.length > 0) {
for (let i = 0; i < hourComponents.length; i++) {
// Determine if this component is also used for minutes
const isAlsoMinutes = minuteComponents.includes(hourComponents[i]) &&
state[fibValues.indexOf(hourComponents[i])] === 3;
fill(isAlsoMinutes ? BOTH_COLOR : HOUR_COLOR);
text(hourComponents[i].toString(), x, y);
x += 20;
if (i < hourComponents.length - 1) {
fill('black');
text(" + ", x, y);
x += 30;
}
}
fill('black');
text(" = " + hours, x, y);
} else {
fill('black');
text("None = " + hours, x, y);
}
// Draw minutes equation
x = 0;
y = drawHeight - 120;
fill('black');
text("Minutes: ", x, y);
x += 80;
if (minuteComponents.length > 0) {
// Draw first part of the equation
for (let i = 0; i < minuteComponents.length; i++) {
// Determine if this component is also used for hours
const isAlsoHours = hourComponents.includes(minuteComponents[i]) &&
state[fibValues.indexOf(minuteComponents[i])] === 3;
fill(isAlsoHours ? BOTH_COLOR : MINUTE_COLOR);
text(minuteComponents[i].toString(), x, y);
x += 20;
if (i < minuteComponents.length - 1) {
fill('black');
text(" + ", x, y);
x += 30;
}
}
const minuteSum = minuteComponents.reduce((sum, val) => sum + val, 0);
fill('black');
text(" = " + minuteSum + " × 5 = " + (minuteSum * 5), x, y);
} else {
fill('black');
text("None = 0", x, y);
}
pop();
}
function windowResized() {
// Update canvas size when the container resizes
updateCanvasSize();
resizeCanvas(containerWidth, containerHeight);
// Resize sliders
hourSlider.size(canvasWidth - sliderLeftMargin - 15);
minuteSlider.size(canvasWidth - sliderLeftMargin - 15);
}
function updateCanvasSize() {
// Get the width of the <main> element
const container = document.querySelector('main').getBoundingClientRect();
containerWidth = Math.floor(container.width); // Avoid fractional pixels
// Set the canvas width to the container width
canvasWidth = containerWidth;
}