xxxxxxxxxx
373
// Initialize arrays to store celestial objects and particles
let celestialBodies = [];
let blackHole;
const G = 0.5; // Gravitational constant for simulation (reduced for visual effect)
let accretionDiskParticles = [];
// Define the main CelestialBody class for all space objects (planets, stars, comets, black holes)
class CelestialBody {
constructor(x, y, mass, type = 'planet') {
// Initialize position vector at given coordinates
this.pos = createVector(x, y);
// Set random initial velocity vector
this.vel = p5.Vector.random2D().mult(random(2));
// Initialize acceleration vector
this.acc = createVector(0, 0);
this.mass = mass;
this.type = type;
// Calculate radius based on mass (square root relationship)
this.radius = sqrt(mass) * 2;
// Set maximum velocity based on object type
this.maxVel = type === 'comet' ? 8 : 5;
// Array to store previous positions for trail effect
this.trail = [];
// Assign colors based on celestial object type
if (type === 'planet') {
this.color = color(100, 150, 255, 200); // Blue for planets
} else if (type === 'star') {
this.color = color(255, 220, 100, 200); // Yellow for stars
} else if (type === 'comet') {
this.color = color(200, 220, 255, 200); // Light blue for comets
} else if (type === 'blackHole') {
this.color = color(0, 0, 0); // Black for black hole
}
}
// Apply a force to the object (F = ma)
applyForce(force) {
// Divide force by mass to get acceleration (a = F/m)
let f = p5.Vector.div(force, this.mass);
this.acc.add(f);
}
//Movement responsible
// Calculate gravitational attraction between this object and another
calculateAttraction(other) {
// Calculate direction of force
let force = p5.Vector.sub(other.pos, this.pos);
// Calculate distance squared (constrained to prevent extreme values)
let distanceSq = constrain(force.magSq(), 100, 1000);
// Calculate gravitational force magnitude using Newton's formula
let strength = (G * this.mass * other.mass) / distanceSq;
force.setMag(strength);
return force;
}
// position update
// Update position based on physics
update() {
// Add acceleration to velocity
this.vel.add(this.acc);
// Limit velocity to prevent extreme speeds
this.vel.limit(this.maxVel);
// Update position based on velocity
this.pos.add(this.vel);
// Reset acceleration
this.acc.mult(0);
// Create trail effect for comets
if (this.type === 'comet') {
this.trail.push(this.pos.copy());
// Limit trail length
if (this.trail.length > 50) {
this.trail.shift();
}
}
}
// Draw the celestial object
show() {
// Draw comet trail if applicable
if (this.type === 'comet' && this.trail.length > 2) {
noFill();
stroke(this.color);
strokeWeight(1);
beginShape();
for (let pos of this.trail) {
vertex(pos.x, pos.y);
}
endShape();
}
// Special rendering for black hole
if (this.type === 'blackHole') {
push();
translate(this.pos.x, this.pos.y);
// Start enhanced distortion field effect
drawingContext.save();
//Keep i low for optimization
// Create multiple distortion rings around black hole
for (let i = 20; i > 0; i--) {
let radius = this.radius * (i * 0.7); // Scale ring size
let alpha = map(i, 0, 20, 100, 0); // Fade out outer rings
// Create warped space-time effect using perlin noise
for (let angle = 0; angle < TWO_PI; angle += 0.05) {
let time = frameCount * 0.02;
// Calculate offset positions for distortion
let xOff = cos(angle + time) * radius;
let yOff = sin(angle + time) * radius;
// Create complex distortion using multiple noise layers
let distortion1 = noise(xOff * 0.01, yOff * 0.01, time) * 20;
let distortion2 = noise(xOff * 0.02, yOff * 0.02, time + 1000) * 15;
let finalDistortion = distortion1 + distortion2;
// Add spiral effect to distortion
let spiralFactor = (sin(angle * 3 + time) * cos(angle * 2 + time * 0.5)) * radius * 0.1;
// Draw distortion points
stroke(0, alpha);
strokeWeight(1.5); // Distortion color emphasize
noFill();
point(
xOff + finalDistortion + spiralFactor * cos(angle),
yOff + finalDistortion + spiralFactor * sin(angle)
);
// Randomly add reddish points for energy effects
if (random() < 0.3) {
stroke(255, 100, 50, alpha * 0.5);
point(
xOff + finalDistortion * 1.1 + spiralFactor * cos(angle),
yOff + finalDistortion * 1.1 + spiralFactor * sin(angle)
);
}
}
}
drawingContext.restore();
// Create event horizon visual effect
drawingContext.save();
for (let i = 0; i < 8; i++) {
let lensRadius = this.radius * (2 + i * 0.1) * 1.5; // Horizon radius
// Add shadow blur for depth effect
drawingContext.shadowBlur = 30 + i * 5;
drawingContext.shadowColor = color(0, 0, 0, 200 - i * 20);
fill(245,242,187,255) //this
noStroke();
circle(0, 0, lensRadius);
}
// Draw core of black hole
fill(0);
noStroke();
circle(0, 0, this.radius * 2 * 1.9); // Disk radius
// Add blue-shift/red-shift light effect around event horizon
for (let i = 0; i < 360; i += 5) {
let r = this.radius * 2.2;
let x = cos(radians(i)) * r;
let y = sin(radians(i)) * r;
let shift = map(sin(radians(i)), -1, 1, 0, 1);
let c = lerpColor(
color(100, 150, 255, 30), // Blue-shifted light
color(255, 50, 50, 30), // Red-shifted light
shift
);
stroke(c);
strokeWeight(2);
point(x, y);
}
drawingContext.restore();
pop();
} else {
// Render non-black hole celestial bodies
fill(this.color);
noStroke();
// Add glow effect for stars
if (this.type === 'star') {
drawingContext.shadowBlur = 20;
drawingContext.shadowColor = color(255, 200, 50);
}
circle(this.pos.x, this.pos.y, this.radius * 2);
drawingContext.shadowBlur = 0;
}
}
}
// Class for particles in the accretion disk around black hole
class AccretionParticle {
constructor(x, y, blackHole) {
this.pos = createVector(x, y);
this.blackHole = blackHole;
this.angle = random(TWO_PI);
// Calculate orbital radius from black hole center
this.radius = dist(x, y, blackHole.pos.x, blackHole.pos.y);
// Random orbital speed
this.speed = random(0.01, 0.03);
this.alpha = random(100, 200);
// Calculate particle color based on distance (temperature)
let colorTemp = map(this.radius,
blackHole.radius * 2,
blackHole.radius * 4,
1, 0);
this.particleColor = lerpColor(
color(255, 255, 255), // Hottest inner disk (white)
color(255, 200, 100), // Outer disk (bright yellow)
colorTemp
);
}
// Update particle position in orbit
update() {
this.angle += this.speed;
this.pos.x = this.blackHole.pos.x + cos(this.angle) * this.radius;
this.pos.y = this.blackHole.pos.y + sin(this.angle) * this.radius;
}
// Draw accretion disk particle
show() {
let distanceToCenter = dist(this.pos.x, this.pos.y,
this.blackHole.pos.x, this.blackHole.pos.y);
// Particle size decreases with distance
let size = map(distanceToCenter,
this.blackHole.radius * 1.5,
this.blackHole.radius * 4, 2, 0.5);
// Add glow effect to particles
drawingContext.save();
drawingContext.shadowBlur = 5;
drawingContext.shadowColor = this.particleColor;
fill(this.particleColor.levels[0],
this.particleColor.levels[1],
this.particleColor.levels[2],
this.alpha);
noStroke();
circle(this.pos.x, this.pos.y, size);
drawingContext.restore();
}
}
// Create background starfield
function createStarfield() {
let stars = createGraphics(width, height);
stars.background(0);
stars.fill(255);
stars.noStroke();
// Generate random stars with varying sizes and brightness
for (let i = 0; i < 1000; i++) {
let x = random(width);
let y = random(height);
let size = random(0.5, 2);
let brightness = random(100, 255);
stars.fill(255, brightness);
stars.circle(x, y, size);
}
return stars;
}
let starfield;
// Initial setup
function setup() {
createCanvas(windowWidth, windowHeight);
starfield = createStarfield();
// Create central black hole
blackHole = new CelestialBody(width/2, height/2, 50, 'blackHole');
// Create accretion disk particles
for (let i = 0; i < 300; i++) {
let angle = random(TWO_PI);
let radius = random(blackHole.radius * 2, blackHole.radius * 2.5);
let x = width/2 + cos(angle) * radius;
let y = height/2 + sin(angle) * radius;
accretionDiskParticles.push(new AccretionParticle(x, y, blackHole));
}
// Create other celestial bodies (planets, stars, comets)
for (let i = 0; i < 5; i++) {
celestialBodies.push(new CelestialBody(
random(width),
random(height),
random(5, 15),
'planet'
));
celestialBodies.push(new CelestialBody(
random(width),
random(height),
random(20, 30),
'star'
));
celestialBodies.push(new CelestialBody(
random(width),
random(height),
random(2, 5),
'comet'
));
}
}
// Animation loop
function draw() {
// Draw background starfield
image(starfield, 0, 0);
// Add random colored nebula effects
for (let i = 0; i < 3; i++) {
let x = random(width);
let y = random(height);
let size = random(100, 200);
let alpha = random(1, 3);
noStroke();
fill(random([
color(0, 0, 255, alpha), // Blue nebula
color(255, 0, 0, alpha), // Red nebula
color(0, 255, 255, alpha) // Cyan nebula
]));
circle(x, y, size);
}
// Draw black hole
blackHole.show();
// Update and draw accretion disk
for (let particle of accretionDiskParticles) {
particle.update();
particle.show();
}
// Attraction to black hole and attraction between bodies
// Update and draw celestial bodies
for (let body of celestialBodies) {
// Calculate gravitational force from black hole
let force = body.calculateAttraction(blackHole);
body.applyForce(force);
// Calculate gravitational forces between celestial bodies
for (let other of celestialBodies) {
if (body !== other) {
force = body.calculateAttraction(other);
body.applyForce(force);
}
}
body.update();
body.show();
// Wrap objects around screen edges
if (body.pos.x < 0) body.pos.x = width;
if (body.pos.x > width) body.pos.x = 0;
if (body.pos.y < 0) body.pos.y = height;
if (body.pos.y > height) body.pos.y = 0;
}
}