36.1 Del modelo físico a los datos
En una simulación, un cuerpo rotatorio no se representa únicamente mediante una imagen girada. Necesitamos almacenar las variables que definen su estado físico y calcular a partir de ellas lo que debe mostrarse.
Estado
Posición, orientación, velocidades, tamaño y propiedades inerciales.
Cálculos derivados
Coordenadas de puntos, vectores de velocidad, energía y otras magnitudes.
Representación
Conversión a píxeles y actualización de SVG, Canvas o elementos HTML.
Mantener estas capas separadas facilita verificar la física, cambiar la escala visual y reutilizar el mismo modelo con distintas formas de dibujo.
36.2 Elegir una convención
Antes de escribir fórmulas conviene fijar una convención y documentarla. En este tema usaremos:
- eje x positivo hacia la derecha;
- eje y físico positivo hacia arriba;
- ángulos medidos desde +x;
- ángulos y velocidades angulares positivos en sentido antihorario;
- unidades del Sistema Internacional para el estado físico.
36.3 Usar radianes internamente
Las funciones trigonométricas de JavaScript reciben radianes. Además, las ecuaciones v = Rω, α = dω/dt y θ = θ₀ + ωt suponen que los ángulos están expresados en radianes.
const gradosARadianes = grados => grados * Math.PI / 180;
const radianesAGrados = radianes => radianes * 180 / Math.PI;
console.log({
grados180: gradosARadianes(180),
piSobreDos: radianesAGrados(Math.PI / 2)
});La interfaz puede mostrar grados para comodidad del usuario, pero el estado interno debe conservar radianes.
36.4 Una estructura para el estado
Un objeto agrupa las variables relacionadas y hace explícitas sus unidades:
const cuerpo = {
posicion: { x: 2, y: 1 }, // m
velocidad: { x: 1, y: 0.5 }, // m/s
angulo: Math.PI / 4, // rad
velocidadAngular: 2, // rad/s
radio: 1.5, // m
masa: 3, // kg
momentoInercia: 3.375 // kg·m²
};
console.log(cuerpo);Los nombres descriptivos reducen errores. Una propiedad llamada angulo es preferible a una abreviatura ambigua; los comentarios o tipos pueden documentar unidades.
36.5 Estado fundamental y valores derivados
Conviene almacenar solo los datos necesarios para definir el estado y calcular el resto. Si se guardan simultáneamente ángulo, seno y coseno, esos tres valores pueden quedar desincronizados.
const cuerpo = {
angulo: Math.PI / 4,
velocidadAngular: 2
};
const coseno = Math.cos(cuerpo.angulo);
const seno = Math.sin(cuerpo.angulo);
const rapidezAngular = Math.abs(cuerpo.velocidadAngular);
console.log({ coseno, seno, rapidezAngular });Los valores derivados se recomputan cuando se necesitan. En una optimización avanzada podrían almacenarse temporalmente, pero deben invalidarse de manera controlada.
36.6 Coordenadas locales del cuerpo
Una forma se describe cómodamente en un sistema local cuyo origen coincide con el centro del cuerpo. Por ejemplo, un punto marcado en el borde a ángulo local φ = 0 tiene:
Los vértices de un rectángulo o de un polígono también pueden almacenarse como pares (x, y) locales. Mover o girar el cuerpo no modifica esta geometría original.
36.7 Matriz de rotación en dos dimensiones
Para girar un vector local (qx, qy) un ángulo θ en sentido antihorario:
function rotar({ x, y }, angulo) {
const c = Math.cos(angulo);
const s = Math.sin(angulo);
return { x: x * c - y * s, y: x * s + y * c };
}
console.log(rotar({ x: 1, y: 0 }, Math.PI / 2));36.8 Transformar de local a global
Después de rotar el punto local, se suma la posición global del centro:
function rotar({ x, y }, angulo) {
const c = Math.cos(angulo);
const s = Math.sin(angulo);
return { x: x * c - y * s, y: x * s + y * c };
}
function localAGlobal(cuerpo, puntoLocal) {
const girado = rotar(puntoLocal, cuerpo.angulo);
return {
x: cuerpo.posicion.x + girado.x,
y: cuerpo.posicion.y + girado.y
};
}
const cuerpo = { posicion: { x: 2, y: 1 }, angulo: Math.PI / 2 };
console.log(localAGlobal(cuerpo, { x: 1.5, y: 0 }));El orden importa: primero se rota alrededor del origen local y después se traslada. Trasladar primero haría que el punto orbitara alrededor del origen global.
36.9 Un punto marcado sobre una rueda
Si el punto está en el borde y θ representa su orientación desde +x:
Estas ecuaciones son un caso particular de la transformación local a global con qlocal = (R, 0). Para un punto interior basta utilizar un radio local menor.
36.10 Del mundo físico a la pantalla
En SVG y Canvas, el origen suele estar arriba a la izquierda y la coordenada y aumenta hacia abajo. Podemos conservar y físico hacia arriba mediante una función de cámara:
function mundoAPantalla(punto, camara) {
return {
x: camara.origenX + punto.x * camara.pixelesPorMetro,
y: camara.origenY - punto.y * camara.pixelesPorMetro
};
}
const camara = { origenX: 320, origenY: 240, pixelesPorMetro: 70 };
console.log(mundoAPantalla({ x: 2, y: 1 }, camara));36.11 La escala no cambia la física
pixelesPorMetro pertenece a la representación, no al cuerpo. Cambiar la escala debe agrandar o reducir el dibujo sin alterar posición física, velocidad, energía ni trayectoria.
Modelo físico
Se conserva en metros y participa en las ecuaciones.
Representación
Solo determina el tamaño visible.
36.12 Velocidad por rotación de un punto
En el plano xy, con ω positiva antihoraria y r = (rx, ry) desde el centro hasta el punto:
function velocidadRotacional(omega, radioVector) {
return {
x: -omega * radioVector.y,
y: omega * radioVector.x
};
}
console.log(velocidadRotacional(2, { x: 1.5, y: 0 }));El vector resultante es tangente a la circunferencia y su módulo es |ω|r.
36.13 Sumar traslación y rotación
Si el centro también se mueve, la velocidad global del punto es:
function velocidadRotacional(omega, radioVector) {
return {
x: -omega * radioVector.y,
y: omega * radioVector.x
};
}
function velocidadDePunto(cuerpo, radioVectorGlobal) {
const rotacional = velocidadRotacional(
cuerpo.velocidadAngular,
radioVectorGlobal
);
return {
x: cuerpo.velocidad.x + rotacional.x,
y: cuerpo.velocidad.y + rotacional.y
};
}
const cuerpo = {
velocidad: { x: 1, y: 0.5 },
velocidadAngular: 2
};
console.log(velocidadDePunto(cuerpo, { x: 1.5, y: 0 }));36.14 Representar aceleraciones
Para un cuerpo con aceleración del centro aCM, velocidad angular ω y aceleración angular α, la aceleración de P es:
En dos dimensiones, los aportes relativos son:
Guardar α y aCM en el estado permite dibujar o verificar estas componentes sin cambiar la geometría.
36.15 Representar una forma mediante puntos
Un cuerpo no circular puede describirse con un arreglo de vértices locales:
function rotar({ x, y }, angulo) {
const c = Math.cos(angulo);
const s = Math.sin(angulo);
return { x: x * c - y * s, y: x * s + y * c };
}
function localAGlobal(cuerpo, puntoLocal) {
const girado = rotar(puntoLocal, cuerpo.angulo);
return {
x: cuerpo.posicion.x + girado.x,
y: cuerpo.posicion.y + girado.y
};
}
const cuerpo = { posicion: { x: 2, y: 1 }, angulo: Math.PI / 4 };
const verticesLocales = [
{ x: -1, y: -0.5 },
{ x: 1, y: -0.5 },
{ x: 1, y: 0.5 },
{ x: -1, y: 0.5 }
];
const verticesGlobales = verticesLocales.map(
punto => localAGlobal(cuerpo, punto)
);
console.log(verticesGlobales);La misma transformación sirve para centros de masa, puntos de aplicación de fuerzas, sensores, articulaciones y marcas visuales.
36.16 Dibujar con SVG
SVG resulta útil cuando cada elemento necesita identidad, etiquetas o interacción. Sus atributos pueden actualizarse con coordenadas ya convertidas:
function mundoAPantalla(punto, camara) {
return {
x: camara.origenX + punto.x * camara.pixelesPorMetro,
y: camara.origenY - punto.y * camara.pixelesPorMetro
};
}
const cuerpo = { posicion: { x: 2, y: 1 }, radio: 1.5 };
const camara = { origenX: 320, origenY: 240, pixelesPorMetro: 70 };
const puntoPantalla = { x: 456, y: 166 };
const atributos = { rueda: {}, marca: {} };
const rueda = {
setAttribute: (nombre, valor) => atributos.rueda[nombre] = valor
};
const marca = {
setAttribute: (nombre, valor) => atributos.marca[nombre] = valor
};
const centroPantalla = mundoAPantalla(cuerpo.posicion, camara);
rueda.setAttribute("cx", centroPantalla.x);
rueda.setAttribute("cy", centroPantalla.y);
rueda.setAttribute("r", cuerpo.radio * camara.pixelesPorMetro);
marca.setAttribute("cx", puntoPantalla.x);
marca.setAttribute("cy", puntoPantalla.y);
console.log(atributos);El modelo entrega números físicos; la función de renderizado es responsable de transformarlos en atributos SVG.
36.17 Dibujar con Canvas
Canvas es conveniente para muchas partículas o dibujos que se renuevan completos. Puede usarse la transformación del contexto, recordando que los ángulos visuales se invierten si el eje y no fue corregido:
const operaciones = [];
const ctx = {
save: () => operaciones.push("save"),
translate: (x, y) => operaciones.push(["translate", x, y]),
rotate: angulo => operaciones.push(["rotate", angulo]),
beginPath: () => operaciones.push("beginPath"),
arc: (...argumentos) => operaciones.push(["arc", ...argumentos]),
stroke: () => operaciones.push("stroke"),
restore: () => operaciones.push("restore")
};
const centroPantalla = { x: 460, y: 170 };
const cuerpo = { angulo: Math.PI / 4 };
const radioEnPixeles = 105;
ctx.save();
ctx.translate(centroPantalla.x, centroPantalla.y);
ctx.rotate(-cuerpo.angulo); // y de pantalla apunta hacia abajo
ctx.beginPath();
ctx.arc(0, 0, radioEnPixeles, 0, 2 * Math.PI);
ctx.stroke();
ctx.restore();
console.log(operaciones);save() y restore() evitan que una transformación aplicada a un cuerpo contamine los dibujos siguientes.
36.18 No usar el dibujo como estado físico
Leer una transformación CSS o un atributo SVG para descubrir dónde está el cuerpo mezcla presentación y simulación. El flujo recomendable es unidireccional:
El estado es la fuente de verdad. El dibujo se puede reconstruir completamente a partir de él y nunca debe introducir modificaciones físicas ocultas.
36.19 Separar actualización y renderizado
Una función actualiza el modelo; otra lo dibuja. En este tema el laboratorio modifica directamente el estado con controles, mientras que el próximo tema integrará su evolución temporal.
function calcularRepresentacion(modelo, camara) {
return {
angulo: modelo.angulo,
omega: modelo.velocidadAngular,
escala: camara.pixelesPorMetro
};
}
function actualizarSVG(vista, datos) {
vista.ultimoDibujo = datos;
}
function actualizar(modelo, entrada) {
modelo.angulo = entrada.angulo;
modelo.velocidadAngular = entrada.omega;
}
function renderizar(modelo, vista) {
const datos = calcularRepresentacion(modelo, vista.camara);
actualizarSVG(vista, datos);
}
const modelo = { angulo: 0, velocidadAngular: 0 };
const vista = { camara: { pixelesPorMetro: 70 } };
actualizar(modelo, { angulo: Math.PI / 4, omega: 2 });
renderizar(modelo, vista);
console.log({ modelo, representacion: vista.ultimoDibujo });Esta separación permite probar actualizar y los cálculos sin necesidad de abrir un navegador.
36.20 Precisión, formato y validación
Los cálculos deben conservar la precisión de punto flotante disponible. El redondeo se aplica solo al mostrar resultados:
const valor = 2.3456;
const salida = { textContent: "" };
const formato = new Intl.NumberFormat("es-AR", {
minimumFractionDigits: 2,
maximumFractionDigits: 2
});
salida.textContent = `${formato.format(valor)} rad/s`;
console.log(salida.textContent);También deben validarse radios positivos, masas no negativas y números finitos. Number.isFinite(valor) ayuda a detectar entradas inválidas antes de que produzcan coordenadas NaN.
Math.abs(a - b) < 1e-9.36.21 Actividad interactiva: del estado a la pantalla
Modificá el estado del cuerpo. El punto naranja está fijo en sus coordenadas locales (R, 0), pero cambia de posición global al variar θ. El vector violeta suma la velocidad del centro y la velocidad tangencial por rotación.
Laboratorio de representación de un cuerpo rotatorio
El panel muestra simultáneamente magnitudes físicas en SI y coordenadas de pantalla en píxeles.
El estado se conserva en metros y radianes. La escala solo modifica el tamaño en pantalla; la inversión del eje y ocurre exclusivamente al representar.
36.22 Implementación completa del cálculo
function rotar({ x, y }, angulo) {
const c = Math.cos(angulo);
const s = Math.sin(angulo);
return { x: x * c - y * s, y: x * s + y * c };
}
function mundoAPantalla(punto, camara) {
return {
x: camara.origenX + punto.x * camara.pixelesPorMetro,
y: camara.origenY - punto.y * camara.pixelesPorMetro
};
}
function calcularRepresentacion(cuerpo, camara) {
if (!Number.isFinite(cuerpo.radio) || cuerpo.radio <= 0) {
throw new Error("Radio no válido");
}
const puntoLocal = { x: cuerpo.radio, y: 0 };
const radioGlobal = rotar(puntoLocal, cuerpo.angulo);
const puntoGlobal = {
x: cuerpo.posicion.x + radioGlobal.x,
y: cuerpo.posicion.y + radioGlobal.y
};
const velocidadRotacion = {
x: -cuerpo.velocidadAngular * radioGlobal.y,
y: cuerpo.velocidadAngular * radioGlobal.x
};
const velocidadPunto = {
x: cuerpo.velocidad.x + velocidadRotacion.x,
y: cuerpo.velocidad.y + velocidadRotacion.y
};
return {
puntoLocal,
radioGlobal,
puntoGlobal,
puntoPantalla: mundoAPantalla(puntoGlobal, camara),
centroPantalla: mundoAPantalla(cuerpo.posicion, camara),
velocidadRotacion,
velocidadPunto
};
}
const cuerpo = {
posicion: { x: 2, y: 1 },
velocidad: { x: 1, y: 0.5 },
angulo: Math.PI / 4,
velocidadAngular: 2,
radio: 1.5
};
const camara = { origenX: 320, origenY: 240, pixelesPorMetro: 70 };
console.log(calcularRepresentacion(cuerpo, camara));La función no toca el DOM y siempre devuelve los mismos resultados para las mismas entradas. Esa propiedad facilita pruebas automatizadas. Otra función puede recibir el objeto devuelto y actualizar SVG o Canvas.
36.23 Ejemplos, errores frecuentes y ejercicios
Ejemplos resueltos
Ejemplo 1: 90° equivalen a π/2 ≈ 1,5708 rad. Un punto local (2, 0) girado ese ángulo queda aproximadamente en (0, 2).
Ejemplo 2: un centro está en (3, −1) m y un punto girado local queda en (0,5; 1) m. Su posición global es (3,5; 0) m.
Ejemplo 3: para ω = 4 rad/s y r = (0, 2) m:
Ejemplo 4: con origen de pantalla (400, 300), escala 50 px/m y punto físico (2, 1):
Ejemplo 5: si vCM = (1, 0) m/s y vrot = (−2, 3) m/s, entonces vP = (−1, 3) m/s.
Errores frecuentes
- pasar grados directamente a
Math.sinoMath.cos; - mezclar metros con píxeles dentro de las ecuaciones físicas;
- olvidar que la coordenada y de pantalla crece hacia abajo;
- trasladar un punto antes de rotarlo cuando debe girar alrededor del centro;
- usar (ωry, −ωrx) e invertir la velocidad tangencial;
- guardar valores derivados que luego quedan desactualizados;
- leer el estado físico desde estilos CSS o atributos gráficos;
- redondear el estado después de cada cálculo;
- comparar números de punto flotante mediante igualdad exacta;
- aplicar transformaciones Canvas sin
save()yrestore().
Ejercicios propuestos
- Convertí 30°, 180° y 270° a radianes.
- Rotá el vector local (1, 0) un ángulo de 60°.
- Un cuerpo está en (2, 3) m y un punto rotado queda a (−1, 2) m del centro. Hallá su posición global.
- Calculá la velocidad rotacional para ω = −3 rad/s y r = (2, 1) m.
- Sumá al resultado anterior una velocidad del centro (4, −1) m/s.
- Transformá el punto físico (−2, 3) a pantalla con origen (400, 300) y escala 40 px/m.
- Explicá por qué cambiar de 40 a 80 px/m no debe modificar la energía calculada.
- Escribí los cuatro vértices locales de un rectángulo de ancho 4 m y alto 2 m centrado en su origen.
- Calculá la aceleración centrípeta vectorial si ω = 2 rad/s y r = (3, −1) m.
- Proponé una prueba para verificar que
rotarconserva la longitud de un vector.
Ver soluciones y explicaciones
- π/6 ≈ 0,5236 rad; π ≈ 3,1416 rad; 3π/2 ≈ 4,7124 rad.
- (cos60°, sen60°) = (0,5; √3/2) ≈ (0,5; 0,866).
- (2, 3) + (−1, 2) = (1, 5) m.
- vrot = (−ωry, ωrx) = (3, −6) m/s.
- vP = (4, −1) + (3, −6) = (7, −7) m/s.
- xpx = 400 + 40(−2) = 320; ypx = 300 − 40·3 = 180; punto (320, 180) px.
- La escala pertenece a la cámara y solo convierte metros a píxeles; las magnitudes físicas usan los valores en SI.
- (−2, −1), (2, −1), (2, 1) y (−2, 1) m.
- acentr = −ω²r = −4(3, −1) = (−12, 4) m/s².
- Comparar
Math.hypot(x, y)antes y después de rotar para varios ángulos, aceptando una diferencia menor que una tolerancia.
36.24 Ideas para recordar
- Separá estado físico, cálculos derivados y representación.
- Documentá ejes, signos y unidades antes de programar.
- Usá radianes dentro de las ecuaciones y convertí solo para la interfaz.
- Describí la geometría en coordenadas locales.
- Rotá primero y trasladá después para pasar de local a global.
- Convertí de mundo a pantalla en una función de cámara independiente.
- En 2D, vrot = (−ωry, ωrx).
- La velocidad global de un punto suma traslación y rotación.
- El estado es la fuente de verdad; SVG, Canvas y CSS solo lo representan.
- Conservá precisión en los cálculos y redondeá únicamente al mostrar.
En el próximo tema aprenderemos a integrar la rotación, normalizar ángulos y verificar resultados, haciendo que el estado evolucione de manera estable a lo largo del tiempo.