#math #poly&on #draw #rescale
#математика #многоугольник #рисуем #изменение масштаба
Вопрос:
Я разрабатываю программу для рисования на основе Python Whyteboard (https://launchpad.net/whyteboard)
Я разрабатываю функции, позволяющие пользователю поворачивать и масштабировать многоугольник, который они рисуют. Вот моя проблема:
У меня есть класс Poly&on, содержащий список всех точек, который «закрыт» в конце. Пользователи могут выбирать нарисованные фигуры в моей программе, которая их «подсвечивает», рисуя маркеры выделения в каждой точке. Эти точки можно «захватить», чтобы изменить их положение и форму многоугольника.
У меня проблема: мне нужно выяснить, как рассчитать «масштаб» изменения размера для применения к полигону. Например, (при удержании мыши нажатой) пользователь, отводящий мышь от фигуры, должен выполнять действие «расти», а наведение мыши на фигуру должно уменьшать ее.
У меня есть код для выполнения масштабирования (который, я считаю, правильный), но я просто не могу создать «хороший» коэффициент масштабирования. Приведенный ниже код — это то, что я придумал, основываясь на ответах
/ редактировать — вот решаемый код.
def rescale(self, x, y):
"""
x and y are the current mouse positions. the center and "ori&inal" 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.ori&_click: # where the user first clicked on
self.ori&_click = (x, y)
if not self.ori&inal_points: # the points before applyin& any scalin&
self.ori&inal_points = list(self.points)
ori&_click = self.ori&_click
ori&inal_distance = math.sqrt((ori&_click[0] - self.center[0]) ** 2 (ori&_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 / ori&inal_distance
for count, point in enumerate(self.ori&inal_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])
В настоящее время этот код, похоже, быстро сокращает мой многоугольник до нуля, и никакие движения мыши не увеличат его обратно. Иногда происходит обратное и он быстро увеличивается, но не сжимается обратно.
Ответ №1:
Сначала давайте исправим ваш код масштабирования:
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])
Я надеюсь, что ваши точки хранятся с плавающей запятой, потому что ошибки усечения целых чисел будут накапливаться очень быстро. Возможно, было бы лучше иметь две копии точек, одну масштабированную и одну немасштабированную.
Чтобы определить масштабный коэффициент, возьмите отношение расстояния от исходного щелчка до центра и текущего положения мыши по отношению к центру.
ori&inal_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 / ori&inal_distance
Редактировать: в вашей последней задаче подчеркивается важность наличия двух наборов точек, исходного и масштабированного. Поскольку коэффициент масштабирования зависит от исходного размера фигуры, при каждом масштабировании необходимо начинать с исходных точек фигуры. Вы можете объединить их обратно в один набор, когда пользователь закончит играть с мышью.
И к вашему комментарию, нет, вам не нужно пересчитывать центр. Центр не должен перемещаться.
Правка 2: При масштабировании происходит масштабирование от одного размера к другому размеру. Если вы постоянно изменяете масштаб, у вас есть два варианта: сохранить одну копию фигуры в исходном размере или установить масштабный коэффициент относительно последнего размера фигуры, а не исходного размера. Я предпочитаю подход с двумя копиями, потому что в противном случае слишком легко накапливаются ошибки, даже если вы используете плавающую точку; также проще правильно использовать логику.
Комментарии:
1. Хорошая мысль о поплавках, я об этом не подумал — я преобразовал заново. Продолжаю ли я пересчитывать центр в своем коде? Я отредактировал свой исходный пост, чтобы добавить в него код, который я придумал, но это работает не слишком хорошо — кажется, что размер многоугольника изменяется слишком быстро.
2. Привет, извините, что продолжаю «продолжать» об этом. Я не уверен, что понимаю — текущие точки используются только для вычисления центра, один раз? Я не уверен, подходит ли наличие двух наборов, если вы можете здесь помочь. Спасибо!
3. Да! Огромное спасибо, Марк! Наконец-то у меня все заработало. Ваша помощь была неоценима. Теперь, чтобы показать вам, в чем вы мне помогли: ima&ebin.or&/78777 — без масштаба ima&ebin.or&/78778 — уменьшено ima&ebin.or&/78779 — еще больше приветствий!
Ответ №2:
Наиболее интуитивно понятным масштабным коэффициентом было бы отношение (расстояние от текущего положения мыши до центра многоугольника) к (расстояние от положения мыши в начале перетаскивания до центра многоугольника) — так что щелчок по точке в многоугольнике и перетаскивание ее вдвое дальше от центра, чем это было, удваивает размер многоугольника.
Ответ №3:
Я не разбираюсь в Python, поэтому постараюсь ответить в псевдокоде.
Прежде всего, вы захотите вычислить центр многоугольника. Это делается очень легко (и имеет смысл, если подумать): просто сложите все точки вместе и разделите их на количество точек.
center = (point1 point2 point3) / 3
Вы хотите масштабировать его на основе мыши, правильно? Это всегда будет сложно, но это должно быть что-то вроде этого:
scale = len&thof(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. Что здесь означает магическое число? Я имею в виду значение .00 — 1.00, 1 — 100 или что?
2. Это магическое число! Это зависит от того, что выглядит лучше. Сначала попробуйте 10.
3. На самом деле, никаких магических чисел не требуется. При щелчке мыши измерьте длину многоугольника (по центру мыши) и сохраните его как исходный . Тогда новый масштаб будет просто len&thof (mouse-center) / ori&inal. Будьте осторожны, если оригинал равен нулю!
4. Спасибо. Я думаю, что это то, что я делаю в своем коде (теперь отредактированном в первом сообщении), но он все еще немного «шаткий».