xxxxxxxxxx
140
let particles = [];
let torus = false;
let v = 1.0; // Constant velocity, pixels/step
let bg; // Background graphics for trails
let size = 2; // point size of rendered particles
let fps = 0;
let decayStrength = 5;
let V2_DAMPING = 0.01; // Global variable for velocity damping
function setup() {
createCanvas(windowWidth, windowHeight);
bg = createGraphics(windowWidth, windowHeight); // Initialize background graphics
bg.background(0); // Start with a white background
for (let i = 0; i < 500; i++) {
particles.push(new Particle());
}
textSize(16); // Set the text size for the frame rate display
fill(255); // Set the text color to black
}
function draw() {
decayBackground();
image(bg, 0, 0); // Display the trails image
//bg.loadPixels();
for (let particle of particles) {
particle.update();
}
//bg.updatePixels();
for (let particle of particles) {
particle.show();
}
// Display the frame rate
fps = fps * 0.95 + frameRate() * 0.05;
fill(255);
stroke(0);
text(fps.toFixed(0), 10, 20); // Display frame rate rounded to 2 decimal places
}
function rotateVelocity(vel, angle) {
let radianAngle = radians(angle);
let cosA = cos(radianAngle);
let sinA = sin(radianAngle);
let newVel = createVector(
vel.x * cosA - vel.y * sinA,
vel.x * sinA + vel.y * cosA
);
return newVel;
}
function decayBackground() {
bg.noStroke(); // Ensure no outline
bg.fill(0, decayStrength); // Set fill to black with low alpha to create the fading effect
bg.rect(0, 0, bg.width, bg.height); // Cover the entire graphics with the semi-transparent black rectangle
}
class Particle {
constructor() {
this.pos = createVector(random(width), random(height));
this.vel = createVector(0, v);
this.vel = rotateVelocity(this.vel, random(360));
this.type = (random() < 0.5) ? 1: 0;
}
decayVelocity(decayFactor) {
// Decay velocity proportional to the square of its magnitude
let speed = this.vel.mag();
let decayAmount = speed * speed * decayFactor; // Calculate decay amount using passed decay factor
this.vel.mult(1 - decayAmount); // Reduce velocity by the decay amount
}
update() {
let radius = 50; // Radius of detection (pixels)
let localParticles = 0;
let alignVel = createVector(0, 0);
/*
// Check for nearby particles and align or repel based on type
particles.forEach(other => {
let separation = p5.Vector.dist(this.pos, other.pos)
if (other !== this && separation < radius) {
localParticles++;
if (this.type === other.type) {
alignVel.add(other.vel); // Add up velocities for alignment
} else {
let repelForce = p5.Vector.sub(this.pos, other.pos);
repelForce.div(1/separation); // Weaken force with distance
this.vel.sub(repelForce); // Repel from different type
}
}
});
*/
// Alignment normalization
if (localParticles > 0) { // Example: only green types align
alignVel.div(localParticles);
this.vel.lerp(alignVel, 0.04); // Adjust by 10% towards average velocity
}
// Damping by velocity squared
this.decayVelocity(V2_DAMPING);
// Regular color-based direction change
let bgCol = bg.get(this.pos.x, this.pos.y);
let angleChange = map(bgCol[1], 0, 255, PI, -PI) + map(bgCol[0], 0, 255, -PI, PI);
this.vel = rotateVelocity(this.vel, angleChange);
// Update trail color based on type
bgCol[this.type] = 255;
bg.stroke(bgCol);
bg.strokeWeight(2);
bg.point(this.pos.x, this.pos.y);
// Update position
this.pos.add(this.vel);
// Handle edges
if (torus) {
this.pos.x = (this.pos.x + width) % width;
this.pos.y = (this.pos.y + height) % height;
} else {
if (this.pos.x < 0 || this.pos.x > width) this.vel.x *= -1;
if (this.pos.y < 0 || this.pos.y > height) this.vel.y *= -1;
}
}
show() {
if (this.type == 1) stroke(0, 255, 0);
else stroke(255, 0, 0);
strokeWeight(2);
fill(0,255,255);
ellipse(this.pos.x, this.pos.y, size, size);
}
}