Spaces:
Running
Running
| let particles = []; | |
| const numParticles = 25; | |
| let cylinderRadius = 200; | |
| let cylinderHeight = 400; | |
| let sphereRadius = 300; | |
| let rotationAngle = 0; | |
| let zoom = 1; | |
| let zoomDirection = 1; | |
| function setup() { | |
| createCanvas(800, 800, WEBGL); | |
| colorMode(HSB, 360, 100, 100); | |
| for (let i = 0; i < numParticles; i++) { | |
| particles.push(new Particle( | |
| random(-cylinderRadius, cylinderRadius), | |
| random(-cylinderHeight/2, cylinderHeight/2), | |
| random(-cylinderRadius, cylinderRadius), | |
| i * (360/numParticles) | |
| )); | |
| } | |
| } | |
| function draw() { | |
| background(0); | |
| scale(zoom); | |
| zoom += 0.002 * zoomDirection; | |
| if (zoom > 1.5) zoomDirection = -1; | |
| if (zoom < 0.8) zoomDirection = 1; | |
| rotateY(rotationAngle); | |
| rotationAngle += 0.005; | |
| push(); | |
| noFill(); | |
| stroke(200, 50, 50, 50); | |
| sphere(sphereRadius); | |
| pop(); | |
| push(); | |
| noFill(); | |
| stroke(100, 50, 50, 50); | |
| cylinder(cylinderRadius, cylinderHeight); | |
| pop(); | |
| for (let particle of particles) { | |
| particle.update(); | |
| particle.checkCollisions(); | |
| particle.display(); | |
| } | |
| } | |
| class Particle { | |
| constructor(x, y, z, hue) { | |
| this.pos = createVector(x, y, z); | |
| this.vel = p5.Vector.random3D().mult(random(2, 5)); | |
| this.radius = 8; | |
| this.hue = hue; | |
| this.trail = []; | |
| this.maxTrailLength = 20; | |
| } | |
| update() { | |
| this.pos.add(this.vel); | |
| this.trail.push(this.pos.copy()); | |
| if (this.trail.length > this.maxTrailLength) { | |
| this.trail.splice(0, 1); | |
| } | |
| } | |
| checkCollisions() { | |
| let radialDist = sqrt(this.pos.x * this.pos.x + this.pos.z * this.pos.z); | |
| if (radialDist + this.radius > cylinderRadius) { | |
| let normal = createVector(this.pos.x, 0, this.pos.z).normalize(); | |
| let dotProduct = this.vel.dot(normal); | |
| this.vel.sub(normal.mult(2 * dotProduct)); | |
| this.pos.set( | |
| this.pos.x * (cylinderRadius - this.radius) / radialDist, | |
| this.pos.y, | |
| this.pos.z * (cylinderRadius - this.radius) / radialDist | |
| ); | |
| } | |
| if (this.pos.y + this.radius > cylinderHeight/2) { | |
| this.vel.y = -abs(this.vel.y); | |
| this.pos.y = cylinderHeight/2 - this.radius; | |
| } | |
| if (this.pos.y - this.radius < -cylinderHeight/2) { | |
| this.vel.y = abs(this.vel.y); | |
| this.pos.y = -cylinderHeight/2 + this.radius; | |
| } | |
| let distFromCenter = this.pos.mag(); | |
| if (distFromCenter + this.radius > sphereRadius) { | |
| let normal = this.pos.copy().normalize(); | |
| let dotProduct = this.vel.dot(normal); | |
| this.vel.sub(normal.mult(2 * dotProduct)); | |
| this.pos.setMag(sphereRadius - this.radius); | |
| } | |
| } | |
| display() { | |
| push(); | |
| noFill(); | |
| strokeWeight(2); | |
| beginShape(); | |
| for (let i = 0; i < this.trail.length; i++) { | |
| let alpha = map(i, 0, this.trail.length, 0, 100); | |
| stroke(this.hue, 80, 100, alpha); | |
| vertex(this.trail[i].x, this.trail[i].y, this.trail[i].z); | |
| } | |
| endShape(); | |
| fill(this.hue, 80, 100); | |
| noStroke(); | |
| translate(this.pos.x, this.pos.y, this.pos.z); | |
| sphere(this.radius); | |
| pop(); | |
| } | |
| } | |