Given a few gravity wells at random positions, spawn particles and trace their paths if they live long enough without crashing into a well.
Log in to post a comment.
// LL 2021 const turtle = new Turtle(); const gravity_wells = 2; // min=2, max=10, step=1 const particles = 5000; // min=1, max=10000, step=1 const opacity = 0.02; // min=0.02, max=1, step=0.01 const gravity = 1000; // min=1, max=10000, step=1 const seed = 0; // min=0, max=1000, step=1 const iterations = 150; // min=1, max=500, step=1 const scale = 1.02; // min=0.1, max=10, step=0.1 const max_radius = 3; const min_radius = 10; var gravity_well_list = null; var particle_count = 0; const max_attempts = 1000000; Canvas.setpenopacity(opacity); var rng; class vec2 { constructor(x, y) { this.x = x; this.y = y; } dup() { return new vec2(this.x, this.y); } set(v2) { this.x = v2.x; this.y = v2.y; } add(v2) { this.x += v2.x; this.y += v2.y; } sub(v2) { this.x -= v2.x; this.y -= v2.y; } length() { return Math.hypot(this.x, this.y); } normalize() { var length = this.length(); if (length > 0) { this.x /= length; this.y /= length; } } multiply_f(f) { this.x *= f; this.y *= f; } } class GravityWell { constructor(x, y, r) { this.position = new vec2(x, y); this.radius = r; } draw() { turtle.jump(this.position.x / scale, (this.position.y - this.radius) / scale); turtle.circle(this.radius / scale); } } class Particle { constructor(x, y, prev_x, prev_y) { this.current_position = new vec2(x, y); this.previous_position = new vec2(prev_x, prev_y); this.died = false; this.history = [this.current_position.dup()]; } update() { var v = this.current_position.dup(); v.sub(this.previous_position); this.previous_position.set(this.current_position); gravity_well_list.forEach(w => { var vgravity = this.current_position.dup(); vgravity.sub(w.position); const distance = vgravity.length(); if (distance > w.radius) { vgravity.multiply_f(-(gravity * w.radius)/1000/distance/distance); v.add(vgravity); } else { this.died = true; } }); const friction = 0.99; v.multiply_f(friction); this.current_position.add(v); this.history.push(this.current_position.dup()); } draw() { turtle.up(); this.history.forEach(pos => { turtle.goto(pos.x / scale, pos.y / scale); turtle.down(); }); } } function plateau(n, level) { return Math.round(n/level)*level; } // The walk function will be called until it returns false. function walk(i, t) { if (i==0) { rng = new RNG(seed); gravity_well_list = []; for (var j=0; j<gravity_wells; j++) { const r = rng.nextFloat() * (max_radius - min_radius) + min_radius; const x = (rng.nextFloat() - 0.5) * (200-r); const y = (rng.nextFloat() - 0.5) * (200-r); gravity_well_list.push(new GravityWell(x, y, r)); } particle_count = 0; } const chance = rng.nextFloat(); const x = 200 * (plateau(rng.nextFloat(), (chance < 0.5) ? 1 : 0.0625) - 0.5); const y = 200 * (plateau(rng.nextFloat(), (chance > 0.5) ? 1 : 0.0625) - 0.5); // const x = 200 * (plateau(rng.nextFloat(), 0.00025) - 0.5); // const y = -100; const vel = 0.25; const prev_x = x + (rng.nextFloat() - 0.5) * vel; const prev_y = y + (rng.nextFloat() - 0.5) * vel; const particle = new Particle(x, y, prev_x, prev_y); for (j=0; j<iterations*t && !particle.died; j++) { particle.update(); } if (!particle.died) { particle.draw(); particle_count++; } //sleep(10); return particle_count < particles && i < max_attempts; } //////////////////////// // Utils //////////////////////// function sleep(milliseconds) { const start=Date.now(); while (Date.now()-start < milliseconds) {} } /////////////// // Minified Random Number Generator from https://turtletoy.net/turtle/ab7a7e539e function RNG(t){return new class{constructor(t){this.m=2147483648,this.a=1103515245,this.c=12345,this.state=t||Math.floor(Math.random()*(this.m-1))}nextFloat(){return this.state=(this.a*this.state+this.c)%this.m,this.state/(this.m-1)}}(t)}