Rotating a rectangle (not image) in pygame

Question:

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.

Asked By: Keatinge

||

Answers:

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.

Answered By: marienbad
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()  
Answered By: Ashish kumar

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]) )
Answered By: Alexandre Mazel

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) )
        
Answered By: Alexandre Mazel

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.

Answered By: Rabbid76

This code simulates rotating rectangles falling towards the ground. I used it in one of my games to make the background look awesome

import pygame
import random

class Square(pygame.sprite.Sprite):
    def __init__(self, x, y):
        super(Square, self).__init__()

        self.win = win
        self.color = (128, 128, 128)
        self.speed = 3
        self.angle = 0

        self.side = random.randint(15, 40)

        self.surface = pygame.Surface((self.side, self.side), pygame.SRCALPHA)
        self.surface.set_colorkey((200,200,200))
        self.rect = self.surface.get_rect(center=(x, y))

    def update(self, win):
        center = self.rect.center
        self.angle = (self.angle + self.speed) % 360
        image = pygame.transform.rotate(self.surface , self.angle)
        self.rect = image.get_rect()
        self.rect.center = center

        self.rect.y += 1.5

        if self.rect.top >= HEIGHT:
            self.kill()

        pygame.draw.rect(self.surface, self.color, (0,0, self.side, self.side), 4)
        win.blit(image, self.rect)

if __name__ == '__main__':
    pygame.init()
    SCREEN = WIDTH, HEIGHT = 288, 512
    win = pygame.display.set_mode(SCREEN, pygame.NOFRAME)

    clock = pygame.time.Clock()
    FPS = 60
    count = 0

    square_group = pygame.sprite.Group()

    running = True
    while running:
        win.fill((200,200,200))

        for event in pygame.event.get():
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_ESCAPE:
                    running = False

        count += 1
        if count % 100 == 0:
            x = random.randint(40, WIDTH-40)
            y = 0
            square = Square(x, y)
            square_group.add(square)
            count = 0

        square_group.update(win)

        pygame.draw.rect(win, (30,30,30), (0, 0, WIDTH, HEIGHT), 8)
        clock.tick(FPS)
        pygame.display.update()
pygame.quit()

Here’s the output, it’s not an gif though

enter image description here

Now if you want color filled rectangle instead of bordered only, update this line on line 31

pygame.draw.rect(self.surface, self.color, (0,0, self.side, self.side))

and if you don’t want the rectangle to fall down comment line 26

Answered By: Prajjwal Pathak

I made a class which handles the rotation for you…
Extended from Ashish’s design

from pygame import Surface, transform
from consts import screen


class BaseEntity:
    def __init__(self, x: int, y: int):
        self.x = x
        self.y = y


class Rectangle(BaseEntity):
    def __init__(self, x: int, y: int, width: int, height: int, color: tuple):
        super().__init__(x, y)
        self.width = width
        self.height = height
        self.color = color
        self.rotatation = 0

        # the rectangle is a surface itself
        self.surface = Surface((width, height))
        self.surface.set_colorkey((0, 0, 0))
        self.surface.fill(color)
        self.rect = self.surface.get_rect()

    def display(self, angle=None):
        # updating values
        self.surface.fill(
            self.color
        )  # refill the surface color if you change it somewhere in the program
        self.rect = self.surface.get_rect()
        self.rect.center = (self.x, self.y)

        # renderer
        if angle is not None:
            self.rotatation = angle

        old_center = self.rect.center
        new = transform.rotate(self.surface, self.rotatation)
        self.rect = new.get_rect()
        self.rect.center = old_center
        screen.blit(new, self.rect)
Answered By: FloatingComet62

Using a bit of trigonometry and the polygon function, I’m able to draw a rotated rectangle.

import math
import pygame.draw


def draw_rectangle(x, y, width, height, color, rotation=0):
    """Draw a rectangle, centered at x, y.

    Arguments:
      x (int/float):
        The x coordinate of the center of the shape.
      y (int/float):
        The y coordinate of the center of the shape.
      width (int/float):
        The width of the rectangle.
      height (int/float):
        The height of the rectangle.
      color (str):
        Name of the fill color, in HTML format.
    """
    points = []

    # The distance from the center of the rectangle to
    # one of the corners is the same for each corner.
    radius = math.sqrt((height / 2)**2 + (width / 2)**2)

    # Get the angle to one of the corners with respect
    # to the x-axis.
    angle = math.atan2(height / 2, width / 2)

    # Transform that angle to reach each corner of the rectangle.
    angles = [angle, -angle + math.pi, angle + math.pi, -angle]

    # Convert rotation from degrees to radians.
    rot_radians = (math.pi / 180) * rotation

    # Calculate the coordinates of each point.
    for angle in angles:
        y_offset = -1 * radius * math.sin(angle + rot_radians)
        x_offset = radius * math.cos(angle + rot_radians)
        points.append((x + x_offset, y + y_offset))

    pygame.draw.polygon(screen, color, points)

https://replit.com/@TimSwast1/RotateARectanlge?v=1

Answered By: Tim Swast

A concise and fast function to draw a rotated rectangle. Uses NumPy

    def rectRotated(self, surface, rect, color, rotation):
        """
        Draws a rotated Rect.
        surface: pygame.Surface
        rect: pygame.Rect
        color: pygame.Color
        rotation: float (degrees)
        return: np.ndarray (vertices)
        """
        # calculate the rotation in radians
        rot_radians = -rotation * pi / 180

        # calculate the points around the center of the rectangle, taking width and height into account
        angle = atan2(rect.height / 2, rect.width / 2)
        angles = [angle, -angle + pi, angle + pi, -angle]
        radius = sqrt((rect.height / 2)**2 + (rect.width / 2)**2)

        # create a numpy array of the points
        points = np.array([
            [rect.x + radius * cos(angle + rot_radians), rect.y + radius * sin(angle + rot_radians)]
            for angle in angles
        ])

        # draw the polygon
        pygame.draw.polygon(surface, color, points)

        # return the vertices of the rectangle
        return points
Answered By: Emmet_v15
Categories: questions Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.