Fotómetro
Entradas Apuntes Composiciones Links

Problema de n (20) cuerpos gravitatorios

Tiempo: 0s
from js import window, setInterval, clearInterval, document, Math from pyodide import create_proxy import random canvas = document.getElementById("my-canvas") ctx = canvas.getContext("2d") G = 6.67430 # Constante gravitacional universal ret = None dt = 0.05 # Paso de tiempo (s) fps = 10 # Fotogramas por segundo start_time = None bodies = [] class Body: def __init__(self, x, y, radius, mass): self.x = x self.y = y self.radius = radius self.mass = mass self.color = f'rgba({random.randint(0, 200)},{random.randint(0, 200)},{random.randint(0, 200)},0.8)' self.dx = (random.random() - 0.5) * 10.5 self.dy = (random.random() - 0.5) * 10.5 self.history = [] def draw(self): ctx.beginPath() ctx.fillStyle = self.color ctx.arc(self.x, self.y, self.radius, 0, 2 * Math.PI) ctx.fill() def update_position(self, forces): ax = sum([f[0] / self.mass for f in forces]) ay = sum([f[1] / self.mass for f in forces]) self.dx += ax * dt self.dy += ay * dt self.x += self.dx * dt self.y += self.dy * dt self.history.append((self.x, self.y)) def generate_and_start(*args, **kwargs): global bodies, ret, start_time num_bodies = 30 bodies = [] for _ in range(num_bodies): radii = random.randint(5, 15) mass = radii**4 bodies.append(Body(random.randint(40, canvas.width - 40), random.randint(40, canvas.height - 40), radii, mass)) if ret is None: start_time = window.performance.now() ret = setInterval(create_proxy(run), int(1000 / fps)) Element("startButton").element.disabled = True Element("stopButton").element.disabled = False Element("stopButton").element.classList.add('active') def stop(*args, **kwargs): global ret if ret is not None: clearInterval(ret) ret = None Element("startButton").element.disabled = False Element("stopButton").element.disabled = True Element("stopButton").element.classList.remove('active') def calculate_gravitational_force(body1, body2): dx = body2.x - body1.x dy = body2.y - body1.y distance = max(Math.sqrt(dx ** 2 + dy ** 2), 0.001) # Evitar divisiones por cero force_magnitude = G * body1.mass * body2.mass / distance ** 2 angle = Math.atan2(dy, dx) force_x = force_magnitude * Math.cos(angle) force_y = force_magnitude * Math.sin(angle) return force_x, force_y def run(): global start_time elapsed_time = (window.performance.now() - start_time) / 1000 Element("timer").element.innerText = f"Tiempo: {elapsed_time:.1f} s" ctx.clearRect(0, 0, canvas.width, canvas.height) # Calcular fuerzas gravitatorias entre todos los pares de cuerpos forces = [] for i, body1 in enumerate(bodies): force = [0, 0] for j, body2 in enumerate(bodies): if i != j: force_x, force_y = calculate_gravitational_force(body1, body2) force[0] += force_x force[1] += force_y forces.append(force) # Actualizar posiciones de los cuerpos y dibujar sus trayectorias for i, body in enumerate(bodies): body.update_position([forces[i]]) for x, y in body.history: ctx.beginPath() ctx.fillStyle = body.color ctx.arc(x, y, 1, 0, 2 * Math.PI) ctx.fill() # Dibujar los cuerpos for body in bodies: body.draw()