O Que Segmento De Reta
Entendendo segmentos de reta na prática
Você já precisou calcular se dois segmentos se cruzam num projeto de CAD, numa simulação ou num jogo? A definição teórica é simples demais e não protege contra os erros que acontecem quando você coloca isso no código. Um segmento de reta é basicamente o conjunto de pontos entre dois extremos, com comprimento finito e bem definido. Isso parece óbvio até você tentar detectar colinearidade ou interseção com coordenadas de ponto flutuante. O conceito de o que segmento de reta se resume a dois pontos no espaço e tudo que está entre eles, mas a implementação real exige atenção a detalhes que livros didáticos costumam ignorar. Eu trabalhei num sistema de rotas para veículos autônomos onde cada obstáculo era representado como um conjunto de segmentos. O problema era que segmentos quase paralelos geravam soluções numericamente instáveis. O algoritmo de interseção clássica falhava silenciosamente em cerca de 3% dos casos, produzindo resultados ligeiramente errados que se acumulavam ao longo do tempo de simulação.
Como representar um segmento corretamente
A representação mais comum usa dois pontos A e B, geralmente como pares ordenados (x, y) e (x, y) no plano cartesiano. Você pode expressar isso de três formas: Forma paramétrica: P(t) = A + t(B - A), onde t varia de 0 a 1. Quando t = 0 você está em A, quando t = 1 está em B, e qualquer valor entre eles determina um ponto no segmento. Essa é a representação que eu mais uso porque facilita verificação de pertinência — basta testar se 0 t 1. Forma cartesiana implícita: (y - y)(x - x) = (x - x)(y - y). Essa forma é útil para calcular distância de um ponto a uma reta, mas exige cuidado porque segmentos são finitos e a equação descreve uma reta infinita. Para restringir ao segmento, você precisa adicionar as restrições de bounding box.
Coordenadas homogêneas: Em projetos de geometria computacional, representações projetivas evitam casos degenerados como retas verticais. Se você trabalha com bibliotecas como CGAL ou geometric tools, provavelmente já esbarrou nisso.

Operações fundamentais
O comprimento do segmento é calculado pela distância euclidiana entre os extremos: |AB| = ((x-x)² + (y-y)²). Simples, mas em sistemas que processam milhares de segmentos por frame, até essa conta básica precisa de otimização. Evite a raiz quadrada quando for apenas comparar comprimentos — trabalhe com o quadrado da distância e aplique a raiz só no final. Para verificar se um ponto P pertence ao segmento AB, o método mais confiável combina duas verificações. Primeiro, o produto vetorial (B-A) × (P-A) deve ser zero (ou muito próximo de zero dentro de uma tolerância). Segundo, o ponto P deve estar dentro do retângulo delimitado por A e B: min(x,x) x max(x,x) e min(y,y) y max(y,y). Só o produto vetorial não basta, porque ele testa se P está na reta suporte, não no segmento. Eu já vi muita gente confiar apenas no produto vetorial e cometer o erro de aceitar pontos que estão na extensão da reta mas fora do segmento. Esse erro aparece especialmente quando você normaliza vetores antes — a normalização amplifica erros de arredondamento em coordenadas mal escaladas.
Interseção entre dois segmentos
Esse é o ponto onde a teoria encontra a realidade. Dois segmentos s = (A, B) e s = (C, D) se intersectam quando os pontos C e D estão em lados opostos em relação à reta que contém s, e os pontos A e B estão em lados opostos em relação à reta que contém s. A verificação usa produtos vetoriais: d = direção(C, D) × direção(A, C)
d = direção(C, D) × direção(B, C)
d = direção(A, B) × direção(C, A)
d = direção(A, B) × direção(D, A) Os segmentos se intersectam se d e d tiverem sinais opostos e d e d tiverem sinais opostos. Se algum resultado for zero, estamos num caso degenerado — pontos colineares, segmentos que se tocam numa extremidade, ou sobreposição parcial.
O problema prático é que essa lógica exata quebra com ponto flutuante. Numericalmente, você precisa de uma tolerância epsilon. Eu adoptei epsilon = 1e-9 para coordenadas na ordem de grandeza de unidades normais, mas em projetos com escala muito grande ou muito pequena, esse valor precisa ser ajustado proporcionalmente. A tolerância fixa funciona mal quando as coordenadas variam em ordens de grandeza diferentes — use epsilon relativo ao tamanho do segmento, não absoluto. Um caso que causa dor de cabeça frequente é quando dois segmentos são colineares e se sobrepõem parcialmente. O algoritmo de interseção padrão retorna falso ou um ponto único, quando na verdade há infinitos pontos em comum. Nesses casos, a solução é tratar segmentos colineares como um cenário separado: projete tudo num eixo, encontre a intersecção dos intervalos e verifique se há sobreposição real.

Midpoint e divisão de um segmento
O ponto médio é M = ((x+x)/2, (y+y)/2). Fácil, mas em iterações sucessivas de subdivisão — algo comum em curvas de Bézier ou refinamento de malhas — o erro de arredondamento se acumula. Use coordenadas fixas ou representações racionais quando a precisão for crítica. Dividir um segmento na razão m:n é um passo essencial para interpolação e tesselação. O ponto de divisão é P = (n·A + m·B)/(m+n). Novamente, a escolha entre aritmética float e rational depende do contexto. Em engines gráficas, float é suficiente. Em sistemas de medição ou CAD de precisão, considere usar bibliotecas de números racionais ou fix-point.
Erros comuns que custam tempo
O primeiro erro frequente é confundir segmento com reta. Segmento tem dois extremos definidos; reta não. Qualquer verificação de pertinência ou interseção que ignore os extremos está testando a reta suporte, não o segmento. O segundo erro é assumir que o ponto de interseção entre duas retas infinitas também pertence aos segmentos. Isso só é válido quando os parâmetros paramétricos t e u estão ambos no intervalo [0, 1]. Testar isso separadamente evita aceitarmos interseções que estão geometricamente corretas mas fora dos segmentos. O terceiro erro, e talvez o mais sutil, é não lidar com degeneração. Um segmento onde A = B tem comprimento zero e direção indefinida. Produtos vetoriais ficam ambíguos, divisões por norma de vetor nulo geram NaN, e algoritmos de interseção podem travar ou retornar valores aleatórios. Sempre valide que os extremos são distintos antes de processar.
Quando a abordagem simples não funciona
Se você está lidando com milhões de segmentos e precisa de detecção de interseção em tempo real, o teste pareado O(n²) é inviável. Estruturas como BVH (Bounding Volume Hierarchy), grid spatial partitioning ou sweep-line algorithm reduzem drasticamente o custo. O algoritmo de sweep-line, por exemplo, consegue encontrar todas as interseções em O((n + k) log n), onde k é o número de interseções. Por outro lado, se o seu problema é puramente geométrico e o número de segmentos é pequeno, complexidade adicional só traz overhead desnecessário. A regra prática que eu uso é: sweep-line ou BVH a partir de cerca de 500 segmentos. Abisso disso, o teste direto costuma ser mais rápido por causa da sobrecarga de estrutura de dados. Há também o limite fundamental: segments em dimensões superiores (3D, 4D) introduzem complexidade adicional significativa. Duas linhas no espaço 3D geralmente não se intersectam — são skew. A generalização para segmentos exige verificar non-collision em vez de interseção pura, o que muda completamente a estratégia algorítmica. Se o seu trabalho envolver 3D, considere usar uma biblioteca robusta em vez de implementar do zero.

Implementação mínima em Python
Um exemplo prático ajuda a consolidar. Este trecho verifica se dois segmentos se intersectam: def cross(o, a, b):
return (a[0] - o[0]) * (b[1] - o[1]) - (a[1] - o[1]) * (b[0] - o[0])
def on_segment(p, a, b, eps=1e-9):
if not (min(a[0], b[0]) - eps <= p[0] <= max(a[0], b[0]) + eps
and min(a[1], b[1]) - eps <= p[1] <= max(a[1], b[1]) + eps):
return False
return abs(cross(a, b, p)) < eps
def segments_intersect(a, b, c, d, eps=1e-9):
d1 = cross(c, d, a)
d2 = cross(c, d, b)
d3 = cross(a, b, c)
d4 = cross(a, b, d)
if ((d1 > eps and d2 < -eps or d1 < -eps and d2 > eps) and
(d3 > eps and d4 < -eps or d3 < -eps and d4 > eps)):
return True
if on_segment(a, c, d, eps) and abs(d1) < eps:
return True
if on_segment(b, c, d, eps) and abs(d2) < eps:
return True
if on_segment(c, a, b, eps) and abs(d3) < eps:
return True
if on_segment(d, a, b, eps) and abs(d4) < eps:
return True
return False Esse código cobre os casos degenerados — quando um extremo de um segmento toca o outro segmento. Sem essa verificação extra, interseções em extremidades seriam perdidas, o que causa problemas em malhas triangulares e polígonos fechados.
Custo real de negligenciar esses detalhes
Numa operação de rasterização de polígonos, falhar na detecção de interseção de segmentos gera self-intersection que o rasterizador interpreta como polígono inválido. O resultado são artefactos visuais, áreas pintadas erroneamente ou crashes. Num projeto de pathfinding, a falha de interseção faz o caminho atravessar obstáculos. No meu caso específico com veículos autônomos, o bug se manifestava como desvios sutis de trajetória que só eram perceptíveis após horas de simulação acumulada — o tipo de erro que passa despercebido em testes unitários rápidos. A lição prática é que a definição de segmento de reta é trivial, mas a manipulação computacional exige tratamento explícito de casos de borda, tolerância numérica adequada e validação de degeneração. Ignorar qualquer um desses três pontos gera bugs que são difíceis de reproduzir e caros para corrigir depois.
Galeria
-
Segmento de Reta: Entenda o que é! - Matemática Básica -
Segmento de Reta: Entenda o que é! - Matemática Básica