简体   繁体   中英

how to rotate a triangle pygame

I have this triangle in pygame

triangle = pygame.draw.polygon(window, (210,180,140), [[x, y], [x -10, y -10], [x + 10, y - 10]], 5)

that i need to rotate towards the mouse, very much like the center arrow in this gif: http://i.stack.imgur.com/yxsV1.gif . Pygame doesn't have a built in function for rotating polygons, so I'll need to manually move the three points in a circle, with the lowermost point [x,y] pointing towards the coords of the mouse. The variables I have are:

the distance between the center of the triangle and the circle i want it to rotate along (ie the radius)

the distance from the center to the mouse coordinates

the coordinates of the lowermost point of the triangle [x,y] and the other two sides

with this information, how can I use trigonometry to rotate all three sides of the triangle so that the bottom point allways faces the mouse position?

EDIT: this is what I've got so far, but it only manages to move the triangle back and forth along a diagonal instead of rotating.

    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)

Edit: Thinking about this again this morning there's another way to do this since the polygon is a triangle. Also the math is potentially easier to understand, and it requires less calculation for each point.

Let Cx and Cy be the center of the circle inscribing the triangle. We can describe the equation of a circle using the parametric equation:

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

Where r is the radius of the circle, and t represents the angle along the circle.

Using this equation we can describe the triangle using the points that touch the circle, in this case we'll use t = { 0, 3 * pi / 4, 5 * pi / 4 } as our points.

We also need to calculate the angle that we need to rotate the triangle so that the point that was at t = (0) is on a line from (Cx, Cy) to the mouse location. The angle between two (normalized) vectors can be calculated by:

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

where . represents the dot product, and acos is the inverse cosine ( arccos or cos^-1 ).

From these two equations we can easily create a python function which, given the center of the triangle/circle, the radius of the circle, and the location of the mouse, returns a list of tuples representing the xy coordinates of the triangle. (For the example the center and mouse position are tuples of the form (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

Calling this function like this:

from pprint import pprint
center = (0,0)
radius = 10
mouse_position = (50, 50)
points = get_points(center, radius, mouse_position)
pprint(points)

produces:

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

which is the three points (x, y) of the triangle.

I'm going to leave the original method below, since it's the way that modern computer graphics systems (OpenGL, DirectX, etc.) do it.


Rotation about the centroid of a arbitrary polygon is a sequence of three distinct matrix operations, Translating the object so that the centroid is at the origin (0,0), applying a rotation, and translating back to the original position.

Calculating the centroid for an arbitrary n-gon is probably outside the scope of an answer here, (Google will reveal many options), but it could be done completely by hand using graph paper. Call that point C .

To simplify operations, and to enable all transformations to be applied using simple matrix multiplications, we use so called Homogeneous coordinates , which are of the form:

    [ x ]
p = | y |
    [ 1 ]

for 2d coordinates.

Let

    [ Cx ]
C = | Cy |
    [ 1  ]

The general form of the translation matrix is:

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

Where <Vx, Vy> represents the translation vector. Since the goal of the translation is to move the centroid C to the origin, Vx = -Cx and Vy = -Cy . The inverse translation T' is simply Vx = Cx, Vy = Cy

Next the rotation matrix is needed. Let r be the desired clockwise rotation angle, and R be the general form of the rotation matrix. Then,

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

The final transformation matrix is therefore:

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

Which simplifies to:

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

Applying this to a point p = (x,y) we obtain the following equation:

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)

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

After typing all that I realize that your object may already be centered on the origin, in which case the function above simplifies to x=p[0]*math.cos(r)+p[1]*math.sin(r) and y=p[0]*math.sin(r)+p[1]*math.cos(r)


I put some faith in Wolfram Alpha here, rather than multiplying everything out by hand. If anyone notices any issues, feel free to make the edit.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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