简体   繁体   中英

How can I rotate my surface in Pygame without using the rotate command?

The code for rotating is:

pygame.transform.rotate(surface, angle)

But I don't want to use the code. I want to write a code for rotating the surface myself. How can I do that?

There are many different ways to tackle the problem, but I've interpreted it as it should rotate the image by degrees, counter-clockwise and resize the image as needed. I also included a crop function to get rid off excessive noise. Although, there are still problems with my implementation, mainly that it's aliased . To fix it you can take look at this answer.

To rotate an image you first need to convert it to an matrix. This can be done with the help of pygame.surfarray.array3d , but it requires you to have numpy installed. Then we need a rotation matrix and a new matrix which we'll copy all the pixels from our image to. When multiplying a position vector with the rotation matrix you'll get a new position vector. So all we're doing is iterating through the xy coordinates of the image, applying the rotation matrix to each coordinate and then add the pixel at then new coordinate in our new matrix.

import numpy as np
from math import sin, cos, radians
import pygame
pygame.init()

SIZE = WIDTH, HEIGHT = 720, 480
FPS = 30
screen = pygame.display.set_mode(SIZE)
clock = pygame.time.Clock()


def crop(array, background=(0, 0, 0)):
    rows, cols, _ = array.shape
    left, right, top, bottom = 0, cols, 0, rows

    for row in range(rows):
        if not np.all(array[row] == background):
            top = row
            break
    for row in range(rows - 1, top, -1):
        if not np.all(array[row] == background):
            bottom = row
            break

    np.transpose(array, axes=(1, 0, 2))

    for col in range(cols):
        if not np.all(array[col] == background):
            left = col
            break
    for col in range(cols - 1, left, -1):
        if not np.all(array[col] == background):
            right = col
            break

    np.transpose(array, axes=(1, 0, 2))

    return array[top:bottom+1, left:right+1]


def rotate(surface, angle):
    rect = surface.get_rect()
    width, height = surface.get_size()
    image_array = pygame.surfarray.array3d(surface)

    angle = radians(angle)

    # Rotation matrix: https://en.wikipedia.org/wiki/Rotation_matrix
    rotation = np.array(
        ((cos(angle), -sin(angle)),
         (sin(angle), cos(angle))
         ), dtype=np.float16
    )

    # Rotates the corners of the image because the distance between 2 opposite corners will always be the furthest
    # distance. This helps us calculate the new width and height of our new rotated image.
    y_list, x_list = zip(*(
        (position @ rotation) for position in (rect.topleft, rect.topright, rect.bottomleft, rect.bottomright)
    ))
    min_x, min_y = min(x_list), min(y_list)
    new_width = int(abs(min_x - max(x_list))) + 1
    new_height = int(abs(min_y - max(y_list))) + 1

    # Since we're rotating the image at the topleft corner and not in the center it'll be moved. By adding the offset
    # we just move it back in place.
    offset = -int(min_y), -int(min_x)

    rotated_image_array = np.zeros(shape=(new_height, new_width, 3), dtype=np.uint8)

    for row in range(height):  # Not the newly calculated height, but the original image's height.
        for col in range(width):
            y, x = (row, col) @ rotation + offset
            rotated_image_array[int(y), int(x)] = image_array[row, col]

    rotated_image_array = crop(rotated_image_array)

    return pygame.surfarray.make_surface(rotated_image_array)


def main():
    running = True
    original_image = pygame.Surface((200, 200))
    original_image.fill(pygame.Color('red'))
    image = original_image
    while running:

        clock.tick(FPS)

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_ESCAPE:
                    running = False
                elif event.key == pygame.K_1:
                    print('Reset image.')
                    image = original_image
                elif event.key == pygame.K_2:
                    print('Starting rotating.')
                    time = pygame.time.get_ticks()
                    image = rotate(image, 20)
                    time = pygame.time.get_ticks() - time
                    print('Finished rotating in {:.4E} s.'.format(time / 1000))

        screen.fill((255, 255, 255))
        screen.blit(image, image.get_rect(center=(WIDTH // 2, HEIGHT // 2)))
        pygame.display.update()


if __name__ == '__main__':
    main()
import pygame

SIZE = 1000, 900

pygame.init()

screen = pygame.display.set_mode(SIZE)

a=0

done = False

screen.fill((0, 0, 0))

other1 = pygame.image.load("image.jpg")

screen.blit(other1, (0, 0))

while not done:

    for event in pygame.event.get():

        if event.type == pygame.KEYDOWN:

            if event.key == pygame.K_LEFT:

                a=a+90

                other2 = pygame.transform.rotate(other1, a)

                screen.blit(other2, (0, 0))

            if event.key == pygame.K_RIGHT:

                a=a-90

                other2 = pygame.transform.rotate(other1, a)

                screen.blit(other2, (0, 0))

    screen.fill((0,0,0))

    pygame.display.flip()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM