A point in a 2D space is commonly identified as an (x,y) pair, representing its distance to the origin along the x-axis and the y-axis. Objects in 2D space have two additional properties that make no sense for mere points: scale and angle (I will ignore here shearing and anisotropic scaling).

A square, scaled and rotated.
An obvious question is, how to represent such transformations in a computer program, such as a video game? The simplest way one could think of is to use a structure that contains:
struct position
{
float x;
float y;
float angle;
float scale;
};
This works in most situations. Most simple 2D video games will be able to get away with this simple version without much difficulty. But there is one thing that it cannot do easily.
Relative Transforms
Suppose for a moment that the object we’re moving around is a spaceship. That spaceship has one gun at the end of each wing, which means positions (-2,0) and (2,0) relative to the center of the spaceship. Now, the ship is placed at a certain position, rotated and scaled. Where should the bullets be fired from?

What is the absolute position and angle of the guns?
On the one hand, we have an absolute position (where the ship is, its angle and its scale) and on the other hand we have a relative position (where the gun is, relative to the center of the ship). The objective is to determine the absolute position of the gun. Mathematically, if an object is at position (a,b) relative to an object that is at position (x,y) with scale s and angle θ, then its absolute position (a,’b) is:
a’ = x + s a cos θ – s b sin θ
b’ = y + s b cos θ + s a sin θ
But what happens if the ship itself is part of a greater whole? Or what if the gun itself is in fact a turret that can rotate independently around a point on the wing?
To handle this, we define a transform as “something that moves an object from a certain position, angle and scale (the source) to another position, angle and scale (the destination)”. The ship has an absolute transform, which moves it from point (0,0), scale 100%, angle 0° to its actual position, scale and angle. The gun has a relative transform, which moves it from the ship’s position, scale and angle to its actual position, scale and angle.
So, the absolute transform of a gun is obtained by applying the absolute transform (move the ship to where it should be) then the relative transform (move the gun to where it should be based on the ship’s position). And once you have the absolute transform of the gun, you can deduce its position, scale and angle.
The problem is that using the above “position, scale, angle” representation for transforms is not handy, because applying two transforms one after another gets complicated very fast. One solution is to use a matrix, which is a commonly used tool for representing transforms. Another solution is using complex numbers.
Complex Numbers
A complex number is an object of the form a + ib, where a is the real part and b is the imaginary part. Complex number multiplication uses the property that i² = -1. From there, we deduce the rules for complex number addition and multiplication:
(a + ib) + (a’ + ib’) = (a + a’) + i(b + b’)
(a + ib) (a’ + ib’) = (aa’ – bb’) + i(ab’ + a’b)
So, how does this help us? Well, consider our original position/angle/scale approach, call these x, y, θ and s and build the complex number function:
t(z) = s (cos θ + i sin θ) z + (x + iy)
This function represents a relative transform. Calling it on a complex number (a + ib) will return a complex number (a’ + ib’) such that (a’,b’) will be the absolute position of a point that was at relative position (a,b) from the center of the transformed object:
a’ = x + s a cos θ – s b sin θ
b’ = y + s b cos θ + s a sin θ
These are the exact formulas we discussed above!
So now, applying two transforms in a row is easy. If gun(z) is the he transform of the gun on the plane’s wing, and plane(z) is the transform of the plane in the world, then the transform of the gun in the world is plane(gun(z)). And since the functions are actually linear complex functions, composing them is exceedingly easy:
t(z) = a z + b
t’(z) = a‘ z + b’
t’(t(z)) = a’ (a z + b) + b’ = aa’ z + (a’b + b’)
Or, in terms of C++:
struct transform
{
std::complex<double> a, b;
transform(double x, double y, double angle = 0.0, double scale = 1.0)
: a(scale * std::cos(angle), scale * std::sin(angle)), b(x, y) {}
transform &operator *=(const transform& o)
{
b += a * o.b;
a *= o.a;
return *this;
}
double x() const { return b.real(); }
double y() const { return b.imag(); }
double angle() const { return std::arg(a); }
double scale() const { return std::abs(a); }
};
transform operator*(const transform &f, const transform &g)
{
transform result(f);
return f *= g;
}
Here, t’ * t represents the transform t’(t(z)).
Recent Comments