26.1 Del modelo físico al modelo de datos
En papel describimos un cuerpo mediante símbolos como m, r y v. En un programa necesitamos decidir cómo guardar esos valores, qué nombres usar y qué condiciones deben cumplir.
Un objeto JavaScript permite agrupar datos que pertenecen a una misma entidad. La estructura elegida debe hacer visibles las decisiones físicas y dificultar estados imposibles.
26.2 Separar identidad, parámetros y estado
La aceleración no suele guardarse como estado fundamental: puede calcularse a partir de las fuerzas del instante. Evitar datos derivados duplicados reduce inconsistencias.
26.3 Vectores como objetos
En dos dimensiones podemos representar un vector mediante propiedades x e y:
function vector(x = 0, y = 0) {
if (!Number.isFinite(x) || !Number.isFinite(y)) {
throw new TypeError('Las componentes deben ser números finitos');
}
return { x, y };
}
const posicion = vector(3, 1.5);
const velocidad = vector(-2, 4);
console.log({ posicion, velocidad });Los nombres x e y no incluyen unidades. La documentación o el nombre del campo contenedor debe establecer que, por ejemplo, la posición está en metros y la velocidad en metros por segundo.
26.4 Estructura mínima de un cuerpo
Un cuerpo puntual móvil puede representarse así:
{ id, masaKg, posicionM: {x, y}, velocidadMps: {x, y}, movil }Los sufijos de unidades hacen explícito qué espera cada campo. Una alternativa válida es declarar una convención global del motor, pero debe mantenerse sin excepciones.
Conviene que id sea único y estable. El nombre visible puede cambiar sin romper referencias internas.
26.5 Cuerpos móviles e inmóviles
Una pared o el suelo pueden modelarse como cuerpos que no responden dinámicamente a las fuerzas. No es necesario asignarles una masa numérica gigantesca.
Cuerpo inmóvil:
movil = false y aceleración impuesta en cero.Muchos motores almacenan la masa inversa:
Esto evita dividir repetidamente y permite que una masa inversa cero represente un cuerpo que no acelera. No significa que su masa física sea literalmente infinita.
26.6 Una fábrica con valores iniciales seguros
Una función de creación centraliza valores predeterminados, copias y validaciones:
function crearCuerpo({
id,
masaKg,
posicionM = { x: 0, y: 0 },
velocidadMps = { x: 0, y: 0 },
movil = true
}) {
if (!id) throw new TypeError('El cuerpo necesita un id');
if (movil && (!Number.isFinite(masaKg) || masaKg <= 0)) {
throw new RangeError('La masa móvil debe ser finita y positiva');
}
return {
id,
masaKg: movil ? masaKg : null,
masaInversa: movil ? 1 / masaKg : 0,
posicionM: { ...posicionM },
velocidadMps: { ...velocidadMps },
movil
};
}
console.log(crearCuerpo({ id: 'caja', masaKg: 5 }));Copiar los vectores de entrada evita que otro código los modifique accidentalmente después de crear el cuerpo.
26.7 Representar una fuerza como dato
Una fuerza evaluada en un instante necesita al menos un receptor y un vector. Agregar el tipo y el agente mejora la trazabilidad:
{ tipo, agenteId, receptorId, vectorN: {x, y} }Ejemplo conceptual:
{ tipo: 'peso', agenteId: 'tierra', receptorId: 'caja', vectorN: { x: 0, y: -49.05 } }El vector debe describir la fuerza sobre el receptor. Esta convención evita ambigüedades al registrar pares de interacción.
26.8 Fuerza instantánea y regla de fuerza
Conviene distinguir dos conceptos:
Función que recibe estado y tiempo.
Calcula el vector actual.
Dato instantáneo en newtons.
Una regla de peso uniforme puede producir un nuevo objeto fuerza en cada evaluación. La regla permanece configurada; el vector resultante puede cambiar si cambian sus parámetros o el estado.
No deben serializarse funciones como si fueran datos JSON. Es mejor guardar el nombre del tipo y sus parámetros, y reconstruir la regla al cargar.
26.9 Unidades visibles en la estructura
JavaScript no distingue metros de segundos. La seguridad depende de convenciones consistentes:
| Campo | Significado | Unidad |
|---|---|---|
masaKg | Masa inercial | kg |
posicionM | Posición cartesiana | m |
velocidadMps | Velocidad cartesiana | m/s |
vectorN | Vector fuerza | N |
tiempoS | Tiempo del modelo | s |
También debe definirse cómo se convierten las coordenadas físicas a píxeles. Guardar la posición física directamente en píxeles mezcla el modelo con la vista.
26.10 Invariantes y validación
Una invariante es una condición que todo objeto válido debe conservar. Validarla al crear o actualizar datos hace que los errores aparezcan cerca de su origen.
NaN ni infinito.id no vacío y único dentro del mundo.masaInversa = 1/masaKg para cuerpos móviles.También pueden validarse límites propios de cada modelo: coeficientes no negativos, radios positivos y referencias a cuerpos existentes.
26.11 Referencias compartidas y aliasing
Los objetos JavaScript se asignan por referencia. Si dos cuerpos reciben el mismo objeto posición, modificar uno también modificará el otro:
cuerpoA.posicionM === cuerpoB.posicionM // no debería ser trueEste problema se denomina aliasing. Para evitarlo, cada cuerpo debe poseer sus propios vectores mutables o usar estructuras inmutables.
La copia superficial { ...vector } alcanza para un vector plano con números. Para estructuras anidadas hace falta copiar cada nivel relevante.
26.12 Estado mutable e instantáneas
Durante una simulación puede ser eficiente actualizar el estado actual. Aun así, para comparar instantes, depurar o interpolar conviene crear una instantánea independiente:
{ posicionM: { ...cuerpo.posicionM }, velocidadMps: { ...cuerpo.velocidadMps } }Guardar una referencia al mismo vector no conserva el pasado: cuando el estado actual cambie, el supuesto historial también cambiará.
Debe quedar claro qué funciones mutan objetos y cuáles devuelven copias. Mezclar ambos estilos sin una convención genera errores difíciles de rastrear.
26.13 Serializar y reconstruir
JSON.stringify puede guardar números, cadenas, booleanos, arreglos y objetos simples, pero no conserva funciones, undefined ni prototipos personalizados.
Una escena serializable puede almacenar:
{ cuerpos: [...], reglas: [{ tipo: 'gravedadUniforme', g: { x: 0, y: -9.81 } }] }Al cargarla, un registro de fábricas transforma cada descripción de regla en la función correspondiente. Después de analizar JSON externo deben repetirse todas las validaciones; que el texto sea JSON válido no garantiza que represente un mundo físico válido.
26.14 Actividad interactiva: inspeccionar un cuerpo
Modificá los datos del cuerpo. El laboratorio genera una representación serializable y una fuerza peso instantánea separada.
Inspector del modelo de datos
Las coordenadas físicas permanecen en metros; el canvas realiza su propia conversión a píxeles.
Objeto cuerpo
Fuerza evaluada
26.15 Datos derivados y fuente única de verdad
Si un valor puede calcularse de manera confiable a partir de otros, almacenarlo por duplicado crea dos posibles versiones de la verdad.
| Dato | Decisión recomendada |
|---|---|
| Aceleración actual | Calcularla desde fuerza resultante y masa. |
| Rapidez | Calcularla con Math.hypot(v.x, v.y). |
| Masa inversa | Puede almacenarse por rendimiento, pero debe actualizarse junto con la masa. |
| Posición en píxeles | Calcularla en la capa de visualización desde la posición física. |
| Fuerza peso | Evaluarla a partir de masa y campo actual. |
Cuando se decide almacenar un dato derivado por rendimiento, la actualización debe centralizarse para conservar la invariante.
26.16 Pruebas del modelo
Las fábricas y validadores pueden probarse sin ejecutar una simulación completa:
- Crear un cuerpo móvil válido y comprobar sus valores predeterminados.
- Rechazar masa cero, negativa, infinita o
NaN. - Comprobar que los vectores de entrada fueron copiados.
- Crear un cuerpo inmóvil y verificar
masaInversa = 0. - Serializar, reconstruir y comparar todos los datos físicos.
- Confirmar que cada fuerza señala un receptor existente.
Probar estas piezas pequeñas reduce la cantidad de causas posibles cuando más adelante falle el acumulador o el integrador.
26.17 Errores frecuentes y ejercicios
Errores frecuentes
- Mezclar metros y píxeles en el mismo campo.
- Usar nombres genéricos como
valorovectorsin contexto ni unidad. - Permitir masas móviles nulas o negativas.
- Compartir accidentalmente el mismo objeto posición entre cuerpos.
- Guardar aceleración y fuerza resultante sin mantenerlas sincronizadas.
- Serializar funciones y esperar que JSON pueda reconstruirlas.
Ejercicios propuestos
- Diseñá el objeto de un cuerpo llamado
pelota, de 2 kg, en(3; 4) my con velocidad(−1; 5) m/s. - Calculá y agregá su masa inversa.
- Representá el peso terrestre sobre la pelota indicando tipo, agente, receptor y vector.
- Explicá por qué
{ masaKg: 0, movil: true }debe rechazarse. - Escribí una instantánea independiente de posición y velocidad para la pelota.
- Indicá qué parte de una regla de fuerza debe guardarse en JSON y cuál debe reconstruirse.
Ver soluciones
{ id: 'pelota', masaKg: 2, posicionM: {x: 3, y: 4}, velocidadMps: {x: -1, y: 5}, movil: true }.masaInversa = 1/2 = 0,5 kg−1.{ tipo: 'peso', agenteId: 'tierra', receptorId: 'pelota', vectorN: {x: 0, y: -19.62} }.- La segunda ley requeriría dividir por cero y el estado no representa un cuerpo móvil clásico válido.
{ posicionM: { ...pelota.posicionM }, velocidadMps: { ...pelota.velocidadMps } }.- Se guardan el tipo y los parámetros serializables; la función que evalúa la regla se reconstruye desde un registro del programa.
26.18 Ideas para recordar
- La estructura de datos debe reflejar el significado físico del modelo.
- Identidad, parámetros, estado y fuerzas cumplen funciones diferentes.
- Los nombres y convenciones deben hacer visibles las unidades.
- Las fábricas centralizan valores iniciales, copias y validaciones.
- Los objetos anidados requieren cuidado con referencias compartidas.
- JSON guarda datos; las reglas de comportamiento deben reconstruirse.
En el próximo tema construiremos un acumulador de fuerzas y calcularemos la aceleración resultante de cada cuerpo.