/*============================================================================= 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 } }