xxxxxxxxxx
220
let pendulums = [];
let numPendulums = 43;
let numSegments = 8;
let pivot;
let dragging = false;
let controls = {};
let trailAlpha = 10; // Default value for trail alpha
let gravity = 0.4;
let resistance = 0.99;
let speedFactor = 1;
let intervals = {reset : null, nudge:null}
let animateSliders = false;
function setup() {
createCanvas(windowWidth, windowHeight);
colorMode(HSB, 360, 100, 100, 100); // Use HSB color mode for vibrant hues
pivot = createVector(width / 2, 100);
createHUD();
resetPendulums();
intervals.reset = setInterval(() => {
resetPendulums();
intervals.nudge = setTimeout(nudgePendulums, 5000)
}, 10000);
}
function draw() {
background(0, trailAlpha); // Leave a fading trail
// Animate sliders if enabled
if (animateSliders) {
animateControlSliders();
}
// Draw and update each pendulum
for (let pendulum of pendulums) {
pendulum.update();
pendulum.show();
}
// Draw the pivot point
fill(255);
noStroke();
ellipse(pivot.x, pivot.y, 10);
// Handle dragging
if (dragging) {
pivot.set(mouseX, mouseY);
}
}
function resetPendulums() {
pendulums = [];
// Define identical segments and initial angles
let segmentLengths = Array.from({ length: numSegments }, () => random(30, 70));
let initialAngles = Array.from({ length: numSegments }, () => random(-PI / 4, PI / 4));
for (let i = 0; i < numPendulums; i++) {
// Map colors across the rainbow spectrum
let hue = map(i, 0, numPendulums - 1, 0, 360);
let offsetAngles = initialAngles.map(angle => angle + random(-0.001, 0.001));
pendulums.push(new MultiSegmentPendulum(pivot.x, pivot.y, segmentLengths, offsetAngles, color(hue, 100, 100, 80)));
}
}
function createHUD() {
let gui = createDiv('').style('position', 'absolute').style('top', '10px').style('left', '10px').style('color', 'white');
gui.child(createSpan('Segments: '));
controls.segments = createSlider(1, 20, numSegments, 1);
gui.child(controls.segments);
gui.child(createSpan('<br>Pendulums: '));
controls.pendulums = createSlider(1, 10, numPendulums, 1);
gui.child(controls.pendulums);
gui.child(createSpan('<br>Gravity: '));
controls.gravity = createSlider(0, 2, gravity, 0.01);
gui.child(controls.gravity);
gui.child(createSpan('<br>Resistance: '));
controls.resistance = createSlider(0, 1, resistance, 0.01);
gui.child(controls.resistance);
gui.child(createSpan('<br>Speed: '));
controls.speed = createSlider(0.1, 5, speedFactor, 0.1);
gui.child(controls.speed);
gui.child(createSpan('<br>Trail Alpha: '));
controls.trailAlpha = createSlider(0.01, 20, trailAlpha, 0.05);
controls.trailAlpha.input(() => {
trailAlpha = controls.trailAlpha.value();
});
gui.child(controls.trailAlpha);
let animateCheckbox = createCheckbox('Animate Sliders', false);
animateCheckbox.style('color', 'white');
animateCheckbox.changed(() => {
animateSliders = animateCheckbox.checked();
});
gui.child(animateCheckbox);
let autoNudgeCheckbox = createCheckbox('auto reset & nudge').checked(true);
autoNudgeCheckbox.style('color', 'yellow')
autoNudgeCheckbox.changed(() => {
if (autoNudgeCheckbox.checked()) {
// Start setInterval
intervals.reset = setInterval(() => {
resetPendulums();
intervals.nudge = setTimeout(nudgePendulums, 5000)
}, 10000);
resetPendulums();
intervals.nudge = setTimeout(nudgePendulums, 5000);
} else {
// Stop setInterval
clearInterval(intervals.reset);
intervals.reset = null;
clearTimeout(intervals.reset)
intervals.nudge = null
}
});
gui.child(autoNudgeCheckbox)
let nudgeButton = createButton('Nudge Pendulums');
nudgeButton.mousePressed(nudgePendulums);
gui.child(nudgeButton);
let resetButton = createButton('Reset');
resetButton.mousePressed(() => {
numSegments = controls.segments.value();
numPendulums = controls.pendulums.value();
resetPendulums();
});
gui.child(resetButton);
}
function animateControlSliders() {
// Animate the gravity slider
let gravityValue = controls.gravity.value();
controls.gravity.value((gravityValue + 0.01) % 2);
// Animate the resistance slider
let resistanceValue = controls.resistance.value();
controls.resistance.value(abs(sin(frameCount * 0.01)));
// Animate the speed slider
let speedValue = controls.speed.value();
controls.speed.value(0.1 + abs(sin(frameCount * 0.02)) * 4.9);
}
function nudgePendulums() {
for (let i = 0; i < pendulums.length; i++) {
let nudgeForce = 0.05 + i * 0.01; // Slightly offset nudge
for (let j = 0; j < pendulums[i].velocities.length; j++) {
pendulums[i].velocities[j] += nudgeForce;
}
}
}
function mousePressed() {
if (dist(mouseX, mouseY, pivot.x, pivot.y) < 20) {
dragging = true;
}
}
function mouseReleased() {
dragging = false;
}
class MultiSegmentPendulum {
constructor(x, y, lengths, angles, color) {
this.pivot = createVector(x, y);
this.lengths = lengths; // Array of segment lengths
this.angles = angles; // Array of initial angles
this.velocities = Array.from({ length: lengths.length }, () => 0);
this.accelerations = Array.from({ length: lengths.length }, () => 0);
this.color = color; // Store the color
}
update() {
gravity = controls.gravity.value();
resistance = controls.resistance.value();
speedFactor = controls.speed.value();
for (let i = 0; i < this.lengths.length; i++) {
let force = -gravity / this.lengths[i] * sin(this.angles[i]);
this.accelerations[i] = force;
this.velocities[i] += this.accelerations[i] * speedFactor;
this.velocities[i] *= resistance > 0 ? resistance : 1; // Avoid divide-by-zero
this.angles[i] += this.velocities[i];
}
}
show() {
let currentX = this.pivot.x;
let currentY = this.pivot.y;
stroke(this.color);
fill(this.color);
for (let i = 0; i < this.lengths.length; i++) {
let nextX = currentX + this.lengths[i] * sin(this.angles[i]);
let nextY = currentY + this.lengths[i] * cos(this.angles[i]);
line(currentX, currentY, nextX, nextY);
ellipse(nextX, nextY, 5);
currentX = nextX;
currentY = nextY;
}
}
}