How to rotate a set of vertices around a pivot

3

I have a set of vertices that I need to rotate in an angle Θ around a pivot, but when I apply the transformation the vertices are distorted. What's going on wrong? I got the answer formula from a similar question in this post

Here's 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;
}
    
asked by anonymous 16.03.2017 / 18:33

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

At first, you change the value of vertexCopy[i].position.x . In the second, you use vertexCopy[i].position.x to be the original X position of your vertex to calculate the new Y position, but vertexCopy[i].position.x is not the original position, it is the final position, already calculated. The good thing is to use the old value to calculate the two new coordinates. The "correct" code 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 one is rotating counterclockwise. It turns out that the rotation matrix is commonly used, which is:

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

supposes a coordinate system where X grows to the right, and Y grows up. Except in your code in SFML, the Y grows down! This causes the rotation to be reversed in the conventional direction. To have a counterclockwise rotation, as conventional, simply reverse the direction of the breasts (which indicates the vertical direction):

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

that 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);

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

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

Instead of doing the rotation around the source, like you did. I suggest you create a class that stores the vertices of objects defined around (0, 0). Only when you draw, you should apply transformations and rotations.

Finally, I do not know which compiler you're using, but the following code is invalid in the default C ++:

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

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

sf::VertexArray vertexRotate(sf::VertexArray &toRotate, float theta, const sf::Vector2f &pivot = sf::Vector2f(0,0)) {
    
16.03.2017 / 21:46