Rotate tetris blocks at runtime

2019-09-06 05:54发布

问题:

I have a class tetronimo (a tetris block) that has four QRect types (named first, second, third, fourth respectively). I draw each tetronimo using a build_tetronimo_L type functions.

These build the tetronimo in a certain direction, but as in tetris you're supposed to be able to rotate the tetronimo's, I'm trying to rotate a tetronimo by rotating each individual square of the tetronimo.

I have found the following formula to apply to each (x, y) coordinate of a particular square.

newx = cos(angle) * oldx - sin(angle) * oldy

newy = sin(angle) * oldx + cos(angle) * oldy

Now, the QRect type of Qt, does only seem to have a setCoords function that takes the (x, y) coordinates of top-left and bottom-right points of the respective square.

I have here an example (which doesn't seem to produce the correct result) of rotating the first two squares in my tetronimo.

Can anyone tell me how I'm supposed to rotate these squares correctly, using runtime rotation calculation?

void tetromino::rotate(double angle) // angle in degrees
{
    std::map<std::string, rect_coords> coords = get_coordinates();
        // FIRST SQUARE
    rect_coords first_coords = coords["first"];

    //top left x and y
    int newx_first_tl = (cos(to_radians(angle)) * first_coords.top_left_x) - (sin(to_radians(angle)) * first_coords.top_left_y);
    int newy_first_tl = (sin(to_radians(angle)) * first_coords.top_left_x) + (cos(to_radians(angle)) * first_coords.top_left_y);

    //bottom right x and y
    int newx_first_bl = (cos(to_radians(angle)) * first_coords.bottom_right_x) - (sin(to_radians(angle)) * first_coords.bottom_right_y);
    int newy_first_bl = (cos(to_radians(angle)) * first_coords.bottom_right_x) + (sin(to_radians(angle)) * first_coords.bottom_right_y);

        //CHANGE COORDINATES
    first->setCoords( newx_first_tl, newy_first_tl, newx_first_tl + tetro_size,newy_first_tl - tetro_size);


        //SECOND SQUARE
    rect_coords second_coords = coords["second"];

    int newx_second_tl = (cos(to_radians(angle)) * second_coords.top_left_x) - (sin(to_radians(angle)) * second_coords.top_left_y);
    int newy_second_tl = (sin(to_radians(angle)) * second_coords.top_left_x) + (cos(to_radians(angle)) * second_coords.top_left_y);

        //CHANGE COORDINATES 
    second->setCoords(newx_second_tl, newy_second_tl, newx_second_tl - tetro_size, newy_second_tl + tetro_size);

first and second are QRect types. rect_coords is just a struct with four ints in it, that store the coordinates of the squares.

The first square and second square calculations are different, as I was playing around trying to figure it out.

I hope someone can help me figure this out?

(Yes, I can do this much simpler, but I'm trying to learn from this)

回答1:

It seems more like a math question than a programming question. Just plug in values like 90 degrees for the angle to figure this out. For 90 degrees, a point (x,y) is mapped to (-y, x). You probably don't want to rotate around the origin but around a certain pivot point c.x, c.y. For that you need to translate first, then rotate, then translate back:

(x,y) := (x-c.x, y-c.y) // translate into coo system w/ origin at c
(x,y) := (-y, x)        // rotate
(x,y) := (x+c.x, y+c.y) // translate into original coo system


回答2:

Before rotating you have to translate so that the piece is centered in the origin:

  1. Translate your block centering it to 0, 0
  2. Rotate the block
  3. Translate again the center of the block to x, y

If you rotate without translating you will rotate always around 0, 0 but since the block is not centered it will be rotated around the center. To center your block is quite simple:

  1. For each point, compute the median of X and Y, let's call it m
  2. Subtract m.X and m.Y to the coordinates of all points
  3. Rotate
  4. Add again m.X and m.Y to points.

Of course you can use linear algebra and vector * matrix multiplication but maybe it is too much :)


Translation

Let's say we have a segment with coordinates A(3,5) B(10,15). If you want to rotate it around its center, we first translate it to our origin. Let's compute mx and my:

mx = (10 - 3) / 2
my = (15 - 5) / 2

Now we compute points A1 and B1 translating the segment so it is centered to the origin:

A1(A.X - mx, A.Y - my)
B1(B.X - mx, B.Y - my)

Now we can perform our rotation of A1 and B1 (you know how). Then we have to translate again to the original position:

A = (rotatedA1.X + mx, rotatedA1.y + my)
B = (rotatedB1.X + mx, rotatedB1.y + my)

If instead of having two points you have n points you have of course do everything for n points.



回答3:

You could use Qt Graphics View which does all the geometric calculations for you.

Or are you just wanting to learn basic linear geometrical transformations? Then reading a math textbook would probably be more appropriate than coding.