﻿ Asynchronous Animation of Realistic Motion

# Asynchronous Animation of Realistic Motionby Petr Ivankov

## 1. Introduction.

In physics, the motion of objects in all but the simplest cases may be described as a solution of some differential equation. For the systems of solid rigid bodies, these are most ususal ordinary differential equations (ODEs). One of the main driving force for the development of computers since mid XX century was to give approximate, numerical solutions to the differential equations appearing in physics and engineering, which were to complex to handle with traditional methods. Indeed, a high precision model of an Earth's satellite motion

• Reduction of step size;
• Application of asynchronous animation.
First way sometimes is not good because it requires substantial enlargement of calculation. Asynchronous animation is explained by following picture.

Real traejctory of 3D object is replaced with piecewise linear trajectory, which is close to real. However synchronous animation corresponds to piecewise constant function which is presented below.

Following movies show difference between different methods of animation.

Synchronous animation occurs substantial jumps of cube, jumps of asynchronous animation are scarcely visible.

Present day technologies support a lot of asynchronous animation methods with a powerful hardware support. In this article I have used WPF animation described here. However my soft contains abstract layer and it can be easy adapted for other types of animation.

## 2. Math Background

Uniform motion from 3D point p1 = (x1, y1, z1) to p2 = (x2, y2, z2) can be represented by following elementary expression

p(t) = (x1(1 - t) + x2t, y1(1 - t) + y2t, z1(1 - t) + z2t)

where t is a current progress (current progress corresponds to the Clock.CurrentProgress Property). Uniform rotational motion can be easy described by quaternions. Uniform rotation from quaternion Q1 = Q01 + Q11i + Q21j + Q11k to Q2=Q02 + Q21i + Q22j + Q12k is described by following equation

Q(t) = Q1Quniform(t)

where Quniform(t) corresponds to uniform rotation. Since uniform rotation is supported by WPF we do not need explicit calculation of Quniform(t). We need axis of rotation and rotation angle only. These parameters can be given from a following relative rotation quaternion

Qrelative = Q1*Q2

where Q1* = Q01 - Q11i - Q21j - Q11k.

Otherwise

Qrelative= cos (a/2) + rxsin(a/2)i + rysin(a/2)i + rzsin(a/2)k.

Where a is a full rotation angle, direction of vector r = (rx, ry, rz) coincides with rotation axis. If r= 0 (resp. r is close to 0) then rotation is abscent (resp. can be neglected). Above equations are implemented by Uniform6DMotion class which is contained is source code. This class is independent from WPF or any other animation implementation.

## 3. WPF Implementation of the Uniform 6D Motion

Uniform 6D motion is implemented by Uniform6DTransformation which is a custom animation class. Following code snippet explains its logics.

/// <summary>
/// Custom animation class for uniform 6D motion
/// </summary>
public class Uniform6DTransformation : AnimationTimeline
{
#region Fields

/// <summary>
/// Full transformation
/// </summary>
Transform3DGroup transform = new Transform3DGroup();

/// <summary>
/// Translation
/// </summary>
TranslateTransform3D translation = new TranslateTransform3D();

/// <summary>
/// Constant rotation
/// </summary>
RotateTransform3D rotate_const = new RotateTransform3D();

/// <summary>
/// Uniform rotation
/// </summary>
RotateTransform3D rotate_uniform = new RotateTransform3D();

/// <summary>
/// Quaternion of constant rotation
/// </summary>
QuaternionRotation3D quaternionConstRotation = new QuaternionRotation3D();

/// <summary>
/// Axis angle rotation for uniform rotation
/// </summary>
AxisAngleRotation3D angle_uniform = new AxisAngleRotation3D();

/// <summary>
/// Calculator
/// </summary>
Motion6D.Uniform6D.Uniform6DMotion calculator;

// ...

// Constructor
internal Uniform6DTransformation(AnimatableWrapper wrapper, Motion6D.ReferenceFrame frame,
bool realtime, double[] changeFrameTime, TimeSpan forecastTime)
{

rotate_const.Rotation = quaternionConstRotation; // Setting quaterion for constant rotation
rotate_uniform.Rotation = angle_uniform;         // Setting axis rotation for uniform rotation
// ...

}

/// <summary>
/// Sets progress time
/// </summary>
/// <param name="progressTime">Progress time</param>
private void SetProgressTime(double progressTime)
{
double angle;               // Angle of rotation
double x, y, z;             // Coordinates of transformation
calculator.SetTime(progressTime, out angle, out x, out y, out z); // Calculation of coordinates and angle
angle_uniform.Angle = (180 / Math.PI) * angle;                    // Sets value of rotation angle
// Setting of coordinates of transformation
translation.OffsetX = x;
translation.OffsetY = y;
translation.OffsetZ = z;
}

/// <summary>
/// Overriden
/// </summary>
/// <param name="defaultOriginValue">Default Origin Value</param>
/// <param name="defaultDestinationValue">Default Destination Value</param>
/// <param name="animationClock">Animation Clock</param>
/// <returns>Transformation</returns>
public override object GetCurrentValue(object defaultOriginValue,
object defaultDestinationValue, AnimationClock animationClock)
{
SetTime(animationClock.CurrentProgress.Value);
return transform;
}

/// <summary>
/// Overriden calculation of current value
/// </summary>
/// <param name="defaultOriginValue">Default Origin Value</param>
/// <param name="defaultDestinationValue">Default Destination Value</param>
/// <param name="animationClock">Animation Clock</param>
/// <returns>Transformation</returns>
public override object GetCurrentValue(object defaultOriginValue,
object defaultDestinationValue, AnimationClock animationClock)
{
SetProgressTime(animationClock.CurrentProgress.Value);
return transform;
}

// ...

}

Above code means that a transformatin is decomoposed to

• Uniform 3D translation;
• Constant 3D rotation;
• Uniform 3D rotation.

## 4. General Asynchronous Animation Algorithm

Asynchronous animation architecture contains abstract level which corresponds to following interface.

/// <summary>
/// Driver of animation
/// </summary>
public interface IAnimationDriver
{
/// <summary>
/// Starts animation
/// </summary>
/// <param name="collection">Collection of components</param>
/// <param name="reasons">Reasons</param>
/// <param name="animationType">Type of animation</param>
/// <param name="pause">Pause</param>
/// <param name="timeScale">Time scale</param>
/// <param name="realTime">The "real time" sign</param>
/// <param name="absoluteTime">The "absolute time" sign</param>
/// <returns>Animation asynchronous calculation</returns>
object StartAnimation(IComponentCollection collection, string[] reasons,
Enums.AnimationType animationType,
TimeSpan pause, double timeScale, bool realTime, bool absoluteTime);

/// <summary>
/// Supports asynchronous
/// </summary>
bool SuppotrsAsynchronous
{
get;
}
}

/// <summary>
/// Type of action
/// </summary>
public enum ActionType
{
/// <summary>
/// Calculation
/// </summary>
Calculation,
/// <summary>
/// Animation
/// </summary>
Animation
}

/// <summary>
/// Type of animation
/// </summary>
public enum AnimationType
{
/// <summary>
/// Synchronous
/// </summary>
Synchronous,

/// <summary>
/// Asynchronous
/// </summary>
Asynchronous
}

The StartAnimation function returns object which implements following interface.

/// <summary>
/// Asynchronous calculation
/// </summary>
public interface IAsynchronousCalculation
{
/// <summary>
/// Starts itself
/// </summary>
/// <param name="time"></param>
void Start(double time);

/// <summary>
/// Step
/// </summary>
Action<double> Step
{
get;
}

/// <summary>
/// Stops itself
/// </summary>
void Interrupt();

/// <summary>
/// Suspends itself
/// </summary>
void Suspend();

/// <summary>
/// The "is running" sign
/// </summary>
bool IsRunning
{
get;
}

/// <summary>
/// Suspend event
/// </summary>
event Action OnSuspend;

/// <summary>
/// Finish event
/// </summary>
event Action Finish;

/// <summary>
/// Interrupt
/// </summary>
event Action OnInterrupt;

}

Above interface is not intended for animation only. Following table contains different implementations of this interface.

 N Class name Purpose 1 PauseAsynchronousCalculation Synchronous animation 2 WpfAsynchronousAnimatedCalculation Asynchronous animation for WPF 3 WpfAsynchronousRealtimeAnimatedCalculation Asynchronous real-time animation for WPF

The real-time mode is described below.

The PauseAsynchronousCalculation does not depend on WPF or other animation technology. This class just performs a pause. Following code explains this circumstance.

/// <summary>
/// Pause asynchronous calculation
/// </summary>
public class PauseAsynchronousCalculation : IAsynchronousCalculation
{
#region Fields

private Action<double> pause;

// ...

/// <summary>
/// Constructor
/// </summary>
/// <param name="pauseSpan">Pause</param>
public PauseAsynchronousCalculation(TimeSpan pauseSpan)
{
// Pause action
pause = (double time) =>
{
};
}

// ...

Action<double> IAsynchronousCalculation.Step
{
get { return pause; }
}

// ...
}

Every step of synchronous animation assumes following operations:

• Calculation of motion parameters;
• Showing of 3D objects with new 6D positions;
• Time pause.

Asynchronous animation assumes one thread of calculation and many threads for animation. Every step of asynchronous animation calculation contains following operations:

• Calculation of motion parameters;
• Enqueuing motion parameters and current time to every animation thread.

Every animation thread performs following operations:

## 5. Calculation of motion parameters

Calculation of motion parameters is already described in my previous article. However I overlap some text from my previous article for convenience.

A kinematics domain contains following basic types:

• 3D Position (IPosition interface);
• 3D Orientation (IOrientation interface;
• Standard 3D Position (Position class which implements IPosition interface);
• 3D Reference frame (ReferenceFrame class which implements both IPosition and IOrientation);
• Holder 3D Reference frame (IReferenceFrame interface) ;
• Reference frame binding (ReferenceFrameArrow class which implements ICategoryArrow interface).

Source (resp. target) of ReferenceFrameArrow is always IPosition (resp. IReferenceFrame). This arrow means that coordinates of IPosition are relative with respect to IReferenceFrame. Following code represents these types:

/// <summary>
/// 3D Position
/// </summary>
public interface IPosition
{

/// <summary>
/// Absolute position coordinates
/// </summary>
double[] Position
{
get;
}

/// <summary>
/// Parent frame
/// </summary>
IReferenceFrame Parent
{
get;
set;
}

/// <summary>
/// Position parameters
/// </summary>
object Parameters
{
get;
set;
}

/// <summary>
/// </summary>
void Update();
}

/// <summary>
/// Object with orientation
/// </summary>
public interface IOrientation
{
/// <summary>
/// Orientation quaternion
/// </summary>
double[] Quaternion
{
get;
}

/// <summary>
/// Orientation matrix
/// </summary>
double[,] Matrix
{
get;
}
}

/// <summary>
/// Standard position
/// </summary>
public class Position : IPosition, IChildrenObject
{

#region Fields

/// <summary>
/// Parent frame
/// </summary>
protected IReferenceFrame parent;

/// <summary>
/// Own position
/// </summary>
protected double[] own = new double[] { 0, 0, 0 };

/// <summary>
/// Relatyive position
/// </summary>
protected double[] position = new double[3];

/// <summary>
/// Parameters
/// </summary>
protected object parameters;

/// <summary>
/// Children objects
/// </summary>
protected IAssociatedObject[] ch = new IAssociatedObject[1];

#endregion

#region Ctor

/// <summary>
/// Default constructor
/// </summary>
protected Position()
{
}

/// <summary>
/// Constructor
/// </summary>
/// <param name="position">Position coordinates</param>
public Position(double[] position)
{
for (int i = 0; i < own.Length; i++)
{
own[i] = position[i];
}
}

#endregion

#region IPosition Members

double[] IPosition.Position
{
get { return position; }
}

/// <summary>
/// Parent frame
/// </summary>
public virtual IReferenceFrame Parent
{
get
{
return parent;
}
set
{
parent = value;
}
}

/// <summary>
/// Position parameters
/// </summary>
public virtual object Parameters
{
get
{
return parameters;
}
set
{
parameters = value;
if (value is IAssociatedObject)
{
IAssociatedObject ao = value as IAssociatedObject;
ch[0] = ao;
}
}
}

/// <summary>
/// </summary>
public virtual void Update()
{
Update(BaseFrame);
}

#endregion

// ...
}

/// <summary>
/// Reference frame
/// </summary>
public class ReferenceFrame : IPosition, IOrientation
{

#region Fields

/// <summary>
/// Orientation quaternion
/// </summary>
protected double[] quaternion = new double[] { 1, 0, 0, 0 };

/// <summary>
/// Absolute position
/// </summary>
protected double[] position = new double[] { 0, 0, 0 };

/// <summary>
/// Orientation matrix
/// </summary>
protected double[,] matrix = new double[,] { { 1, 0, 0 }, { 0, 1, 0 }, { 0, 0, 1 } };

/// <summary>
/// Auxiliary array
/// </summary>
protected double[,] qq = new double[4, 4];

/// <summary>
/// Auxiliary array
/// </summary>
protected double[] p = new double[3];

/// <summary>
/// Parent frame
/// </summary>
protected IReferenceFrame parent;

/// <summary>
/// Parameters
/// </summary>
protected object parameters;

/// <summary>
/// Auxliary position
/// </summary>
private double[] auxPos = new double[3];

#endregion

#region Ctor

/// <summary>
/// Constructor
/// </summary>
public ReferenceFrame()
{
}

/// <summary>
/// Constructor
/// </summary>
/// <param name="b">Auxiliary</param>
private ReferenceFrame(bool b)
{
}

#endregion

#region IPosition Members

/// <summary>
/// Absolute position
/// </summary>
public double[] Position
{
get { return position; }
}

/// <summary>
/// Parent frame
/// </summary>
public virtual IReferenceFrame Parent
{
get
{
return parent;
}
set
{
parent = value;
}
}

/// <summary>
/// Position parameters
/// </summary>
public virtual object Parameters
{
get
{
return parameters;
}
set
{
parameters = value;
}
}

/// <summary>
/// </summary>
public virtual void Update()
{
ReferenceFrame p = ParentFrame;
position = p.Position;
quaternion = p.quaternion;
matrix = p.matrix;
}

#endregion

#region IOrientation Members

/// <summary>
/// Orientation quaternion
/// </summary>
public double[] Quaternion
{
get { return quaternion; }
}

/// <summary>
/// Orientation matrix
/// </summary>
public double[,] Matrix
{
get { return matrix; }
}

#endregion

// ...

}

/// <summary>
/// Reference frame holder
/// </summary>
public interface IReferenceFrame : IPosition
{
/// <summary>
/// Own frame
/// </summary>
ReferenceFrame Own
{
get;
}

/// <summary>
/// Children objects
/// </summary>
List<IPosition> Children
{
get;
}

}

/// <summary>
/// </summary>
[Serializable()]
public class ReferenceFrameArrow : CategoryArrow, ISerializable, IRemovableObject
{
#region Fields

IPosition source;

IReferenceFrame target;

#endregion

#region Constructors

/// <summary>
/// Default constructor
/// </summary>
public ReferenceFrameArrow()
{

}

/// <summary>
/// Deserialization constructor
/// </summary>
/// <param name="info">Serialization info</param>
/// <param name="context">Streaming context</param>
protected ReferenceFrameArrow(SerializationInfo info, StreamingContext context)
{
}

#endregion

#region ISerializable Members

void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
{
}

#endregion

#region ICategoryArrow Members

/// <summary>
/// The source of this arrow
/// </summary>
public override ICategoryObject Source
{
get
{
return source as ICategoryObject;
}
set
{
IPosition position = value.GetSource<IPosition>();
if (position.Parent != null)
{
throw new CategoryException("Root", this);
}
source = position;
}
}

/// <summary>
/// The target of this arrow
/// </summary>
public override ICategoryObject Target
{
get
{
return target as ICategoryObject;
}
set
{
IReferenceFrame rf = value.GetTarget<IReferenceFrame>();
IAssociatedObject sa = source as IAssociatedObject;
IAssociatedObject ta = value as IAssociatedObject;
INamedComponent ns = sa.Object as INamedComponent;
INamedComponent nt = ta.Object as INamedComponent;
target = rf;
source.Parent = target;
}
}

#endregion

//...

}

Some code is omitted, a reader can find it in source files. Let us consider a following picture:

Type of the Point is Position and the Frame implements IReferenceFrame interface. The Link arrow means that motion of Point is relative with respect to Frame. Absolute coordinates of Point are calculated by following way:

Xa = XF + A11Xr + A12Yr + A13Zr;

Ya = YF + A21Xr + A22Yr + A23Zr;

Za = ZF + A31Xr + A32Yr + A33Zr.

where

• Xa, Ya, Za absolute coordinates of Point
• XF, YF, ZF absolute coordinates of Frame
• Xr, Yr, Zr relative coordinates of Point
• A11,..., A33 - elements of 3D rotation matrix.

## 6. Animated objects

There is a many to many relation of following objects:

Following movies contains 7 shapes and 5 cameras with independent motion

Helicoper contains following shapes:

• Fuselage;
• Tail rotor;
• 5 blades of main rotor.

Independent motion of blades is due to main rotor assembly

WPF allows usage of single Visual3D in single camera only. So we need for copies of 3D shapes. Above picture contains 5 * 7 + 5 - 40 animatable (IAnimatable interface), they are 5 cameras and 5 copies for each 7 Visual3D objects. However simulation of motion is performed for 5 + 7 = 12 objects (5 cameras and 7 3D shapes). So we need 12 objects of the ReferenceFrame type. Following interface is responsible for interoperability between animated objects and objects of 6D motion simulation.

/// <summary>
/// Linear forecast
/// </summary>
public interface ILinear6DForecast
{

/// <summary>
/// Frame
/// </summary>
ReferenceFrame ReferenceFrame
{
get;
}

/// <summary>
/// Forecast time
/// </summary>
TimeSpan ForecastTime
{
get;
set;
}

/// <summary>
/// Error of coordinate
/// </summary>
double CoordinateError
{
get;
set;
}

/// <summary>
/// Error of angle
/// </summary>
double AngleError
{
get;
set;
}
}

Above interface does not depend on 3D graphics technology, following interface is a WPF version.

/// <summary>
/// Animated object
/// </summary>
interface IAnimatedObject : ILinear6DForecast
{
/// <summary>
/// Initialze animation
/// </summary>
void InitAnimation(AnimationType animationType);

/// <summary>
/// Initialze animation
/// </summary>
/// <param name="animationType">Type of animation</param>
/// <param name="changeFrameTime">Time of frame change</param>
void InitRealtime(AnimationType animationType, double[] changeFrameTime);

/// <summary>
/// Children
/// </summary>
AnimatableWrapper[] Children
{
get;
}

/// <summary>
/// Change event
/// </summary>
event Action Change;

/// <summary>
/// Stops animation
/// </summary>
void StopAnimation();

/// <summary>
/// On stop action
/// </summary>
event Action OnStop;

/// <summary>
/// Supports animation events
/// </summary>
bool SupportsAnimationEvents
{
get;
set;
}

}

The Children property contains wrappers of animatable (IAnimatable interface). In previous picture every 3D shape contains 5 children, every perspective camera has a single child. Following code contains wrapper of animatable object.

/// <summary>
/// Wrapper of animatable object
/// </summary>
public class AnimatableWrapper : IDisposable
{
#region Fields

private IAnimatable animatable;

private DependencyProperty dependencyProperty;

private object value;

private Action change = () => { };

private IAnimatedObject animatedObject;

private Uniform6DTransformation transformation;

private Action finish = () => { };

double[] auxQuaternion = new double[4];

bool isStopped = false;

object loc = new object();

#endregion

#region Ctor

internal AnimatableWrapper(IAnimatable animatable, DependencyProperty dependencyProperty,
IAnimatedObject animatedObject, bool realtime, double[] changeFrameTime)
{
this.animatable = animatable;
this.dependencyProperty = dependencyProperty;
value = (animatable as DependencyObject).GetValue(dependencyProperty);
this.animatedObject = animatedObject;
transformation = new Uniform6DTransformation(this,animatedObject.ReferenceFrame,
realtime, changeFrameTime, animatedObject.ForecastTime);
}

#endregion

#region Public Members

/// <summary>
/// Animatable
/// </summary>
public IAnimatable Animatable
{
get
{
return animatable;
}
}

/// <summary>
/// Dependency property
/// </summary>
public DependencyProperty DependencyProperty
{
get
{
return dependencyProperty;
}
}

#endregion

#region Internal Members

internal IAnimatedObject Animated
{
get
{
return animatedObject;
}
}

internal void Stop()
{
lock (loc)
{
transformation.Stop();
isStopped = true;
finish();
}
}

internal void StartRealtime(double time, DateTime start)
{
transformation.StartRealtime(time, start);
}

internal void Init(double[] coord, double[] quaternion)
{
transformation.Init(coord, quaternion);
}

internal void StartAnimation(double[] coord, double[] quaternion, DateTime start)
{
transformation.StartAnimation(coord, quaternion, start);
}

internal void Enqueue(Tuple<TimeSpan, double[], double[]> parameters)
{
transformation.Enqueue(parameters);
}

internal void Finish()
{
finish();
}

internal event Action OnFinish
{
add { finish += value; }
remove { finish -= value; }
}

internal  Action Event
{
get
{
return transformation.Event;
}
}

#endregion

#region IDisposable Members

void IDisposable.Dispose()
{
(animatable as DependencyObject).SetValue(dependencyProperty, value);
animatedObject.Change -= change;
}

#endregion
}

Above class contains a transformation field of the Uniform6DTransformation : AnimationTimeline type. This field is responsible for animation.

## 7. Real-time

Recently I wrote an article devoted to real-time. This section can be regarded as an animation extension of the real-time simulation. Above animation is explained by following picture.

However above scheme is not sufficient for real-time because animation lag behind simulation. Sufficient scheme is presented below.

This scheme implies usage of the linear prediction. Both velocity vector and angular velocity vectors supply necessary data for linear prediction. My software supports calculation of velocity and angular velocity for different engineering problems (See here and here). Now these calculations are used for the asynchronous animation. Following interfaces are responsible for calculation of velocity vectors.

/// <summary>
/// Object with linear velocity
/// </summary>
public interface IVelocity
{
/// <summary>
/// Linear velocity
/// </summary>
double[] Velocity
{
get;
}

}

/// <summary>
/// Object that have angular velocity
/// </summary>
public interface IAngularVelocity
{
/// <summary>
/// Angular velocity of object
/// </summary>
double[] Omega
{
get;
}
}

Following code explains application of these interfaces for linear prediction.

/// <summary>
/// Reference frame
/// </summary>
ReferenceFrame frame;

/// <summary>
/// Velocity object
/// </summary>
IVelocity velocity;

/// <summary>
/// Angular velocity object
/// </summary>
IAngularVelocity angularVelocity;

// ...

/// <summary>
/// Initialization of prediction
/// </summary>
public void InitializePrediction(double forecastTime)
{
if (!(frame is IVelocity))
{
throw new Exception("Frame does not support velocity");
}
if (!(frame is IAngularVelocity))
{
throw new Exception("Frame does not support angular velocity");
}
velocity = frame as IVelocity;
angularVelocity = frame as IAngularVelocity;
}

/// <summary>
/// Initialization of prediction
/// </summary>
public void InitializePrediction(double forecastTime)
{
if (!(frame is IVelocity))
{
throw new Exception("Frame does not support velocity");
}
if (!(frame is IAngularVelocity))
{
throw new Exception("Frame does not support angular velocity");
}
velocity = frame as IVelocity;
angularVelocity = frame as IAngularVelocity;
}

/// <summary>
/// Linear prediction
/// </summary>
/// <param name="time">Current time</param>
private void LinearPrediction(double time)
{
lastTime = time;
double delta = time - changeFrameTime[0]; // Forecast time
double[] coord = frame.Position;          // Coordinates of frame
double[] v = velocity.Velocity;           // Velocity vector
for (int i = 0; i < 3; i++)
{
auxVectorFrame[i] = coord[i] + v[i] * delta; // Linear prediction of coordinates
}
double[] omega = angularVelocity.Omega; // Angular velocity vector
double mod = omega[0] * omega[0] + omega[1] * omega[1] + omega[2] * omega[2];
mod = Math.Sqrt(mod);
Array.Copy(omega, 0, auxQuarterInter, 1, 3); // Calculation of "shift" quaternion
double angle = 0.5 * mod * delta;
double s = Math.Sin(angle);
double c = Math.Cos(angle);
auxQuarterInter[0] = c;
double smod = s / mod;
for (int i = 1; i < 3; i++)
{
auxQuarterInter[i] *= smod;
}
QuaternionMultiply(frame.Quaternion, auxQuarterInter, auxQuaterFrame); // Calculation of quaternion
}

## 8. Examples

### 8.1 Animation of a Cube

This sample is rather demo than realistic. Motion of cube is described by following finite formulas.

Above formulas are used as coordinates and components of 3D orientation quaternion.

Following movie represents animation of the cube.

### 8.2 Animation of a Helicopter

Motion of helicopter is also rather demo and it also is defined by finite formulas. But it includes 7 3D shapes and 5 virtual cameras. Following movie represents motion of helicopter.

### 8.3 Animation of Artificial Earth's Satellite.

This sample is quite realistic because it uses following ingredients:

Description of math model details is here. Following movie represents this animation.

### 8.4 Manual Control of an Airplane

This sample is close to realistic. It uses real-time manual control of an airplane. Motion model of airplane is quite realistic because it uses realistic model atmosphere and an aerodynamics model. However manual control is not realistic. Instead a pilot wheel the "roll pitch robot pilot" is used. The ForcedEventData class is used for manual control. Object of this class performs following operations:

• Manual input of parameters (roll and pitch);
• Raising of input event.

User can update required values of pith and roll. Any update occurs a transient state. Following chart contains two transient states.

.

Real-time simulation is based on event driven calculations. Following scheme of events is used for airplane simulation.

Our calculation is driven by Event collection object which is simultaneously a generator of events and an event handler. It is an object of the EventCollection class. The Event collection is a "sum" of Timer, Transition and Pilot Control events. The Timer raises one event per 2000 ms = 2 s. The Pilot Control raises event of user manual control. The Transition raises one event per 0.2 s during transition state. The Transition is an object of the TransientProcessEvent class. This class is simultaneously a source of event and event handler. Following code explains how does this class works.

/// <summary>
/// Transient State Event
/// </summary>
[Serializable()]
public class TransientProcessEvent : CategoryObject, IEvent, IEventHandler, ISerializable
{
#region Fields

TimeSpan[] spans;

private Action ev = () => { };

object loc = new object();

bool isEnabled = false;

DateTime previous = DateTime.Now;

bool isRunning = false;

// ...

#endregion

#region Ctor

/// <summary>
/// Default constructor
/// </summary>
public TransientProcessEvent()
{
}

// ...

#endregion

#region IEvent Members

event Action IEvent.Event
{
add { ev += value; }
remove { ev -= value; }
}

// ...

#endregion

#region IEventHandler Members

{
}

// ...

#endregion

#region Private Members

/// <summary>
/// Enanble/Disable operation
/// </summary>
void Enable()
{
lock (loc)
{
isRunning = false;
if (isEnabled)
{
previous = DateTime.Now;
foreach (IEvent ev in events)
{
ev.Event += EventHandler; // Adds event handler
}
return;
}
foreach (IEvent ev in events)
{
ev.Event -= EventHandler;    // Removes event handler
}
}
}

/// <summary>
/// Event handler
/// </summary>
void EventHandler()
{
if (isRunning)
{
previous = DateTime.Now;
return;
}
lock (loc)
{
if (isRunning)
{
return;
}
isRunning = true;
currentStep = 0;
Action act = AsyncEvent; // Asynchronous event
act.BeginInvoke(null, null);
}
}

/// <summary>
/// Asynchronous event
/// </summary>
void AsyncEvent()
{
for (int i = 0; i < spans.Length; i++)
{
ev();                   // Raise event
}
}

#endregion

}

The AsyncEvent performs cyclic sleep operation and raising of event. Following movies explains how does it works.

After user action (mouse click/move) we have 10 updates distinguished by update per 0.2 s. In passive mode we have one update per 2 s. Following movie shows this sample.

## Points of Interest

I am a software developer since 1977. During development of this article I first time encoutered with deadlock.