views:

608

answers:

4

I am writing a drawing program, Whyteboard -- http://code.google.com/p/whyteboard/

I have implemented image rotating functionality, except that its behaviour is a little odd. I can't figure out the proper logic to make rotating the image in relation to the mouse position

My code is something similar to this:

(these are called from a mouse event handler)

def resize(self, x, y, direction=None):
    """Rotate the image"""
    self.angle += 1
    if self.angle > 360:
        self.angle = 0
    self.rotate()


def rotate(self, angle=None):
    """Rotate the image (in radians), turn it back into a bitmap"""
    rad = (2 * math.pi * self.angle) / 360
    if angle:
        rad = (2 * math.pi * angle) / 360
    img = self.img.Rotate(rad, (0, 0))

So, basically the angle to rotate the image keeps getting increased when the user moves the mouse. However, this sometimes means you have to "circle" the mouse many times to rotate an image 90 degrees, let alone 360.

But, I need it similar to other programs - how the image is rotated in relation to your mouse's position to the image.

This is the bit I'm having trouble with. I've left the question language-independent, although using Python and wxPython it could be applicable to any language

+5  A: 

I'm assuming resize() is called for every mouse movement update. Your problem seems to be the self.angle += 1, which makes you update your angle by 1 degree on each mouse event.

A solution to your problem would be: pick the point on the image where the rotation will be centered (on this case, it's your (0,0) point on self.img.Rotate(), but usually it is the center of the image). The rotation angle should be the angle formed by the line that goes from this point to the mouse cursor minus the angle formed by the line that goes from this point to the mouse position when the user clicked.

To calculate the angle between two points, use math.atan2(y2-y1, x2-x1) which will give you the angle in radians. (you may have to change the order of the subtractions depending on your mouse position axis).

fserb
Thanks, I'll go try it. I was just using 1 for the angle because I didn't really know how to approach the problem. Good catch on (0, 0) too, I forgot to calculate the image center and kept rotating it around the image's top-left. Thank you.
Steven Sproat
+1  A: 

fserb's solution is the way I would go about the rotation too, but something additional to consider is your use of:

img = self.img.Rotate(rad, (0, 0))

If you are performing a bitmap image rotation in response to every mouse drag event, you are going to get a lot of data loss from the combined effect of all the interpolation required for the rotation. For example, rotating by 1 degree 360 times will give you a much blurrier image than the original.

Try having a rotation system something like this:

display_img = self.img.Rotate(rad, pos)

then use the display_img image while you are in rotation mode. When you end rotation mode (onMouseUp maybe), img = display_img.

This type of strategy is good whenever you have a lossy operation with a user preview.

tkerwin
Thanks, this is a good idea. I'll be converting to using a transparent polygon and rotating that as the user moves the mouse, and then doing the rotation only when they release the mouse button - it's a bit too performance intense to display the image as it rotates
Steven Sproat
You could also display a low res version of the image rotated, depending on performance.
tkerwin
A: 

Here's the solution in the end,

def rotate(self, position, origin):
    """ position: mouse x/y position, origin: x/y to rotate around"""
    origin_angle = self.find_angle(origin, self.center)
    mouse_angle = self.find_angle(position, self.center)

    angle = mouse_angle - origin_angle 
    # do the rotation here


def find_angle(self, a, b):
    try:
        answer = math.atan2((a[0] - b[0]) , (a[1] - b[1]))
    except:
        answer = 0
    return answer
Steven Sproat
A: 

Hi Steven Sproat , I am working on the same scenario,could please explain the code for calling from mouse event handler? . I am having dynamic images ad have to attach the rotating function based on mouse movements or handler ? Thanks

Vani
The function I posted needs to know the original mouse click (origin of (x/y), and the current mouse position (x/y) tuple.
Steven Sproat