简体   繁体   中英

Rotating a triangle around a fixed point

I'm trying to recreate the game Asteroids and I'm currently getting the ship to display and move on the screen. At the moment I've got the function below which draws a triangle on the screen around an origin. What I'm trying to do is rotate the ship around the origin too.

At the moment the triangle gets completely warped by this function and I'm not sure what's going wrong. The angle I'm trying to rotate around is shipHeading and I'm trying to follow this formula:

x' = x cos f - y sin f y' = y cos f + x sin f

where f is equal to the angle of rotation

void drawShip(void){
    int shipX = getShipX();
    int shipY = getShipY();
    int shipHeading = getShipHeading();

    int x1, y1, x2, y2, x3, y3;

    x1 = shipX;
    y1 = shipY - 20;
    x2 = shipX - 10;
    y2 = shipY + 10;
    x3 = shipX + 10;
    y3 = shipY + 10;

    screen->drawTriangle(x1, y1, x2, y2, x3, y3, RED); //pre rotation

    x1 = (x1 * cos(double(shipHeading))) - (y1 * sin(double(shipHeading)));
    y1 = (x1 * sin(double(shipHeading))) + (y1 * cos(double(shipHeading)));
    x2 = (x2 * cos(double(shipHeading))) - (y2 * sin(double(shipHeading)));
    y2 = (x2 * sin(double(shipHeading))) + (y2 * cos(double(shipHeading)));
    x3 = (x3 * cos(double(shipHeading))) - (y3 * sin(double(shipHeading)));
    y3 = (x3 * sin(double(shipHeading))) + (y3 * cos(double(shipHeading)));

    x1 *= -1;
    y1 *= -1;
    x2 *= -1;
    y2 *= -1;
    x3 *= -1;
    y3 *= -1;

    screen->drawTriangle(x1, y1, x2, y2, x3, y3, BLUE); //post rotation

The issue seems to be related to overwriting x1, y1, ... during the rotation. Introduce post transformation versions of your coordinates and it might work better.

A few problems I see in your math:

  1. You are rotating your points while you are changing them. This means that while the X may be computed correctly, the computation of the new Y will be using the new X instead of the original value of X . That is wrong, and you need to save the old point when computing the new point. See an example of doing this in rotatePoint() below.
  2. You need to specify a pivot point when rotating. Again, refer to rotatePoint to see how this is done. This is based on the algorithm posted here .
  3. cos() and sin() expect the angle to be in radian units. Not sure if you are already taking this into account with shipHeading , but it seems unlikely since it is an int . In my solution, I assume shipHeading is in degrees and convert it to radians before invoking the trig functions.
  4. Your tranlation idea of multiplying the points by -1 seems strange, and would result in a vary chaotic looking flip. In asteroids, this should be adding an int to the points based on current speed, and acceleration.

The step in creating an algorithm is encapsulation. Do not try to do everything in one function with all ints. Create abstract data types like Point to represent an (x,y) pair.

typedef struct {
          int x;
          int y;
        } Point;

Then create another abstract data type called Ship to represent a ship that is made up of three points.

typedef struct {
           Point top;
           Point left;
           Point right;
        } Ship;

Next, create a helper function to abstract rotating points around a pivot point. Then create a high level function that rotates the ship around a pivot point, that calls the roatePoint function to rotate each of its three points.

By abstracting the details, the algorithm becomes clear. It also means that bug fixes show up in one place, instead of many. For example, cos() and sin() expect the rotation to be expressed in Radians, but we typically code thinking in terms of degrees of rotation. By isolating the rotation into PivotPoint(), all the math is located in just this one place.

Here is my solution, with several of the missing functions stubbed:

#include <stdio.h>
#include <math.h>


typedef struct {
          int x;
          int y;
        } Point;

typedef struct {
           Point top;
           Point left;
           Point right;
        } Ship;

// See https://stackoverflow.com/a/2259502/6693299 for point rotation algorithm
Point rotatePoint(Point pivotPoint, Point point, int shipHeading)
{
 Point newPoint = {0};

 // Translate point back to the origin
 point.x -= pivotPoint.x;
 point.y -= pivotPoint.y;

 // Cos and Sin expect angle in radians, but let's use shipHeading in degrees
 // To convert shipHeading in degrees to radians, multiply by 0.0174533
 double radians = ((double) shipHeading) * 0.0174533;

 // Rotate Point 
 // Compute new point based on old point and shipHeading
 newPoint.x = (point.x * cos(radians)) - (point.y * sin(radians));
 newPoint.y = (point.x * sin(radians)) + (point.y * cos(radians));

 // Translate the point back 
 newPoint.x += pivotPoint.x;
 newPoint.y += pivotPoint.y;

 return newPoint;
}

Ship rotateShip(Ship* ship, int shipHeading)
{
 Ship newPosition = {0};

 // Rotate ship by shipHeading degrees, centered around top
 newPosition.top   = rotatePoint(ship->top, ship->top,   shipHeading);
 newPosition.left  = rotatePoint(ship->top, ship->left,  shipHeading);
 newPosition.right = rotatePoint(ship->top, ship->right, shipHeading);

 return newPosition;
}

Point newPoint(int x, int y)
{
 Point point = {0};
 point.x = x;
 point.y = y;
 return point;
}

Ship newShip(int shipX, int shipY)
{
 Ship newShip    = {0};
 newShip.top     = newPoint(shipX, shipY);
 newShip.left    = newPoint(shipX + 10, shipY - 5);
 newShip.right   = newPoint(shipX + 10, shipY + 5);
 return newShip;
}

// stubs
int getShipX(void) { return 10;}
int getShipY(void) { return 10;}
int getShipHeading(void) {return 90;} // Expressed in degrees

void drawTriangle(int x1, int y1, int x2, int y2, int x3, 
{
 // stub
 printf("Ship location: (%d,%d) (%d,%d) (%d,%d)\n", x1,y1, x2,y2, x3,y3);
}

void drawShip(void){
    Ship ship        = newShip(getShipX(), getShipY());
    int  shipHeading = getShipHeading();
    int  RED         = 0; // stub

    drawTriangle(ship.top.x,   ship.top.y,
                 ship.left.x,  ship.left.y,
                 ship.right.x, ship.right.y, RED); //pre rotation

    ship = rotateShip(&ship, shipHeading);

#if 0
    // Multiplying by -1 will Flip the ship? 
    // Use += instead to move the ship up by one 
    // Better to use a function like moveShip()
    ship.top.x   += -1;
    ship.top.y   += -1;
    ship.left.x  += -1;
    ship.left.y  += -1;
    ship.right.x += -1;
    ship.right.y += -1;
#endif

    drawTriangle(ship.top.x,   ship.top.y,
                 ship.left.x,  ship.left.y,
                 ship.right.x, ship.right.y, RED); //pre rotation
}

int main(int argc, char** argv)
{
 drawShip();
}

The output first displays the ship's three points (top) (left) (right) which I default to (10,10) (20,5) (20,15) using X windows notation (X,Y) where the top left portion of the screen is (0,0). The second line of output are the points after a 90 degree rotate around the top point (10,10). You can pick any pivot point. For more on Point Rotation, refer to this stackOverflow article.

scott> gcc asteroids.c -lm
scott> a.out
Ship location: (10,10) (20,5) (20,15)
Ship location: (10,10) (14,20) (5,19)
scott> 

Hope this helps as a starting point. Make changes to fit into your framework.

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