Rotating a rectangle (not image) in pygame
Asked Answered
M

10

13

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.

Making answered 8/4, 2016 at 23:15 Comment(4)
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.Oldfashioned
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 consequencesMaking
@Oldfashioned 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 wayMaking
The question is duplicate, because you cannot rotate a rectangle. You have to create a transparent Surface and rotate the Surface.Enrica
D
7

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.

Deyoung answered 9/4, 2016 at 3:31 Comment(0)
S
9
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()  

OUTPUT:

https://www.youtube.com/watch?v=eQxdYb_iKU4&t=2s

Scientism answered 17/7, 2018 at 12:27 Comment(0)
D
7

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.

Deyoung answered 9/4, 2016 at 3:31 Comment(0)
A
4

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

Ambert answered 26/9, 2022 at 14:32 Comment(0)
V
1

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) )
        
Vc answered 19/3, 2021 at 22:58 Comment(0)
E
1

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.

Enrica answered 21/3, 2021 at 21:31 Comment(0)
K
1

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)
Klemens answered 8/7, 2022 at 5:55 Comment(0)
V
0

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]) )
Vc answered 18/3, 2021 at 9:23 Comment(0)
P
0

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

Pint answered 18/11, 2021 at 14:20 Comment(0)
T
0

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
Traver answered 17/2, 2023 at 23:14 Comment(0)
G
0

You can rotate a polygon using a rotation matrix in numpy:

# create polygon (a rectangle)
polygon = np.array([(10,10),(10,-10),(-10,-10),(-10,10)])

# angle to rotate
angle = math.radians(45)  # convert from degrees

# create rotation matrix
sin, cos = np.sin(angle), np.cos(angle)
rotation_matrix = np.array([(cos, -sin),(sin, cos)])

# apply rotation matrix
rotated_polygon = polygon @ rotation_matrix

# draw result
pygame.draw.polygon(window, (0,255,0), rotated_polygon.tolist())

Pygame won't accept a numpy array, but the numpy tolist() method converts it.

If you want to move the polygon to another location in the window:

location = (50,50)
pygame.draw.polygon(window, (0,255,0), (rotated_polygon+location).tolist())
Gaillard answered 14/9, 2023 at 1:56 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.