Fazendo Geometria Real no Navegador com Geometric.js
Geometric.js leva interseção de polígonos, limites, teste point-in-polygon e interpolação de caminhos a gráficos, mapas, SVG e Canvas no navegador.
Geometric.js é uma biblioteca de geometria 2D onde cada primitiva é um array JavaScript simples: um ponto é [x, y], uma linha é [[x, y], [x, y]], e um polígono é um array de pontos — portanto, o mesmo valor que você calcula também é o valor que você serializa para JSON e renderiza em SVG ou Canvas.
Qualquer pessoa que desenvolve gráficos interativos ou mapas já sentiu o atrito de precisar de uma pequena operação geométrica e ter que reinventar a matemática do zero a cada vez. Se você tem implementado ray-casting manualmente para testes de ponto em polígono ou copiado e colado cálculos de bounding box em gráficos D3, o Geometric.js substitui esses trechos de código por funções nomeadas e testadas que operam diretamente nos arrays que você já possui. Este guia aborda três funcionalidades principais (interseção de polígonos, bounding boxes e interpolação), além de dois casos de uso em que recorrer à biblioteca supera escrever a trigonometria manualmente.
Principais Conclusões
- O Geometric.js representa um ponto como
[x, y], uma linha como dois pontos e um polígono como um array de pontos — sem classes para instanciar e nada para adaptar antes de renderizar em SVG ou Canvas. pointInPolygon(point, polygon)retorna um booleano usando ray casting, fornecendo um teste de hit em canvas com uma única chamada e sem matemática geométrica externa.polygonBounds(polygon)retorna[topLeft, bottomRight]como dois pontos[x, y], ounullpara menos de três vértices — suficiente para posicionar uma caixa de tooltip em torno de qualquer forma.- As operações booleanas que retornam geometria —
polygonIntersection,polygonUnion,polygonDifferenceepolygonXor— exigem geometric v3 ou superior; o predicadopolygonIntersectsPolygonestá disponível desde a v2. - A partir da versão 3.x, o geometric inclui suas próprias declarações TypeScript, portanto, basta instalar
geometrice ignorar o@types/geometric.
Primitivas são arrays simples, e esse é o diferencial
O motivo pelo qual o Geometric.js é agradável de usar é que ele não introduz estruturas de dados. Um ponto é um array [x, y], uma linha é um array de dois pontos e um polígono é um array de vértices. Não há classes customizadas para instanciar nem estruturas de dados especiais para aprender, o que significa que os valores que você passa são os mesmos que você pode serializar, inspecionar e renderizar. Como a saída é um array, ela se integra diretamente ao D3, a um atributo points de SVG ou a um loop ctx.lineTo em Canvas sem nenhum código adaptador:
import { polygonRegular } from "geometric";
const pentagon = polygonRegular(5, 10000, [150, 150]);
// pentagon é [[x, y], [x, y], ...] — renderize diretamente:
// 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();
Esse ciclo completo (calcular, serializar e renderizar, tudo no mesmo array) é o que o README quer dizer ao manter a geometria simples.
Instalação e importação
Discover how at OpenReplay.com.
Instale geometric via npm, pnpm ou yarn; ele é distribuído como um pacote ESM-first com suporte a CommonJS e declarações TypeScript incluídas. A versão mais recente no npm é a 3.0.9 (julho de 2026).
npm i geometric
# ou: pnpm add geometric
# ou: yarn add geometric
import { pointInPolygon, polygonBounds } from "geometric"; // ESM
const geometric = require("geometric"); // CommonJS
Duas observações sobre a configuração. Primeiro, as declarações TypeScript são geradas a partir do código-fonte e publicadas com o pacote, portanto, você não deve instalar @types/geometric. Esse pacote do DefinitelyTyped está congelado na versão 2.5.3 e é redundante na v3. Segundo, as operações booleanas que retornam geometria descritas abaixo exigem geometric@^3; na v2, elas retornam undefined.
Interseção de polígonos e operações booleanas
Para testes de sobreposição, polygonIntersection(a, b) retorna a área compartilhada como um novo polígono, enquanto polygonIntersectsPolygon(a, b) retorna um booleano. O predicado é a verificação mais barata; a operação que retorna geometria fornece uma 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ão de sobreposição como um novo polígono (v3+)
O conjunto completo de operações booleanas inclui polygonIntersection, polygonUnion, polygonDifference e polygonXor, cada uma retornando um array de pontos. Todas as quatro são adições da v3. Para containment em vez de sobreposição, polygonInPolygon(polygonA, polygonB) retorna um booleano indicando se o primeiro polígono está completamente dentro do segundo, tratando pontos na fronteira como contidos.
Como obter o bounding box de um polígono?
polygonBounds(polygon) retorna [topLeft, bottomRight] como dois pontos [x, y], ou null para menos de três vértices, o que é suficiente para posicionar uma caixa de tooltip ou rótulo em torno de qualquer forma com uma única chamada. Ele ignora pontos com valores inválidos (null, undefined, NaN, Infinity), portanto, uma lacuna nos seus dados não vai corromper a caixa.
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
// posicione um <rect> ou tooltip em topLeft com esta largura/altura
Essa é a tarefa comum de overlay em visualização de dados: dado uma região arbitrária em um mapa ou gráfico, adicionar um quadro de rótulo ao redor dela sem recalcular manualmente os valores mínimos e máximos em cada vértice.
Interpolação: animando ao longo de um caminho ou perímetro
lineInterpolate(line) e polygonInterpolate(polygon) retornam uma função interpoladora que você chama com t em [0, 1], que é exatamente o que você precisa para animar um marcador ao longo de um caminho ou ao redor de um perímetro. Na versão atual 3.x, clamp tem como padrão true, restringindo a saída ao segmento; passá-lo explicitamente mantém a intenção 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]
Controle t a partir de requestAnimationFrame ou de uma transição D3 e você terá um marcador deslizando ao longo da linha. polygonInterpolate faz o mesmo ao redor de um perímetro fechado, útil para traçar um contorno ou mover um ponto ao longo da borda de uma forma.
Como testar se um ponto está dentro de um polígono?
Para verificar se um clique está dentro de uma região, pointInPolygon(point, polygon) retorna um booleano usando ray casting, sem necessidade de matemática geométrica externa. Esse é o clássico teste de hit em canvas, e ele reduz uma dúzia de linhas de lógica de cruzamento de arestas implementada manualmente a uma única chamada testada.
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
Os testes de interior têm uma limitação previsível: cliques que caem exatamente sobre uma aresta. Gravações de sessão de interações de arrastar para selecionar e seletores de regiões em mapas frequentemente revelam esse modo de falha: um bug do tipo “por que minha seleção não foi registrada?” que testes unitários não detectam, mas que fica óbvio ao observar a interação. A solução adequada é pointOnPolygon(point, polygon, epsilon), que testa a fronteira com uma tolerância epsilon opcional, como 1e-6, para a ambiguidade de ponto sobre a linha que uma verificação de interior simples ignora.
| Funcionalidade | Função | Retorna | Versão mínima |
|---|---|---|---|
| Teste de ponto em região | pointInPolygon | booleano (ray casting) | v2 |
| Teste de fronteira | pointOnPolygon | booleano (epsilon) | v2 |
| Bounding box | polygonBounds | [topLeft, bottomRight] ou null | v2 |
| Forma de sobreposição | polygonIntersection | polígono | v3 |
| Predicado de sobreposição | polygonIntersectsPolygon | booleano | v2 |
| Interpolação de caminho | lineInterpolate | função (t) => [x, y] | v2 |
Recorra ao Geometric.js no momento em que um gráfico, mapa ou interface canvas precisar de mais de uma operação geométrica: o modelo de entrada e saída em arrays significa que cada resultado alimenta a próxima chamada ou o renderizador diretamente. Instale geometric, importe as duas ou três funções que sua funcionalidade precisa e delete a trigonometria copiada do Stack Overflow. A referência da API lista o conjunto completo quando uma quarta operação for necessária.
Perguntas Frequentes
Qual é a diferença entre polygonIntersection e polygonIntersectsPolygon no Geometric.js?
polygonIntersectsPolygon(a, b) retorna um booleano indicando se dois polígonos se sobrepõem, enquanto polygonIntersection(a, b) retorna a própria região compartilhada como um novo array de polígono que você pode renderizar. Use o predicado para uma verificação simples de sim/não e a operação que retorna geometria quando você precisa da forma de sobreposição real. O predicado está disponível desde a v2, mas polygonIntersection requer geometric v3 ou superior.
Preciso instalar @types/geometric para usar o Geometric.js com TypeScript?
Não. A partir da versão 3.x, o geometric inclui suas próprias declarações TypeScript geradas a partir do código-fonte e publicadas com o pacote, portanto, instalar apenas geometric já fornece autocomplete no editor e verificação de tipos. O pacote separado do DefinitelyTyped @types/geometric está congelado na versão 2.5.3 de novembro de 2023 e é redundante na v3; instalá-lo pode sobrepor os tipos precisos incluídos no pacote com tipos desatualizados.
O que o Geometric.js retorna quando um ponto cai exatamente sobre a aresta de um polígono?
pointInPolygon usa ray casting para testar o interior, e pontos que caem exatamente sobre uma aresta são tratados como ambíguos e podem retornar false, o que se manifesta como cliques que não são registrados em interfaces de arrastar para selecionar ou seletores de regiões em mapas. Use pointOnPolygon(point, polygon, epsilon) para casos de fronteira; a tolerância epsilon opcional, como 1e-6, controla o quão próximo da linha é considerado sobre ela.
A saída do Geometric.js pode ser desenhada diretamente em SVG ou Canvas sem conversão?
Sim. Como cada primitiva é um array JavaScript simples em vez de uma instância de classe, os arrays retornados por funções como polygonRegular ou polygonIntersection se integram diretamente a um atributo points de SVG ou a um loop ctx.lineTo em Canvas sem nenhum código adaptador. O mesmo array que você usa para calcular é o valor que você serializa para JSON, passa para o D3 ou renderiza, o que é o princípio central de design da biblioteca.
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