# How do I rotate an image around its center using Pygame?

Get the rectangle of the original image and set the position. Get the rectangle of the rotated image and set the center position through the center of the original rectangle. Return a tuple of the rotated image and the rectangle:

``````def rot_center(image, angle, x, y):

rotated_image = pygame.transform.rotate(image, angle)
new_rect = rotated_image.get_rect(center = image.get_rect(center = (x, y)).center)

return rotated_image, new_rect
``````

Or write a function which rotates and `.blit` the image:

``````def blitRotateCenter(surf, image, topleft, angle):

rotated_image = pygame.transform.rotate(image, angle)
new_rect = rotated_image.get_rect(center = image.get_rect(topleft = topleft).center)

surf.blit(rotated_image, new_rect)
``````

An image (`pygame.Surface`) can be rotated by `pygame.transform.rotate`.

If that is done progressively in a loop, then the image gets distorted and rapidly increases:

``````while not done:

# [...]

image = pygame.transform.rotate(image, 1)
screen.blit(image, pos)
pygame.display.flip()
``````

This is because the bounding rectangle of a rotated image is always greater than the bounding rectangle of the original image (except some rotations by multiples of 90 degrees).
The image gets distort because of the multiply copies. Each rotation generates a small error (inaccuracy). The sum of the errors is growing and the images decays.

That can be fixed by keeping the original image and “blit” an image which was generated by a single rotation operation form the original image.

``````angle = 0
while not done:

# [...]

rotated_image = pygame.transform.rotate(image, angle)
angle += 1

screen.blit(rotated_image, pos)
pygame.display.flip()
``````

Now the image seems to arbitrary change its position, because the size of the image changes by the rotation and origin is always the top left of the bounding rectangle of the image.

This can be compensated by comparing the axis aligned bounding box of the image before the rotation and after the rotation.
For the following math `pygame.math.Vector2` is used. Note in screen coordinates the y points down the screen, but the mathematical y axis points form the bottom to the top. This causes that the y axis has to be “flipped” during calculations

Set up a list with the 4 corner points of the bounding box:

``````w, h = image.get_size()
box = [pygame.math.Vector2(p) for p in [(0, 0), (w, 0), (w, -h), (0, -h)]]
``````

Rotate the vectors to the corner points by `pygame.math.Vector2.rotate`:

``````box_rotate = [p.rotate(angle) for p in box]
``````

Get the minimum and the maximum of the rotated points:

``````min_box = (min(box_rotate, key=lambda p: p[0])[0], min(box_rotate, key=lambda p: p[1])[1])
max_box = (max(box_rotate, key=lambda p: p[0])[0], max(box_rotate, key=lambda p: p[1])[1])
``````

Calculate the “compensated” origin of the upper left point of the image by adding the minimum of the rotated box to the position. For the y coordinate `max_box[1]` is the minimum, because of the “flipping” along the y axis:

``````origin = (pos[0] + min_box[0], pos[1] - max_box[1])

rotated_image = pygame.transform.rotate(image, angle)
screen.blit(rotated_image, origin)
``````

It is even possible to define a pivot on the original image. Compute the offset vector from the center of the image to the pivot and rotate the vector. A vector can be represented by `pygame.math.Vector2` and can be rotated with `pygame.math.Vector2.rotate`. Notice that `pygame.math.Vector2.rotate` rotates in the opposite direction than `pygame.transform.rotate`. Therefore the angle has to be inverted:

Compute the vector from the center of the image to the pivot:

``````image_rect = image.get_rect(topleft = (pos[0] - originPos[0], pos[1]-originPos[1]))
offset_center_to_pivot = pygame.math.Vector2(pos) - image_rect.center
``````

Rotate the vector

``````rotated_offset = offset_center_to_pivot.rotate(-angle)
``````

Calculate the center of the rotated image:

``````rotated_image_center = (pos[0] - rotated_offset.x, pos[1] - rotated_offset.y)
``````

Rotate and blit the image:

``````rotated_image = pygame.transform.rotate(image, angle)
rotated_image_rect = rotated_image.get_rect(center = rotated_image_center)

screen.blit(rotated_image, rotated_image_rect)
``````

In the following example program, the function `blitRotate(surf, image, pos, originPos, angle)` does all the above steps and “blit” a rotated image to a surface.

• `surf` is the target Surface

• `image` is the Surface which has to be rotated and `blit`

• `pos` is the position of the pivot on the target Surface `surf` (relative to the top left of `surf`)

• `originPos` is position of the pivot on the `image` Surface (relative to the top left of `image`)

• `angle` is the angle of rotation in degrees

This means, the 2nd argument (`pos`) of `blitRotate` is the position of the pivot point in the window and the 3rd argument (`originPos`) is the position of the pivot point on the rotating Surface:

Minimal example: repl.it/@Rabbid76/PyGame-RotateAroundPivot

``````import pygame

pygame.init()
screen = pygame.display.set_mode((300, 300))
clock = pygame.time.Clock()

def blitRotate(surf, image, pos, originPos, angle):

# offset from pivot to center
image_rect = image.get_rect(topleft = (pos[0] - originPos[0], pos[1]-originPos[1]))
offset_center_to_pivot = pygame.math.Vector2(pos) - image_rect.center

# roatated offset from pivot to center
rotated_offset = offset_center_to_pivot.rotate(-angle)

# roatetd image center
rotated_image_center = (pos[0] - rotated_offset.x, pos[1] - rotated_offset.y)

# get a rotated image
rotated_image = pygame.transform.rotate(image, angle)
rotated_image_rect = rotated_image.get_rect(center = rotated_image_center)

# rotate and blit the image
surf.blit(rotated_image, rotated_image_rect)

# draw rectangle around the image
pygame.draw.rect(surf, (255, 0, 0), (*rotated_image_rect.topleft, *rotated_image.get_size()),2)

def blitRotate2(surf, image, topleft, angle):

rotated_image = pygame.transform.rotate(image, angle)
new_rect = rotated_image.get_rect(center = image.get_rect(topleft = topleft).center)

surf.blit(rotated_image, new_rect.topleft)
pygame.draw.rect(surf, (255, 0, 0), new_rect, 2)

try:
except:
text = pygame.font.SysFont('Times New Roman', 50).render('image', False, (255, 255, 0))
image = pygame.Surface((text.get_width()+1, text.get_height()+1))
pygame.draw.rect(image, (0, 0, 255), (1, 1, *text.get_size()))
image.blit(text, (1, 1))
w, h = image.get_size()

angle = 0
done = False
while not done:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True

pos = (screen.get_width()/2, screen.get_height()/2)

screen.fill(0)
blitRotate(screen, image, pos, (w/2, h/2), angle)
#blitRotate2(screen, image, pos, angle)
angle += 1

pygame.draw.line(screen, (0, 255, 0), (pos[0]-20, pos[1]), (pos[0]+20, pos[1]), 3)
pygame.draw.line(screen, (0, 255, 0), (pos[0], pos[1]-20), (pos[0], pos[1]+20), 3)
pygame.draw.circle(screen, (0, 255, 0), pos, 7, 0)

pygame.display.flip()

pygame.quit()
exit()
``````