如何在图像尺寸变大时绕其中心旋转图像(在 Pygame 中)

jwo*_*in9 2 python pygame image rotation scale

我加载了一张图像,我希望它绕其中心旋转,同时其比例越来越大。我最初知道如何围绕图像的中心旋转图像,但如果比例变大,我很难计算位置。我尝试过,但图像只是“跳舞”,而不是停留在中心。

Rab*_*d76 5

简短回答:

存储源图像矩形的中心,并在旋转和缩放操作之后通过存储的中心位置更新旋转和缩放图像矩形的中心。通过以下方式旋转和缩放图像pygame.transform.rotozoom()

def blitRotateCenter(surf, image, topleft, angle, scale):
    
    center = image.get_rect(topleft = topleft).center
    rotated_image = pygame.transform.rotozoom(image, angle, scale)
    new_rect = rotated_image.get_rect(center = center)

    surf.blit(rotated_image, new_rect.topleft)
Run Code Online (Sandbox Code Playgroud)

长答案:

图像 ( pygame.Surface) 可以旋转pygame.transform.rotate

如果在循环中逐步完成此操作,则图像会扭曲并迅速增大:

while not done:

    # [...]

    image = pygame.transform.rotate(image, 1)
    screen.blit(image, pos)
    pygame.display.flip()
Run Code Online (Sandbox Code Playgroud)

这是因为旋转图像的边界矩形总是大于原始图像的边界矩形(除了一些 90 度倍数的旋转)。
由于多重副本,图像会变形。每次旋转都会产生一个小误差(不准确)。误差总和不断增大,图像逐渐衰减。

这可以通过保留原始图像并“位块传输”由原始图像的单个旋转操作生成的图像来解决。

angle = 0
while not done:

    # [...]

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

    screen.blit(rotated_image, pos)
    pygame.display.flip()
Run Code Online (Sandbox Code Playgroud)

现在图像似乎可以任意改变其位置,因为图像的大小通过旋转而改变,并且原点始终是图像边界矩形的左上角。

这可以通过比较旋转之前和旋转之后图像的轴对齐边界框来补偿。使用
以下数学。pygame.math.Vector2请注意,在屏幕坐标中,y 指向屏幕下方,但数学 y 轴点从底部到顶部。这导致计算时必须“翻转”y轴

设置一个包含边界框 4 个角点的列表:

w, h = image.get_size()
box = [pygame.math.Vector2(p) for p in [(0, 0), (w, 0), (w, -h), (0, -h)]]
Run Code Online (Sandbox Code Playgroud)

将矢量旋转到角点pygame.math.Vector2.rotate

box_rotate = [p.rotate(angle) for p in box]
Run Code Online (Sandbox Code Playgroud)

获取旋转点的最小值和最大值:

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])
Run Code Online (Sandbox Code Playgroud)

通过将旋转框的最小值添加到该位置来计算图像左上点的“补偿”原点。因为 y 坐标max_box[1]是最小值,因为沿 y 轴“翻转”:

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

rotated_image = pygame.transform.rotate(image, angle)
screen.blit(rotated_image, origin)
Run Code Online (Sandbox Code Playgroud)

要在原始图像上定义枢轴,必须计算枢轴相对于图像左上方的“平移”,并且必须通过该平移来移动图像的“blit”位置。

定义一个枢轴,例如在图像的中心:

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

rotated_image = pygame.transform.rotate(image, angle)
screen.blit(rotated_image, origin)
Run Code Online (Sandbox Code Playgroud)

计算旋转枢轴的平移:

pivot = pygame.math.Vector2(w/2, -h/2)
Run Code Online (Sandbox Code Playgroud)

最后计算旋转图像的原点:

pivot_rotate = pivot.rotate(angle)
pivot_move   = pivot_rotate - pivot
Run Code Online (Sandbox Code Playgroud)

如果图像必须额外缩放,则在计算图像原点时必须考虑缩放:

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

rotated_image = pygame.transform.rotate(image, angle)
screen.blit(rotated_image, origin)
Run Code Online (Sandbox Code Playgroud)

在下面的示例程序中,该函数blitRotate执行上述所有步骤并将旋转图像“blit”到表面。pos是图像的位置。originPos是图像上放置的点pos和枢轴。


最小的例子: repl.it/@Rabbid76/PyGame-RotateAroundPivotAndZoom

move   = (-pivot[0] + min_box[0] - pivot_move[0], pivot[1] - max_box[1] + pivot_move[1])
origin = (pos[0] + zoom * move[0], pos[1] + zoom * move[1])

rotozoom_image = pygame.transform.rotozoom(image, angle, zoom)
screen.blit(rotozoom_image, origin)
Run Code Online (Sandbox Code Playgroud)