How to rotate a set of vertices around a pivot

Asked

Viewed 169 times

3

I have a set of vertices that I need to rotate at an angle Θ around a pivot, but when I apply the transformation the vertices distort. What’s wrong? I got the answer formula for a similar question in this post

Here is my code:


#include <SFML/Graphics.hpp>
#include <cmath>
#define PI 3.14159265359f

using namespace std;

sf::VertexArray vertexRotate(sf::VertexArray &toRotate, float theta, sf::Vector2f &pivot);
sf::VertexArray vertexTranslate(sf::VertexArray &toTranslate, float x, float y);

sf::VertexArray vertexRotate(sf::VertexArray &toRotate, float theta, sf::Vector2f &pivot = sf::Vector2f(0,0)) {
    sf::VertexArray vertexCopy(toRotate);
    for (int i = 0; i < vertexCopy.getVertexCount(); i++) {
        vertexCopy[i].position.x = pivot.x + (vertexCopy[i].position.x - pivot.x) * cos(theta) - (vertexCopy[i].position.y - pivot.y) * sin(theta);
        vertexCopy[i].position.y = pivot.y + (vertexCopy[i].position.x - pivot.x) * sin(theta) + (vertexCopy[i].position.y - pivot.y) * cos(theta);
    }
    return vertexCopy;
}

sf::VertexArray vertexTranslate(sf::VertexArray &toTranslate, float x, float y) {
    sf::VertexArray vertexCopy(toTranslate);
    for (int i = 0; i < vertexCopy.getVertexCount(); i++) {
        vertexCopy[i].position.x += x;
        vertexCopy[i].position.y += y;
    }
    return vertexCopy;
}

int main()
{   
    sf::RenderWindow window(sf::VideoMode(800, 600), "Linear Algebra");
    sf::VertexArray nave(sf::PrimitiveType::Points, 4);
    nave[0].position = sf::Vector2f(100, 100);
    nave[1].position = sf::Vector2f(130, 10);
    nave[2].position = sf::Vector2f(130, 80);
    nave[3].position = sf::Vector2f(160, 100);  

    for (int i = 0; i < nave.getVertexCount(); i++)
        nave[i].color = sf::Color::White;

    sf::Transform t;
    t.rotate(90);

    while (window.isOpen())
    {
        sf::Event event;
        while (window.pollEvent(event))
        {
            if (event.type == sf::Event::Closed)
                window.close();
        }

        window.clear();
        window.draw(nave);
        auto nave2 = vertexRotate(nave, PI/2);
        nave2 = vertexTranslate(nave2, 300, 300);
        window.draw(nave2);
        window.display();
    }

    return 0;
}

1 answer

4


Consider these two lines of your rotation code:

vertexCopy[i].position.x = pivot.x + (vertexCopy[i].position.x - pivot.x) * cos(theta) - (vertexCopy[i].position.y - pivot.y) * sin(theta);
vertexCopy[i].position.y = pivot.y + (vertexCopy[i].position.x - pivot.x) * sin(theta) + (vertexCopy[i].position.y - pivot.y) * cos(theta);

In the first, you change the value of vertexCopy[i].position.x. On Monday, you wear vertexCopy[i].position.x as the original X position of its vertex to calculate the new Y position, but vertexCopy[i].position.x is not the original position, is the final position, already calculated. It is right to use the old value to calculate the two new coordinates. The code "correct" is:

vertexCopy[i].position.x = pivot.x + (toRotate[i].position.x - pivot.x) * cos(theta) - (toRotate[i].position.y - pivot.y) * sin(theta);
vertexCopy[i].position.y = pivot.y + (toRotate[i].position.x - pivot.x) * sin(theta) + (toRotate[i].position.y - pivot.y) * cos(theta);

Correct in quotation marks because this code will rotate the object clockwise, while the conventional is to rotate counterclockwise. It turns out that the spin matrix normally used, which is:

| cos θ   -sin θ |
| sin θ    cos θ |

assumes a coordinate system where X grows right, and Y grows up. Only in its SFML code, Y grows down! This causes the rotation to be reversed to conventional. To have a counter-clockwise rotation, as conventional, just reverse the direction of the sines (indicating the vertical direction):

| cos θ    sin θ |
|-sin θ    cos θ |

that is to say:

vertexCopy[i].position.x = pivot.x + (toRotate[i].position.x - pivot.x) * cos(theta) + (toRotate[i].position.y - pivot.y) * sin(theta);
vertexCopy[i].position.y = pivot.y - (toRotate[i].position.x - pivot.x) * sin(theta) + (toRotate[i].position.y - pivot.y) * cos(theta);

Another thing that is also conventional is to make the rotations around the center of objects:

auto nave2 = vertexRotate(nave, PI/2, sf::Vector2f(130, 80));

instead of rotating around the origin, as you did. I suggest you create a class that stores the vertices of the objects defined around (0, 0). Only when drawing, you should apply the transformations and rotations.

Finally, I don’t know which compiler you’re using, but the following code is invalid in the standard C++ :

sf::VertexArray vertexRotate(sf::VertexArray &toRotate, float theta, sf::Vector2f &pivot = sf::Vector2f(0,0)) {

In sf::Vector2f &pivot, you can’t pass a value default for a parameter that is not a reference-const, you would be building a reference to a temporary object. The standard C++ allows only references const for temporary objects, which in this case extends the useful life of the object by the duration of the reference:

sf::VertexArray vertexRotate(sf::VertexArray &toRotate, float theta, const sf::Vector2f &pivot = sf::Vector2f(0,0)) {
  • Thank you very much for the clear and objective answer.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.