Масштабирование 2D-полигона с помощью мыши - PullRequest
2 голосов
/ 06 января 2010

Я разрабатываю программу для рисования на основе Python, Whyteboard (https://launchpad.net/whyteboard)

Я разрабатываю функции, позволяющие пользователю поворачивать и масштабировать нарисованный им многоугольник. Вот моя проблема:

У меня есть класс Polygon, содержащий список всех точек, который в конце «закрыт». Пользователи могут выбрать нарисованные фигуры в моей программе, которая «подсвечивает» их, рисуя маркеры выбора в каждой точке. Эти точки можно «схватить», чтобы изменить их положение и изменить форму многоугольника.

У меня проблема: мне нужно выяснить, как рассчитать «масштаб» изменения размера, чтобы применить его к многоугольнику. Например, (удерживая мышь нажатой), пользователь, перемещающий мышь от фигуры, должен выполнять действие «расти», а приведение мыши к фигуре должно уменьшить его.

У меня есть код для выполнения масштабирования (который я считаю правильным), но я просто не могу создать «хороший» коэффициент масштабирования. Код ниже - это то, что я придумал, основываясь на ответах

/ edit - вот решенный код.

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]) 

В настоящее время этот код, кажется, быстро сокращает мой полигон до нуля, и никакое движение мышью не вырастет обратно. Иногда это будет наоборот и быстро расти; но не отступать.

Ответы [ 3 ]

5 голосов
/ 06 января 2010

Во-первых, давайте исправим ваш масштабный код:

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]) 

Я надеюсь, что ваши очки хранятся в плавающей запятой, потому что целочисленные ошибки усечения будут накапливаться очень быстро. Возможно, было бы лучше иметь две копии точек, одну масштабированную и одну немасштабированную.

Чтобы определить масштабный коэффициент, возьмите отношение расстояния от исходного щелчка к центру и текущей позиции мыши к центру.

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

Редактировать: Ваша последняя проблема подчеркивает важность наличия двух наборов точек, оригинала и в масштабе. Поскольку масштабный коэффициент зависит от исходного размера фигуры, вам нужно начинать с исходных точек фигуры при каждом масштабировании. Вы можете объединить его обратно в один набор, когда пользователь закончит играть с мышью.

И на ваш комментарий, нет, вам не нужно пересчитывать центр. Центр не должен двигаться.

Изменить 2: При масштабировании вы масштабируете от один размер до другой размер. Если вы постоянно изменяете масштаб, у вас есть два варианта: оставить одну копию фигуры в ее исходном размере или сделать масштабный коэффициент относительно последнего размера фигуры, а не исходного размера. Я предпочитаю подход с двумя копиями, потому что в противном случае ошибки накапливаться слишком легко, даже если вы используете плавающую точку; также легче понять правильную логику.

1 голос
/ 06 января 2010

Я не разбираюсь в Python, поэтому постараюсь ответить в псевдокоде.

Прежде всего, вы захотите вычислить центр многоугольника. Это делается очень легко (и имеет смысл, когда вы об этом думаете): просто сложите все точки вместе и разделите их на количество очков.

center = (point1 + point2 + point3) / 3

Вы хотите масштабировать его на основе мыши, правильно? Это всегда будет неудобно, но это должно быть примерно так:

scale = lengthof(mouse - center) / MAGIC_NUMBER

Затем вы вычисляете относительные точки к центру. Вы фактически устанавливаете начало графика в центральной точке.

relative_point1 = point1 - center
relative_point2 = point2 - center
relative_point3 = point3 - center

Затем вы масштабируете относительные точки по шкале:

relative_point1 *= scale
relative_point2 *= scale
relative_point3 *= scale

И поместите их обратно в правильное положение:

point1 = center + relative_point1
point2 = center + relative_point2
point3 = center + relative_point3

Чтобы избежать ошибок округления, вы, вероятно, захотите сохранить исходные точки, пока пользователь не закончит масштабирование.

1 голос
/ 06 января 2010

Наиболее интуитивно понятным масштабным коэффициентом будет отношение (расстояние от текущей позиции мыши до центра многоугольника) к (расстоянию от позиции мыши в начале перетаскивания до центра многоугольника), чтобы щелкнуть точку в многоугольнике перетаскивая его вдвое дальше от центра, чем это было, удваивает размер многоугольника.

...