Eu ficaria tentado a fazer isso usando anjos, quase como uma linha de visão. Se ao iterar os vértices na forma, os ângulos entre o vértice de origem e o vértice de destino continuarem em uma direção consistente, todos os pontos serão candidatos ao antípoda. Se um ângulo muda de direção, esse ponto fica oculto ou oculta o ponto anterior. Se estiver oculto pelo ponto anterior, o ponto precisará ser ignorado. Se ocultar o ponto anterior, é necessário remover o (s) ponto (s) anterior (s) da lista de candidatos.
- Criar uma lista PolygonCandidates
- Para cada vértice (ponto k)
- Criar nova lista de candidatos (Ponto, Ângulo)
- Adicionar o vértice atual à lista de candidatos (ponto k)
- Iterar no sentido horário ao redor do polígono, para cada vértice restante (ponto i)
- Se o ângulo do ponto atual (do ponto k ao ponto i) continuar no sentido horário 1.adicione o ponto
- Se o ângulo para o ponto atual continuar no sentido anti-horário
- Se os dois candidatos anteriores apontarem, mais o ponto atual, formará uma curva à direita.
- Remova o último ponto da lista até que o ângulo atual e o último ângulo da lista de candidatos estejam no sentido anti-horário.
- Adicione o ponto atual à lista de candidatos
- Adicione todos, exceto os dois primeiros e o último candidato, a uma lista de PolygonCandidates
- Encontre o ponto mais distante da lista PolygonCandidates.
Não sei o que fazer com casos em que a origem e dois outros vértices caem na mesma linha. Nesse caso, o ângulo seria o mesmo. Se você tivesse um polígono com orifícios, poderia encontrar o ângulo mínimo / máximo de cada orifício e remover qualquer ponto candidato que estivesse dentro desse intervalo.
A principal vantagem dessa abordagem seria que você não precisa testar a interseção de linha entre o segmento de linha atual e todas as arestas do polígono.
Isso funciona ... eu acho. Atualizei o pseudo-código acima e o python para facilitar a leitura.
Essa deve ser a última edição. O exemplo abaixo deve encontrar o maior anitólio para uma determinada geometria. Alterei o script para usar pontos e vetores, para tentar facilitar a leitura.
import math
from collections import namedtuple
Point = namedtuple("Point", "position x y")
Vector = namedtuple("Vector", "source dest angle")
def isClockwise(angle1, angle2):
diff = angle2 - angle1
#print(" angle1:%s angle2:%s diff: %s" % (angle1, angle2, diff))
if(diff > math.pi/2):
diff = diff - math.pi/2
elif (diff < -math.pi/2):
diff = diff + math.pi/2
#print(" diff:%s" % (diff))
if(diff > 0):
return False
return True
def getAngle(origin, point):
return math.atan2(point.y - origin.y, point.x-origin.x)
#returns a list of candidate vertcies. This will include the first, second, and second to last points
#the first and last points in the polygon must be the same
#k is the starting position, only vertices after this position will be evaluated
def getCandidates (k, polygon):
origin = polygon[k]
candidates = [Vector(k,k,0)]
prevAngle = 0;
currentAngle = 0;
for i in range(k + 1, len(polygon) - 1):
current = polygon[i]
#print("vertex i:%s x:%s y:%s " % (i, current.x, current.y))
if(i == k+1):
prevAngle = getAngle(origin, current)
candidates.append(Vector(k,i,prevAngle))
else:
currentAngle = getAngle(origin, current)
#print(" prevAngle:%s currentAngle:%s " % (prevAngle, currentAngle))
if isClockwise(prevAngle, currentAngle):
#print(" append")
candidates.append(Vector(k,i,currentAngle))
prevAngle = currentAngle
else:
#look at the angle between current, candidate-1 and candidate-2
if(i >= 2):
lastCandinate = polygon[candidates[len(candidates) - 1].dest]
secondLastCandidate = polygon[candidates[len(candidates) - 2].dest]
isleft = ((lastCandinate.x - secondLastCandidate.x)*(current.y - secondLastCandidate.y) - (lastCandinate.y - secondLastCandidate.y)*(current.x - secondLastCandidate.x)) > 0
#print(" test for what side of polygon %s" % (isleft))
if(i-k >= 2 and not isleft):
while isClockwise(currentAngle, candidates[len(candidates) - 1].angle):
#print(" remove %s" % (len(candidates) - 1))
candidates.pop()
#print(" append (after remove)")
candidates.append(Vector(k,i,currentAngle))
prevAngle = currentAngle
#for i in range(len(candidates)):
# print("candidate i:%s x:%s y:%s a:%s " % (candidates[i][0], candidates[i][1], candidates[i][2], candidates[i][3]))
return candidates
def calcDistance(point1, point2):
return math.sqrt(math.pow(point2.x - point1.x, 2) + math.pow(point2.y - point1.y, 2))
def findMaxDistance(polygon, candidates):
#ignore the first 2 and last result
maxDistance = 0
maxVector = Vector(0,0,0);
for i in range(len(candidates)):
currentDistance = calcDistance(polygon[candidates[i].source], polygon[candidates[i].dest])
if(currentDistance > maxDistance):
maxDistance = currentDistance
maxVector = candidates[i];
if(maxDistance > 0):
print ("The Antipodal distance is %s from %s to %s" % (maxDistance, polygon[candidates[i].source], polygon[candidates[i].dest]))
else:
print ("There is no Antipodal distance")
def getAntipodalDist(polygon):
polygonCandidates = []
for j in range(0, len(polygon) - 1):
candidates = getCandidates(j, polygon)
for i in range(2, len(candidates) - 1):
#print("candidate i:%s->%s x:%s y:%s " % (candidates[i].source, candidates[i].dest, candidates[i].x, candidates[i].y))
polygonCandidates.append(candidates[i])
for i in range(len(polygonCandidates)):
print("candidate i:%s->%s" % (polygonCandidates[i].source, polygonCandidates[i].dest))
findMaxDistance(polygon, polygonCandidates)
getAntipodalDist([Point(0,0,0),Point(1,-2,0),Point(2,-2,3),Point(3,2,2),Point(4,-1,1),Point(5,4,0),Point(6,0,0)])
getAntipodalDist([Point(0,0,0),Point(1,2,1),Point(2,1,4),Point(3,3,5),Point(4,5,4),Point(5,4,1),Point(6,0,0)])
getAntipodalDist([Point(0,0,0),Point(1,1,1),Point(2,2,1),Point(3,1,4),Point(4,3,5),Point(5,5,4),Point(6,4,1),Point(7,0,0)])
getAntipodalDist([Point(0,0,0),Point(1,-1,3),Point(2,1,4),Point(3,3,3),Point(4,2,0),Point(5,-2,-1),Point(6,0,0)])