7
votes

In pygame I use pygame.draw.rect(screen, color, rectangle) for all the rectangles in my program. I want to be able to rotate these rectangles to any angle. I have seen the following code to rotate IMAGES but my question is with RECTANGLES.

pygame.transform.rotate(image, angle)

But I am working with rectangles, I don't have an image or "surface" that I can rotate. When I try to rotate a rectangle with

rect = pygame.draw.rect(screen, self.color, self.get_rectang())
rotatedRect = pygame.transform.rotate(rect, self.rotation)
screen.blit(rotatedRect)

This gives TypeError: must be pygame.Surface, not pygame.Rect on the line with .rotate()

My question is, how can I rotate a and display a RECTANGLE(x,y,w,h), not an image, in pygame.

The linked post that this is a "potential duplicate" of is not a duplicate. One answer explains about the consequences of rotating a rectangle and the other uses code for rotating an image.

5
You could rotate each of the 4 points yourself like so x2= cos a * x1 - sin a y1 y2= sin a *x1 + cos a y1, but the rectangle function might only plot a rectangle that is aligned square with the axes, so you'd have to look for a polygon plotting function. Alternatively you could make a surface with visible outline and transparent fill (alpha), which is probably easier. A rectangle may just be a helper function in pygame, and not be directly displayable, so there are two separate questions in your last line.roadrunner66
I read that before I posted my question. It's not a duplicate of that, look at the answers, they explain how to rotate an image. This is a quote from one of the answers image2 = pygame.transform.rotate(image1, angle) and the accepted answer doesn't explain anything about actually rotating a rectangle, just about some of the unexpected consequencesKeatinge
@roadrunner66 I was hoping I would be able to avoid doing trig and plotting points in a polygon, but I appreciate the suggestion and will do that if there isn't an easier wayKeatinge
The question is duplicate, because you cannot rotate a rectangle. You have to create a transparent Surface and rotate the Surface.Rabbid76

5 Answers

4
votes

See the second answer here: Rotating a point about another point (2D)

I think rectangles can only be horiz or vertical in their oreintation. You need to define the corners and rotate them and then draw and fill between them.

The other way is to make a class

class myRect(pygame.Surface):
    def __init__(self, parent, xpos, ypos, width, height):
      super(myRect, self).__init__(width, height)
      self.xpos = xpos
      self.ypos = ypos
      self.parent = parent

    def update(self, parent):
      parent.blit(self, (self.xpos, self.ypos))

    def rotate(self, angle):
      #(your rotation code goes here)

and use that instead, as then you will be able to rotate it using the transform function.

6
votes
import pygame as py  

# define constants  
WIDTH = 500  
HEIGHT = 500  
FPS = 30  

# define colors  
BLACK = (0 , 0 , 0)  
GREEN = (0 , 255 , 0)  

# initialize pygame and create screen  
py.init()  
screen = py.display.set_mode((WIDTH , HEIGHT))  
# for setting FPS  
clock = py.time.Clock()  

rot = 0  
rot_speed = 2  

# define a surface (RECTANGLE)  
image_orig = py.Surface((100 , 100))  
# for making transparent background while rotating an image  
image_orig.set_colorkey(BLACK)  
# fill the rectangle / surface with green color  
image_orig.fill(GREEN)  
# creating a copy of orignal image for smooth rotation  
image = image_orig.copy()  
image.set_colorkey(BLACK)  
# define rect for placing the rectangle at the desired position  
rect = image.get_rect()  
rect.center = (WIDTH // 2 , HEIGHT // 2)  
# keep rotating the rectangle until running is set to False  
running = True  
while running:  
    # set FPS  
    clock.tick(FPS)  
    # clear the screen every time before drawing new objects  
    screen.fill(BLACK)  
    # check for the exit  
    for event in py.event.get():  
        if event.type == py.QUIT:  
            running = False  

    # making a copy of the old center of the rectangle  
    old_center = rect.center  
    # defining angle of the rotation  
    rot = (rot + rot_speed) % 360  
    # rotating the orignal image  
    new_image = py.transform.rotate(image_orig , rot)  
    rect = new_image.get_rect()  
    # set the rotated rectangle to the old center  
    rect.center = old_center  
    # drawing the rotated rectangle to the screen  
    screen.blit(new_image , rect)  
    # flipping the display after drawing everything  
    py.display.flip()  

py.quit()  
1
votes

a more complex version of the quick replacement, in which you can define an arbitrary rotation center point for your rectangle - even outside of it (tested in python3):

    def rectRotated( surface, color, pos, fill, border_radius, rotation_angle, rotation_offset_center = (0,0), nAntialiasingRatio = 1 ):
        """
        - rotation_angle: in degree
        - rotation_offset_center: moving the center of the rotation: (-100,0) will turn the rectangle around a point 100 above center of the rectangle,
                                             if (0,0) the rotation is at the center of the rectangle
        - nAntialiasingRatio: set 1 for no antialising, 2/4/8 for better aliasing
        """
        nRenderRatio = nAntialiasingRatio
        
        sw = pos[2]+abs(rotation_offset_center[0])*2
        sh = pos[3]+abs(rotation_offset_center[1])*2

        surfcenterx = sw//2
        surfcentery = sh//2
        s = pg.Surface( (sw*nRenderRatio,sh*nRenderRatio) )
        s = s.convert_alpha()
        s.fill((0,0,0,0))
        
        rw2=pos[2]//2 # halfwidth of rectangle
        rh2=pos[3]//2

        pg.draw.rect( s, color, ((surfcenterx-rw2-rotation_offset_center[0])*nRenderRatio,(surfcentery-rh2-rotation_offset_center[1])*nRenderRatio,pos[2]*nRenderRatio,pos[3]*nRenderRatio), fill*nRenderRatio, border_radius=border_radius*nRenderRatio )
        s = pygame.transform.rotate( s, rotation_angle )        
        if nRenderRatio != 1: s = pygame.transform.smoothscale(s,(s.get_width()//nRenderRatio,s.get_height()//nRenderRatio))
        incfromrotw = (s.get_width()-sw)//2
        incfromroth = (s.get_height()-sh)//2
        surface.blit( s, (pos[0]-surfcenterx+rotation_offset_center[0]+rw2-incfromrotw,pos[1]-surfcentery+rotation_offset_center[1]+rh2-incfromroth) )
        
0
votes

a quick replacement of the base pygame function adding rotation:

def rectRotated( surface, color, pos, fill, border_radius, angle ):
    """
    - angle in degree
    """
    max_area = max(pos[2],pos[3])
    s = pg.Surface((max_area,max_area))
    s = s.convert_alpha()
    s.fill((0,0,0,0))
    pg.draw.rect(s, color,(0,0,pos[2],pos[3]),fill, border_radius=border_radius)
    s = pygame.transform.rotate(s,angle)
    surface.blit( s, (pos[0],pos[1]) )
0
votes

You cannot rotate a rectangle drawn by pygame.draw.rect. You have to create a transparent pygame.Surface and rotate the Surface:

rect_surf = pygame.Surface((widht, height), pygame.SRCLAPHA)
rect_surf.fill(color)

See How do I rotate an image around its center using PyGame?, to rotate the Surface.