Questions about Rotation

I’m trying to rotate static bitmaps by converting them back to an image, applying the rotation
and converting back to a bitmap. That part of the process seems to work as long as it’s an
90 degree rotation and the bitmap fits neatly into a square. I’d like to be able to resize the
window to fit the bitmap after it’s been rotated if required. Also, the 45 degree rotation seems
a bit off as you’ll see. I’m not sure what’s happening there. I’d also like to know if there’s a problem clicking on the screen object and clicking on it again without moving the mouse.
On my Mac I have to move the mouse pointer to get another click. I’ve had this problem before when deleting bitmaps but I’ve found it in non-python code as well. I’m wondering if it works differently on another OS.
Thanks…
rotate.py (2.9 KB)

When you Rotate with a non 90 degree angle then the image is resized to be able to fit everything from the original image in it. So your 200x200 image will end up being sized to 285x285. Then on the next rotation you Scale it back down to 200x200 again, and then it’s sized up to to 285x285 again with the Rotate. And so on… Each of those scales and rotates are cumulatively distorting the image and so it looks like it’s shrinking and getting uglier with just a few clicks.

A better approach would probably be to keep the original image and always start with it when you want to do a rotation. Also keep track of the current angle, and just increment it with the clicks and then rotate the original image the full amount of the current angle instead of doing it bit by bit. You should also probably do the Scale after the Rotate so you can fit a possibly expanded image into the size of the widget.

Hello,
Here’s what I have for rotate now:

def onRotate(self, value):
        img = self._bmp.ConvertToImage()
        W, H = self._bmp.GetSize() 
        print( "a: ", value, self._bmp.GetSize() )
        # ctr = ((W/2), (H/2))
        val = float(value * -pi/180)        ## negative value rotates clockwise
        img = img.Rotate(val, self._ctr)   ## use orignal center
        self._bmp = img.ConvertToBitmap()     
        print( "b: ", value, self._bmp.GetSize() )
        self.Layout()
        self.Update() 
        self.Refresh()

and here’s the result

Without doing anything the box seems to grow at 90 degrees and

at 45 it drifts out of the window.

I’m not sure if center is being properly set which may account for

the problem with non 90 degrees but I don’t understand why at 90

it seems to grow. I noticed it earlier with a bitmap scaled to fit,

that’s why I was setting the size back to stop it from getting cut off.

Thanks again,

Mel

Just removing the Scale does not really comply with my suggestion. This is what I had in mind:

import wx
import wx.lib.statbmp
from math import pi as pi

print(wx.version())

class Actor(wx.lib.statbmp.GenStaticBitmap):
    def __init__(self, parent, bitmap, idNum, imgFile, **kwargs):
        wx.lib.statbmp.GenStaticBitmap.__init__(self, parent, -1, bitmap, **kwargs)
        self._parent = parent  ## if needed
        self._original_bmp = self._current_bmp = bitmap
        self._W, self._H = self.GetSize()
        self._ctr = ((self._W/2), (self._H/2))
        self._current_angle = 0

        self.Bind(wx.EVT_PAINT, self.OnPaint)
        self.Bind(wx.EVT_LEFT_DOWN, self.rotate90)
        self.Bind(wx.EVT_RIGHT_DOWN, self.rotate45)

    def OnPaint(self, evt=None):
        dc = wx.PaintDC(self)  ## draw window boundary
        dc.SetPen(wx.Pen('blue', 3, wx.SHORT_DASH))
        dc.SetBrush(wx.Brush('blue', wx.TRANSPARENT))
        dc.DrawRectangle(0,0,200,200)
        brush = wx.TRANSPARENT_BRUSH
        dc.SetBackground(brush)
        dc.Clear()
        dc.DrawBitmap(self._current_bmp, 0, 0, True)

    def rotate90(self, evt):
        self._current_angle += 90
        self.onRotate(self._current_angle)
        evt.Skip()

    def rotate45(self, evt):
        self._current_angle += 45
        self.onRotate(self._current_angle)
        evt.Skip()

    def onRotate(self, value):
        img = self._original_bmp.ConvertToImage()
        val = float(value * -pi/180)       ## negative value rotates clockwise
        img = img.Rotate(val, self._ctr)   ## use orignal center
        img = img.Scale(self._W, self._H)  ## use original size
        self._current_bmp = img.ConvertToBitmap()
        print('scale:{}  ctr:{}  size:{}'.format((self._W, self._H), self._ctr, self._current_bmp.GetSize()))
        self.Refresh()

    def makeBox():
        bmp = wx.Bitmap(200,200)
        dc = wx.MemoryDC(bmp)
        dc.SetPen(wx.Pen('red', 3, wx.SOLID))
        dc.SetBrush(wx.Brush('red'))
        dc.DrawRectangle(50,25,100,150)
        dc.SetFont(wx.Font(16, wx.DEFAULT, wx.NORMAL, wx.NORMAL))
        dc.DrawText("Top", 80, 30)
        dc.SelectObject(wx.NullBitmap)
        return wx.Bitmap(bmp)

class Apanel(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent, -1)
        self.SetBackgroundColour('white')
        bob = Actor(self, Actor.makeBox(), 1, "")
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add((0,50),0)
        sizer.Add(bob, 0, wx.ALIGN_CENTER)
        sizer.Add((0,50),0)
        lbl = wx.StaticText(self, label="Left Click for 90 degrees, right for 45")
        sizer.Add(lbl, 0, wx.ALIGN_CENTER)
        self.SetSizer(sizer)

class FrameIt(wx.Frame):
    def __init__(self, parent):
        wx.Frame.__init__(self, parent, -1, title='rotate', size=(400,400))
        Apanel(self)
        self.Center()
        self.Show()

if __name__ == '__main__':
    app = wx.App(False)
    frame = FrameIt(None)
    app.MainLoop()

Thanks Robin,

Your changes helped solve the problem I was experiencing.

Mel