41. Transformaciones geométricas: traslación, rotación y escalado

Las transformaciones geométricas permiten mover, orientar y cambiar el tamaño de objetos. Las matrices y las coordenadas homogéneas ofrecen una forma uniforme de aplicarlas y combinarlas.

41.1 Introducción

Un objeto gráfico suele definirse mediante puntos. Para colocarlo en una escena se transforman esos puntos sin necesidad de modificar manualmente su geometría original.

Las tres operaciones fundamentales son:

  • Traslación: cambia la posición.
  • Rotación: cambia la orientación.
  • Escalado: cambia el tamaño y, a veces, la forma.

41.2 Puntos y vectores

Un punto representa una posición y un vector, una dirección o desplazamiento. Esta diferencia es especialmente importante al trasladar.

ObjetoTraslaciónRotaciónEscalado
PuntoCambiaCambia respecto del centroCambia respecto del centro
Vector direcciónNo cambiaCambiaPuede cambiar magnitud y dirección

Una traslación mueve ambos extremos de una flecha por igual; por eso su vector libre permanece igual.

41.3 Traslación en el plano

Trasladar un punto p = (x, y) mediante t = (tx, ty) consiste en sumar:

p' = p + t = (x + tx, y + ty)

La traslación no nula no es lineal en ℝ2, porque no conserva el origen. Es una transformación afín.

function trasladar([x, y], [tx, ty]) {
  return [x + tx, y + ty];
}

console.log(trasladar([2, 3], [5, -1])); // [7, 2]

41.4 Trasladar una figura

Una figura se traslada aplicando el mismo vector a todos sus vértices. Las distancias y los ángulos internos no cambian.

function trasladarFigura(vertices, desplazamiento) {
  return vertices.map(punto =>
    punto.map((coordenada, i) => coordenada + desplazamiento[i])
  );
}

const triangulo = [[0, 0], [2, 0], [0, 1]];
console.log(trasladarFigura(triangulo, [3, 4]));
// [[3,4], [5,4], [3,5]]

41.5 Escalado respecto del origen

El escalado multiplica cada coordenada por un factor:

[[x'], [y']] = [[sx, 0], [0, sy]][[x], [y]]
x' = sxx,   y' = syy
function escalar([x, y], sx, sy = sx) {
  return [sx * x, sy * y];
}

console.log(escalar([3, 2], 2));      // [6, 4]
console.log(escalar([3, 2], 2, 0.5)); // [6, 1]

41.6 Escalado uniforme, no uniforme y reflexión

FactoresEfecto
sx = sy > 0Escalado uniforme; conserva proporciones
sx ≠ syEscalado no uniforme; puede deformar
Algún factor negativoIncluye una reflexión sobre un eje
Algún factor igual a ceroColapsa una dimensión y pierde invertibilidad

41.7 Escalado respecto de un punto

Para escalar alrededor de un centro c, primero se traslada c al origen, se escala y se regresa:

p' = c + S(p − c)
function escalarDesde(punto, centro, sx, sy = sx) {
  return punto.map(
    (coordenada, i) => centro[i] + (coordenada - centro[i]) * (i === 0 ? sx : sy)
  );
}

console.log(escalarDesde([4, 3], [2, 1], 2)); // [6, 5]

El centro permanece fijo durante la transformación.

41.8 Rotación 2D alrededor del origen

Con vectores columna y ángulos positivos antihorarios:

R(θ) = [[cos θ, −sen θ], [sen θ, cos θ]]

x' = x cos θ − y sen θ
y' = x sen θ + y cos θ

Una rotación conserva distancias, ángulos y áreas.

41.9 Grados, radianes y rotación en JavaScript

Las funciones trigonométricas de JavaScript reciben radianes. La conversión es θrad = θgradosπ/180.

function rotar([x, y], grados) {
  const theta = grados * Math.PI / 180;
  const c = Math.cos(theta);
  const s = Math.sin(theta);
  return [c * x - s * y, s * x + c * y];
}

console.log(rotar([2, 0], 90)); // aproximadamente [0, 2]

41.10 Rotación alrededor de un punto

Para rotar alrededor de c en lugar del origen:

p' = c + R(θ)(p − c)
function rotarDesde(punto, centro, grados) {
  const theta = grados * Math.PI / 180;
  const c = Math.cos(theta);
  const s = Math.sin(theta);
  const x = punto[0] - centro[0];
  const y = punto[1] - centro[1];
  return [centro[0] + c * x - s * y, centro[1] + s * x + c * y];
}

console.log(rotarDesde([3, 1], [1, 1], 90)); // aproximadamente [1, 3]

41.11 Transformaciones en ℝ3

La traslación y el escalado se extienden agregando la componente z:

p' = (x + tx, y + ty, z + tz)
S(p) = (sxx, syy, szz)
const transformar3D = (punto, traslacion, escala) =>
  punto.map((valor, i) => valor * escala[i] + traslacion[i]);

console.log(transformar3D([1, 2, 3], [5, 0, -2], [2, 1, 0.5]));
// [7, 2, -0.5]

41.12 Rotaciones alrededor de los ejes 3D

En un sistema de mano derecha, las matrices básicas son:

Rx = [[1, 0, 0], [0, c, −s], [0, s, c]]
Ry = [[c, 0, s], [0, 1, 0], [−s, 0, c]]
Rz = [[c, −s, 0], [s, c, 0], [0, 0, 1]]
function rotarZ([x, y, z], grados) {
  const theta = grados * Math.PI / 180;
  const c = Math.cos(theta);
  const s = Math.sin(theta);
  return [c * x - s * y, s * x + c * y, z];
}

console.log(rotarZ([1, 0, 5], 90)); // aproximadamente [0, 1, 5]

41.13 Coordenadas homogéneas

Se agrega una coordenada w para representar transformaciones afines mediante matrices:

Punto 2D: (x, y) ⟶ (x, y, 1)
Vector 2D: (vx, vy) ⟶ (vx, vy, 0)

En 3D se usan vectores de cuatro componentes y matrices 4 × 4.

const puntoHomogeneo = ([x, y]) => [x, y, 1];
const vectorHomogeneo = ([x, y]) => [x, y, 0];

console.log(puntoHomogeneo([3, 4]));  // [3, 4, 1]
console.log(vectorHomogeneo([3, 4])); // [3, 4, 0]

41.14 Matrices homogéneas 2D

Traslación: [[1, 0, tx], [0, 1, ty], [0, 0, 1]]

Escalado: [[sx, 0, 0], [0, sy, 0], [0, 0, 1]]

Rotación: [[c, −s, 0], [s, c, 0], [0, 0, 1]]
const matrizTraslacion = (tx, ty) => [
  [1, 0, tx],
  [0, 1, ty],
  [0, 0, 1]
];

console.log(matrizTraslacion(5, -2));

41.15 Aplicar una matriz homogénea

La misma matriz distingue puntos y vectores mediante w. La columna de traslación se multiplica por 1 para puntos y por 0 para vectores.

function aplicar(M, v) {
  return M.map(fila =>
    fila.reduce((suma, valor, i) => suma + valor * v[i], 0)
  );
}

const T = [[1, 0, 5], [0, 1, -2], [0, 0, 1]];
console.log(aplicar(T, [3, 4, 1])); // [8, 2, 1]: punto
console.log(aplicar(T, [3, 4, 0])); // [3, 4, 0]: vector

41.16 Composición y orden

Con vectores columna, si primero se aplica A y después B, la matriz combinada es BA:

p' = B(Ap) = (BA)p

El producto matricial no es conmutativo. Escalar y luego trasladar generalmente no produce lo mismo que trasladar y luego escalar.

const aplicar = (M, v) => M.map(fila =>
  fila.reduce((suma, valor, i) => suma + valor * v[i], 0)
);
const S = [[2, 0, 0], [0, 2, 0], [0, 0, 1]];
const T = [[1, 0, 5], [0, 1, 0], [0, 0, 1]];
const p = [1, 0, 1];

console.log(aplicar(T, aplicar(S, p))); // [7, 0, 1]
console.log(aplicar(S, aplicar(T, p))); // [12, 0, 1]

41.17 Composición T · R · S

Una convención frecuente construye la matriz de modelo M = TRS. Al multiplicar un punto, se aplica primero S, después R y finalmente T.

function multiplicar(A, B) {
  return A.map(fila => B[0].map((_, j) =>
    fila.reduce((suma, valor, k) => suma + valor * B[k][j], 0)
  ));
}
const aplicar = (M, v) => M.map(fila =>
  fila.reduce((suma, valor, i) => suma + valor * v[i], 0)
);

const S = [[2, 0, 0], [0, 2, 0], [0, 0, 1]];
const R = [[0, -1, 0], [1, 0, 0], [0, 0, 1]];
const T = [[1, 0, 5], [0, 1, 3], [0, 0, 1]];
const M = multiplicar(multiplicar(T, R), S);

console.log(aplicar(M, [1, 0, 1])); // [5, 5, 1]

41.18 Transformaciones inversas y errores frecuentes

Las inversas deshacen las operaciones:

  • Traslación inversa: usar −t.
  • Rotación inversa: usar −θ.
  • Escalado inverso: usar 1/si, si cada si ≠ 0.
  • La inversa de TRS aplica S-1R-1T-1.
const punto = [7, 2];
const traslacion = [5, -1];
const trasladado = punto.map((valor, i) => valor + traslacion[i]);
const recuperado = trasladado.map((valor, i) => valor - traslacion[i]);

console.log(trasladado); // [12, 1]
console.log(recuperado); // [7, 2]

Errores habituales son mezclar grados y radianes, invertir el orden de composición, confundir filas con columnas, rotar alrededor del origen cuando se necesitaba otro centro, trasladar vectores dirección y olvidar que un escalado con factor cero no tiene inversa.

41.19 Qué debes recordar de este tema

  • La traslación suma un desplazamiento a cada punto.
  • El escalado multiplica coordenadas respecto de un centro.
  • La rotación 2D usa coseno y seno y conserva distancias.
  • Para transformar alrededor de un centro, se traslada al origen y se regresa.
  • Una traslación no nula es afín, no lineal en las coordenadas ordinarias.
  • Las coordenadas homogéneas representan transformaciones afines con matrices.
  • w = 1 identifica puntos y w = 0 identifica vectores.
  • Con vectores columna, M = TRS aplica primero S, luego R y finalmente T.

41.20 Conclusión

Traslación, rotación y escalado forman el núcleo de la geometría computacional. Las coordenadas homogéneas permiten combinarlas en una matriz y aplicar la misma operación a todos los vértices de un objeto.

En el próximo tema estudiaremos cómo se utiliza el álgebra lineal para construir gráficos 2D y 3D.