如何在Pygame中旋转一个三角形

3
我在pygame中有一个三角形。
triangle = pygame.draw.polygon(window, (210,180,140), [[x, y], [x -10, y -10], [x + 10, y - 10]], 5)

我需要朝鼠标旋转,就像这个gif图中心箭头一样:http://i.stack.imgur.com/yxsV1.gif。Pygame没有内置的旋转多边形的函数,所以我需要手动将三个点在圆上移动,使最下方的点[x,y]指向鼠标的坐标。 我拥有的变量是:
三角形中心和想要绕其旋转的圆之间的距离(即半径)
从中心到鼠标坐标的距离
三角形下部点的坐标[x,y]和其他两侧的坐标
有了这些信息,如何使用三角函数来旋转三角形的所有三条边,以便底部始终面向鼠标位置?
编辑:这是我目前已经完成的内容,但它只能沿对角线来回移动三角形,而无法旋转。
    def draw(self):
        curx,cury = cur
        #cur is a global var that is mouse coords
        angle = math.atan2(self.x - curx, self.y - cury)
        distance = math.sqrt(200 - (200 * math.cos(angle)))
        x = self.x + distance
        y = self.y + distance
        triangle = pygame.draw.polygon(window, (210,180,140), [[x, y], [x - 10,y - 10], [x + 10,y - 10]], 5)

你已经尝试过什么了吗?请在问题中包含你的代码。另外,也许快速谷歌一下“pygame将表面旋转到鼠标”会有所帮助。有很多教程可以参考。 - Roope
1
Pygame非常适用于旋转多边形的内置函数,您可以在此处找到有关其文档:https://www.pygame.org/docs/ref/transform.html - J. da Silva
1
可能是如何使用Pygame围绕其中心旋转图像?的重复问题。 - J. da Silva
1
你们都建议我使用 transform.rotate,但这只适用于 pygame surfaces,而不是 rect 样式的对象。如果有一种方法可以单独旋转 rects,我会非常乐意听取建议。 - user3150635
1个回答

3

编辑:今天早上我重新考虑了一下,因为多边形是三角形,所以还有另一种方法可以做到这一点。而且数学可能更容易理解,每个点的计算量也更少。

设Cx和Cy为内切于三角形的圆的圆心。我们可以使用参数方程描述一个圆的方程:

 F(t) = { x = Cx + r * cos(t)
        { y = Cy + r * sin(t)

这个公式中,r代表圆的半径,t表示沿圆的角度。

使用这个公式,我们可以用接触圆的点来描述三角形,本例中我们使用 t = { 0, 3 * pi / 4, 5 * pi / 4 } 作为我们的点。

我们还需要计算旋转三角形所需的角度,以便将在 t = (0) 时的点移到从 (Cx, Cy) 到鼠标位置的直线上。两个(标准化的)向量之间的夹角可以通过以下公式计算:

t = acos(v1 . v2) = acos(<x1, y1> . <x2, y2>) = acos(x1 * x2 + y1 * y2)

这里,.代表点积,acos是反余弦函数(arccoscos^-1)。

通过这两个方程,我们可以轻松创建一个Python函数,该函数接受三角形/圆的中心、圆的半径和鼠标位置,并返回表示三角形x-y坐标的元组列表。 (对于示例,中心和鼠标位置都是(x,y)格式的元组)

def get_points(center, radius, mouse_position):
    # calculate the normalized vector pointing from center to mouse_position
    length = math.hypot(mouse_position[0] - center[0], mouse_position[1] - center[1])
    # (note we only need the x component since y falls 
    # out of the dot product, so we won't bother to calculate y)
    angle_vector_x = (mouse_position[0] - center[0]) / length

    # calculate the angle between that vector and the x axis vector (aka <1,0> or i)
    angle = math.acos(angle_vector_x)

    # list of un-rotated point locations
    triangle = [0, (3 * math.pi / 4), (5 * math.pi / 4)]

    result = list()
    for t in triangle:
        # apply the circle formula
        x = center[0] + radius * math.cos(t + angle)
        y = center[1] + radius * math.sin(t + angle)
        result.append((x, y))

    return result

调用此函数的方式如下:
from pprint import pprint
center = (0,0)
radius = 10
mouse_position = (50, 50)
points = get_points(center, radius, mouse_position)
pprint(points)

产生:

[(7.071067811865475, 7.0710678118654755),
 (-10.0, 1.2246467991473533e-15),
 (-1.8369701987210296e-15, -10.0)]

这是三角形的三个点坐标(x,y)。

以下是原始方法,因为现代计算机图形系统(OpenGL,DirectX等)使用此方法。


绕任意多边形的重心旋转是通过三个不同的矩阵操作序列实现的,即将对象平移,使重心位于原点(0,0),应用旋转并返回到原始位置。

对于任意n-gon计算重心可能超出了这里的答案范围(谷歌将显示许多选项),但可以使用方格纸手工完成。将该点称为 C

为了简化操作并使所有变换都能使用简单的矩阵乘法应用,我们使用所谓的齐次坐标,其形式为:

    [ x ]
p = | y |
    [ 1 ]

对于2D坐标,请使用以下格式。
让我们用代码来说明。
    [ Cx ]
C = | Cy |
    [ 1  ]

翻译矩阵的一般形式为:

    [ 1  0  Vx ]
T = | 0  1  Vy |
    [ 0  0  1  ]

其中<Vx,Vy>表示翻译向量。由于翻译的目标是将质心C移动到原点,所以Vx = -Cx并且Vy = -Cy。反向翻译T'就是Vx = Cx,Vy = Cy

接下来需要旋转矩阵。设r为所需的顺时针旋转角度,R为旋转矩阵的一般形式。那么,

    [  cos(r)  sin(r)  0 ]
R = | -sin(r)  cos(r)  0 |
    [  0       0       1 ]

因此,最终变换矩阵如下:
       [ 1  0  -Cx ]   [  cos(r)  sin(r)  0 ]   [ 1  0  Cx ]
TRT' = | 0  1  -Cy | * | -sin(r)  cos(r)  0 | * | 0  1  Cy |
       [ 0  0   1  ]   [    0       0     1 ]   [ 0  0  1  ]

这可以简化为:

[ cos(r)  sin(r)  cos(r)*Cx-Cx+Cy*sin(r) ]
|-sin(r)  cos(r)  cos(r)*Cy-Cy-Cx*sin(r) |
[  0       0                1            ]

将此应用于点p = (x,y),我们得到以下方程:
p' = { x' =  Cx*cos(r)-Cx+Cy*sin(r)+x*cos(r)+y*sin(r)
     { y' = -Cx*sin(r)+Cy*cos(r)-Cy-x*sin(r)+y*cos(r)

在Python中:

def RotatePoint(c, p, r):
    x = c[0]*math.cos(r)-c[0]+c[1]*math.sin(r)+p[0]*math.cos(r)+p[1]*math.sin(r)
    y = -c[0]*math.sin(r)+c[1]*math.cos(r)-c[1]-p[0]*math.sin(r)+p[1]*math.cos(r)
    return (x, y)

在输入了所有内容后,我意识到您的对象可能已经居中于原点,此时上述函数简化为x=p[0]*math.cos(r)+p[1]*math.sin(r)y=p[0]*math.sin(r)+p[1]*math.cos(r)

我在这里对Wolfram Alpha有些信任,而不是手动算出所有内容。如果有人发现任何问题,请随意进行编辑。

所以为了澄清,rotatePoint() 接受的参数是 c = 三角形点,p = 鼠标点,r = 圆的半径?当你说“原点”时,你是指窗口的原点还是三角形的中心或其他什么? - user3150635
原点是画布上的任何位置(0,0)。c是多边形的质心,p是三角形上的任意一点(您必须转换所有三个点),r是旋转角度(顺时针方向)。 (在计算步骤中使用的相同名称也用于Python函数中。) - theB
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - user3150635
是的,我稍后会进行编辑以澄清,但 cp 应该是形如 (x, y) 的元组。 - theB
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - user3150635
@user3150635 我更新了答案,使用一种_希望_更易理解的方法。它可以绘制所有点都在圆上的多边形。如果您有任何不明白的地方,请告诉我,我会尝试澄清。 - theB

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接