xxxxxxxxxx
408
const SEPARATION_RADIUS = 30;
const COHESION_RADIUS = 150;
const ALIGNMENT_RADIUS = 100;
const MOUSE_RADIUS = 100;
const MOUSE_FORCE = 10;
const c_ = 40;
const NUM_AGENTS = 6;
const AGENT_SPEED = 10;
const NUM_RAYS_PER_AGENT = 100;
const COLORS = [
{name: 'cyan', rgb: [0, 0, 255]},
{name: 'magenta', rgb: [255, 0, 0]},
{name: 'yellow', rgb: [0, 255, 0]}
];
// Segment definitions for each digit (0-9)
const DIGIT_SEGMENTS = [
// 0
[
[0.2, 0, 0.8, 0], // top
[0.8, 0, 0.8, 1], // right top
[0.8, 1, 0.8, 2], // right bottom
[0.2, 2, 0.8, 2], // bottom
[0.2, 1, 0.2, 2], // left bottom
[0.2, 0, 0.2, 1] // left top
],
// 1
[
[0.5, 0, 0.5, 2], // vertical line
[0.2, 0, 0.5, 0], // top connection
],
// 2
[
[0.2, 0, 0.8, 0], // top
[0.8, 0, 0.8, 1], // right top
[0.2, 1, 0.8, 1], // middle
[0.2, 1, 0.2, 2], // left bottom
[0.2, 2, 0.8, 2] // bottom
],
// 3
[
[0.2, 0, 0.8, 0], // top
[0.8, 0, 0.8, 1], // right top
[0.2, 1, 0.8, 1], // middle
[0.8, 1, 0.8, 2], // right bottom
[0.2, 2, 0.8, 2] // bottom
],
// 4
[
[0.2, 0, 0.2, 1], // left top
[0.2, 1, 0.8, 1], // middle
[0.8, 0, 0.8, 2] // right full
],
// 5
[
[0.2, 0, 0.8, 0], // top
[0.2, 0, 0.2, 1], // left top
[0.2, 1, 0.8, 1], // middle
[0.8, 1, 0.8, 2], // right bottom
[0.2, 2, 0.8, 2] // bottom
],
// 6
[
[0.2, 0, 0.8, 0], // top
[0.2, 0, 0.2, 2], // left full
[0.2, 1, 0.8, 1], // middle
[0.8, 1, 0.8, 2], // right bottom
[0.2, 2, 0.8, 2] // bottom
],
// 7
[
[0.2, 0, 0.8, 0], // top
[0.8, 0, 0.8, 2] // right full
],
// 8
[
[0.2, 0, 0.8, 0], // top
[0.2, 0, 0.2, 2], // left full
[0.8, 0, 0.8, 2], // right full
[0.2, 1, 0.8, 1], // middle
[0.2, 2, 0.8, 2] // bottom
],
// 9
[
[0.2, 0, 0.8, 0], // top
[0.2, 0, 0.2, 1], // left top
[0.8, 0, 0.8, 2], // right full
[0.2, 1, 0.8, 1], // middle
[0.2, 2, 0.8, 2] // bottom
]
];
class Ray {
constructor(p, a) {
this.pos = p;
this.dir = p5.Vector.fromAngle(a);
}
cast(o) {
const x1 = o.a.x, y1 = o.a.y,
x2 = o.b.x, y2 = o.b.y,
x3 = this.pos.x, y3 = this.pos.y,
x4 = x3 + this.dir.x * 1000,
y4 = y3 + this.dir.y * 1000;
const d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
if (d === 0) return null;
const t = ((x1 - x3) * (y3 - y4) - (y1 - y3) * (x3 - x4)) / d;
const u = -((x1 - x2) * (y1 - y3) - (y1 - y2) * (x1 - x3)) / d;
return (t > 0 && t < 1 && u > 0) ?
createVector(x1 + t * (x2 - x1), y1 + t * (y2 - y1)) :
null;
}
}
class Obstacle {
constructor(x1, y1, x2, y2) {
this.a = createVector(x1, y1);
this.b = createVector(x2, y2);
}
show() {
stroke(255);
strokeWeight(10);
line(this.a.x, this.a.y, this.b.x, this.b.y);
}
getNormal() {
const dx = this.b.x - this.a.x, dy = this.b.y - this.a.y;
return createVector(-dy, dx).normalize();
}
intersectsAgent(a) {
const r = 5;
const t = p5.Vector.sub(a.pos, this.a);
const d = p5.Vector.sub(this.b, this.a);
const p = t.dot(d) / d.magSq();
const c = constrain(p, 0, 1);
const s = p5.Vector.add(this.a, d.mult(c));
return p5.Vector.dist(a.pos, s) < r;
}
getSideOfLine(a) {
const l = p5.Vector.sub(this.b, this.a);
const v = p5.Vector.sub(a.pos, this.a);
return (l.x * v.y - l.y * v.x) > 0 ? 1 : -1;
}
}
class Agent {
constructor(c) {
this.pos = createVector(random(width), random(height));
this.vel = p5.Vector.random2D().mult(AGENT_SPEED);
this.acc = createVector(0, 0);
this.rays = [];
this.radius = 10;
this.colorIndex = c;
this.mouseForce = MOUSE_FORCE;
// Boid parameters
this.maxSpeed = AGENT_SPEED;
this.maxForce = 0.1;
// Create rays
for (let i = 0; i < NUM_RAYS_PER_AGENT; i++) {
const a = (TWO_PI / NUM_RAYS_PER_AGENT) * i;
this.rays.push(new Ray(this.pos.copy(), a));
}
}
// Boid behaviors
align(agents) {
let steering = createVector(0, 0);
let total = 0;
for (let other of agents) {
const d = p5.Vector.dist(this.pos, other.pos);
if (other !== this && d < ALIGNMENT_RADIUS) {
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(agents) {
let steering = createVector(0, 0);
let total = 0;
for (let other of agents) {
const d = p5.Vector.dist(this.pos, other.pos);
if (other !== this && d < COHESION_RADIUS) {
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;
}
separation(agents) {
let steering = createVector(0, 0);
let total = 0;
for (let other of agents) {
const d = p5.Vector.dist(this.pos, other.pos);
if (other !== this && d < SEPARATION_RADIUS) {
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;
}
followMouse() {
let mousePos = createVector(mouseX, mouseY);
let d = p5.Vector.dist(this.pos, mousePos);
if (d < MOUSE_RADIUS) {
let desired = p5.Vector.sub(mousePos, this.pos);
desired.setMag(this.maxSpeed);
let steering = p5.Vector.sub(desired, this.vel);
steering.limit(this.maxForce);
steering.mult(this.mouseForce);
return steering;
}
return createVector(0, 0);
}
update(o, agents) {
// Apply boid behaviors
const alignForce = this.align(agents);
const cohesionForce = this.cohesion(agents);
const separationForce = this.separation(agents);
const mouseForce = this.followMouse();
this.acc.add(alignForce);
this.acc.add(cohesionForce);
this.acc.add(separationForce);
this.acc.add(mouseForce);
// Update velocity and position
this.vel.add(this.acc);
this.vel.limit(this.maxSpeed);
const n = p5.Vector.add(this.pos, this.vel);
let hit = false;
let collision = null;
// Collision detection with obstacles
for (let b of o) {
if (!hit && b.intersectsAgent(this)) {
const s = b.getSideOfLine(this);
collision = b.getNormal().mult(s);
hit = true;
break;
}
}
if (hit && collision) {
this.vel.reflect(collision);
n.add(collision.copy().mult(this.radius * 1.1));
}
this.pos = n;
// Wrap around screen edges
if (this.pos.x < 0) this.pos.x = width;
if (this.pos.x > width) this.pos.x = 0;
if (this.pos.y < 0) this.pos.y = height;
if (this.pos.y > height) this.pos.y = 0;
// Reset acceleration
this.acc.mult(0);
// Update ray positions
this.rays.forEach(r => { r.pos = this.pos.copy(); });
}
show(o) {
const c = COLORS[this.colorIndex].rgb;
noStroke();
blendMode(ADD);
fill(c, 155);
ellipse(this.pos.x, this.pos.y, this.radius, this.radius);
this.rays.forEach(r => {
let shortest = null;
let minDist = Infinity;
o.forEach(b => {
const point = r.cast(b);
if (point) {
const m = p5.Vector.dist(r.pos, point);
if (m < minDist) {
minDist = m;
shortest = point;
}
}
});
stroke(c,c_ );
strokeWeight(5);
shortest ?
line(r.pos.x, r.pos.y, shortest.x, shortest.y) :
line(r.pos.x, r.pos.y,
r.pos.x + r.dir.x * 1000,
r.pos.y + r.dir.y * 1000);
});
blendMode(BLEND);
}
}
let agents = [];
let obstacles = [];
function setup() {
createCanvas(windowWidth, windowHeight);
// Create agents
for (let i = 0; i < NUM_AGENTS; i++) {
agents.push(new Agent(i % COLORS.length));
}
}
function draw() {
background(0);
// Clear previous obstacles
obstacles = [];
// Get current seconds
const seconds = second();
const secondsTens = Math.floor(seconds / 10);
const secondsOnes = seconds % 10;
// Create digit obstacles
const digitHeight = height * 0.25;
const digitWidth = digitHeight * 0.6;
const verticalCenter = height / 2;
const horizontalCenter = width / 2;
// Function to create segments for a digit
function createDigitObstacles(digit, offsetX, offsetY) {
DIGIT_SEGMENTS[digit].forEach(segment => {
const x1 = offsetX + segment[0] * digitWidth;
const y1 = offsetY + segment[1] * digitHeight;
const x2 = offsetX + segment[2] * digitWidth;
const y2 = offsetY + segment[3] * digitHeight;
obstacles.push(new Obstacle(x1, y1, x2, y2));
});
}
// Draw tens digit
createDigitObstacles(secondsTens,
horizontalCenter - digitWidth * 1.2,
verticalCenter - digitHeight / 2);
// Draw ones digit
createDigitObstacles(secondsOnes,
horizontalCenter + digitWidth * 0.2,
verticalCenter - digitHeight / 2);
// Show obstacles (digit segments)
obstacles.forEach(o => { o.show(); });
// Update and show agents
agents.forEach(a => {
a.update(obstacles, agents);
a.show(obstacles);
});
}
function windowResized() {
resizeCanvas(windowWidth, windowHeight);
}