Click here to Skip to main content
15,894,896 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.8K   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 System.IO;

#endregion Using Statements

namespace AViD
{
  /// <summary>
  ///   The CoreType defines what sort of core is used for the dendrimer.
  /// </summary>
  public enum CoreType
  {
    Linear,
    Trigonal,
    Tetrahedral,
  }
  /// <summary>
  ///   Indicates what dimensions the dendrimer should use (ie. 2D or 3D).
  /// </summary>
  public enum Dimension
  {
    TwoDimensional,
    ThreeDimensional
  }

  /// <summary>
  ///   The DendrimerBuilder class constructs the dendrimer with the provided inputs asynchronously.
  /// </summary>
  public class DendrimerBuilder : IWorker
  {
    #region Public Static Methods

    /// <summary>
    ///   Takes the provided color and produces a darker color for a shadow.
    /// </summary>
    /// <param name="color">The color to base off the darker 'shadow' color.</param>
    /// <returns>The shadow <see cref="Color"/>.</returns>
    public static Color GetShadowColor(Color color)
    {
      return Color.FromArgb(
        color.A,
        Math.Max(color.R - _AMOUNT_OF_SHADOW_DARKNESS, 0),
        Math.Max(color.G - _AMOUNT_OF_SHADOW_DARKNESS, 0),
        Math.Max(color.B - _AMOUNT_OF_SHADOW_DARKNESS, 0));
    }

    #endregion Public Static Methods

    #region Constants

    /// <summary>
    ///   Provides the maximum number of generations for the <see cref="DendrimerFactor"/>
    ///   to produce of a Dendrimer.
    /// </summary>
    public const int MAX_NUMBER_OF_GENERATIONS = 6;

    /// <summary>
    ///   Defines how much the original color is added to to get the shadow color.
    /// </summary>
    private const int _AMOUNT_OF_SHADOW_DARKNESS = 50;
    /// <summary>
    ///   Determines the number of colors required, which is one more than the max number of generations (one for the core).
    /// </summary>
    private const int _REQUIRED_NUMBER_OF_COLORS = DendrimerBuilder.MAX_NUMBER_OF_GENERATIONS + 1;

    /// <summary>
    ///   The threshold used to compare two vectors to see if they are indeed the same.  We have to use this
    ///   because floating point arithmetic operations are known to have very small amounts of error.
    /// </summary>
    private const float _COMPARISON_THRESHOLD = 0.9999f;

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

    /// <summary>
    ///   Radius of the spheres which make up the ball figures in two dimensions.
    /// </summary>
    private const float _RADIUS_OF_BALL_TWO_DIMENSIONAL = 0.75f;
    /// <summary>
    ///   Radius of the spheres which make up the ball figures in three dimensions.
    /// </summary>
    private const float _RADIUS_OF_BALL_THREE_DIMENSIONAL = 1.0f;
    /// <summary>
    ///   Radius of the cylinders which make up the stick figures in two dimensions.
    /// </summary>
    private const float _RADIUS_OF_STICK_TWO_DIMENSIONAL = .20f;
    /// <summary>
    ///   Radius of the cylinders which make up the stick figures in three dimensions.
    /// </summary>
    private const float _RADIUS_OF_STICK_THREE_DIMENSIONAL = .50f;

    /// <summary>
    ///   The error message used whenever an exception is thrown when
    ///   needed DirectX files are not found.
    /// </summary>
    private const string @_DIRECT_X_EXCEPTION_MESSAGE = "Unable to load necessary DirectX files, please read the 'System Requirements' section in the help documentation to determine how to resolve this issue.";

    #endregion Constants

    #region Constructor

    /// <summary>
    ///   Constructs a new <see cref="DendrimerBuilder"/> instance to construct a dendrimer.
    /// </summary>
    /// <param name="device">The device for which this will be rendered for.</param>
    /// <param name="coreType">The type of the core.</param>
    /// <param name="intNumberOfChildren">The number of children at each subsequent generation.</param>
    /// <param name="dendrimerDimension">The dimensions to use when creating the dendrimer model.</param>
    /// <param name="clrGenerationColors">The colors to use for each generation, must match the number of generations.</param>
    /// <param name="fltStartingLength">The starting length of the sticks.</param>
    public DendrimerBuilder(Device device, CoreType coreType, int intNumberOfChildren, Dimension dendrimerDimension, Color[] clrGenerationColors, float fltStartingLength)
    {
      // Validate we have enough colors.
      if (clrGenerationColors.Length < DendrimerBuilder._REQUIRED_NUMBER_OF_COLORS)
        throw new ArgumentException("The number of generation colors is insufficient.", "clrGenerationColors");

      this.m_blnAbortBuild = false;
      this.m_blnEnableEvents = true;
      this.m_Handlers = new EventHandlerList();
      this.m_Thread = null;

      this.m_Device = device;
      this.m_coreType = coreType;
      this.m_intNumberOfChildren = intNumberOfChildren;
      this.m_dendrimerDimension = dendrimerDimension;
      this.m_clrGenerationColors = clrGenerationColors;
      this.m_fltStartingLength = fltStartingLength;

      // Get the 'ambient' (or colors for the shadows) for the figures.
      this.m_clrAmbientGenerationColors = this._GetAmbientGenerationColors();

      // Create the length multipliers array.
      if (dendrimerDimension == Dimension.TwoDimensional)
        this.m_arLengthMultipliers = new float[DendrimerBuilder.MAX_NUMBER_OF_GENERATIONS + 1]
        {
          1.000f,
          1.000f,
          0.750f,
          0.300f,
          0.120f,
          0.050f,
          0.030f,
        };
      else
        this.m_arLengthMultipliers = new float[DendrimerBuilder.MAX_NUMBER_OF_GENERATIONS + 1]
        {
          1.000f,
          1.000f,
          0.550f,
          0.300f,
          0.150f,
          0.110f,
          0.090f,
        };

      // Determine the number of figures that will be created.
      this.m_intTotalFiguresToCreate = this._GetTotalFigureCount();
    }

    #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 DendrimerBuilder is working.
    /// </summary>
    public bool IsWorking
    {
      get
      {
        lock (this.m_objThreadLock)
        {
          return ((this.m_Thread != null) && this.m_Thread.IsAlive);
        }
      }
    }
    /// <summary>
    ///   Gets the produced <see cref="Figure"/> representing the requested dendrimer.
    ///   It is only available after the <see cref="DendrimerBuilder"/> has completed
    ///   its work.
    /// </summary>
    public Figure Dendrimer
    {
      get
      {
        lock (this.m_objThreadLock)
        {
          return this.m_Dendrimer;
        }
      }
      protected set
      {
        lock (this.m_objThreadLock)
        {
          this.m_Dendrimer = value;
        }
      }
    }
    /// <summary>
    ///   Gets the <see cref="Dimension"/> of the <see cref="Figure"/> built.
    /// </summary>
    public Dimension DendrimerDimension
    {
      get
      {
        return this.m_dendrimerDimension;
      }
    }
    /// <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(DendrimerBuilder._DEFAULT_JOIN_TIME);
        }

        // Clear the thread out.
        this.Thread = null;
      }
    }
    /// <summary>
    ///   Performs the build of the dendrimer 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 DendrimerBuilder 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 DendrimerBuilder 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);
        }
      }
    }

    #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_intTotalFiguresToCreate > 0)
        dblPercentComplete = (double)this.m_intFiguresCreated / (double)this.m_intTotalFiguresToCreate;
      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 DendrimerBuilder.
    /// </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 Device m_Device;
    private readonly CoreType m_coreType;
    private readonly int m_intNumberOfChildren;
    private readonly Dimension m_dendrimerDimension;
    private readonly Color[] m_clrGenerationColors;
    private readonly Color[] m_clrAmbientGenerationColors;
    private readonly float m_fltStartingLength;
    private readonly float[] m_arLengthMultipliers;

    private Figure m_Dendrimer;
    private int m_intTotalFiguresToCreate;
    private int m_intFiguresCreated;

    #endregion Private Members

    #region Private Methods

    /// <summary>
    ///   Performs the build of the dendrimer.
    /// </summary>
    private void _Build()
    {
      Ball core;

      // Initialize the build.
      this.Dendrimer = core = null;
      this.m_intFiguresCreated = 0;

      try
      {
        // Create the core.
        core = new Ball(
          0,
          this.m_Device,
          Vector3.Empty,
          (this.m_dendrimerDimension == Dimension.TwoDimensional) ?
            DendrimerBuilder._RADIUS_OF_BALL_TWO_DIMENSIONAL :
            DendrimerBuilder._RADIUS_OF_BALL_THREE_DIMENSIONAL,
          this.m_clrGenerationColors[0],
          this.m_clrAmbientGenerationColors[0]);
        ++this.m_intFiguresCreated;

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

        if (this.AbortBuild)
          return;

        if ((core.Generation + 1) <= DendrimerBuilder.MAX_NUMBER_OF_GENERATIONS)
        {
          // Iterate over each of the vectors necessary for the core type.
          foreach (Vector3 vecDirection in this._GetCoreVectors(this.m_coreType))
          {
            this._ExpandGeneration(core, core.Position, vecDirection, vecDirection);

            if (this.AbortBuild)
              return;
          }
        }

        // Expose the finished dendrimer to the world.
        this.Dendrimer = core;
      }
      catch (TypeInitializationException e)
      {
        // In the event a user doesn't have all the DirectX files installed, they'll get this exception.
        this.Exception = new ApplicationException(
          string.Format("{0}{1}{1}", _DIRECT_X_EXCEPTION_MESSAGE, Environment.NewLine),
          e);
      }
      catch (Exception e)
      {
        // Save off this exception.
        this.Exception = e;
      }
      finally
      {
        // Let the calling body know the work is done.
        this.OnComplete();
      }
    }
    /// <summary>
    ///   Expands the current ball with subsequent generations using the provided parameters.
    /// </summary>
    /// <param name="parent">The parent figure from which these figures will be children.</param>
    /// <param name="vecPosition">The starting position of the new generation.</param>
    /// <param name="vecDirection">The direction for the current generation to take.</param>
    /// <param name="vecCore">The vector for the originating core vector.</param>
    private void _ExpandGeneration(Figure parent, Vector3 vecPosition, Vector3 vecDirection, Vector3 vecCore)
    {
      Vector3 vecEndingPoint;
      Color clrDiffuseColor, clrAmbientColor;
      float fltLength;
      int intCurrentGeneration;
      Figure newParent;
      Stick stick;
      Ball ball;

      intCurrentGeneration = parent.Generation + 1;
      fltLength = this.m_arLengthMultipliers[intCurrentGeneration] * this.m_fltStartingLength;

      // Find the end point and the line color.
      vecEndingPoint = vecPosition + (vecDirection * fltLength);
      clrDiffuseColor = this.m_clrGenerationColors[intCurrentGeneration];
      clrAmbientColor = this.m_clrAmbientGenerationColors[intCurrentGeneration];

      // Create the stick.
      stick = new Stick(
        intCurrentGeneration,
        this.m_Device,
        vecPosition,
        vecDirection,
        fltLength,
        (this.m_dendrimerDimension == Dimension.TwoDimensional) ?
          DendrimerBuilder._RADIUS_OF_STICK_TWO_DIMENSIONAL :
          DendrimerBuilder._RADIUS_OF_STICK_THREE_DIMENSIONAL,
        clrDiffuseColor,
        clrAmbientColor);

      // Add it to the parent and set it as the new parent
      parent.AddChild(stick);
      ++this.m_intFiguresCreated;
      newParent = stick;

      // If we're in three dimensions, add a ball at the end.
      if (this.m_dendrimerDimension == Dimension.ThreeDimensional)
      {
        ball = new Ball(
          intCurrentGeneration,
          this.m_Device,
          vecEndingPoint,
          (this.m_dendrimerDimension == Dimension.TwoDimensional) ?
            DendrimerBuilder._RADIUS_OF_BALL_TWO_DIMENSIONAL :
            DendrimerBuilder._RADIUS_OF_BALL_THREE_DIMENSIONAL,
          clrDiffuseColor,
          clrAmbientColor);

        stick.AddChild(ball);
        ++this.m_intFiguresCreated;
        newParent = ball;
      }

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

      if (this.AbortBuild)
        return;

      // Recurse to find the other generations, if needed.
      if ((intCurrentGeneration + 1) <= DendrimerBuilder.MAX_NUMBER_OF_GENERATIONS)
      {
        // Make sure the direction isn't where we came from (we have a threshold because of imprecision in floats)
        foreach (Vector3 vecChild in this._GetChildVectors(vecDirection, vecCore))
        {
          this._ExpandGeneration(newParent, vecEndingPoint, vecChild, vecCore);

          if (this.AbortBuild)
            return;
        }
      }
    }
    /// <summary>
    ///   Gets the vectors that will come off from the core of the dendrimer.
    /// </summary>
    /// <param name="coreType">The type of the core.</param>
    /// <returns>The vectors that will come off from the core of the dendrimer.</returns>
    private Vector3[] _GetCoreVectors(CoreType coreType)
    {
      switch (coreType)
      {
        case CoreType.Linear:
          return new Vector3[]
          {
            new Vector3(1f, 0f, 0f),
            new Vector3(-1f, 0f, 0f),
          };

        case CoreType.Trigonal:
          return new Vector3[]
          {
            new Vector3(1f, 0f, 0f),
            new Vector3(-.5f, (-(float)Math.Sqrt(3f) / 2f), 0f),
            new Vector3(-.5f, ((float)Math.Sqrt(3f) / 2f), 0f),
          };

        case CoreType.Tetrahedral:

          if (this.m_dendrimerDimension == Dimension.TwoDimensional)
            return new Vector3[]
            {
              new Vector3(1f, 0f, 0f),
              new Vector3(-1f, 0f, 0f),
              new Vector3(0f, 1f, 0f),
              new Vector3(0f, -1f, 0f),
            };
          else /* if (this.m_dendrimerDimension == Dimension.ThreeDimensional) */
            return new Vector3[]
            {
              new Vector3(1f, 0f, 0f),
              new Vector3((-1f / 3f), (-(float)Math.Sqrt(2f) / 3f), (float)Math.Sqrt(2f / 3f)),
              new Vector3((-1f / 3f), ((2f * (float)Math.Sqrt(2f)) / 3f), 0f),
              new Vector3((-1f / 3f), (-(float)Math.Sqrt(2f) / 3f), -(float)Math.Sqrt(2f / 3f)),
            };

        default:
          throw new NotImplementedException(string.Format("CoreType, {0}, has not been implemented yet.", coreType.ToString()));
      }
    }
    /// <summary>
    ///   Gets the vectors that will come off from subsequent generations.
    /// </summary>
    /// <param name="vecInitialDirection">The initial vector for which we'll use to determine rotation.</param>
    /// <param name="vecCore">The vector for the originating core vector.</param>
    /// <returns>The vectors that will come off from the core of the dendrimer.</returns>
    private Vector3[] _GetChildVectors(Vector3 vecInitialDirection, Vector3 vecCore)
    {
      Vector3[] vectors;
      Matrix matrix;
      Vector3 vecStarting, vecAxisToRotate;
      float fltComparison;
      int intIndex;

      // To ensure that the code generates child vectors that are within the same plane
      // as the core's vector, when two children are expected and in 3d, I'm handling it specially here.
      if ((this.m_dendrimerDimension == Dimension.ThreeDimensional) && (this.m_intNumberOfChildren == 2))
      {
        // Find the axis to rotate along that is perpendicular to the core vector
        // and the 'up vector'.  It will allow us to keep within the same plane
        // for all child vectors.
        vecAxisToRotate = Vector3.Cross(vecCore, new Vector3(0f, 0f, 1f));

        // Rotate the initial vector by PI/3 and -PI/3 to get the two child vectors.
        vectors = new Vector3[]
        {
          Vector3.TransformCoordinate(vecInitialDirection, Matrix.RotationAxis(vecAxisToRotate, (float)Math.PI/3.0f)),
          Vector3.TransformCoordinate(vecInitialDirection, Matrix.RotationAxis(vecAxisToRotate, -(float)Math.PI/3.0f))
        };
      }
      else
      {
        switch (this.m_intNumberOfChildren)
        {
          case 1:
            vectors = new Vector3[]
            {
              new Vector3(1f, 0f, 0f),
            };
            break;

          case 2:
            vectors = new Vector3[]
            {
              new Vector3(.5f, ((float)Math.Sqrt(3f) / 2f), 0f),
              new Vector3(.5f, (-(float)Math.Sqrt(3f) / 2f), 0f),
            };
            break;

          case 3:

            if (this.m_dendrimerDimension == Dimension.TwoDimensional)
              vectors = new Vector3[]
              {
                new Vector3(1f, 0f, 0f),
                new Vector3(.5f, (float)Math.Sqrt(3f) / 2f, 0f),
                new Vector3(.5f, (float)Math.Sqrt(3f) / -2f, 0f),
              };
            else /* if (this.m_dendrimerDimension == Dimension.ThreeDimensional) */
              vectors = new Vector3[]
              {
                new Vector3((1f / 3f), ((float)Math.Sqrt(2f) / 3f), (float)Math.Sqrt(2f / 3f)),
                new Vector3((1f / 3f), ((-2f * (float)Math.Sqrt(2f)) / 3f), 0f),
                new Vector3((1f / 3f), ((float)Math.Sqrt(2f) / 3f), -(float)Math.Sqrt(2f / 3f)),
              };

            break;

          default:
            throw new NotImplementedException(string.Format("NumberOfChildren, {0}, has not been implemented yet.", this.m_intNumberOfChildren.ToString()));
        }

        // Assume the starting vector will be (1, 0, 0) because all the vectors assume a starting point
        // of (1, 0, 0).
        vecStarting = new Vector3(1f, 0f, 0f);

        // Determine if the two vectors are very close to each other. If they're not, then we can rotate freely.
        if (Math.Abs(fltComparison = Vector3.Dot(vecStarting, vecInitialDirection)) < DendrimerBuilder._COMPARISON_THRESHOLD)
        {
          matrix = Utility.ComputeRotationMatrix(vecStarting, vecInitialDirection);

          // Rotate each vector by the rotational matrix.
          for (intIndex = 0; intIndex < vectors.Length; ++intIndex)
            vectors[intIndex] = Vector3.TransformNormal(vectors[intIndex], matrix);
        }
        else if (fltComparison < 0)
        {
          // If we're less than zero, the vector is very close and in the opposite direction.
          // Just negate the vectors to rotate them properly.
          for (intIndex = 0; intIndex < vectors.Length; ++intIndex)
            vectors[intIndex] = vectors[intIndex] * -1;
        }
      }

      return vectors;
    }
    /// <summary>
    ///   Gets the ambient (or shadow) generation colors for the dendrimer.
    /// </summary>
    /// <returns>The array of ambient generation colors we produced from the generation colors.</returns>
    private Color[] _GetAmbientGenerationColors()
    {
      Color[] clrAmbientGenerationColors;

      clrAmbientGenerationColors = new Color[this.m_clrGenerationColors.Length];

      for (int i = 0; i < this.m_clrGenerationColors.Length; ++i)
        clrAmbientGenerationColors[i] = DendrimerBuilder.GetShadowColor(this.m_clrGenerationColors[i]);

      return clrAmbientGenerationColors;
    }
    /// <summary>
    ///   Determines the total number of figures that will comprise the Dendrimer being created.
    /// </summary>
    /// <returns>The total number of figures that will comprise the Dendrimer being created.</returns>
    private int _GetTotalFigureCount()
    {
      int intGenerationOneGrowthFactor, intFiguresPerGeneration;

      // Handle the first generation directly attached to the core.
      if (DendrimerBuilder.MAX_NUMBER_OF_GENERATIONS > 0)
      {
        switch (this.m_coreType)
        {
          case CoreType.Linear:
            intGenerationOneGrowthFactor = 2;
            break;
          case CoreType.Trigonal:
            intGenerationOneGrowthFactor = 3;
            break;
          case CoreType.Tetrahedral:
            intGenerationOneGrowthFactor = 4;
            break;
          default:
            throw new NotImplementedException(string.Format("CoreType, {0}, has not been implemented yet.", this.m_coreType.ToString()));
        }
      }

      intFiguresPerGeneration = (this.m_dendrimerDimension == Dimension.TwoDimensional) ? 1 : 2;

      // Proof of Correctness
      //
      // F1 = intGenerationOneGrowthFactor
      // F2 = this.m_intNumberOfChildren
      //  n = Number of Generations (DendrimerBuilder.MAX_NUMBER_OF_GENERATIONS)
      //  p = Number of figures per generation
      //  
      // Series: Sn = 1 + (p * F1) + (p * F1 * F2) + (p * F1 * F2^2) + (p * F1 * F2^3) + ... + (p * F1 * F2^n)
      //            = 1 + [p * F1 * (1 + F2 + F2^2 + F2^3 + ... + F2^n)]
      //            = 1 + [p * F1 * ((1 - F2^n) / (1 - F2))]

      if (this.m_intNumberOfChildren > 1)
        return (1 + (intFiguresPerGeneration * intGenerationOneGrowthFactor * ((1 - ((int)Math.Pow(this.m_intNumberOfChildren, DendrimerBuilder.MAX_NUMBER_OF_GENERATIONS)))
                                                                             / (1 - this.m_intNumberOfChildren))));
      else
        return (1 + (intFiguresPerGeneration * intGenerationOneGrowthFactor * DendrimerBuilder.MAX_NUMBER_OF_GENERATIONS));
    }

    #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