如何在不使用rotate命令的情况下在Pygame中旋转曲面?

时间:2017-02-09 13:45:08

标签: python python-3.x rotation pygame

旋转代码是:

pygame.transform.rotate(surface, angle)

但我不想使用这些代码。我想写一个自己旋转表面的代码。我怎么能这样做?

2 个答案:

答案 0 :(得分:0)

有许多不同的方法可以解决这个问题,但我把它解释为它应该按度旋转图像,逆时针旋转并根据需要调整图像大小。我还添加了crop函数来消除过多的噪音。虽然,我的实现仍然存在问题,主要是aliased。要解决此问题,您可以查看this回答。

要旋转图像,首先需要将其转换为矩阵。这可以在pygame.surfarray.array3d的帮助下完成,但它要求您安装numpy。然后我们需要一个rotation matrix和一个新矩阵,我们将所有像素从图像复制到。将位置矢量与旋转矩阵相乘时,您将获得一个新的位置矢量。所以我们所做的只是迭代图像的x-y坐标,将旋转矩阵应用于每个坐标,然后在新矩阵中的新坐标处添加像素。

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()

答案 1 :(得分:-2)

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()