xxxxxxxxxx
232
let inputLines;
let antennas = {};
let WIDTH = 0;
let HEIGHT = 0;
let scaleFactor = 20;
let animationSpeed = 15; // speed of line drawing progress per frame
let steps = []; // queue of animation steps
function preload() {
inputLines = loadStrings('input.txt');
}
function setup() {
parseInput();
createCanvas((WIDTH+1)*scaleFactor, (HEIGHT+1)*scaleFactor);
frameRate(30);
// Build the animation steps
buildAnimationSteps();
}
function draw() {
// background(255);
// Draw grid
stroke(230);
for (let x = 0; x <= WIDTH; x++) {
line(x*scaleFactor, 0, x*scaleFactor, HEIGHT*scaleFactor);
}
for (let y = 0; y <= HEIGHT; y++) {
line(0, y*scaleFactor, WIDTH*scaleFactor, y*scaleFactor);
}
// Draw antenna points
noStroke();
fill(0);
for (let letter in antennas) {
for (let coord of antennas[letter]) {
let x = coord[0]*scaleFactor;
let y = coord[1]*scaleFactor;
ellipse(x, y, scaleFactor*0.5, scaleFactor*0.5);
}
}
// Update and draw steps
if (steps.length > 0) {
let currentStep = steps[0];
if (currentStep.type === 'line') {
// Animate line drawing
stroke(currentStep.color || [0,0,255]);
strokeWeight(2);
let dx = currentStep.x2 - currentStep.x1;
let dy = currentStep.y2 - currentStep.y1;
currentStep.progress = min(currentStep.progress + animationSpeed, 1);
let xEnd = currentStep.x1 + dx * currentStep.progress;
let yEnd = currentStep.y1 + dy * currentStep.progress;
line(currentStep.x1*scaleFactor, currentStep.y1*scaleFactor, xEnd*scaleFactor, yEnd*scaleFactor);
if (currentStep.progress >= 1) {
// Step done
steps.shift();
}
} else if (currentStep.type === 'point') {
// Just show a point (instant)
noStroke();
fill(currentStep.color || [255,0,0]);
ellipse(currentStep.x*scaleFactor, currentStep.y*scaleFactor, scaleFactor*0.5, scaleFactor*0.5);
// After a short delay, we move on
currentStep.timer = (currentStep.timer || 0) + 1;
if (currentStep.timer > 30) { // wait ~1 second at 30 fps
steps.shift();
}
} else if (currentStep.type === 'wait') {
// Just wait a certain number of frames
currentStep.timer = (currentStep.timer || 0) + 1;
if (currentStep.timer >= currentStep.duration) {
steps.shift();
}
}
// Draw all previously completed lines fully
// and points that have been revealed
for (let i=1; i<steps.length; i++) {
let s = steps[i];
if (s.done && s.type==='line') {
stroke(s.color || [0,0,255]);
strokeWeight(2);
line(s.x1*scaleFactor, s.y1*scaleFactor, s.x2*scaleFactor, s.y2*scaleFactor);
}
if (s.done && s.type==='point') {
noStroke();
fill(s.color || [255,0,0]);
ellipse(s.x*scaleFactor, s.y*scaleFactor, scaleFactor*0.5, scaleFactor*0.5);
}
}
} else {
// All steps done
// Draw all final lines and points
for (let s of steps) {
if (s.type==='line') {
stroke(s.color || [0,0,255]);
strokeWeight(2);
line(s.x1*scaleFactor, s.y1*scaleFactor, s.x2*scaleFactor, s.y2*scaleFactor);
} else if (s.type==='point') {
noStroke();
fill(s.color || [255,0,0]);
ellipse(s.x*scaleFactor, s.y*scaleFactor, scaleFactor*0.5, scaleFactor*0.5);
}
}
}
}
// ----------------------
// Helper functions
// ----------------------
function parseInput() {
// Parse the input lines and build antennas dictionary
for (let y = 0; y < inputLines.length; y++) {
let line = inputLines[y];
for (let x = 0; x < line.length; x++) {
let c = line[x];
if (isLetter(c)) {
if (!antennas[c]) antennas[c] = [];
antennas[c].push([x, y]);
}
WIDTH = max(WIDTH, x);
HEIGHT = max(HEIGHT, y);
}
}
}
function buildAnimationSteps() {
// For each pair of antenna points for each letter:
// Step 1: Connect them (line)
// Step 2: Find Q1, Q2
// Step 3: Show Q1, Q2
// Step 4: Connect A->Q1, B->Q1
// Step 5: Connect A->Q2, B->Q2
for (let letter in antennas) {
let coords = antennas[letter];
for (let i = 0; i < coords.length; i++) {
for (let j = i+1; j < coords.length; j++) {
let A = coords[i];
let B = coords[j];
// Add step: line from A to B
steps.push({
type: 'line',
x1: A[0], y1: A[1],
x2: B[0], y2: B[1],
progress:0
});
// After line is drawn, find Q1, Q2
let [Q1, Q2] = findEquidistantPoints(A, B);
// Add step: wait
steps.push({type:'wait', duration:30}); // short pause
// Add step: point Q1
steps.push({
type:'point',
x: Q1[0],
y: Q1[1]
});
// Add step: point Q2
steps.push({
type:'point',
x: Q2[0],
y: Q2[1]
});
// Add small wait
steps.push({type:'wait', duration:30});
// Add step: line A->Q1
steps.push({
type:'line',
x1: A[0], y1: A[1],
x2: Q1[0], y2: Q1[1],
progress:0,
color:[255,0,0]
});
// Add step: line B->Q1
steps.push({
type:'line',
x1: B[0], y1: B[1],
x2: Q1[0], y2: Q1[1],
progress:0,
color:[255,0,0]
});
// Add step: line A->Q2
steps.push({
type:'line',
x1: A[0], y1: A[1],
x2: Q2[0], y2: Q2[1],
progress:0,
color:[255,0,0]
});
// Add step: line B->Q2
steps.push({
type:'line',
x1: B[0], y1: B[1],
x2: Q2[0], y2: Q2[1],
progress:0,
color:[255,0,0]
});
steps.push({type:'wait', duration:30});
}
}
}
}
function findEquidistantPoints(p1, p2) {
let [x1, y1] = p1;
let [x2, y2] = p2;
// Q1: twice as far from A in direction of B
let q1_x = 2*x2 - x1;
let q1_y = 2*y2 - y1;
// Q2: twice as far from B in direction of A
let q2_x = 2*x1 - x2;
let q2_y = 2*y1 - y2;
return [[q1_x, q1_y], [q2_x, q2_y]];
}
function isLetter(c) {
return c.toLowerCase() !== c.toUpperCase();
}