Tendo uma lista de pontos, como localizo se eles estão no sentido horário?
Por exemplo:
point[0] = (5,0)
point[1] = (6,4)
point[2] = (4,5)
point[3] = (1,5)
point[4] = (1,0)
diria que é anti-horário (ou anti-horário, para algumas pessoas).
Tendo uma lista de pontos, como localizo se eles estão no sentido horário?
Por exemplo:
point[0] = (5,0)
point[1] = (6,4)
point[2] = (4,5)
point[3] = (1,5)
point[4] = (1,0)
diria que é anti-horário (ou anti-horário, para algumas pessoas).
Respostas:
Alguns dos métodos sugeridos falharão no caso de um polígono não convexo, como um crescente. Aqui está um exemplo simples que funcionará com polígonos não convexos (até funcionará com um polígono auto-interceptável como a figura oito, informando se é principalmente no sentido horário).
Soma sobre as arestas, (x 2 - x 1 ) (y 2 + y 1 ). Se o resultado for positivo, a curva será no sentido horário; se for negativa, a curva será no sentido anti-horário. (O resultado é o dobro da área fechada, com uma convenção +/-.)
fonte
Sum( (x[(i+1) mod N] - x[i]) * (y[i] + y[(i+1) mod N]) )
para i = 0 a N-1. Ou seja, deve-se levar o índice Módulo N (N ≡ 0
) A fórmula funciona apenas para polígonos fechados . Os polígonos não têm arestas imaginárias.O produto cruzado mede o grau de perpendicularidade de dois vetores. Imagine que cada aresta do seu polígono é um vetor no plano xy de um espaço tridimensional (3-D) xyz. O produto cruzado de duas arestas sucessivas é um vetor na direção z (direção z positiva se o segundo segmento for no sentido horário, menos a direção z se for no sentido anti-horário). A magnitude desse vetor é proporcional ao seno do ângulo entre as duas arestas originais, portanto atinge o máximo quando são perpendiculares e diminui para desaparecer quando as arestas são colineares (paralelas).
Portanto, para cada vértice (ponto) do polígono, calcule a magnitude do produto cruzado das duas arestas adjacentes:
Portanto, rotule as arestas consecutivamente como
edgeA
é o segmento depoint0
parapoint1
eedgeB
entrepoint1
parapoint2
...
edgeE
é entrepoint4
epoint0
.Então o vértice A (
point0
) está entreedgeE
[Frompoint4
topoint0
]edgeA
[Frompoint0
to `point1 'Essas duas arestas são vetores, cujas coordenadas x e y podem ser determinadas subtraindo as coordenadas de seus pontos inicial e final:
edgeE
=point0
-point4
=(1, 0) - (5, 0)
=(-4, 0)
eedgeA
=point1
-point0
=(6, 4) - (1, 0)
=(5, 4)
eE o produto de cruzamento destas duas bordas adjacentes é calculada usando o determinante da matriz seguinte, o qual é construído colocando as coordenadas dos dois vectores abaixo os símbolos que representam as três coordenadas eixo (
i
,j
, &k
). A terceira coordenada avaliada (zero) existe porque o conceito de produto cruzado é uma construção 3D e, portanto, estendemos esses vetores 2-D em 3-D para aplicar o produto cruzado:Dado que todos os produtos cruzados produzem um vetor perpendicular ao plano de dois vetores sendo multiplicados, o determinante da matriz acima tem apenas um
k
componente (ou eixo z).A fórmula para calcular a magnitude do
k
componente ou do eixo z éa1*b2 - a2*b1 = -4* 4 - 0* 1
=-16
A magnitude desse valor (
-16
) é uma medida do seno do ângulo entre os 2 vetores originais, multiplicado pelo produto das magnitudes dos 2 vetores.Na verdade, outra fórmula para seu valor é
A X B (Cross Product) = |A| * |B| * sin(AB)
.Portanto, voltando a uma medida do ângulo, você precisa dividir esse valor (
-16
) pelo produto das magnitudes dos dois vetores.|A| * |B|
=4 * Sqrt(17)
=16.4924...
Então a medida do pecado (AB) =
-16 / 16.4924
=-.97014...
Esta é uma medida de se o próximo segmento após o vértice se curvou para a esquerda ou direita e quanto. Não há necessidade de tomar arco-seno. Tudo o que nos importa é a sua magnitude e, claro, o seu sinal (positivo ou negativo)!
Faça isso para cada um dos outros 4 pontos ao redor do caminho fechado e adicione os valores desse cálculo em cada vértice.
Se a soma final for positiva, você foi no sentido horário, negativo e anti-horário.
fonte
Acho que essa é uma pergunta bastante antiga, mas vou lançar outra solução de qualquer maneira, porque é direta e não matematicamente intensa - ela usa apenas álgebra básica. Calcule a área assinada do polígono. Se negativo, os pontos estão no sentido horário, se positivo, estão no sentido anti-horário. (Isso é muito semelhante à solução Beta).
Calcule a área assinada: A = 1/2 * (x 1 * y 2 - x 2 * y 1 + x 2 * y 3 - x 3 * y 2 + ... + x n * y 1 - x 1 * y n )
Ou no pseudo-código:
Observe que, se você estiver apenas verificando o pedido, não precisará se preocupar em dividir por 2.
Fontes: http://mathworld.wolfram.com/PolygonArea.html
fonte
previousPoint
da próxima iteração. Antes de iniciar o loop, definapreviousPoint
o último ponto da matriz. O trade-off é uma cópia extra local da variável, mas menos acessos à matriz. E o mais importante, não precisa tocar na matriz de entrada.Encontre o vértice com o menor y (e o maior x se houver laços). Seja o vértice
A
e o vértice anterior na lista sejaB
e o próximo vértice na lista sejaC
. Agora calcule o sinal do produto cruzado deAB
eAC
.Referências:
Como encontro a orientação de um polígono simples? em perguntas freqüentes: comp.graphics.algorithms .
Orientação de curva na Wikipedia.
fonte
O(1)
solução. Todas as outras respostas produzemO(n)
soluções paran
o número de pontos poligonais. Para otimizações ainda mais profundas, consulte a subseção Considerações práticas do fantástico artigo de orientação de curvas da Wikipedia .O(1)
apenas se (A) esse polígono é convexo (nesse caso, qualquer vértice arbitrário reside no casco convexo e, portanto, é suficiente) ou (B) você já conhece o vértice com a menor coordenada Y. Se esse não foro caso (ou seja, esse polígono não é convexo e você não sabe nada sobre isso),O(n)
é necessáriaumapesquisa. Como nenhum somatório é necessário, no entanto, isso ainda é muito mais rápido do que qualquer outra solução para polígonos simples.Aqui está uma implementação simples de C # do algoritmo com base nesta resposta .
Vamos supor que temos um
Vector
tipo tendoX
eY
propriedades do tipodouble
.%
é o operador de módulo ou restante executando a operação de módulo que (de acordo com a Wikipedia ) encontra o restante após a divisão de um número por outro.fonte
Comece em um dos vértices e calcule o ângulo subtendido por cada lado.
O primeiro e o último serão zero (pule esses); para o resto, o seno do ângulo será dado pelo produto cruzado das normalizações para o comprimento unitário de (ponto [n] - ponto [0]) e (ponto [n-1] - ponto [0]).
Se a soma dos valores for positiva, seu polígono será desenhado no sentido anti-horário.
fonte
Pelo que vale, usei esse mixin para calcular a ordem de enrolamento dos aplicativos da API do Google Maps v3.
O código aproveita o efeito colateral das áreas poligonais: uma ordem de vértices no sentido horário produz uma área positiva, enquanto uma ordem de giro no sentido anti-horário dos mesmos vértices produz a mesma área que um valor negativo. O código também usa uma espécie de API privada na biblioteca de geometria do Google Maps. Eu me senti confortável em usá-lo - use por seu próprio risco.
Uso da amostra:
Exemplo completo com testes de unidade @ http://jsfiddle.net/stevejansen/bq2ec/
fonte
Uma implementação da resposta de Sean em JavaScript:
Tenho certeza que isso está certo. Parece que está funcionando :-)
Esses polígonos ficam assim, se você está se perguntando:
fonte
Esta é a função implementada para o OpenLayers 2 . A condição para ter um polígono no sentido horário é
area < 0
, confirmada por esta referência .fonte
Se você usa o Matlab, a função
ispolycw
retornará true se os vértices do polígono estiverem no sentido horário.fonte
Como também explicado neste artigo da Wikipedia Orientação em curva , dados 3 pontos
p
,q
er
no plano (ou seja, com coordenadas x e y), você pode calcular o sinal do seguinte determinanteSe o determinante for negativo (ie
Orient(p, q, r) < 0
), o polígono será orientado no sentido horário (CW). Se o determinante for positivo (ou sejaOrient(p, q, r) > 0
), o polígono é orientado no sentido anti-horário (CCW). O determinante é zero (ou sejaOrient(p, q, r) == 0
) se pontosp
,q
er
são colineares .Na fórmula acima, nós preceder os em frente das coordenadas
p
,q
er
porque está a utilizar coordenadas homogéneas .fonte
Penso que, para que alguns pontos sejam dados no sentido horário, todas as arestas precisam ser positivas, não apenas a soma das arestas. Se uma aresta for negativa, pelo menos 3 pontos serão dados no sentido anti-horário.
fonte
Minha solução C # / LINQ é baseada nos conselhos de vários produtos da @charlesbretana abaixo. Você pode especificar uma referência normal para o enrolamento. Ele deve funcionar desde que a curva esteja principalmente no plano definido pelo vetor up.
com um teste de unidade
fonte
Esta é a minha solução usando as explicações nas outras respostas:
fonte
Um método muito mais computacionalmente mais simples, se você já conhece um ponto dentro do polígono :
Escolha qualquer segmento de linha do polígono original, pontos e suas coordenadas nessa ordem.
Adicione um ponto "interno" conhecido e forme um triângulo.
Calcule CW ou CCW como sugerido aqui com esses três pontos.
fonte
Após testar várias implementações não confiáveis, o algoritmo que forneceu resultados satisfatórios em relação à orientação CW / CCW fora da caixa foi o postado pelo OP neste segmento (
shoelace_formula_3
).Como sempre, um número positivo representa uma orientação CW, enquanto um número negativo CCW.
fonte
Aqui está a solução 3.0 rápida, com base nas respostas acima:
fonte
Outra solução para isso;
Pegue todos os vértices como uma matriz como esta;
fonte
Solução para R para determinar a direção e reverter no sentido horário (considerado necessário para objetos owin):
fonte
Embora essas respostas estejam corretas, elas são mais matematicamente intensas do que o necessário. Assuma as coordenadas do mapa, onde o ponto mais ao norte é o ponto mais alto no mapa. Encontre o ponto mais ao norte e, se 2 pontos estiverem empatados, é o mais ao norte e o mais ao leste (este é o ponto que lhf usa em sua resposta). Nos seus pontos,
ponto [0] = (5,0)
ponto [1] = (6,4)
ponto [2] = (4,5)
ponto [3] = (1,5)
ponto [4] = (1,0)
Se assumirmos que P2 é o ponto mais ao norte, então o ponto leste, o ponto anterior ou o próximo, determinam no sentido horário, CW ou CCW. Como o ponto mais ao norte está na face norte, se o P1 (anterior) ao P2 se mover para o leste, a direção é CW. Nesse caso, ele se move para oeste, então a direção é anti-horária, como diz a resposta aceita. Se o ponto anterior não tiver movimento horizontal, o mesmo sistema se aplica ao próximo ponto, P3. Se P3 estiver a oeste de P2, o movimento é no sentido anti-horário. Se o movimento P2 para P3 for leste, é oeste neste caso, o movimento é CW. Suponha que nte, P2 nos seus dados, seja o ponto mais ao norte que o leste e o prv seja o ponto anterior, P1 nos seus dados e nxt seja o próximo ponto, P3 nos seus dados e [0] seja horizontal ou leste / oeste, onde oeste é menor que leste e [1] é vertical.
fonte
.x
e.y
de uma estrutura, em vez de[0]
e[1]
eu não sabia o que seu código estava dizendo, primeira vez que eu olhei para ele..)Código C # para implementar a resposta do lhf :
fonte
Aqui está uma implementação simples do Python 3 com base nesta resposta (que, por sua vez, se baseia na solução proposta na resposta aceita )
fonte
encontre o centro de massa desses pontos.
suponha que existam linhas deste ponto para seus pontos.
Encontre o ângulo entre duas linhas para a linha0 linha1
do que para as linhas 1 e 2
...
...
se esse ângulo estiver aumentando monotonicamente do que no sentido anti-horário,
caso contrário, se diminuir monotonicamente, é no sentido horário
mais (não é monotônico)
você não pode decidir, então não é sábio
fonte