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.

Foi útil?

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.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top