简体   繁体   English

如何在图像的比例变大时围绕其中心旋转图像(在 Pygame 中)

[英]How to rotate an image around its center while its scale is getting larger(in Pygame)

I loaded an image and I want it to rotate around its center, while its scale is getting larger.我加载了一个图像,我希望它围绕它的中心旋转,同时它的比例越来越大。 I know how to rotate an image around its center originally, but it's hard for me to calculate the position if the scale is getting larger.我知道如何最初围绕其中心旋转图像,但如果比例变大,我很难计算位置。 I tried it, but the image just 'dances', not staying in the center.我试过了,但图像只是“跳舞”,而不是停留在中心。

Short answer:简短的回答:

Store the center of the source image rectangle and update the center of the rotated and zoomed image rectangle after the rotation and zoom operation, by the stored center position.存储源图像矩形的中心,并根据存储的中心位置更新旋转和缩放操作后旋转和缩放的图像矩形的中心。 Rotate and zoom the image by pygame.transform.rotozoom() :通过pygame.transform.rotozoom()旋转和缩放图像:

def blitRotateCenter(surf, image, topleft, angle):
    
    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)

Long Answer:长答案:

An image ( pygame.Surface ) can be rotated by pygame.transform.rotate .图像( pygame.Surface )可以通过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 cause, 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).这是原因,因为旋转图像的边界矩形总是大于原始图像的边界矩形(除了某些旋转 90 度的倍数)。
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.这可以通过保留原始图像并将通过单个旋转操作生成的图像从原始图像“blit”来解决。

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.对于以下数学,使用pygame.math.Vector2 Note in screen coordinates the y points down the screen, but the mathematical y axis points form the bottom to the top.注意屏幕坐标中的 y 指向屏幕下方,但数学 y 轴点从底部到顶部。 This causes that the y axis has to be "flipped" during calculations这会导致在计算过程中必须“翻转”y 轴

Set up a list with the 4 corner points of the bounding box:用边界框的 4 个角点设置一个列表:

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 :通过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:对于 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)

To define a pivot on the original image, the "translation" of the pivot in relation to the upper left of the image has to be calculated and the "blit" position of the image has to be displaced by the translation.要在原始图像上定义枢轴,必须计算相对于图像左上角的枢轴的“平移”,并且图像的“位点”位置必须通过平移进行移位。

Define a pivot eg in the center of the image:定义一个枢轴,例如在图像的中心:

pivot = pygame.math.Vector2(w/2, -h/2)

Calculate the translation of the rotated pivot:计算旋转轴的平移:

pivot_rotate = pivot.rotate(angle)
pivot_move   = pivot_rotate - pivot

Finally calculate the origin of the rotated image:最后计算旋转图像的原点:

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)

If the image has to be additionally zoomed, then the zoom has to be taken into account when the origin of the image is calculated:如果必须对图像进行额外缩放,则在计算图像原点时必须考虑缩放:

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)

In the following example program, the function blitRotate does all the above steps and "blit" a rotated image to a surface.在以下示例程序中,函数blitRotate执行上述所有步骤并将旋转的图像“blit”到表面。 pos is the position of the image. pos是图像的位置。 originPos is the point on the image which is placed on pos and the pivot. originPos是图像上放置在pos和枢轴上的点。


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

import pygame

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

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

    # calcaulate the axis aligned bounding box of the rotated image
    w, h       = image.get_size()
    box        = [pygame.math.Vector2(p) for p in [(0, 0), (w, 0), (w, -h), (0, -h)]]
    box_rotate = [p.rotate(angle) for p in box]
    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 translation of the pivot 
    pivot        = pygame.math.Vector2(originPos[0], -originPos[1])
    pivot_rotate = pivot.rotate(angle)
    pivot_move   = pivot_rotate - pivot

    # calculate the upper left origin of the rotated image
    move   = (-originPos[0] + min_box[0] - pivot_move[0], -originPos[1] - max_box[1] + pivot_move[1])
    origin = (pos[0] + zoom * move[0], pos[1] + zoom * move[1])

    # get a rotated image
    rotozoom_image = pygame.transform.rotozoom(image, angle, zoom)
  
    # rotate and blit the image
    surf.blit(rotozoom_image, origin)

    # draw rectangle around the image
    pygame.draw.rect (surf, (255, 0, 0), (*origin, *rotozoom_image.get_size()),2)

try:
    image = pygame.image.load('AirPlaneFront1-128.png')
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()

start = False
angle, zoom = 0, 1
done = False
while not done:
    clock.tick(60)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            done = True
        elif event.type == pygame.KEYDOWN or event.type == pygame.MOUSEBUTTONDOWN:
            start = True

    pos = (screen.get_width()/2, screen.get_height()/2)
    
    screen.fill(0)
    blitRotate(screen, image, pos, (w/2, h/2), angle, zoom)
    if start:
        angle += 1
        zoom += 0.01
        if zoom > 5:
            zoom = 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()

You need to re-set the image's centre after it is scaled (& rotated) as both of these can change the image size.您需要在缩放(和旋转)后重新设置图像的中心,因为这两者都会改变图像大小。

# rotate and zoom the sprite
self.image = pygame.transform.rotozoom(self.original_image, self.angle, self.scale)
# reset it back to original centre
self.rect = self.image.get_rect(center=self.rect.center)

Another thing to consider is to ensure your image-content is centred within itself.要考虑的另一件事是确保您的图像内容以自身为中心。 (Imagine a rectangle with only content drawn to one-side) the rotation will be centred geometrically, but still look weird visually as it rotates. (想象一个只在一侧绘制内容的矩形)旋转将以几何为中心,但在旋转时在视觉上仍然看起来很奇怪。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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