33. Proyección de un vector sobre otro

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.

33.1 Introducción

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.

u = componente paralela a v + componente perpendicular a v

33.2 Interpretación geométrica

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.

  • La proyección vectorial es paralela a v.
  • La diferencia entre u y su proyección es perpendicular a v.
  • La proyección puede apuntar en el mismo sentido de v o en el contrario.

El vector sobre el cual se proyecta debe ser no nulo, porque el vector cero no define una dirección.

33.3 Proyección escalar

La proyección escalar, también llamada componente escalar de u en la dirección de v, mide una longitud con signo:

compv(u) = (u · v) / ||v||

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

33.4 Proyección vectorial

La proyección vectorial combina la longitud con signo y la dirección unitaria:

proyv(u) = ((u · v) / (v · v)) v

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]

33.5 Fórmula con un vector unitario

Si la dirección v̂ ya está normalizada, v̂ · v̂ = 1 y la expresión queda:

proy(u) = (u · v̂)v̂
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.

33.6 Ejemplo sobre un eje

Para u = (4, 3) y v = (1, 0), la proyección sobre el eje x conserva la primera componente y elimina la segunda:

proyv(u) = (4, 0)
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]

33.7 Ejemplo sobre una dirección oblicua

Sean u = (3, 4) y v = (2, 1):

u · v = 10
v · v = 5
factor = 10/5 = 2
proyv(u) = 2(2, 1) = (4, 2)
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]

33.8 Signo de la componente escalar

u · vComponente escalarSentido de la proyección
PositivoPositivaEl mismo que v
CeroCerou es perpendicular a v o alguno es nulo
NegativoNegativaOpuesto 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

33.9 Descomposición paralela y perpendicular

Todo vector u puede descomponerse respecto de una dirección v no nula:

uparalela = proyv(u)
uperpendicular = u − uparalela
u = uparalela + uperpendicular
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]

33.10 Verificar la perpendicularidad

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.

33.11 Proyección sobre los ejes coordenados

Proyectar sobre los vectores canónicos extrae las componentes individuales:

proye1(x, y, z) = (x, 0, 0)
proye2(x, y, z) = (0, y, 0)
proye3(x, y, z) = (0, 0, z)
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]]

33.12 Proyección sobre una recta por el origen

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

33.13 Punto más cercano sobre una recta

Si la recta pasa por A con dirección v y deseamos proyectar un punto P, primero trasladamos el problema al origen:

Q = A + proyv(P − A)
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]

33.14 Proyección sobre un plano

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:

proyplano(u) = u − proyn(u)
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]

33.15 Reflexión respecto de una superficie

Para reflejar un vector incidente u respecto de un plano con normal n, se invierte dos veces su componente normal:

r = u − 2 proyn(u)
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]

33.16 Aplicación: movimiento sobre una pendiente

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]

33.17 Matriz de proyección

Si v̂ es un vector columna unitario, la proyección sobre su recta puede representarse mediante:

P = v̂v̂T,   proy(u) = Pu

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]

33.18 Validación, precisión y errores frecuentes

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.

33.19 Qué debes recordar de este tema

  • La proyección extrae la parte de u alineada con v.
  • La componente escalar es (u · v)/||v||.
  • La proyección vectorial es ((u · v)/(v · v))v.
  • Si v es unitario, la fórmula se reduce a (u · v)v.
  • El vector cero no define una dirección de proyección.
  • u se descompone en una parte paralela y otra perpendicular a v.
  • La proyección proporciona el punto más cercano sobre una recta.
  • Restar la proyección sobre una normal produce la componente contenida en un plano.

33.20 Conclusión

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.