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

2023-12-04

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


简短回答:

存储源图像矩形的中心,并在旋转和缩放操作之后通过存储的中心位置更新旋转和缩放图像矩形的中心。旋转和缩放图像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)

长答案:

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

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

while not done:

    # [...]

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

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

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

angle = 0
while not done:

    # [...]

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

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

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

这可以通过比较来补偿轴对齐边界框旋转前和旋转后的图像。
对于下面的数学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)]]

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

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

通过将旋转框的最小值添加到该位置来计算图像左上点的“补偿”原点。对于 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)

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

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

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

计算旋转枢轴的平移:

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

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

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)

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

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)

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


Minimal example: 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()
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在图像尺寸变大时绕其中心旋转图像(在 Pygame 中) 的相关文章

  • ca 证书 Mac OS X

    我需要在emacs 上安装offlineimap 和mu4e 问题是配置 当我运行 Offlineimap 时 我得到 OfflineIMAP 6 5 5 Licensed under the GNU GPL v2 v2 or any la
  • Python GTK + webkit - 在 gtk.main() 之后插入 JavaScript

    我在终端中尝试了这个 一切正常 但是如果我在脚本内运行这个 我无法在 gtk main 之后插入 JavaScript import gtk import webkit w gtk Window b webkit WebView w add
  • 以矢量化方式在另一个 DataFrame 中查找包含值子集的行

    如何匹配此 DataFrame 中的值source car id lat lon 0 100 10 0 15 0 1 100 12 0 10 0 2 100 09 0 08 0 3 110 23 0 12 0 4 110 18 0 32 0
  • 最小二乘法拟合直线 python 代码

    我有一个由 X 和 Y 坐标组成的散点图 我想使用直线的最小二乘拟合来获得最佳拟合线 直线最小二乘拟合是指 如果 x 1 y 1 x n y n 是测量数据对 则最佳直线是y A Bx 这是我的Python代码 number of poin
  • 使用 pandas 将字符串对象转换为 int/float

    import pandas as pd path1 home supertramp Desktop 100 life 180 data csv mydf pd read csv path1 numcigar Never 0 1 5 Ciga
  • Paramiko SSHException 通道已关闭

    我一直在使用 Paramiko 在 Linux Windows 机器上发送命令 它可以很好地在 Ubuntu 机器上远程执行测试 但是 它不适用于 Windows 7 主机 以下是我收到的错误 def unit for event self
  • 如何在Python代码中查找列号

    简短问题 当按上述方式调用函数时 我可以找到行号here https stackoverflow com questions 3056048 filename and line number of python script 同样 如何找到
  • 张量流和线程

    下面是来自 Tensorflow 网站的简单 mnist 教程 即单层 softmax 我尝试通过多线程训练步骤对其进行扩展 from tensorflow examples tutorials mnist import input dat
  • Django 模型字段默认基于另一个模型字段

    我使用 Django Admin 构建一个管理站点 有两张表 一张是ModelA其中有数据 另一个是ModelB里面什么也没有 如果一个模型字段b b in ModelB为None 可以显示在网页上 值为ModelA的场a b 我不知道该怎
  • 返回上个月的日期时间对象

    如果 timedelta 在它的构造函数中有一个月份参数就好了 那么最简单的方法是什么 EDIT 正如下面指出的那样 我并没有认真考虑这一点 我真正想要的是上个月的任何一天 因为最终我只会获取年份和月份 因此 给定一个日期时间对象 返回的最
  • Pandas:将 pytz.FixedOffset 应用于系列

    我有一个带有timestamp列看起来像这样 0 2020 01 26 05 00 00 08 00 1 2020 01 26 06 00 00 08 00 Name timestamp dtype datetime64 ns pytz F
  • PyArmor - 打包为一个可执行文件

    当我执行此命令时 您好 使用 PyArmor pyarmor pack main py 它将它打包到一个名为的文件夹中dist里面包含我的 exe 以及许多 Python 扩展文件 据我所知 PyArmor 使用 PyInstaller 来
  • 用于多个窗口的 Tkinter 示例代码,为什么按钮无法正确加载?

    我正在编写一个程序 应该 按一下按钮即可打开一个窗口 按另一个按钮关闭新打开的窗口 我使用类 以便稍后可以将代码插入到更大的程序中 但是 我无法正确加载按钮 import tkinter as tk class Demo1 tk Frame
  • 线性同余生成器 - 如何选择种子和统计检验

    我需要做一个线性同余生成器 它将成功通过所选的统计测试 我的问题是 如何正确选择发电机的数字以及 我应该选择哪些统计检验 我想 均匀性的卡方频率测试 每代收集10 000个号码的方法 将 0 1 细分为10个相等的细分 柯尔莫哥洛夫 斯米尔
  • 在python中读取PASCAL VOC注释

    我在 xml 文件中有注释 例如这个 它遵循 PASCAL VOC 约定
  • Python 导入非常慢 - Anaconda python 2.7

    我的 python import 语句变得非常慢 我使用 Anaconda 包在本地运行 python 2 7 导入模块后 我编写的代码运行得非常快 似乎只是导入需要很长时间 例如 我使用以下代码运行了一个 tester py 文件 imp
  • 在 HDF5 (PyTables) 中存储 numpy 稀疏矩阵

    我在使用 PyTables 存储 numpy csr matrix 时遇到问题 我收到此错误 TypeError objects of type csr matrix are not supported in this context so
  • Streamlabs API 405 响应代码

    我正在尝试使用Streamlabs API https dev streamlabs com Streamlabs API 使用 Oauth2 来创建应用程序 因此 首先我将使用我的应用程序的用户发送到一个授权链接 其中包含我的应用程序的客
  • 在父类中访问子类变量

    我有一个父类和一个继承的子类 我想知道如何访问我的父类中的子类变量 我尝试了这个但失败了 class Parent object def init self print x class Child Parent x 1 x Child Er
  • 如何使用 Python 3 正确显示倒计时日期

    我正在尝试获取将显示的倒计时 基本上就像一个世界末日时钟哈哈 有人可以帮忙吗 import os import sys import time import datetime def timer endTime datetime datet

随机推荐