12k
All articles

Geometría Real en el Navegador con Geometric.js

Geometric.js lleva intersección de polígonos, límites, prueba de punto en polígono e interpolación de rutas a gráficos, mapas, SVG y Canvas.

OpenReplay Team
OpenReplay Team
Geometría Real en el Navegador con Geometric.js

Geometric.js es una biblioteca de geometría 2D donde cada primitiva es un array de JavaScript plano: un punto es [x, y], una línea es [[x, y], [x, y]], y un polígono es un array de puntos — de modo que el mismo valor con el que se opera es el que se serializa a JSON y se dibuja en SVG o Canvas.

Cualquier persona que desarrolle gráficos o mapas interactivos ha experimentado la fricción de necesitar una pequeña operación geométrica y tener que reinventar la matemática desde cero cada vez. Si has estado implementando manualmente el ray casting para pruebas de punto en polígono o copiando y pegando cálculos de bounding box en un gráfico D3, Geometric.js reemplaza esos fragmentos con funciones nombradas y probadas que operan directamente sobre los arrays que ya tienes. Este recorrido cubre tres capacidades principales (intersección de polígonos, bounding boxes e interpolación) y dos casos de uso donde recurrir a la biblioteca supera escribir la trigonometría por cuenta propia.

Conclusiones Clave

  • Geometric.js representa un punto como [x, y], una línea como dos puntos y un polígono como un array de puntos — sin clases que instanciar ni adaptaciones previas para dibujar en SVG o Canvas.
  • pointInPolygon(point, polygon) devuelve un booleano mediante ray casting, proporcionando una prueba de hit en canvas con una sola llamada y sin matemáticas geométricas externas.
  • polygonBounds(polygon) devuelve [topLeft, bottomRight] como dos puntos [x, y], o null si hay menos de tres vértices — suficiente para posicionar un cuadro de tooltip alrededor de cualquier forma.
  • Las operaciones booleanas que devuelven geometría — polygonIntersection, polygonUnion, polygonDifference y polygonXor — requieren geometric v3 o posterior; el predicado polygonIntersectsPolygon está disponible desde v2.
  • A partir de la versión 3.x, geometric incluye sus propias declaraciones TypeScript, por lo que solo debes instalar geometric y omitir @types/geometric por completo.

Las primitivas son arrays planos, y esa es toda la propuesta

La razón por la que Geometric.js es agradable de usar es que no introduce estructuras de datos. Un punto es un array [x, y], una línea es un array de dos puntos y un polígono es un array de vértices. No hay clases personalizadas que instanciar ni estructuras de datos especiales que aprender, lo que significa que los valores que se pasan son los mismos que se pueden serializar, inspeccionar y dibujar. Dado que la salida es un array, se integra directamente en D3, en un atributo points de SVG o en un bucle ctx.lineTo de Canvas sin código adaptador:

import { polygonRegular } from "geometric";

const pentagon = polygonRegular(5, 10000, [150, 150]);
// pentagon es [[x, y], [x, y], ...] — se dibuja directamente:

// SVG
polygon.setAttribute("points", pentagon.map(p => p.join(",")).join(" "));

// Canvas
ctx.beginPath();
pentagon.forEach(([x, y], i) => (i ? ctx.lineTo(x, y) : ctx.moveTo(x, y)));
ctx.closePath();

Ese ciclo completo (calcular, serializar y renderizar, todo sobre el mismo array) es lo que el README quiere decir con mantener la geometría simple.

Instalación e importación

Instala geometric desde npm, pnpm o yarn; se distribuye como un paquete ESM-first con soporte CommonJS y declaraciones TypeScript incluidas. La versión más reciente en npm es la 3.0.9 (julio de 2026).

npm i geometric
# o: pnpm add geometric
# o: yarn add geometric
import { pointInPolygon, polygonBounds } from "geometric"; // ESM
const geometric = require("geometric");                     // CommonJS

Dos notas sobre la configuración. Primero, las declaraciones TypeScript se generan desde el código fuente y se publican con el paquete, por lo que no debes instalar @types/geometric. Ese paquete de DefinitelyTyped está congelado en la versión 2.5.3 y es redundante en v3. Segundo, las operaciones booleanas que devuelven geometría descritas a continuación requieren geometric@^3; en v2 devuelven undefined.

Intersección de polígonos y operaciones booleanas

Para pruebas de superposición, polygonIntersection(a, b) devuelve el área compartida como un nuevo polígono, mientras que polygonIntersectsPolygon(a, b) devuelve un booleano. El predicado es la comprobación económica; la operación que devuelve geometría proporciona una forma real para renderizar.

import { polygonIntersection, polygonIntersectsPolygon } from "geometric";

const a = [[0, 0], [4, 0], [4, 4], [0, 4]];
const b = [[2, 2], [6, 2], [6, 6], [2, 6]];

polygonIntersectsPolygon(a, b); // true — predicado booleano (v2+)
polygonIntersection(a, b);      // región de superposición como nuevo polígono (v3+)

El conjunto completo de operaciones booleanas es polygonIntersection, polygonUnion, polygonDifference y polygonXor, cada una devolviendo un array de puntos. Las cuatro son incorporaciones de v3. Para pruebas de contención en lugar de superposición, polygonInPolygon(polygonA, polygonB) devuelve un booleano que indica si el primer polígono está completamente dentro del segundo, tratando los puntos en el límite como contenidos.

¿Cómo se obtiene el bounding box de un polígono?

polygonBounds(polygon) devuelve [topLeft, bottomRight] como dos puntos [x, y], o null si hay menos de tres vértices, lo cual es suficiente para posicionar un tooltip o un cuadro de etiqueta alrededor de cualquier forma con una sola llamada. Ignora los puntos con valores no válidos (null, undefined, NaN, Infinity), por lo que un hueco en los datos no corromperá el cuadro.

import { polygonBounds } from "geometric";

const region = [[12, 8], [40, 20], [30, 44], [6, 30]];
const [topLeft, bottomRight] = polygonBounds(region);
// topLeft = [6, 8], bottomRight = [40, 44]

const width = bottomRight[0] - topLeft[0];   // 34
const height = bottomRight[1] - topLeft[1];  // 36
// posiciona un <rect> o tooltip en topLeft con este ancho/alto

Esta es la tarea de superposición habitual en visualización de datos: dada una región arbitraria en un mapa o gráfico, colocar un marco de etiqueta alrededor de ella sin recalcular manualmente el mínimo y máximo de cada vértice.

Interpolación: animación a lo largo de un trayecto o perímetro

lineInterpolate(line) y polygonInterpolate(polygon) devuelven una función interpoladora que se llama con t en [0, 1], que es exactamente lo que se necesita para animar un marcador a lo largo de un trayecto o alrededor de un perímetro. En la versión actual 3.x, clamp tiene como valor predeterminado true, restringiendo la salida al segmento; pasarlo explícitamente mantiene la intención clara.

import { lineInterpolate } from "geometric";

const path = [[0, 0], [100, 50]];
const at = lineInterpolate(path, true); // clamp = true

at(0);   // [0, 0]
at(0.5); // [50, 25]
at(1);   // [100, 50]

Controla t desde requestAnimationFrame o una transición de D3 y tendrás un marcador deslizándose a lo largo de la línea. polygonInterpolate hace lo mismo alrededor de un perímetro cerrado, lo que resulta útil para trazar un contorno o mover un punto alrededor del borde de una forma.

¿Cómo se comprueba si un punto está dentro de un polígono?

Para comprobar si un clic cae dentro de una región, pointInPolygon(point, polygon) devuelve un booleano mediante ray casting, sin necesidad de matemáticas geométricas externas. Esta es la prueba de hit clásica en canvas, y condensa una docena de líneas de lógica de cruce de aristas implementada a mano en una única llamada probada.

import { pointInPolygon, pointOnPolygon } from "geometric";

const region = [[0, 0], [100, 0], [100, 100], [0, 100]];

pointInPolygon([50, 50], region);  // true
pointInPolygon([150, 50], region); // false

Las pruebas de interior tienen una debilidad predecible: los clics que caen exactamente sobre un borde. Las repeticiones de sesión en herramientas de selección por arrastre y selectores de región en mapas revelan frecuentemente este fallo: un bug del tipo “¿por qué no se registró mi selección?” que las pruebas unitarias no detectan pero que resulta obvio al observar la interacción. La solución adecuada es pointOnPolygon(point, polygon, epsilon), que comprueba el límite con una tolerancia epsilon opcional, como 1e-6, para la ambigüedad de puntos exactamente sobre la línea que una comprobación de interior pura ignora.

CapacidadFunciónDevuelveVersión mínima
Prueba de punto en regiónpointInPolygonbooleano (ray casting)v2
Prueba de límitepointOnPolygonbooleano (epsilon)v2
Bounding boxpolygonBounds[topLeft, bottomRight] o nullv2
Forma de superposiciónpolygonIntersectionpolígonov3
Predicado de superposiciónpolygonIntersectsPolygonbooleanov2
Interpolación de trayectolineInterpolatefunción (t) => [x, y]v2

Recurre a Geometric.js en el momento en que un gráfico, mapa o interfaz canvas necesite más de una operación geométrica: el modelo de entrada y salida basado en arrays significa que cada resultado alimenta la siguiente llamada o el renderizador directamente. Instala geometric, importa las dos o tres funciones que necesita tu funcionalidad y elimina la trigonometría de Stack Overflow. La referencia de la API lista el conjunto completo cuando surge una cuarta operación.

Preguntas Frecuentes

¿Cuál es la diferencia entre polygonIntersection y polygonIntersectsPolygon en Geometric.js?

polygonIntersectsPolygon(a, b) devuelve un booleano que indica si dos polígonos se superponen, mientras que polygonIntersection(a, b) devuelve la región compartida en sí misma como un nuevo array de polígono que se puede renderizar. Usa el predicado para una comprobación rápida de sí/no y la operación que devuelve geometría cuando necesitas la forma de superposición real. El predicado está disponible desde v2, pero polygonIntersection requiere geometric v3 o posterior.

¿Es necesario instalar @types/geometric para usar Geometric.js con TypeScript?

No. A partir de la versión 3.x, geometric incluye sus propias declaraciones TypeScript generadas desde el código fuente y publicadas con el paquete, por lo que instalar geometric por sí solo proporciona autocompletado en el editor y verificación de tipos. El paquete separado de DefinitelyTyped @types/geometric está congelado en la versión 2.5.3 de noviembre de 2023 y es redundante en v3; instalarlo puede reemplazar los tipos incluidos y precisos con versiones desactualizadas.

¿Qué devuelve Geometric.js cuando un punto cae exactamente sobre el borde de un polígono?

pointInPolygon usa ray casting para comprobar el interior, y los puntos que caen exactamente sobre un borde se tratan como ambiguos y pueden devolver false, lo que se manifiesta como clics que no se registran en interfaces de selección por arrastre o selectores de región en mapas. Usa pointOnPolygon(point, polygon, epsilon) para los casos de límite; la tolerancia epsilon opcional, como 1e-6, controla qué tan cerca de la línea se considera que un punto está sobre ella.

¿Se puede dibujar la salida de Geometric.js directamente en SVG o Canvas sin conversión?

Sí. Dado que cada primitiva es un array de JavaScript plano en lugar de una instancia de clase, los arrays devueltos por funciones como polygonRegular o polygonIntersection se integran directamente en un atributo points de SVG o en un bucle ctx.lineTo de Canvas sin código adaptador. El mismo array con el que se opera es el valor que se serializa a JSON, se pasa a D3 o se renderiza, que es la propuesta de diseño central de la biblioteca.

DevTools for the frontend

Gain Debugging Superpowers

Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers.

Star on GitHub12k

We use cookies to improve your experience. By using our site, you accept cookies.