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.
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:
Un punto representa una posición y un vector, una dirección o desplazamiento. Esta diferencia es especialmente importante al trasladar.
| Objeto | Traslación | Rotación | Escalado |
|---|---|---|---|
| Punto | Cambia | Cambia respecto del centro | Cambia respecto del centro |
| Vector dirección | No cambia | Cambia | Puede cambiar magnitud y dirección |
Una traslación mueve ambos extremos de una flecha por igual; por eso su vector libre permanece igual.
Trasladar un punto p = (x, y) mediante t = (tx, ty) consiste en sumar:
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]
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]]
El escalado multiplica cada coordenada por un factor:
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]
| Factores | Efecto |
|---|---|
| sx = sy > 0 | Escalado uniforme; conserva proporciones |
| sx ≠ sy | Escalado no uniforme; puede deformar |
| Algún factor negativo | Incluye una reflexión sobre un eje |
| Algún factor igual a cero | Colapsa una dimensión y pierde invertibilidad |
Para escalar alrededor de un centro c, primero se traslada c al origen, se escala y se regresa:
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.
Con vectores columna y ángulos positivos antihorarios:
Una rotación conserva distancias, ángulos y áreas.
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]
Para rotar alrededor de c en lugar del origen:
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]
La traslación y el escalado se extienden agregando la componente z:
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]
En un sistema de mano derecha, las matrices básicas son:
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]
Se agrega una coordenada w para representar transformaciones afines mediante matrices:
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]
const matrizTraslacion = (tx, ty) => [
[1, 0, tx],
[0, 1, ty],
[0, 0, 1]
];
console.log(matrizTraslacion(5, -2));
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
Con vectores columna, si primero se aplica A y después B, la matriz combinada es BA:
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]
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]
Las inversas deshacen las operaciones:
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.
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.