The 3D Curves app allows users to control parameters and visualization of the following three-dimensional curves:
Viviani's curve
Spherical nephroid
Ellipse
Trefoil knot
Baseball seam
Ring
Spherical cardioid
Wire curve
Double folium
Astroid
Lissajous curve
Hypocycloid
Technical Details
To animate the curves, the 3D Curve app uses a technique called Parallel Transport Frames. The technique takes an arbitrary initial frame (i.e., an orientation of an object such as a cube or a sphere), translates it along the curve, and, at each iteration, rotates it to stay as parallel to the curve as possible. The technique can be described mathematically as follows:
Given:
a curve C (for example a Viviani curve)
an existing frame F1 at t-1
a tangent T1 at t-1 (i.e. the 1st derivative of C at t-1)
a tangent T2 at t
... a new frame F2 at t can be calculated as follows:
F2's position is the value of C at t
F2's orientation can be found by rotating F1 about an axis A with angle α
the axis A is given by A = T1 × T2
the angle α is given by α = arccos( (T1 • T2) / (|T1| |T2|) )
In the 3D Curves app we sample frames along the curve using the parallel transport technique. Then, in order to achieve smooth rotation between the frames, we utilize quaternion interpolation. Another technique (the Fixed Up method) is used to obtain the initial frame.
Both of the techniques (Parallel Transport Frame as well as Fixed Up method) are described in the book Game Programming Gems 2.
There are plenty of resources regarding quaternions but in order to understand how they are used in 3D Curves, we only need to know that quaternions allow us to interpolate between two orientations in 3D space. In other words, if we have an object facing in certain direction and we have the same object facing another direction (i.e. rotated a little bit), quaternions allow us to find all intermediate stages of this rotation (i.e. interpolate from one orientation to another). This is very helpful in creating smooth animations involving 3D rotations.
To be more specific, only unit quaternions are used to represent 3D rotations. That's why we normalize quaternions. Furthermore, there are many methods of interpolating the unit quaternions. The one used in 3D Curves is called spherical linear interpolation which computes an interpolated quaternion (let's call it Q) given two unit quaternions (let's call them Q1 and Q2) and a parameter t. The parameter t can take values between 0 and 1. It indicates the position of the interpolated quaternion Q between the two unit quaternions Q1 and Q2. It follows that:
for t=0, Q=Q1
for t=0.5, Q is exactly between Q1 and Q2
for t=1, Q=Q2
Below, there is a code snippet from 3D Curves showing the operations on quaternions:
// Returns a frame (represented as a matrix) for a given index.
D3DXMATRIX Trajectory::GetFrame(int shapeIndex)
{
int index1 = m_Seq->Get(shapeIndex); // current index
int index2 = m_Seq->GetNext(shapeIndex); // next index
float angle = m_Frames[index1].Angle; // current angle
// Scale the quaternion param to [0.0, 1.0]
float t = m_QuatParam / m_Step;
// Calculate quaternions.
D3DXQUATERNION Q1, QN1; // quaternion from the current rotation matrix
D3DXMATRIX RM1 = m_Frames[index1].RotationMatrix;
D3DXQuaternionRotationMatrix(&Q1, &RM1);
D3DXQuaternionNormalize(&QN1, &Q1);
D3DXQUATERNION Q2, QN2; // quaternion from the next rotation matrix
D3DXMATRIX RM2 = m_Frames[index2].RotationMatrix;
D3DXQuaternionRotationMatrix(&Q2, &RM2);
D3DXQuaternionNormalize(&QN2, &Q2);
// Iterpolate
D3DXQUATERNION Q;
D3DXQuaternionSlerp(&Q, &QN1, &QN2, t);
// Build rotation matrix from quaternion.
D3DXMATRIX RM;
D3DXMatrixRotationQuaternion(&RM, &Q);
// Calculate translation matrix for the current angle.
D3DXMATRIX TM = m_Curve->GetTranslationMatrix(angle + m_QuatParam);
// Calculate the final world matrix.
D3DXMATRIX WM = RM * TM;
return WM;
}