I develop a Python-based drawing program, Whyteboard (https://launchpad.net/whyteboard)
I’m developing features to allow the user to rotate and scale a polygon that they draw. Here’s my problem:
I have a Polygon class containing a list of all points, which is “closed off” at the end. Users can select drawn shapes in my program, which “highlights” them, drawing selection handles at each point. These points can be “grabbed” to change its position, and altering the polygon’s shape.
I have a problem: I need to figure out how to calculate a the resizing “scale” to apply to the polygon. For example, (with the mouse held down), the user moving their mouse away from the shape should be a “grow” action, and bringing the mouse towards the shape should shrink it.
I have code in place to perform the scale (which I believe is correct) but I just can’t create a “good” scaling factor. The code below is what I’ve come up with, based on the answers
/edit — here is the solved code.
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])
Currently this code seems to scale my polygon down to nothing quickly, and no amount of mouse movement will grow it back. Sometimes it will do the opposite, and grow quickly; but not shrink back.
First, let’s correct your scaling code:
I hope your points are kept in floating point, because integer truncation errors are going to accumulate very quickly. It might be better to have two copies of the points, one scaled and one unscaled.
To determine the scale factor, take the ratio of the distance from the original click to the center, and the current mouse position to the center.
Edit: Your latest problem emphasizes the importance of having two sets of points, the original and the scaled. Since the scale factor is relative to the original size of the shape, you need to start with the original points of the shape each time you scale. You can consolidate that back down to one set when the user is done playing with the mouse.
And to your comment, no you don’t have to recalculate the center. The center should not be moving.
Edit 2: When you scale, you’re scaling from one size to another size. If you’re rescaling constantly, you have two choices: keep one copy of the shape at its original size, or make your scale factor relative to the last size of the shape, rather than the original size. I prefer the two copy approach, because otherwise it’s too easy for errors to accumulate, even if you’re using floating point; it’s also easier to get the logic right.