/*=============================================================================
Project: SharpImage
Module: siTransferFunctionAnimator.cs
Language: C#
Author: Dan Mueller
Date: $Date$
Revision: $Revision$
Copyright (c) Queensland University of Technology (QUT) 2008.
All rights reserved.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notices for more information.
=============================================================================*/
using System;
using System.IO;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;
using System.Collections.Generic;
using System.Diagnostics;
using System.Windows.Forms;
using System.Xml.Serialization;
using System.Threading;
namespace SharpImage.Rendering
{
///
/// Performs animation between two transfer functions.
/// The animator looks at the differences between the functions
/// and performs linear interpolation between each part.
///
public class siTransferFunctionAnimator : IDisposable
{
#region Constants
//=====================================================================
//=====================================================================
#endregion
#region Instance Variables
//=====================================================================
private bool m_Disposed = false;
private siTransferFunction m_TransferFunctionStart;
private siTransferFunction m_TransferFunctionEnd;
//=====================================================================
#endregion
#region Construction and Disposal
//=====================================================================
///
/// Default constructor.
///
public siTransferFunctionAnimator(siTransferFunction tf)
{
this.m_TransferFunctionToAnimate = tf;
}
///
/// Dispose of the siTransferFunction resources.
///
public void Dispose()
{
// Only dispose once
if (this.m_Disposed)
return;
else
this.m_Disposed = true;
// Tell the GC that the Finalize process no longer needs
// to be run for this object
GC.SuppressFinalize(this);
}
//=====================================================================
#endregion
#region Properties
//=====================================================================
#region TransferFunctionToAnimate
//=====================================================================
private siTransferFunction m_TransferFunctionToAnimate = null;
///
/// Gets the transfer function instance which is animated.
/// On the call to Animate(), the function is set to the starting
/// value, and subsequently updated on a separate thread each step
/// until it equals the end function.
///
public siTransferFunction TransferFunctionToAnimate
{
get { return this.m_TransferFunctionToAnimate; }
set { this.m_TransferFunctionToAnimate = value; }
}
//=====================================================================
#endregion
#region NumberOfSteps
//=====================================================================
private int m_NumberOfSteps = 50;
///
/// Gets/sets the number of steps to take to morph from one function
/// to another. Default is 50.
///
public int NumberOfSteps
{
get { return this.m_NumberOfSteps; }
set { this.m_NumberOfSteps = value; }
}
//=====================================================================
#endregion
#region PauseBetweenSteps
//=====================================================================
private int m_PauseBetweenSteps = 200;
///
/// Gets/sets the length of time (in milliseconds) to pause between
/// each step of the animation process. Default is 200 milliseconds.
///
public int PauseBetweenSteps
{
get { return this.m_PauseBetweenSteps; }
set { this.m_PauseBetweenSteps = value; }
}
//=====================================================================
#endregion
#region TotalAnimationTime
//=====================================================================
///
/// Gets/sets the total time (in milliseconds) to animate between the two functions.
/// This is computed as NumberOfSteps * PauseBetweenSteps. The default is
/// 50 * 200 = 10000 milliseconds = 10 seconds.
/// Setting the total animation time does not change the PauseBetweenSteps,
/// only the NumberOfSteps. Therefore the user must set PauseBetweenSteps
/// and NumberOfSteps, or PauseBetweenSteps and TotalAnimationTime.
///
public int TotalAnimationTime
{
get { return this.NumberOfSteps * this.PauseBetweenSteps; }
set { this.NumberOfSteps = value / this.PauseBetweenSteps; }
}
//=====================================================================
#endregion
//=====================================================================
#endregion
#region Events and Delegates
//=====================================================================
public delegate void siTransferFunctionHandler(siTransferFunction sender);
#region AnimationStarted
//=====================================================================
private siTransferFunctionHandler m_EventStorage_AnimationStarted;
///
/// An event raised when the animation process is started.
///
public event siTransferFunctionHandler Started
{
add { this.m_EventStorage_AnimationStarted += value; }
remove { this.m_EventStorage_AnimationStarted -= value; }
}
///
/// Raises the Started event.
///
protected void RaiseAnimationStarted()
{
if (this.m_EventStorage_AnimationStarted != null)
this.m_EventStorage_AnimationStarted(this.TransferFunctionToAnimate);
}
//=====================================================================
#endregion
#region AnimationFinished
//=====================================================================
private siTransferFunctionHandler m_EventStorage_AnimationFinished;
///
/// An event raised when the animation process is finished.
///
public event siTransferFunctionHandler Finished
{
add { this.m_EventStorage_AnimationFinished += value; }
remove { this.m_EventStorage_AnimationFinished -= value; }
}
///
/// Raises the finished event.
///
protected void RaiseAnimationFinished()
{
if (this.m_EventStorage_AnimationFinished != null)
this.m_EventStorage_AnimationFinished(this.TransferFunctionToAnimate);
}
//=====================================================================
#endregion
//=====================================================================
#endregion
#region Public Methods
//=====================================================================
///
/// Animate the given transfer function starting at the given start function.
/// A call to this method will firstly set the transfer function to animate to
/// the starting function, then update the function with a linearlly interpolated
/// function at each step on a background thread.
///
public void Start()
{
//Thread thread = new Thread(new ThreadStart(AnimateStepThenWait));
//thread.Name = "Animate Transfer Function";
//thread.Priority = ThreadPriority.AboveNormal;
//thread.Start();
this.AnimateStepThenWait();
}
///
/// Set the start and end functions.
///
///
///
public void SetStartAndEndFunctions(siTransferFunction tfStart, siTransferFunction tfEnd)
{
this.m_TransferFunctionStart = tfStart;
this.m_TransferFunctionEnd = tfEnd;
}
///
/// Set the start and end functions for a serialised XML file.
///
///
///
public void SetStartAndEndFunctions(String fileTfStart, String fileTfEnd)
{
this.m_TransferFunctionStart = siTransferFunction.FromXmlFile(fileTfStart);
this.m_TransferFunctionEnd = siTransferFunction.FromXmlFile(fileTfEnd);
}
//=====================================================================
#endregion
#region Private Methods
//=====================================================================
///
/// Perform the anitmate step, then causes the thread to sleep for the
/// PauseBetweenSteps milliseconds
///
private void AnimateStepThenWait()
{
double step = 0.0;
this.RaiseAnimationStarted();
while (step < 1.0)
{
this.AnimateStep(step);
step += (1.0 / (double)this.m_NumberOfSteps);
Thread.Sleep(this.m_PauseBetweenSteps);
}
this.AnimateStep(1.0);
this.RaiseAnimationFinished();
}
///
/// Perform an animation step. The input parameter step must vary between
/// 0.0 and 1.0 to indicate the start and end steps respectively.
///
/// A value between 0.0 and 1.0 indicating the proportion of animation.
private void AnimateStep(double step)
{
if (step < 0.0) return;
if (step > 1.0) return;
// Disable the modified event
this.m_TransferFunctionToAnimate.ModifiedEventEnabled = false;
// Do for each part
for (int i = 0; i < this.m_TransferFunctionStart.Parts.Count; i++)
{
if (this.m_TransferFunctionStart.Parts[i] is siTransferFunctionPartTrapezoid)
this.AnimateStepTrapezoid(step,
this.m_TransferFunctionStart.Parts[i] as siTransferFunctionPartTrapezoid,
this.m_TransferFunctionEnd.Parts[i] as siTransferFunctionPartTrapezoid,
this.m_TransferFunctionToAnimate.Parts[i] as siTransferFunctionPartTrapezoid);
}
// Enable the modified event
this.m_TransferFunctionToAnimate.ModifiedEventEnabled = true;
}
private void AnimateStepTrapezoid(double step,
siTransferFunctionPartTrapezoid partStart,
siTransferFunctionPartTrapezoid partEnd,
siTransferFunctionPartTrapezoid partOut)
{
double backColor1DiffA = (double)partEnd.BackColor1Alpha - (double)partStart.BackColor1Alpha;
double backColor1A = (double)partStart.BackColor1Alpha + backColor1DiffA * step;
double backColor1DiffR = (double)partEnd.BackColor1RGB.R - (double)partStart.BackColor1RGB.R;
double backColor1R= (double)partStart.BackColor1RGB.R + backColor1DiffR * step;
double backColor1DiffG = (double)partEnd.BackColor1RGB.G - (double)partStart.BackColor1RGB.G;
double backColor1G = (double)partStart.BackColor1RGB.G + backColor1DiffG * step;
double backColor1DiffB = (double)partEnd.BackColor1RGB.B - (double)partStart.BackColor1RGB.B;
double backColor1B = (double)partStart.BackColor1RGB.B + backColor1DiffB * step;
partOut.BackColor1 = Color.FromArgb((int)Math.Round(backColor1A),
(int)Math.Round(backColor1R),
(int)Math.Round(backColor1G),
(int)Math.Round(backColor1B));
double backColor2DiffA = (double)partEnd.BackColor2Alpha - (double)partStart.BackColor2Alpha;
double backColor2A = (double)partStart.BackColor2Alpha + backColor2DiffA * step;
double backColor2DiffR = (double)partEnd.BackColor2RGB.R - (double)partStart.BackColor2RGB.R;
double backColor2R = (double)partStart.BackColor2RGB.R + backColor2DiffR * step;
double backColor2DiffG = (double)partEnd.BackColor2RGB.G - (double)partStart.BackColor2RGB.G;
double backColor2G = (double)partStart.BackColor2RGB.G + backColor2DiffG * step;
double backColor2DiffB = (double)partEnd.BackColor2RGB.B - (double)partStart.BackColor2RGB.B;
double backColor2B = (double)partStart.BackColor2RGB.B + backColor2DiffB * step;
partOut.BackColor2 = Color.FromArgb((int)Math.Round(backColor2A),
(int)Math.Round(backColor2R),
(int)Math.Round(backColor2G),
(int)Math.Round(backColor2B));
}
//=====================================================================
#endregion
}
}