Dimensionamento de um polígono 2D com o mouse
Pergunta
Eu desenvolver um programa de desenho baseado em Python, Whyteboard ( https://launchpad.net/whyteboard )
Estou desenvolvendo recursos para permitir que o usuário girar e dimensionar um polígono que desenhar. Aqui está o meu problema:
Eu tenho uma classe Polygon contendo uma lista de todos os pontos, que é "fechado" no final. Os usuários podem selecionar formas desenhadas no meu programa, que "destaques" eles, desenho alças de seleção em cada ponto. Estes pontos podem ser "agarrado" para alterar a sua posição, e alterando a forma do polígono.
Eu tenho um problema: Eu preciso descobrir como calcular uma "escala" o redimensionamento para aplicar ao polígono. Por exemplo, (com o mouse pressionado), o usuário mover o mouse para fora da forma deve ser um "crescer" ação, e trazendo o mouse para a forma deve reduzi-lo.
Eu tenho o código no local para realizar a escala (que eu acredito que é correto), mas eu só não pode criar um "bom" fator de escala. O código abaixo é o que eu vim acima com, com base nas respostas
/ edit -. Aqui é o código resolvido
def rescale(self, x, y):
"""
x and y are the current mouse positions. the center and "original" mouse
coords are calculated below
"""
if not self.center:
a = sum([x for x, y in self.points]) / len(self.points)
b = sum([y for x, y in self.points]) / len(self.points)
self.center = (a, b)
if not self.orig_click: # where the user first clicked on
self.orig_click = (x, y)
if not self.original_points: # the points before applying any scaling
self.original_points = list(self.points)
orig_click = self.orig_click
original_distance = math.sqrt((orig_click[0] - self.center[0]) ** 2 + (orig_click[1] - self.center[1]) ** 2)
current_distance = (math.sqrt((x - self.center[0]) ** 2 +
(y - self.center[1]) ** 2))
self.scale_factor = current_distance / original_distance
for count, point in enumerate(self.original_points):
dist = (point[0] - self.center[0], point[1] - self.center[1])
self.points[count] = (self.scale_factor * dist[0] + self.center[0], self.scale_factor * dist[1] + self.center[1])
Atualmente esta código parece escalar meu polígono para baixo a nada rapidamente, e nenhuma quantidade de movimento do mouse vai crescer de volta. Às vezes ele vai fazer o oposto, e crescem rapidamente; mas sem recuar.
Solução
Primeiro, vamos corrigir o seu código de escala:
for count, point in enumerate(self.points):
dist = (point[0] - self.center[0], point[1] - self.center[1])
self.points[count] = (self.scale_factor * dist[0] + self.center[0], self.scale_factor * dist[1] + self.center[1])
Espero que seus pontos são mantidos em ponto flutuante, porque os erros inteiro truncamento vai acumular muito rapidamente. Pode ser melhor ter duas cópias dos pontos, um escalados e um fora de escala.
Para determinar o fator de escala, pegue a razão entre a distância do original clique para o centro, e a posição atual do mouse para o centro.
original_distance = sqrt((click[0] - self.center[0])**2 + (click[1] - self.center[1])**2)
current_distance = sqrt((current_position[0] - self.center[0])**2 + (current_position[1] - self.center[1])**2)
self.scale_factor = current_distance / original_distance
Editar: Seu problema mais recente enfatiza a importância de ter dois conjuntos de pontos, o original ea escalado. Desde o fator de escala é relativa ao tamanho original da forma, você precisa para começar com os pontos originais da forma cada vez que você escala. Você pode consolidar que volta para um conjunto quando o usuário é feito jogando com o mouse.
E para o seu comentário, não, você não tem que recalcular o centro. O centro não deve se mover.
Editar 2: Quando você escalar, você escala de um tamanho para outro tamanho. Se você está redimensionando constantemente, você tem duas opções: manter uma cópia da forma em seu tamanho original, ou fazer o seu fator de escala em relação ao último tamanho da forma, ao invés do tamanho original. Eu prefiro a abordagem em duas cópias, porque caso contrário é muito fácil para os erros de acumular, mesmo se você estiver usando ponto flutuante; é também mais fácil de obter o direito de lógica.
Outras dicas
O fator de escala mais intuitiva seria a razão de (distância a partir da posição atual do mouse para o centro do polígono) a (distância da posição do mouse no início do arraste para o centro do polígono) - assim que clicar em um ponto do polígono e arrastando-o duas vezes mais longe do centro como era dobra o tamanho do polígono.
Eu não estou versado em Python, então eu vou tentar responder em pseudocódigo.
Em primeiro lugar, você vai querer calcular o centro do polígono. Isto é feito muito facilmente (e faz sentido quando você pensa sobre isso.): Basta adicionar todos os pontos juntos e dividi-los pela quantidade de pontos
center = (point1 + point2 + point3) / 3
Você quer escalá-lo baseado no mouse, correto? Isso sempre vai ser trabalhosa, mas deve ser algo como isto:
scale = lengthof(mouse - center) / MAGIC_NUMBER
Em seguida, você calcula os pontos em relação ao centro. Você está definindo efetivamente a origem de um gráfico no ponto central.
relative_point1 = point1 - center
relative_point2 = point2 - center
relative_point3 = point3 - center
Em seguida, você escala os pontos relativos pela escala:
relative_point1 *= scale
relative_point2 *= scale
relative_point3 *= scale
e colocá-los de volta para a posição correta:
point1 = center + relative_point1
point2 = center + relative_point2
point3 = center + relative_point3
Para evitar erros de arredondamento, você provavelmente vai querer manter os pontos originais até que o usuário é feito escala.