Detectar si un punto está dentro, fuera o sobre el borde de una figura es la forma más elemental de colisión. Estas pruebas sostienen botones, selección, mapas, zonas activas, pinceles y consultas espaciales.
Una colisión no siempre ocurre entre objetos complejos. Muchas interacciones comienzan preguntando si una posición pertenece a una región: el puntero sobre un botón, un proyectil dentro de una zona, un personaje sobre una baldosa o una muestra dentro de una máscara.
La figura determina la prueba adecuada. Elegir una representación coherente y decidir cómo tratar el borde son tan importantes como la fórmula.
Dado un punto P y una región F, buscamos una clasificación:
Para una respuesta booleana puede considerarse el borde como interior. En editores o herramientas de precisión conviene conservar las tres posibilidades.
El punto y la figura deben expresarse en el mismo sistema. Un clic puede estar en coordenadas de pantalla mientras el objeto está definido en coordenadas locales. Antes de probar la pertenencia hay que transformar uno de ellos.
Esta estrategia simplifica figuras trasladadas, rotadas o escaladas: se lleva el punto hacia la figura original en lugar de reconstruir todos sus límites.
Para centro C=(cx,cy) y radio r, calculamos la distancia cuadrática:
Comparar cuadrados evita la raíz cuadrada. Además de ser más simple, conserva exactamente la misma relación de orden para cantidades no negativas.
Un rectángulo alineado con los ejes se describe mediante límites mínimos y máximos:
La prueba es rápida porque cada eje se evalúa por separado. Si el rectángulo usa esquina, ancho y alto, entonces xmax=x+ancho e ymax=y+alto.
La prueba AABB no puede aplicarse directamente a un rectángulo girado. Se resta su centro y se rota el punto por el ángulo opuesto:
En el espacio local, el rectángulo vuelve a estar alineado. Basta comprobar |xlocal|≤ancho/2 e |ylocal|≤alto/2.
El signo del producto cruzado indica a qué lado de cada arista está P. Si los tres signos son compatibles, el punto pertenece al triángulo:
Otra opción son las coordenadas baricéntricas. Ambas técnicas exigen decidir si un valor cero —punto sobre una arista— cuenta como interior.
El método de paridad lanza un rayo desde P y cuenta cruces con el contorno. Un número impar significa interior; uno par, exterior.
Funciona con polígonos cóncavos simples. El borde se comprueba primero para evitar ambigüedades cuando el rayo pasa exactamente por un vértice.
Los cálculos decimales rara vez producen cero exacto. Una tolerancia ε permite considerar cercanos dos valores:
La tolerancia debe relacionarse con la escala del problema. Un ε útil para coordenadas medidas en píxeles puede ser inadecuado para un mundo medido en kilómetros.
Para figuras complejas conviene usar dos etapas:
Un descarte barato reduce mucho el trabajo cuando existen cientos o miles de regiones.
Una prueba booleana solo responde dentro o fuera. Una función de distancia con signo agrega cuánto falta para llegar al borde:
La magnitud sirve para efectos suaves, selección por proximidad, empujes y visualización de zonas de influencia.
Mueve el punto y cambia la figura. La prueba muestra la clasificación, la fase amplia y los datos geométricos empleados.
function puntoEnCirculo(p, c, radio) {
const dx = p.x - c.x;
const dy = p.y - c.y;
return dx*dx + dy*dy <= radio*radio;
}
function puntoEnRectangulo(p, r) {
return p.x >= r.x && p.x <= r.x+r.ancho &&
p.y >= r.y && p.y <= r.y+r.alto;
}
console.log(puntoEnCirculo({x:3,y:2},{x:0,y:0},4));
console.log(puntoEnRectangulo({x:3,y:2},{x:1,y:1,ancho:5,alto:3}));Ambas llamadas muestran true. Los ejemplos incluyen todos los datos necesarios para ejecutarse de forma independiente.
function signo(p, a, b) {
return (p.x-b.x)*(a.y-b.y) -
(a.x-b.x)*(p.y-b.y);
}
function puntoEnTriangulo(p, a, b, c) {
const s1=signo(p,a,b), s2=signo(p,b,c), s3=signo(p,c,a);
const hayNegativo=s1<0 || s2<0 || s3<0;
const hayPositivo=s1>0 || s2>0 || s3>0;
return !(hayNegativo && hayPositivo);
}
const dentro=puntoEnTriangulo(
{x:2,y:2}, {x:0,y:0}, {x:6,y:0}, {x:2,y:5}
);
console.log("¿Está dentro?", dentro);La función considera el borde como parte del triángulo. Si los tres vértices están alineados, debe rechazarse previamente la figura degenerada.
function puntoEnPoligono(p, vertices) {
let dentro=false;
for (let i=0,j=vertices.length-1;i<vertices.length;j=i++) {
const a=vertices[i], b=vertices[j];
const cruza=(a.y>p.y)!==(b.y>p.y) &&
p.x < (b.x-a.x)*(p.y-a.y)/(b.y-a.y)+a.x;
if (cruza) dentro=!dentro;
}
return dentro;
}
const figura=[{x:0,y:0},{x:6,y:0},{x:4,y:2},{x:6,y:5},{x:0,y:5}];
console.log(puntoEnPoligono({x:2,y:3},figura));El método funciona tanto para polígonos convexos como cóncavos, siempre que el contorno sea simple y sus vértices estén ordenados.
Un dron recorre una instalación y un sensor puntual cambia de estado al atravesar regiones con formas distintas. La fase amplia descarta zonas lejanas y la prueba precisa identifica la región activa.
Estas pruebas aparecen en interfaces, selección de objetos, mapas interactivos, zonas de daño, disparos puntuales, partículas, herramientas de pintura, navegación y consultas geográficas.
Recuerda: usa el mismo sistema de coordenadas, elige la prueba según la figura, trata el borde explícitamente y combina una fase amplia barata con una fase estrecha precisa.
En el próximo tema estudiaremos colisiones entre rectángulos.