xxxxxxxxxx
405
let boids = [];
const numBoids = 75;
let timeObstacles = [];
// Segment definitions for 0-9
const segments = {
'0': [1,1,1,1,1,1,0],
'1': [0,1,1,0,0,0,0],
'2': [1,1,0,1,1,0,1],
'3': [1,1,1,1,0,0,1],
'4': [0,1,1,0,0,1,1],
'5': [1,0,1,1,0,1,1],
'6': [1,0,1,1,1,1,1],
'7': [1,1,1,0,0,0,0],
'8': [1,1,1,1,1,1,1],
'9': [1,1,1,1,0,1,1]
};
function setup() {
createCanvas(480, 720);
// Initialize boids with random positions
for (let i = 0; i < numBoids; i++) {
boids.push(new Boid(random(width), random(height)));
}
}
// Helper function to check if a line segment intersects with a point (with thickness)
function lineIntersectsPoint(x1, y1, x2, y2, px, py, thickness) {
// Calculate distance from point to line segment
let d = distToSegment(createVector(px, py), createVector(x1, y1), createVector(x2, y2));
return d < thickness;
}
// Helper function to calculate distance from point to line segment
function distToSegment(p, v, w) {
let l2 = dist(v.x, v.y, w.x, w.y);
l2 = l2 * l2;
if (l2 === 0) return dist(p.x, p.y, v.x, v.y);
let t = ((p.x - v.x) * (w.x - v.x) + (p.y - v.y) * (w.y - v.y)) / l2;
t = constrain(t, 0, 1);
let projected = createVector(
v.x + t * (w.x - v.x),
v.y + t * (w.y - v.y)
);
return dist(p.x, p.y, projected.x, projected.y);
}
// Improved collision detection for line segments and digits
function connectionIntersectsDigit(x1, y1, x2, y2) {
const obstacleThickness = 15; // Increased thickness for better avoidance
const numChecks = 10; // Number of points to check along the line
// Check multiple points along the line segment
for (let i = 0; i <= numChecks; i++) {
const t = i / numChecks;
const checkX = lerp(x1, x2, t);
const checkY = lerp(y1, y2, t);
// Check against all obstacle points
for (let obstacle of timeObstacles) {
let d = dist(checkX, checkY, obstacle.x, obstacle.y);
if (d < obstacleThickness) {
return true; // Line intersects with an obstacle
}
}
}
return false;
}
function drawPlexusConnections() {
// Adjustable parameters for plexus effect
let connectionDistance = 70; // Maximum distance for connections
let maxAlpha = 250; // Maximum opacity (0-255)
let minAlpha = 0; // Minimum opacity
let maxLineWeight = 2; // Maximum line thickness
let minLineWeight = 0.2; // Minimum line thickness
for (let i = 0; i < boids.length; i++) {
for (let j = i + 1; j < boids.length; j++) {
let d = dist(
boids[i].pos.x, boids[i].pos.y,
boids[j].pos.x, boids[j].pos.y
);
if (d < connectionDistance) {
// Early intersection check to improve performance
if (!connectionIntersectsDigit(
boids[i].pos.x, boids[i].pos.y,
boids[j].pos.x, boids[j].pos.y
)) {
// Calculate alpha based on distance using quadratic falloff
let alpha = map(d * d, 0, connectionDistance * connectionDistance, maxAlpha, minAlpha);
let lineWeight = map(d, 0, connectionDistance, maxLineWeight, minLineWeight);
// Get colors of both boids
let color1 = boids[i].getCurrentColor();
let color2 = boids[j].getCurrentColor();
// Interpolate between the two colors
let lerpAmount = 0.5;
let r = lerp(red(color1), red(color2), lerpAmount);
let g = lerp(green(color1), green(color2), lerpAmount);
let b = lerp(blue(color1), blue(color2), lerpAmount);
stroke(r, g, b, alpha);
strokeWeight(lineWeight);
line(
boids[i].pos.x, boids[i].pos.y,
boids[j].pos.x, boids[j].pos.y
);
}
}
}
}
}
function drawDigit(x, y, digit, size) {
const segWidth = size * 0.075;
const segLength = size * 0.3;
const segPadding = segWidth * 1.5; // Added padding for better plexus avoidance
push();
translate(x, y);
strokeWeight(segWidth);
strokeCap(ROUND);
let pattern = segments[digit];
// Horizontal segments
if (pattern[0]) line(-segLength/2, -segLength, segLength/2, -segLength);
if (pattern[6]) line(-segLength/2, 0, segLength/2, 0);
if (pattern[3]) line(-segLength/2, segLength, segLength/2, segLength);
// Vertical segments
if (pattern[5]) line(-segLength/2, -segLength, -segLength/2, 0);
if (pattern[4]) line(-segLength/2, 0, -segLength/2, segLength);
if (pattern[1]) line(segLength/2, -segLength, segLength/2, 0);
if (pattern[2]) line(segLength/2, 0, segLength/2, segLength);
// Store obstacle points with higher density
let points = [];
let step = segWidth * 0.5; // Smaller step size for more precise collision detection
// Helper function to add points with padding
const addSegmentPoints = (startX, startY, endX, endY) => {
const length = dist(startX, startY, endX, endY);
const steps = Math.ceil(length / step);
for (let i = 0; i <= steps; i++) {
const t = i / steps;
const px = lerp(startX, endX, t);
const py = lerp(startY, endY, t);
// Add main point and padding points
points.push({x: x + px, y: y + py});
// Add padding points in a small circle around the main point
for (let angle = 0; angle < TWO_PI; angle += PI/4) {
points.push({
x: x + px + cos(angle) * segPadding,
y: y + py + sin(angle) * segPadding
});
}
}
};
// Add points for each segment with padding
if (pattern[0]) addSegmentPoints(-segLength/2, -segLength, segLength/2, -segLength);
if (pattern[6]) addSegmentPoints(-segLength/2, 0, segLength/2, 0);
if (pattern[3]) addSegmentPoints(-segLength/2, segLength, segLength/2, segLength);
if (pattern[5]) addSegmentPoints(-segLength/2, -segLength, -segLength/2, 0);
if (pattern[4]) addSegmentPoints(-segLength/2, 0, -segLength/2, segLength);
if (pattern[1]) addSegmentPoints(segLength/2, -segLength, segLength/2, 0);
if (pattern[2]) addSegmentPoints(segLength/2, 0, segLength/2, segLength);
timeObstacles.push(points);
pop();
}
function draw() {
background(0);
// Reset obstacle points
timeObstacles = [];
// Draw plexus connections first (behind everything)
drawPlexusConnections();
// Display digital clock
let digitSize = width * 0.3;
let spacing = digitSize * 0.6;
let startX = width/2 - spacing * 1.5;
let y = height/2;
push();
stroke(255);
noFill();
let now = new Date();
let totalSeconds = (now.getMinutes() * 60 + now.getSeconds()).toString().padStart(3, '0');
let timeString = totalSeconds;
for (let i = 0; i < timeString.length; i++) {
let x = startX + i * spacing;
drawDigit(x, y, timeString[i], digitSize);
}
pop();
// Update and show all boids
for (let boid of boids) {
boid.update();
boid.show();
}
}
class Boid {
constructor(x, y) {
this.pos = createVector(x, y);
this.vel = p5.Vector.random2D();
this.vel.setMag(random(2, 4));
this.acc = createVector();
this.maxForce = 0.65;
this.maxSpeed = 5;
this.size = 5;
}
getCurrentColor() {
let speed = this.vel.mag();
let slowColor = color(255, 105, 180); // Pink
let fastColor = color(0, 255, 200); // Cyan-green
return lerpColor(fastColor, slowColor, speed / this.maxSpeed);
}
align() {
let perceptionRadius = 50;
let steering = createVector();
let total = 0;
for (let other of boids) {
let d = dist(
this.pos.x, this.pos.y,
other.pos.x, other.pos.y
);
if (other != this && d < perceptionRadius) {
steering.add(other.vel);
total++;
}
}
if (total > 0) {
steering.div(total);
steering.setMag(this.maxSpeed);
steering.sub(this.vel);
steering.limit(this.maxForce);
}
return steering;
}
cohesion() {
let perceptionRadius = 50;
let steering = createVector();
let total = 0;
for (let other of boids) {
let d = dist(
this.pos.x, this.pos.y,
other.pos.x, other.pos.y
);
if (other != this && d < perceptionRadius) {
steering.add(other.pos);
total++;
}
}
if (total > 0) {
steering.div(total);
steering.sub(this.pos);
steering.setMag(this.maxSpeed);
steering.sub(this.vel);
steering.limit(this.maxForce);
}
return steering;
}
separate() {
let perceptionRadius = 50;
let steering = createVector();
let total = 0;
for (let other of boids) {
let d = dist(
this.pos.x, this.pos.y,
other.pos.x, other.pos.y
);
if (other != this && d < perceptionRadius) {
let diff = p5.Vector.sub(this.pos, other.pos);
diff.div(d * d);
steering.add(diff);
total++;
}
}
if (total > 0) {
steering.div(total);
steering.setMag(this.maxSpeed);
steering.sub(this.vel);
steering.limit(this.maxForce);
}
return steering;
}
avoidObstacles() {
let steering = createVector();
let outerRadius = 10;
let innerRadius = this.size + 3;
for (let obstacle of timeObstacles) {
let d = dist(this.pos.x, this.pos.y, obstacle.x, obstacle.y);
if (d < outerRadius) {
let normal = p5.Vector.sub(this.pos, createVector(obstacle.x, obstacle.y));
normal.normalize();
if (d < innerRadius) {
this.pos.add(normal.copy().mult(innerRadius - d));
let reflection = this.vel.copy();
let dotProduct = normal.dot(reflection);
normal.mult(2 * dotProduct);
reflection.sub(normal);
this.vel = reflection.mult(0.95);
let tangent = createVector(-normal.y, normal.x);
if (tangent.dot(this.vel) < 0) tangent.mult(-1);
steering.add(tangent.mult(this.maxForce * 3));
} else {
let force = map(d, innerRadius, outerRadius, 1, 0);
let avoidForce = normal.copy().mult(force * this.maxForce * 2);
let tangent = createVector(-normal.y, normal.x);
if (tangent.dot(this.vel) < 0) tangent.mult(-1);
avoidForce.add(tangent.mult(force * this.maxForce));
steering.add(avoidForce);
}
}
}
return steering;
}
update() {
let mouse = createVector(mouseX, mouseY);
let distToMouse = p5.Vector.dist(this.pos, mouse);
let desired = p5.Vector.sub(mouse, this.pos);
let speedMultiplier = map(distToMouse, 0, width/2, 0.5, 2);
desired.setMag(this.maxSpeed * speedMultiplier);
let steer = p5.Vector.sub(desired, this.vel);
steer.limit(this.maxForce * speedMultiplier);
this.acc.add(steer);
this.acc.add(this.align().mult(0.8));
this.acc.add(this.cohesion().mult(0.4));
this.acc.add(this.separate().mult(2.0));
this.acc.add(this.avoidObstacles());
this.vel.add(this.acc);
this.vel.limit(this.maxSpeed);
this.pos.add(this.vel);
this.acc.mult(0);
this.edges();
}
show() {
push();
translate(this.pos.x, this.pos.y);
noStroke();
let speed = this.vel.mag();
let dynamicSize = map(speed, 0, this.maxSpeed, this.size * 0.8, this.size * 1.2);
let dynamicOpacity = map(speed, 0, this.maxSpeed, 180, 255);
let speedColor = this.getCurrentColor();
fill(speedColor, dynamicOpacity);
circle(0, 0, dynamicSize * 1);
pop();
}
edges() {
if (this.pos.x > width) this.pos.x = 0;
if (this.pos.x < 0) this.pos.x = width;
if (this.pos.y > height) this.pos.y = 0;
if (this.pos.y < 0) this.pos.y = height;
}
}