Circular rotation around an arbitrary axis

Viewed 64056

I am programming Starcraft 2 custom maps and got some proglems with math in 3D. Currently I am trying to create and rotate a point around an arbitrary axis, given by x,y and z (the xyz vector is normalized).

I've been trying around a lot and read through a lot of stuff on the internet, but I just cant get how it works correctly. My current script (you probably dont know the language, but it's nothing special) is the result of breaking everything for hours (doesn't work correctly):

    point CP;
fixed AXY;
point D;
point DnoZ;
point DXY_Z;
fixed AZ;
fixed LXY;
missile[Missile].Angle = (missile[Missile].Angle + missile[Missile].Acceleration) % 360.0;
missile[Missile].Acceleration += missile[Missile].AirResistance;
if (missile[Missile].Parent > -1) {
    D = missile[missile[Missile].Parent].Direction;
    DnoZ = Point(PointGetX(D),0.0);
    DXY_Z = Normalize(Point(SquareRoot(PointDot(DnoZ,DnoZ)),PointGetHeight(D)));
    AZ = MaxF(ACos(PointGetX(DXY_Z)),ASin(PointGetY(DXY_Z)))+missile[Missile].Angle;
    DnoZ = Normalize(DnoZ);
    AXY = MaxF(ACos(PointGetX(DnoZ)),ASin(PointGetY(DnoZ)));
    CP = Point(Cos(AXY+90),Sin(AXY+90));
    LXY = SquareRoot(PointDot(CP,CP));
    if (LXY > 0) {
        CP = PointMult(CP,Cos(AZ)/LXY);
        PointSetHeight(CP,Sin(AZ));
    } else {
        CP = Point3(0.0,0.0,1.0);
    }
} else {
    CP = Point(Cos(missile[Missile].Angle),Sin(missile[Missile].Angle));
}
missile[Missile].Direction = Normalize(CP);
missile[Missile].Position = PointAdd(missile[Missile].Position,PointMult(missile[Missile].Direction,missile[Missile].Distance));

I just cant get my mind around the math. If you can explain it in simple terms that would be the best solution, a code snipped would be good as well (but not quite as helpful, because I plan to do more 3D stuff in the future).

7 Answers

A very neat way to program this, especially if you are able to operate with matrices (like in Matlab) is the Rodrigues' Rotation Formula.

The formula creates a rotation matrix R around an axis defined by the unit vector \hat{u} = [u_x,u_y,u_z] by an angle phi using a very simple equation:

R=I+sin(phi)·W + (1-cos(phi))·W²

Where I is the identity matrix and W is a matrix given by the components of the unit vector u:

W= [0 -uz uy; uz 0 -ux; -uy ux 0];

Note that it is very important that the vector u is a unit vector, i.e. the norm of u must be 1.

You can check that for the euclidean axis, the formula is exact as those found on Wikipedia and published here by Aakash Anuj.

I only use this formula for rotations since I discovered it. Hope it helps to anyone.

Axis-angle converts directly to quaternion; given that the axis is a unit vector, and the angle is the spin around that axis. The axis is given regular (x,y,z) direction coordinates. The quaternion is (cos(theta),sin(theta)*x, sin(theta)*y, sin(theta)*z) then multiply as appropriate.

The basis can be formed by rotating (1,0,0),(0,1,0),(0,0,1) around the axis-angle using Rodrigues Rotation Formula which results in the following... and then the forward, right, and up vectors can be used to scale the point to the correct location. (This is just a matrix, but essentially transposed so that axii are immediately extractable themselves, giving you the relative 'up' at any particular point)

    const nt = q.θ * del;  // scaled angle by some dT
    const s  = Math.sin( nt ); // sin/cos are the function of exp()
    const c1 = Math.cos( nt );
    const c = 1- c1;

    const qx = /*Axis unit vector X*/;
    const qy = /*Axis unit vector Y*/;
    const qz = /*Axis unit vector Z*/;

    const cnx = c*qx;
    const cny = c*qy;
    const cnz = c*qz;

    const xy = cnx*qy;  // x * y / (xx+yy+zz) * (1 - cos(2t))
    const yz = cny*qz;  // y * z / (xx+yy+zz) * (1 - cos(2t))
    const xz = cnz*qx;  // x * z / (xx+yy+zz) * (1 - cos(2t))

    const wx = s*qx;     // x / sqrt(xx+yy+zz) * sin(2t)
    const wy = s*qy;     // y / sqrt(xx+yy+zz) * sin(2t)
    const wz = s*qz;     // z / sqrt(xx+yy+zz) * sin(2t)

    const xx = cnx*qx;  // y * y / (xx+yy+zz) * (1 - cos(2t))
    const yy = cny*qy;  // x * x / (xx+yy+zz) * (1 - cos(2t))
    const zz = cnz*qz;  // z * z / (xx+yy+zz) * (1 - cos(2t))

    const basis = { right  :{ x : c1 + xx, y : wz + xy, z : xz - wy }
                  , up     :{ x : xy - wz, y : c1 + yy, z : wx + yz }
                  , forward:{ x : wy + xz, y : yz - wx, z : c1 + zz }
                  };

Related