ClanLib Tips & Tricks


HOME  ¦  MATH  ¦  NETWORK

Links »

ClanLib Home


Rotating objects »

Sometimes you need to rotate not only an image, but also the images location. Then the rotate() method of CL_Sprite and CL_Surface is not enough.

Rotation math »

To rotate a point around (0, 0), you'll need to apply the following transformation.

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

Simple rotation
    float new_x = old_x * ::cos(fi_rad) - old_y * ::sin(fi_rad);
    float new_y = old_x * ::sin(fi_rad) + old_y * ::cos(fi_rad);

To rotate around an arbitrary point in the plane, the point needs first be translated to (0, 0), rotated, and finally translated back to the original position.

Rotation around arbitrary point
    CL_Pointf the_point(230.f, 480.f);

    // The point to rotate around
    CL_Pointf rotate_around_me(230.f, 480.f);

    float rotation_angle_degrees = 45.f;

    // The trigonometric functions needs radians
    float rotation_angle_radians = rotation_angle_degrees * M_PI / 180;

    CL_Pointf new_pos(the_point);

    // Translate
    new_pos -= rotate_around_me;

    // Rotate
    the_point.x = new_pos.x * ::cos(rotation_angle_radians) -
                  new_pos.y * ::sin(rotation_angle_radians);
    the_point.y = new_pos.x * ::sin(rotation_angle_radians) +
                  new_pos.y * ::cos(rotation_angle_radians);

    // Translate back
    the_point += rotate_around_me;

Now that we know how to do it we can optimise the code. There is no point in calculating the cosŲ and sinŲ twice for each calculation. The value should be cached. And what if we need to rotate multiple points at the same time?

Final solution »

rotation2dcalculator.h
class Rotation2DCalculator
{
public:
    Rotation2DCalculator(const CL_Pointf& rotation_hotspot, float angle_deg)
       : hotspot(rotation_hotspot), angle(angle_deg)
    {
        // cache sin and cos calcs
        float angle_rad = angle_deg * M_PI / 180;
        sin_fi = ::sin(angle_rad);
        cos_fi = ::cos(angle_rad);
    }

    inline void rotate(CL_Pointf &p) const;

protected:
    CL_Pointf hotspot;
    float angle, sin_fi, cos_fi;
};

inline void Rotation2DCalculator::rotate(CL_Pointf &p) const
{
    CL_Pointf new_pos(p);

    // Translate
    new_pos -= hotspot;

    // Rotate
    p.x = new_pos.x * cos_fi - new_pos.y * sin_fi;
    p.y = new_pos.x * sin_fi + new_pos.y * cos_fi;

    // Translate back
    p += hotspot;
}

Now it's possible to use a Rotation2DCalculator object for multiple transformations. Supplying getters and setters for the rotation hotspot and the rotation angle will complete the class.

Note: This functionality can also be acheived using CL_Vector::rotate(). But if speed is an issue, this here idea is the way to go. It can be extended to three dimensions for those in need.

Back