simu.js
19 de enero de 2024Una librería JavaScript para simular física de una forma diferente, incorporando un sistema para calcular el estado de una simulación en cualquier momento.
Tabla de Contenidos
¿Por qué la creé?
Esta idea surgió de desarrollar el motor lógico de Solv3D, una aplicación para representar problemas de física, donde no pude implementar todas las funcionalidades que me hubiera gustado.
Importante: Esta librería está en desarrollo, por lo que puede sufrir cambios drásticos en el futuro.
Instalación
Puedes instalar esta librería usando npm:
npm install simu
Unidades
Las unidades se expresan en el Sistema Internacional.
Vectores
Posición, velocidad y aceleración se expresan en unidades vectoriales. Puedes crear un vector de la siguiente manera:
import * as SIMU from 'simu';
// Position x=1m, y=0m, z=0m
let position = SIMU.vector(1, 0, 0);
// Velocity x=0m/s, y=1m/s, z=0m/s
let velocity = SIMU.vector(0, 1, 0);
// Acceleration x=0m/s^2, y=0m/s^2, z=1m/s^2
let acceleration = SIMU.vector(0, 0, 1);
Acceso a los componentes del vector:
console.log(position.vector); // [1, 0, 0]
console.log(velocity.vector); // [0, 1, 0]
console.log(acceleration.vector); // [0, 0, 1]
console.log(position.x); // 1
console.log(velocity.y); // 1
console.log(acceleration.z); // 1
Modificar componentes del vector:
position.vector = [2, 0, 0];
velocity.y = 2;
Clonar un vector:
let anotherVector = position.clone();
Conversiones
Puedes convertir unidades a otras unidades del mismo tipo:
let position = SIMU.vector(1, 0);
let velocity = SIMU.vector(SIMU.SPEED_OF_LIGHT * 0.5, 0);
let acceleration = SIMU.vector(0, -SIMU.G_SUN);
// Get position as cm
console.log(position.x + "m = " + SIMU.convert(position.x, SIMU.PositionUnit.Centimeters) + "cm");
// Get velocity as c
console.log(velocity.x + "m/s = " + SIMU.convert(velocity.x, SIMU.VelocityUnit.SpeedOfLight) + "c");
// Get acceleration as km/h^2
console.log(acceleration.y + "m/s^2 = " + SIMU.convert(acceleration.y, SIMU.AccelerationUnit.KilometersPerSecondSquared) + "km/s^2");
Output:
1m = 100cm
149896229m/s = 0.49999985730156c
-274m/s^2 = -0.274km/s^2
Módulos
Puedes usar diferentes módulos de física preparados para ser cargados en una simulación. Estos son los que están disponibles actualmente:
Cinemática
El módulo de cinemática te permite crear escenas dinámicas con la flexibilidad de introducir aceleraciones que influyen en todos los objetos de la escena, como la gravedad o las fuerzas del aire, por ejemplo.
import * as SIMU from 'simu';
const kinematicsScene = new SIMU.Kinematics.Scene();
// Permanent acceleration (earth gravity)
kinematicsScene.addAcceleration({
vector: SIMU.vector(0, -9.81);
});
// Airflow acceleration that starts at second 2 with 1s duration
kinematicsScene.addAcceleration({
startAt: 2,
duration: 1,
vector: SIMU.vector(1, 0)
})
Tienes la flexibilidad de crear objetos cinemáticos con propiedades iniciales personalizables como posición y velocidad. Además, puedes incorporar aceleraciones a tu objeto de forma sencilla, como una aceleración vertical de un helicóptero.
const helicopter = new SIMU.Kinematics.Object({
_name: 'Helicopter',
_initialPosition: SIMU.vector(0, 10, 0);
});
// Add permanent vertical helicopter acceleration
helicopter.addAcceleration({
vector: SIMU.vector(0, 9.81);
});
kinematicsScene.add(helicopter);
Usando este ejemplo, puedes simular un helicóptero manteniendo su altitud mientras es afectado por una corriente de aire que empieza en el segundo 2.
for(let i = 0; i <= 5; i++) {
scene.update(i);
console.log(helicopter.name + " position at second " + i + ": " + helicopter.position.vector)
}
Output:
"Helicopter position at second 0: [0, 10, 0]"
"Helicopter position at second 1: [0, 10, 0]"
"Helicopter position at second 2: [0, 10, 0]"
"Helicopter position at second 3: [1, 10, 0]"
"Helicopter position at second 4: [2, 10, 0]"
"Helicopter position at second 5: [3, 10, 0]"
Relatividad especial
El módulo de Relatividad Especial te permite crear escenas con objetos afectados por fenómenos relativistas, especialmente a velocidades cercanas a la velocidad de la luz.
import * as SIMU from 'simu';
const rocket = new SIMU.SpecialRelativity.Object({
_name: 'Rocket',
_velocity: SIMU.vector(SIMU.SPEED_OF_LIGHT * 0.95, 0, 0)
});
for(let i = 0; i <= 10; i++) {
rocket.update(i);
console.log("At second " + i + ", Rocket proper time is " + rocket.properTime)
}
Output:
At second 0, Rocket proper time is 0
At second 1, Rocket proper time is 3.2025630761017405
At second 2, Rocket proper time is 6.405126152203481
At second 3, Rocket proper time is 9.60768922830522
At second 4, Rocket proper time is 12.810252304406962
At second 5, Rocket proper time is 16.0128153805087
At second 6, Rocket proper time is 19.21537845661044
At second 7, Rocket proper time is 22.41794153271218
At second 8, Rocket proper time is 25.620504608813924
At second 9, Rocket proper time is 28.823067684915664
At second 10, Rocket proper time is 32.0256307610174
Vamos a crear una escena y añadir un segundo objeto.
const specialRelativityScene = new SIMU.SpecialRelativity.Scene();
const mike = new SIMU.SpecialRelativity.Object({
_name: 'Mike',
_initialPosition: SIMU.vector(0, 0, 0),
_velocity: SIMU.vector(0, 0, 0)
});
specialRelativityScene.add(rocket);
specialRelativityScene.add(mike);
Ahora vamos a observar algunas propiedades relativistas del cohete desde la perspectiva de Mike:
specialRelativityScene.update(0);
let properties = mike.calculateRelativisticProperties(rocket);
// Obtaining rocket's lorentz factor
console.log("At second 0, the rocket's lorentz factor is " + properties.lorentzFactor + " from Mike's perspective");
// Obtaining rocket's velocity relative to Mike
console.log("At second 0, the rocket's velocity relative to Mike is " + properties.vRelative.vector);
// Obtaining rocket's time relative to Mike
console.log("At second 0, the rocket's time is " + (rocket.properTime - mike.properTime) + " seconds behind Mike's");
// Obtaining rocket's length in meters at different moments
let rocketLength = 10;
console.log("At second 0, the rocket's length is " + properties.lorentzFactor * rocketLength + " meters from Mike's perspective");
// Obtaining rocket's mass in kilograms
console.log("At second 0, the rocket's mass is " + properties.mRelative + " kilograms from Mike's perspective");
Gravedad
El módulo de Gravedad te permite crear escenas con objetos afectados por la gravedad de otros objetos. Por ejemplo, puedes crear una escena con la Tierra y la Luna, y ver cómo la Luna orbita alrededor de la Tierra.
Una escena de gravedad debe ser cacheada antes de ser usada en una simulación. Esto es debido a que el módulo de gravedad es computacionalmente costoso, por lo que se recomienda cachear la escena antes de usarla en una simulación. De todas formas, si quieres reproducir una simulación de gravedad desde el tiempo 0, no necesitas cachear la escena, pero si quieres reproducirla desde un tiempo específico, tienes que “cachear” la escena con una duración especificada. Mira cómo cachear una escena de gravedad para más información.
Este es un ejemplo de cómo implementar una simulación de gravedad:
import * as SIMU from 'simu';
const earth = new SIMU.Gravity.Object({
_name: 'Earth',
_mass: 5.972e+24,
_initialPosition: SIMU.vector(0, 0, 0),
_velocity: SIMU.vector(0, 0, 0)
});
const moon = new SIMU.Gravity.Object({
_name: 'Moon',
_mass: 7.34767309e+22,
_initialPosition: SIMU.vector(384400000, 0, 0),
_velocity: SIMU.vector(0, 1022, 0)
});
const gravityScene = new SIMU.Gravity.Scene();
gravityScene.add(earth);
gravityScene.add(moon);
gravityScene.update(0);
console.log("Moon acceleration: " + moon.acceleration.vector)
Cómo cachear una escena de gravedad
Como se ha dicho, si quieres reproducir una simulación de gravedad, puedes hacerlo reproduciéndola desde el tiempo 0, pero si quieres reproducirla desde un tiempo específico, tienes que “cachear” la escena con una duración especificada:
// With the code provided before
const simulation = new SIMU.Simulation();
simulation.loadScene(gravityScene);
/* Reproduce from time 0, not cache required because
* next step (0.1s-0.2s) is not far away from
* previous step (0s), so is not required too much
* processing capacity */
simulation.time = 0;
simulation.play();
simulation.pause();
/* Reproduce from time 10, cache required because next
* step (time 10s) is far away from previous step (time 0s) */
gravityScene.cache(10);
simulation.time = 10;
simulation.play();
simulation.pause();
Puedes controlar la precisión de la escena cachada en la función cache():
// Cache 100 seconds of simulation with a precision of 0.001 seconds between steps
gravityScene.cache(100, 0.001);
// Additionally, you can change the period scene is cached. Default is 1s, so if you indicate to cache 100s there will be 100 steps cached. The smaller the value, the less computational power will be required, but more memory will be used.
gravityScene.cache(100, 0.001, 1);
Simulación
Una simulación es una instancia que te permite dar vida a la escena dada, pudiendo manipular varios aspectos. Esto incluye funciones como reproducir, pausar, rebobinar, ajustar la velocidad de reproducción y establecer una duración máxima, entre otras.
Este es un ejemplo ilustrativo de cómo implementar una simulación usando la escena cinemática detallada en el punto del módulo cinemática:
import * as SIMU from './simu';
const simulation = new SIMU.Simulation({
duration: 3600,
inLoop: true,
playbackSpeed: 1
});
simulation.loadScene(kinematicsScene);
simulation.play();
simulation.updateEventEmitter.subscribe(() = {
console.log("Helicopter's position at second " + simulation.time + ": " + helicopter.position.vector)
})