Proyectar un vector sobre otro permite extraer la parte que apunta en una dirección determinada. El resultado separa una componente paralela y otra perpendicular.
Imagine la sombra perpendicular que un vector u produce sobre la recta dirigida por v. Esa sombra es la proyección vectorial de u sobre v.
La proyección responde cuánto de u actúa a lo largo de v y permite separar el movimiento, la fuerza o el error según direcciones relevantes.
Desde el extremo de u se traza una línea perpendicular a la recta que contiene a v. El punto donde ambas se encuentran determina el extremo de la proyección.
El vector sobre el cual se proyecta debe ser no nulo, porque el vector cero no define una dirección.
La proyección escalar, también llamada componente escalar de u en la dirección de v, mide una longitud con signo:
Si v̂ = v/||v|| es unitario, la fórmula se simplifica a u · v̂.
function componenteEscalar(u, v) {
const longitudV = Math.hypot(...v);
if (longitudV === 0) return null;
const punto = u.reduce((suma, valor, i) => suma + valor * v[i], 0);
return punto / longitudV;
}
console.log(componenteEscalar([3, 4], [2, 0])); // 3
La proyección vectorial combina la longitud con signo y la dirección unitaria:
El cociente es un escalar y multiplica todas las componentes de v.
function proyectar(u, v) {
const vv = v.reduce((suma, valor) => suma + valor * valor, 0);
if (vv === 0) return null;
const uv = u.reduce((suma, valor, i) => suma + valor * v[i], 0);
const factor = uv / vv;
return v.map(valor => factor * valor);
}
console.log(proyectar([3, 4], [2, 0])); // [3, 0]
Si la dirección v̂ ya está normalizada, v̂ · v̂ = 1 y la expresión queda:
const u = [4, 3];
const direccionUnitaria = [0.6, 0.8];
const componente = u.reduce(
(suma, valor, i) => suma + valor * direccionUnitaria[i],
0
);
const proyeccion = direccionUnitaria.map(valor => componente * valor);
console.log(componente); // 4.8
console.log(proyeccion); // [2.88, 3.84]
Esta forma es práctica cuando una dirección unitaria se reutiliza muchas veces.
Para u = (4, 3) y v = (1, 0), la proyección sobre el eje x conserva la primera componente y elimina la segunda:
const u = [4, 3];
const ejeX = [1, 0];
const factor = (u[0] * ejeX[0] + u[1] * ejeX[1]) /
(ejeX[0] ** 2 + ejeX[1] ** 2);
console.log(ejeX.map(valor => factor * valor)); // [4, 0]
Sean u = (3, 4) y v = (2, 1):
const u = [3, 4];
const v = [2, 1];
const uv = u.reduce((suma, valor, i) => suma + valor * v[i], 0);
const vv = v.reduce((suma, valor) => suma + valor ** 2, 0);
const proyeccion = v.map(valor => uv / vv * valor);
console.log(proyeccion); // [4, 2]
| u · v | Componente escalar | Sentido de la proyección |
|---|---|---|
| Positivo | Positiva | El mismo que v |
| Cero | Cero | u es perpendicular a v o alguno es nulo |
| Negativo | Negativa | Opuesto a v |
const direccion = [1, 0];
const componente = u => u[0] * direccion[0] + u[1] * direccion[1];
console.log(componente([3, 2])); // 3
console.log(componente([0, 4])); // 0
console.log(componente([-3, 2])); // -3
Todo vector u puede descomponerse respecto de una dirección v no nula:
const u = [3, 4];
const paralela = [4, 2];
const perpendicular = u.map((valor, i) => valor - paralela[i]);
const reconstruida = paralela.map((valor, i) => valor + perpendicular[i]);
console.log(perpendicular); // [-1, 2]
console.log(reconstruida); // [3, 4]
La componente residual u − proyv(u) debe tener producto escalar cero con v.
const residual = [-1, 2];
const v = [2, 1];
const producto = residual.reduce(
(suma, valor, i) => suma + valor * v[i],
0
);
console.log(producto); // 0
Esta propiedad sirve para comprobar tanto el razonamiento matemático como una implementación.
Proyectar sobre los vectores canónicos extrae las componentes individuales:
const u = [2, -3, 5];
const ejes = [[1, 0, 0], [0, 1, 0], [0, 0, 1]];
const proyecciones = ejes.map(eje => {
const componente = u.reduce((suma, valor, i) => suma + valor * eje[i], 0);
return eje.map(valor => componente * valor);
});
console.log(proyecciones); // [[2,0,0], [0,-3,0], [0,0,5]]
La recta L = {tv : t ∈ ℝ} contiene todos los múltiplos de v. proyv(u) es el punto de L más cercano a u.
La distancia mínima desde u hasta la recta es la norma de la componente perpendicular.
function distanciaALineaPorOrigen(u, v) {
const vv = v.reduce((suma, valor) => suma + valor ** 2, 0);
if (vv === 0) return null;
const uv = u.reduce((suma, valor, i) => suma + valor * v[i], 0);
const proyeccion = v.map(valor => uv / vv * valor);
const residual = u.map((valor, i) => valor - proyeccion[i]);
return Math.hypot(...residual);
}
console.log(distanciaALineaPorOrigen([3, 4], [2, 1])); // aproximadamente 2.236
Si la recta pasa por A con dirección v y deseamos proyectar un punto P, primero trasladamos el problema al origen:
function puntoMasCercano(P, A, v) {
const AP = P.map((valor, i) => valor - A[i]);
const vv = v.reduce((suma, valor) => suma + valor ** 2, 0);
if (vv === 0) return null;
const factor = AP.reduce((suma, valor, i) => suma + valor * v[i], 0) / vv;
return A.map((valor, i) => valor + factor * v[i]);
}
console.log(puntoMasCercano([4, 2], [0, 1], [1, 1])); // [2.5, 3.5]
Si n es una normal no nula del plano, la parte perpendicular al plano es proyn(u). Al restarla queda la componente contenida en el plano:
function proyectarSobrePlano(u, normal) {
const nn = normal.reduce((suma, valor) => suma + valor ** 2, 0);
if (nn === 0) return null;
const factor = u.reduce((suma, valor, i) => suma + valor * normal[i], 0) / nn;
return u.map((valor, i) => valor - factor * normal[i]);
}
console.log(proyectarSobrePlano([2, 3, 4], [0, 0, 1])); // [2, 3, 0]
Para reflejar un vector incidente u respecto de un plano con normal n, se invierte dos veces su componente normal:
function reflejar(u, normal) {
const nn = normal.reduce((suma, valor) => suma + valor ** 2, 0);
if (nn === 0) return null;
const factorDoble = 2 * u.reduce(
(suma, valor, i) => suma + valor * normal[i], 0
) / nn;
return u.map((valor, i) => valor - factorDoble * normal[i]);
}
console.log(reflejar([3, -4], [0, 1])); // [3, 4]
Para conocer qué parte de una velocidad actúa a lo largo de una pendiente, se proyecta sobre su dirección.
const velocidad = [5, 0];
const direccionPendiente = [0.6, 0.8]; // unitaria
const componente = velocidad.reduce(
(suma, valor, i) => suma + valor * direccionPendiente[i],
0
);
const velocidadSobrePendiente = direccionPendiente.map(
valor => componente * valor
);
console.log(componente); // 3
console.log(velocidadSobrePendiente); // [1.8, 2.4]
Si v̂ es un vector columna unitario, la proyección sobre su recta puede representarse mediante:
Para una dirección no normalizada, P = vvT/(vTv). Una matriz de proyección cumple P2 = P: proyectar dos veces produce el mismo resultado.
const v = [0.6, 0.8];
const P = v.map(vi => v.map(vj => vi * vj));
const u = [5, 0];
const proyectado = P.map(fila =>
fila.reduce((suma, valor, i) => suma + valor * u[i], 0)
);
console.log(P); // [[0.36, 0.48], [0.48, 0.64]]
console.log(proyectado); // [1.8, 2.4]
La dirección de proyección debe ser no nula y tener la misma dimensión que el vector proyectado. En cálculos decimales se comprueba v · v mediante una tolerancia.
function proyeccionSegura(u, v, tolerancia = 1e-12) {
const validos = Array.isArray(u) && Array.isArray(v) &&
u.length > 0 && u.length === v.length &&
[...u, ...v].every(Number.isFinite);
if (!validos) throw new Error("Vectores inválidos o incompatibles");
const vv = v.reduce((suma, valor) => suma + valor ** 2, 0);
if (vv <= tolerancia ** 2) return null;
const factor = u.reduce((suma, valor, i) => suma + valor * v[i], 0) / vv;
return v.map(valor => factor * valor);
}
console.log(proyeccionSegura([3, 4], [2, 1])); // [4, 2]
Errores habituales son invertir qué vector se proyecta, dividir solo por ||v||, olvidar multiplicar por v, proyectar sobre el vector cero, suponer que el resultado siempre apunta en el mismo sentido y confundir la proyección escalar con la vectorial.
La proyección utiliza el producto escalar para descomponer un vector según una dirección. Esta idea permite encontrar componentes, puntos cercanos, movimientos restringidos y reflexiones.
En el próximo tema estudiaremos independencia y dependencia lineal, que permiten determinar cuándo un conjunto de vectores aporta direcciones nuevas.