Click here to Skip to main content
15,886,110 members
Articles / Desktop Programming / Windows Forms

AViD

Rate me:
Please Sign up or sign in to vote.
4.81/5 (18 votes)
3 Mar 2009CPOL10 min read 53.6K   1.2K   24  
An application for visualizing common dendrimer models
#region Using Statements

using System;
using System.Collections.Generic;
using System.Drawing;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;

using AViD.Figures;
using System.ComponentModel;
using System.Threading;
using Gif.Components;

#endregion Using Statements

namespace AViD
{
  /// <summary>
  ///   The AnimatedGifBuilder class constructs the animated gif with the provided inputs asynchronously.
  /// </summary>
  public class AnimatedGifBuilder : IWorker
  {
    #region Constants

    /// <summary>
    ///   The default time we'll wait on the thread after we've aborted it.
    /// </summary>
    private const int DEFAULT_JOIN_TIME = 5000;

    #endregion Constants

    #region Constructor

    /// <summary>
    ///   Constructs a new <see cref="AnimatedGifBuilder"/> instance to construct an animated gif.
    /// </summary>
    /// <param name="strFileName">The filename of which the animated gif will be written to.</param>
    /// <param name="intNumberOfFrames">The number of frames to create in the animated gif.</param>
    /// <param name="intDelayBetweenFrames">The delay in milliseconds between the frames of the animated gif.</param>
    /// <param name="formAViD">The <see cref="AViD"/> form which will allow us to capture the screen shots.</param>
    /// <param name="camera">The camera which controls the point of view of the capture.</param>
    public AnimatedGifBuilder(string strFileName, int intNumberOfFrames, int intDelayBetweenFrames, AViD formAViD, Camera camera)
    {
      this.m_blnAbortBuild = false;
      this.m_blnEnableEvents = true;
      this.m_Handlers = new EventHandlerList();
      this.m_Thread = null;

      this.m_strFileName = strFileName;
      this.m_intNumberOfFrames = intNumberOfFrames;
      this.m_intDelayBetweenFrames = intDelayBetweenFrames;
      this.m_formAViD = formAViD;
      this.m_Camera = camera;
    }

    #endregion Constructor

    #region Public Properties

    /// <summary>
    ///   Gets/sets whether or not the events are enabled.
    /// </summary>
    public bool EnableEvents
    {
      get
      {
        lock (this.m_objThreadLock)
        {
          return this.m_blnEnableEvents;
        }
      }
      set
      {
        lock (this.m_objThreadLock)
        {
          if (!this.IsWorking)
          {
            this.m_blnEnableEvents = value;
          }
        }
      }
    }
    /// <summary>
    ///   Gets whether or not the AnimatedGifBuilder is working.
    /// </summary>
    public bool IsWorking
    {
      get
      {
        lock (this.m_objThreadLock)
        {
          return ((this.m_Thread != null) && this.m_Thread.IsAlive);
        }
      }
    }
    /// <summary>
    ///   Gets the exception that was produced (if any) during the thread's execution.
    /// </summary>
    public Exception Exception
    {
      get
      {
        lock (this.m_objThreadLock)
        {
          return this.m_Exception;
        }
      }
      protected set
      {
        lock (this.m_objThreadLock)
        {
          this.m_Exception = value;
        }
      }
    }

    #endregion Public Properties

    #region Public Methods

    /// <summary>
    ///   Attempts to abort the current task.
    /// </summary>
    public void Abort()
    {
      Thread thread;

      if (this.IsWorking)
      {
        // Attempt to inform the thread to abort (this is more graceful than an abort call on the thread).
        this.AbortBuild = true;

        if ((thread = this.Thread) != null)
        {
          // Attempt to join on the thread, waiting for it to end.
          thread.Join(AnimatedGifBuilder.DEFAULT_JOIN_TIME);
        }

        // Clear the thread out.
        this.Thread = null;
      }
    }
    /// <summary>
    ///   Performs the build of the animated gif asynchronously.
    /// </summary>
    public void Work()
    {
      lock (this.m_objThreadLock)
      {
        if (this.IsWorking && !this.AbortBuild)
          throw new ArgumentException("Existing thread still exists, thus the build was aborted.");
        else
        {
          // Clear the abort work flag.
          this.AbortBuild = false;

          this.m_Thread = new Thread(new ThreadStart(this._Build));
          this.m_Thread.Start();
        }
      }
    }

    #endregion Public Methods

    #region Events/Delegates

    /// <summary>
    ///   Handles the event at which the AnimatedGifBuilder is done.
    /// </summary>
    public event EventHandler Complete
    {
      add
      {
        lock (this.m_objThreadLock)
        {
          this.m_Handlers.AddHandler(this.m_objCompleteEventHandler, value);
        }
      }
      remove
      {
        lock (this.m_objThreadLock)
        {
          this.m_Handlers.RemoveHandler(this.m_objCompleteEventHandler, value);
        }
      }
    }
    /// <summary>
    ///   Handles the event at which the AnimatedGifBuilder has a progress message for an external body.
    /// </summary>
    public event EventHandler<ProgressEventArgs> Progress
    {
      add
      {
        lock (this.m_objThreadLock)
        {
          this.m_Handlers.AddHandler(this.m_objProgressEventHandler, value);
        }
      }
      remove
      {
        lock (this.m_objThreadLock)
        {
          this.m_Handlers.RemoveHandler(this.m_objProgressEventHandler, value);
        }
      }
    }

    /// <summary>
    ///   Gets the screen shot of the currently displayed dendrimer.
    /// </summary>
    /// <param name="blnShowSelectedFigures">If true, the glow given to selected figures will be shown; If false, it will not be shown.</param>
    /// <param name="blnForceRender">If true, forces the form to rerender the dendrimer.</param>
    /// <returns>The current screen shot of the dendrimer being rendered.</returns>
    public delegate Bitmap GetScreenShotHandler(bool blnShowSelectedFigures, bool blnForceRender);

    #endregion Events/Delegates

    #region Protected Members

    /// <summary>
    ///   Gets/sets the flag to indicate the build needs to be aborted.
    /// </summary>
    protected bool AbortBuild
    {
      get
      {
        lock (this.m_objThreadLock)
        {
          return this.m_blnAbortBuild;
        }
      }
      set
      {
        lock (this.m_objThreadLock)
        {
          this.m_blnAbortBuild = value;
        }
      }
    }
    /// <summary>
    ///   Gets/sets the <see cref="Thread"/> used to execute the build asynchronously.
    /// </summary>
    protected Thread Thread
    {
      get
      {
        lock (this.m_objThreadLock)
        {
          return this.m_Thread;
        }
      }
      set
      {
        lock (this.m_objThreadLock)
        {
          this.m_Thread = value;
        }
      }
    }

    #endregion Protected Members

    #region Protected Methods

    /// <summary>
    ///   Notifies any listeners of what the current progress is.
    /// </summary>
    protected void SendProgress()
    {
      double dblPercentComplete;

      if (this.m_intNumberOfFrames > 0)
        dblPercentComplete = (double)this.m_intCurrentFrame / (double)this.m_intNumberOfFrames;
      else
        dblPercentComplete = 1.0d;

      this.OnProgress(new ProgressEventArgs(Math.Max(0.0d, Math.Min(dblPercentComplete, 1.0d))));
    }
    /// <summary>
    ///   Handles firing the Progress event for the AnimatedGifBuilder.
    /// </summary>
    /// <param name="e">The <see cref="ProgressEventArgs"/> arguments for the event.</param>
    protected void OnProgress(ProgressEventArgs e)
    {
      EventHandler<ProgressEventArgs> handler;

      if (this.EnableEvents)
      {
        if ((handler = (this.m_Handlers[this.m_objProgressEventHandler] as EventHandler<ProgressEventArgs>)) != null)
          handler(this, e);
      }
    }
    /// <summary>
    ///   Handles firing the Complete event for the Worker.
    /// </summary>
    protected void OnComplete()
    {
      EventHandler handler;

      if (this.EnableEvents)
      {
        if ((handler = (this.m_Handlers[this.m_objCompleteEventHandler] as EventHandler)) != null)
          handler(this, EventArgs.Empty);
      }
    }

    #endregion Protected Methods

    #region Private Members

    private object m_objThreadLock = new object();
    private object m_objCompleteEventHandler = new object();
    private object m_objProgressEventHandler = new object();

    private bool m_blnAbortBuild;
    private bool m_blnEnableEvents;
    private EventHandlerList m_Handlers;
    private Thread m_Thread;
    private Exception m_Exception;

    private readonly string m_strFileName;
    private readonly int m_intNumberOfFrames;
    private readonly int m_intDelayBetweenFrames;
    private readonly AViD m_formAViD;
    private readonly Camera m_Camera;

    private int m_intCurrentFrame;

    #endregion Private Members

    #region Private Methods

    /// <summary>
    ///   Builds the animated gif by rotating and then capturing the displayed image of the dendrimer.
    /// </summary>
    private void _Build()
    {
      AnimatedGifEncoder gifEncoder;
      GetScreenShotHandler delegateScreenShot;
      float fltAnglePerIteration;

      // Initialize the build.
      this.m_intCurrentFrame = 0;

      // Creates the Gif Encoder.
      gifEncoder = new AnimatedGifEncoder();

      try
      {
        // Set the output file name.
        gifEncoder.Start(this.m_strFileName);

        // Set the delay between the frames.
        gifEncoder.SetDelay(this.m_intDelayBetweenFrames);

        // Set the image to repeat infinitly.
        gifEncoder.SetRepeat(0);

        // Determine the angle per iteration.
        fltAnglePerIteration = (((float)Math.PI) * 2.0f) / (float)this.m_intNumberOfFrames;

        // Generate the delegate.
        delegateScreenShot = new GetScreenShotHandler(this.m_formAViD.GetScreenShot);

        // Now, rotate the camera around taking snapshots.
        for (this.m_intCurrentFrame = 0; this.m_intCurrentFrame < this.m_intNumberOfFrames; ++this.m_intCurrentFrame)
        {
          // Rotate the camera.
          this.m_Camera.Rotate(fltAnglePerIteration, 0);

          // Rerender the scene and then capture the screenshot.
          using (Bitmap image = (Bitmap)this.m_formAViD.Invoke(delegateScreenShot, new object[] { true, true }))
            gifEncoder.AddFrame(image);

          // Notify any listeners of the progress we've made.
          this.SendProgress();

          if (this.AbortBuild)
            return;
        }
      }
      catch (Exception e)
      {
        // Save off this exception.
        this.Exception = e;
      }
      finally
      {
        // Finishes the animated gif.
        gifEncoder.Finish();

        // Let the calling body know the work is done.
        this.OnComplete();
      }
    }

    #endregion Private Methods
  }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect
United States United States
Since I've begun my profession as a software developer, I've learned one important fact - change is inevitable. Requirements change, code changes, and life changes.

So..If you're not moving forward, you're moving backwards.

Comments and Discussions