Click here to Skip to main content
15,895,084 members
Articles / Programming Languages / C#

DSGraphEdit: A Reasonable Facsimile of Microsoft's GraphEdit in .NET

Rate me:
Please Sign up or sign in to vote.
4.93/5 (79 votes)
28 Jan 2018MIT7 min read 303.3K   10K   142  
A library for adding DirectShow GraphEdit-like abilities to .NET applications
using System;
using System.IO;
using System.ComponentModel;
using System.Collections.Generic;
using System.Text;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Reflection;

using DaggerLib.UI;
using DaggerLib.Interfaces;

namespace DaggerLib.Core
{
    [Serializable]
    public class DaggerGraph :  ISerializable , ICloneable
    {
        #region Fields

        private List<DaggerNode> _nodes;

        //the user interface graph handling this graph
        private IDaggerUIGraph _parentUIGraph;

        //the number of subgraphs in this graph as computed by CalculateTopology
        private int _subGraphCount = 1;

        //list of guids of pin connections used to reconstruct after deserialization
        private List<PinConnection> _allConnections;

        internal DaggerPinCollection<DaggerOutputPin> _importedPins;
        internal DaggerPinCollection<DaggerInputPin> _exportedPins;

        private List<DaggerOutputPin> inlist;
        private List<DaggerInputPin> exlist;

        //flag to indicate if this graph has been fully deserialized
        private bool _isDeserialized = false;

        //flag to prevent pin AutoCloning during the deserialization process
        internal bool _isDeserializing = false;

        /// <summary>
        /// Which subgraph to serialize.  -1 for all subgraphs
        /// </summary>
        private int _subGraphToSerialize = -1;

        /// <summary>
        /// Selection of nodes and noodles to serialize
        /// </summary>
        private ISelector _selectionToSerialize = null;

        // SubNode this graph lies within
        internal DaggerSubNode _parentSubNode;

        /// <summary>
        /// Nodes that need assistance deserializing because they're not marked "Serializable"
        /// </summary>
        private List<DaggerNodeNonSerializationAssistant> _assistNodes;

        /// <summary>
        /// Graph scheduler that performs processing of the the graph
        /// </summary>
        private IGraphScheduler _scheduler;

        // stored layout of ui elements
        private List<GraphLayout> _layout;

        #endregion

        #region ctor

        public DaggerGraph()
        {
            _nodes = new List<DaggerNode>();

            _importedPins = new DaggerPinCollection<DaggerOutputPin>(this);
            _importedPins.PinAdded += new DaggerPinAdded(_importedPins_PinAdded);
            _exportedPins = new DaggerPinCollection<DaggerInputPin>(this);
            _exportedPins.PinAdded += new DaggerPinAdded(_exportedPins_PinAdded);

            _scheduler = new OrdinalExecutionScheduler();
            _scheduler.Graph = this;
        }

        /// <summary>
        /// Construct an empty DaggerGraph from a Pin Interface
        /// </summary>
        /// <param name="pinInterface"></param>
        public DaggerGraph(DaggerInterface pinInterface)
        {
            _nodes = new List<DaggerNode>();

            _importedPins = new DaggerPinCollection<DaggerOutputPin>(this);
            _importedPins.PinAdded += new DaggerPinAdded(_importedPins_PinAdded);
            _exportedPins = new DaggerPinCollection<DaggerInputPin>(this);
            _exportedPins.PinAdded += new DaggerPinAdded(_exportedPins_PinAdded);

            // Add the imported/exported pins
            AddImportPins(pinInterface.InputPins);
            AddExportPins(pinInterface.OutputPins);

            _scheduler = new OrdinalExecutionScheduler();
            _scheduler.Graph = this;
        }

        /// <summary>
        /// Deserialization Constructor
        /// </summary>
        /// <param name="info"></param>
        /// <param name="ctxt"></param>
        protected DaggerGraph(SerializationInfo info, StreamingContext ctxt)
        {
            if (info == null)
                throw new System.ArgumentNullException("info");

            _importedPins = new DaggerPinCollection<DaggerOutputPin>(this);
            _importedPins.PinAdded += new DaggerPinAdded(_importedPins_PinAdded);
            _exportedPins = new DaggerPinCollection<DaggerInputPin>(this);
            _exportedPins.PinAdded += new DaggerPinAdded(_exportedPins_PinAdded);

            inlist = (List<DaggerOutputPin>)info.GetValue("ImportedPins", typeof(List<DaggerOutputPin>));
            exlist  = (List<DaggerInputPin>)info.GetValue("ExportedPins", typeof(List<DaggerInputPin>));

            // get serialized nodes
            _nodes = (List<DaggerNode>)info.GetValue("AllNodes", typeof(List<DaggerNode>));

            // get list of nodes that need assistance to deserialize
            _assistNodes = (List<DaggerNodeNonSerializationAssistant>)info.GetValue("AssistNodes", typeof(List<DaggerNodeNonSerializationAssistant>));
            
            // try to get the layout
            try
            {
                _layout = (List<GraphLayout>)info.GetValue("Layout",typeof(List<GraphLayout>));
            }
            catch (Exception ex)
            {
                //layout wasn't stored
            }

            _allConnections = (List<PinConnection>)info.GetValue("AllConnections", typeof(List<PinConnection>));            
        }

        #endregion

        #region Events

        /// <summary>
        /// Called whenever a node is added/removed or a pin is connected/disconnected
        /// </summary>
        public event EventHandler OnTopologyChanged;

        /// <summary>
        /// Called when a node has been added to the graph
        /// </summary>
        public event EventHandler NodeAdded;

        /// <summary>
        /// Called before a node is removed from the graph
        /// </summary>
        public event BeforeNodeRemoveHandler BeforeNodeRemoved;

        /// <summary>
        /// Called after a node is removed from the graph
        /// </summary>
        public event AfterNodeRemoveHandler AfterNodeRemoved;

        /// <summary>
        /// Called before two pins are connected
        /// </summary>
        public event PinBeforeConnectedHandler BeforePinsConnected;

        /// <summary>
        /// Called after two pins are connected
        /// </summary>
        public event PinAfterConnectedHandler AfterPinsConnected;

        /// <summary>
        /// Called before two pins are disconnected
        /// </summary>
        public event PinBeforeDisconnectedHandler BeforePinsDisconnected;

        /// <summary>
        /// Called after two pins are disconnected
        /// </summary>
        public event PinAfterDisconnectedHandler AfterPinsDisconnected;

        #endregion

        #region Properties

        [Browsable(false)]
        public IDaggerUIGraph ParentUIGraph
        {
            get
            {
                return _parentUIGraph;
            }
            set
            {
                _parentUIGraph = value;
            }
        }

        /// <summary>
        /// Get the stored layout of ui elements
        /// </summary>
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public List<GraphLayout> UILayout
        {
            get
            {
                return _layout;
            }
        }

        /// <summary>
        /// Gets the SubNode this graph lies within
        /// </summary>
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public DaggerSubNode ParentSubNode
        {
            get
            {
                return _parentSubNode;
            }
        }

        /// <summary>
        /// Sets a selection of Nodes and Noodles to serialize
        /// </summary>
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public ISelector SelectionToSerialize
        {
            set
            {
                _selectionToSerialize = value;
            }
        }

        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public IGraphScheduler GraphScheduler
        {
            get
            {
                return _scheduler;
            }
            internal set
            {
                if (_scheduler != null)
                {
                    _scheduler.CancelProcessing();
                    _scheduler.Dispose();
                    _scheduler = null;
                }

                // we never want the Scheduler to be null
                if (value != null)
                {
                    _scheduler = value;
                    _scheduler.Graph = this;
                }
                else
                {
                    _scheduler = new OrdinalExecutionScheduler();
                    _scheduler.Graph = this;
                }

                _scheduler.OnTopologyChanged();
            }
        }

        public DaggerPinCollection<DaggerOutputPin> ImportedPins
        {
            get
            {
                return _importedPins;
            }
        }

        public DaggerPinCollection<DaggerInputPin> ExportedPins
        {
            get
            {
                return _exportedPins;
            }
        }

        public List<DaggerNode> AllNodes
        {
            get
            {
                //create a duplicate list and sort them by thier ordinals
                List<DaggerNode> allnodes = new List<DaggerNode>(_nodes);
                allnodes.Sort(new OrdinalComparer());
                return allnodes;
            }
        }

        /// <summary>
        /// Gets the number of subgraphs in this graph
        /// </summary>
        [Browsable(false)]
        public int SubGraphCount
        {
            get
            {
                return _subGraphCount;
            }
        }

        /// <summary>
        /// Gets the nodes in a subgraph of this graph
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        [Browsable(false)]
        public List<DaggerNode> this[int subgraph]
        {
            get
            {
                if (subgraph > (_subGraphCount - 1))
                {
                    throw new ArgumentOutOfRangeException();
                }

                List<DaggerNode> snodes = new List<DaggerNode>();
                foreach (DaggerNode node in _nodes)
                {
                    if (subgraph == node.SubgraphAffiliation)
                    {
                        snodes.Add(node);
                    }
                }

                //sort the new list by the ordinals
                snodes.Sort(new OrdinalComparer());
                return snodes;
            }
        }

        /// <summary>
        /// Gets all the nodes in an Ordinal of a SubGraph
        /// </summary>
        /// <param name="subgraph"></param>
        /// <param name="ordinal"></param>
        /// <returns></returns>
        [Browsable(false)]
        public List<DaggerNode> this[int subgraph, int ordinal]
        {
            get
            {
                List<DaggerNode> sgraph = this[subgraph];

                List<DaggerNode> nodes = new List<DaggerNode>();

                foreach (DaggerNode node in sgraph)
                {
                    if (node.Ordinal == ordinal)
                    {
                        nodes.Add(node);
                    }
                }

                return nodes;
            }
        }

        /// <summary>
        /// Get list of all nodes that have no connected input pins
        /// </summary>
        public List<DaggerNode> TopLevelNodes
        {
            get
            {
                List<DaggerNode> list = new List<DaggerNode>();

                foreach (DaggerNode node in _nodes)
                {
                    if ((node != null) && (node.IsTopLevel))
                    {
                        list.Add(node);
                    }
                }
                return list;
            }
        }

        /// <summary>
        /// Get list of all nodes that have no connected output pins
        /// </summary>
        public List<DaggerNode> BottomLevelNodes
        {
            get
            {
                List<DaggerNode> list = new List<DaggerNode>();

                foreach (DaggerNode node in _nodes)
                {
                    if ((node != null) && (node.IsBottomLevel))
                    {
                        list.Add(node);
                    }
                }
                return list;
            }
        }

        /// <summary>
        /// Get a list of all valid pin connections in a selection
        /// </summary>
        /// <param name="nodes"></param>
        /// <param name="includeExports"></param>
        /// <returns></returns>
        private List<PinConnection> this[ISelector selection]
        {
            get
            {
                List<PinConnection> con = new List<PinConnection>();

                foreach (IDaggerNoodle noodle in selection.SelectedNoodles)
                {                    
                    if (noodle.OutputPin.ParentNode == null)
                    {
                        // is it in imported noodle?
                        if (selection.SelectedNodes.Contains(noodle.InputPin.ParentNode.UINode))
                        {
                            PinConnection pc = new PinConnection();
                            pc.ConnectedFrom = noodle.OutputPin.InstanceGuid;
                            pc.ConnectedTo = noodle.InputPin.InstanceGuid;
                            con.Add(pc);
                        }
                    }
                    else if (noodle.InputPin.ParentNode == null)
                    {
                        // is it an exported noodle?
                        if (selection.SelectedNodes.Contains(noodle.OutputPin.ParentNode.UINode))
                        {
                            PinConnection pc = new PinConnection();
                            pc.ConnectedFrom = noodle.OutputPin.InstanceGuid;
                            pc.ConnectedTo = noodle.InputPin.InstanceGuid;
                            con.Add(pc);
                        }
                    }
                    else
                    {
                        // it's a regular noodle
                        if (selection.SelectedNodes.Contains(noodle.InputPin.ParentNode.UINode) && selection.SelectedNodes.Contains(noodle.OutputPin.ParentNode.UINode))
                        {
                            PinConnection pc = new PinConnection();
                            pc.ConnectedFrom = noodle.OutputPin.InstanceGuid;
                            pc.ConnectedTo = noodle.InputPin.InstanceGuid;
                            con.Add(pc);
                        }
                    }
                }
                return con;
            }
        }

        /// <summary>
        /// Get a list of all pin connections in a subgraph
        /// </summary>
        private List<PinConnection> this[int subgraph,bool includeExports]
        {
            get
            {
                List<PinConnection> con = new List<PinConnection>();

                if (subgraph < SubGraphCount)
                {
                    List<DaggerNode> allnodes = AllNodes;
                    foreach (DaggerNode node in allnodes)
                    {
                        if (node.SubgraphAffiliation == subgraph)
                        {
                            foreach (DaggerOutputPin outpin in node.OutputPins)
                            {
                                foreach (DaggerInputPin inpin in outpin._connectedTo)
                                {
                                    if (includeExports)
                                    {
                                        PinConnection pc = new PinConnection();
                                        pc.ConnectedFrom = outpin.InstanceGuid;
                                        pc.ConnectedTo = inpin.InstanceGuid;
                                        con.Add(pc);
                                    }
                                    else
                                    {
                                        //check to see if it is exported
                                        if (inpin.ParentNode != null)
                                        {
                                            PinConnection pc = new PinConnection();
                                            pc.ConnectedFrom = outpin.InstanceGuid;
                                            pc.ConnectedTo = inpin.InstanceGuid;
                                            con.Add(pc);
                                        }
                                    }
                                }
                            }
                        }
                    }

                    if (includeExports)
                    {
                        foreach (DaggerOutputPin pin in ImportedPins)
                        {
                            foreach (DaggerInputPin inpin in pin.ConnectedTo)
                            {
                                if (inpin.ParentNode.SubgraphAffiliation == subgraph)
                                {
                                    PinConnection pc = new PinConnection();
                                    pc.ConnectedFrom = pin.InstanceGuid;
                                    pc.ConnectedTo = inpin.InstanceGuid;
                                    con.Add(pc);
                                }
                            }
                        }
                    }
                }

                return con;
            }
        }

        /// <summary>
        /// Get a list of all pin connections in this graph
        /// </summary>
        private List<PinConnection> AllGuidConnections
        {
            get
            {
                List<PinConnection> con = new List<PinConnection>();

                List<DaggerNode> allnodes = AllNodes;
                foreach (DaggerNode node in allnodes)
                {
                    foreach (DaggerOutputPin outpin in node.OutputPins)
                    {
                        foreach(Guid guid in outpin.ConnectedToGuids)
                        {
                            PinConnection pc = new PinConnection();
                            pc.ConnectedFrom = outpin.InstanceGuid;
                            pc.ConnectedTo = guid;
                            con.Add(pc);
                        }
                    }
                }

                foreach (DaggerOutputPin pin in ImportedPins)
                {
                    foreach (Guid guid in pin.ConnectedToGuids)
                    {
                        PinConnection pc = new PinConnection();
                        pc.ConnectedFrom = pin.InstanceGuid;
                        pc.ConnectedTo = guid;
                        con.Add(pc);
                    }
                }

                return con;
            }
        }

        private DaggerOutputPin FindOutputPinByGuid(Guid guid)
        {
            DaggerOutputPin pin = null;

            foreach (DaggerNode node in AllNodes)
            {
                pin = node.OutputPins[guid];
                if (pin != null)
                {
                    break;
                }
            }

            if (pin == null)
            {
                //look in the imported pins
                pin = ImportedPins[guid];
            }

            return pin;
        }

        private DaggerInputPin FindInputPinByGuid(Guid guid)
        {
            DaggerInputPin pin = null;

            foreach (DaggerNode node in AllNodes)
            {
                pin = node.InputPins[guid];
                if (pin != null)
                {
                    break;
                }
            }

            if (pin == null)
            {
                //look in the exported pins
                pin = ExportedPins[guid];
            }

            return pin;
        }

        #endregion

        #region Virtual Methods

        /// <summary>
        /// Call the BeforeNodeRemoved Handler
        /// </summary>
        /// <param name="node"></param>
        /// <returns>true if node can be removed</returns>
        public virtual bool OnBeforeNodeRemoved(DaggerNode node)
        {
            if (BeforeNodeRemoved == null)
            {
                return true;
            }
            else
            {
                return BeforeNodeRemoved(node);
            }
        }

        /// <summary>
        /// Call before connect event to see if the two pins can be connected
        /// </summary>
        /// <param name="connectFrom"></param>
        /// <param name="connectTo"></param>
        /// <returns>true if pins can be connected</returns>
        public virtual bool OnBeforePinsConnected(DaggerOutputPin connectFrom, DaggerInputPin connectTo)
        {
            if (BeforePinsConnected == null)
            {
                return true;
            }
            else
            {
                return BeforePinsConnected(connectFrom, connectTo);
            }
        }

        /// <summary>
        /// Call before disconnect event to see if the two pins can be connected
        /// </summary>
        /// <param name="connectFrom"></param>
        /// <param name="connectTo"></param>
        /// <returns>true if pins can be disconnected</returns>
        public virtual bool OnBeforePinsDisconnected(DaggerOutputPin connectFrom, DaggerInputPin connectTo)
        {
            if (BeforePinsDisconnected == null)
            {
                return true;
            }
            else
            {
                return BeforePinsDisconnected(connectFrom, connectTo);
            }
        }

        void _exportedPins_PinAdded(object sender, DaggerBasePin pin)
        {
            OnExportPinAdded((DaggerInputPin)pin);
        }

        void _importedPins_PinAdded(object sender, DaggerBasePin pin)
        {
            OnImportPinAdded((DaggerOutputPin)pin);
        }

        public virtual void OnImportPinAdded(DaggerOutputPin pin)
        {
            if (_parentUIGraph != null)
            {
                _parentUIGraph.OnImportPinAdded(pin);
            }
        }

        public virtual void OnImportPinRemoved(DaggerOutputPin pin)
        {
            if (_parentUIGraph != null)
            {
                _parentUIGraph.OnImportPinRemoved(pin);
            }
        }

        public virtual void OnExportPinAdded(DaggerInputPin pin)
        {
            if (_parentUIGraph != null)
            {                
                _parentUIGraph.OnExportPinAdded(pin);
            }
        }

        public virtual void OnExportPinRemoved(DaggerInputPin pin)
        {
            if (_parentUIGraph != null)
            {
                _parentUIGraph.OnExportPinRemoved(pin);
            }
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Find and return an imported pin
        /// </summary>
        /// <param name="PinName">Name of Pin to find</param>
        /// <param name="PinDataType">Data Type of pin to find</param>
        /// <returns></returns>
        public DaggerOutputPin GetImportPin(string PinName, Type PinDataType)
        {
            DaggerOutputPin foundpin = null;

            foreach (DaggerOutputPin pin in _importedPins)
            {
                if (pin.Name == PinName && pin.DataType == PinDataType)
                {
                    foundpin = pin;
                    break;
                }
            }

            return foundpin;
        }

        /// <summary>
        /// Find and return an imported pin
        /// </summary>
        /// <param name="PinName">Name of Pin to find</param>
        /// <param name="PinDataType">Data Type of pin to find</param>
        /// <returns></returns>
        public DaggerInputPin GetExportPin(string PinName, Type PinDataType)
        {
            DaggerInputPin foundpin = null;

            foreach (DaggerInputPin pin in _exportedPins)
            {
                if (pin.Name == PinName && pin.DataType == PinDataType)
                {
                    foundpin = pin;
                    break;
                }
            }

            return foundpin;
        }

        /// <summary>
        /// Creates a Graph Scheduler of Type schedulertype for this Graph and all subnodes of the Graph
        /// </summary>
        /// <param name="schedulertype"></param>
        public void SetSchedulerType(Type schedulertype)
        {
            if (schedulertype.GetInterface("IGraphScheduler") == null)
            {
                throw new InvalidCastException("Type " + schedulertype + " does not implement interface IGraphScheduler");
            }

            GraphScheduler = (IGraphScheduler)Activator.CreateInstance(schedulertype);

            // propogate the scheduler type to subnodes
            foreach (DaggerNode node in AllNodes)
            {
                DaggerSubNode snode = node as DaggerSubNode;
                if (snode != null)
                {
                    snode.SubNodeGraph.SetSchedulerType(schedulertype);
                }
            }
        }

        private void AddImportPins(List<DaggerInterfacePin> importedPins)
        {
            foreach (DaggerInterfacePin pin in importedPins)
            {
                DaggerOutputPin outpin = new DaggerOutputPin();
                outpin.Name = pin.PinName;
                outpin.DataType = pin.PinDataType;
                outpin.AllowMultiConnect = true;
                outpin.IsInterfacePin = true;
                _importedPins.Add(outpin);
            }
        }

        private void AddExportPins(List<DaggerInterfacePin> exportedPins)
        {
            foreach (DaggerInterfacePin pin in exportedPins)
            {
                DaggerInputPin inpin = new DaggerInputPin();
                inpin.Name = pin.PinName;
                inpin.DataType = pin.PinDataType;
                inpin.IsInterfacePin = true;
                _exportedPins.Add(inpin);
            }
        }

        public DaggerOutputPin ExportPin(DaggerInputPin input)
        {
            DaggerOutputPin output = null;

            if (input.IsConnected)
            {
                return null;
            }

            //create an output pin to connect the input to
            output = new DaggerOutputPin();
            output.Name = input.Name;
            output.DataType = input.DataType;
            _importedPins.Add(output);
            output.ConnectToInput(input);

            // if this graph is inside a subnode, create an external pin
            if (_parentSubNode != null)
            {
                DaggerInputPin newinput = new DaggerInputPin();
                newinput.Name = input.Name;
                newinput.DataType = input.DataType;
                _parentSubNode.InputPins.Add(newinput);
                if (_parentSubNode.UINode != null)
                {
                    _parentSubNode.UINode.CalculateLayout();
                }
            }
            return output;
        }

        public DaggerInputPin ExportPin(DaggerOutputPin output)
        {
            DaggerInputPin input = null;

            if (!output.AllowMultiConnect && output.IsConnected)
            {
                return null;
            }

            //create an input pin to connect the output to
            input = new DaggerInputPin();
            input.Name = output.Name;
            input.DataType = output.DataType;
            _exportedPins.Add(input);
            output.ConnectToInput(input);

            // if this graph is inside a subnode, create an external pin
            if (_parentSubNode != null)
            {
                DaggerOutputPin newoutput = new DaggerOutputPin();
                newoutput.Name = output.Name;
                newoutput.DataType = output.DataType;
                newoutput.AllowMultiConnect = output.AllowMultiConnect;
                newoutput.PassByClone = output.PassByClone;
                _parentSubNode.OutputPins.Add(newoutput);
                if (_parentSubNode.UINode != null)
                {
                    _parentSubNode.UINode.CalculateLayout();
                }
            }
            return input;
        }

        /// <summary>
        /// Get the number of Ordinals in a subgraph
        /// </summary>
        /// <param name="subgraph"></param>
        /// <returns></returns>
        public int OrdinalCount(int subgraph)
        {
            if (subgraph > (_subGraphCount - 1))
            {
                throw new ArgumentOutOfRangeException();
            }

            List<DaggerNode> snode = this[subgraph];
            return snode[snode.Count - 1].Ordinal + 1;
        }

        /// <summary>
        /// Add a DaggerNode to this graph
        /// </summary>
        /// <param name="node"></param>
        public void AddNode(DaggerNode node)
        {
            if (node.ParentGraph != null)
            {
                throw new InvalidOperationException("Node already belongs to a graph");
            }
            else
            {
                node.ParentGraph = this;
                _nodes.Add(node);

                CalculateTopology();
            }

            if (this.NodeAdded != null)
            {
                NodeAdded(node, new EventArgs());
            }
        }

        /// <summary>
        /// Serialize a subgraph in the DaggerGraph to a byte array
        /// </summary>
        /// <param name="subGraphToSerialize">subgraph index to serialize</param>
        /// <returns>An array of bytes containing a binary serialization of the subgraph</returns>
        public byte[] SerializeSubGraph(int subGraphToSerialize)
        {
            if (subGraphToSerialize < this.SubGraphCount)
            {
                _subGraphToSerialize = subGraphToSerialize;
            }

            MemoryStream ms = new MemoryStream();

            BinaryFormatter bformatter = new BinaryFormatter();

            bformatter.Serialize(ms, this);

            // set _subGraphToSerialize back to all
            _subGraphToSerialize = -1;

            if (ms.Length > 0)
            {
                ms.Close();
                return ms.ToArray();
            }
            else
            {
                return null;
            }
        }

        /// <summary>
        /// Serialize the entire DaggerGraph to a byte array
        /// </summary>
        /// <returns>An array of bytes containing a binary serialization of the Graph</returns>
        public byte[] SerializeGraph()
        {
            // serialize the entire graph
            _subGraphToSerialize = -1;

            // create MemoryStream and Serialize via the Binaryformatter
            MemoryStream ms = new MemoryStream();
            BinaryFormatter bformatter = new BinaryFormatter();
            bformatter.Serialize(ms, this);

            if (ms.Length > 0)
            {
                ms.Close();
                return ms.ToArray();
            }
            else
            {
                return null;
            }
        }

        /// <summary>
        /// Remove all nodes from a graph
        /// </summary>
        /// <returns></returns>
        public bool ClearGraph()
        {
            for (int i = AllNodes.Count - 1; i > -1; i--)
            {
                if(!DeleteNode(AllNodes[i]))
                {
                    return false;
                }
            }

            // clear out the import/export pins
            ExportedPins.Clear();
            ImportedPins.Clear();
            return true;
        }

        /// <summary>
        /// Removes a node from this graph
        /// </summary>
        /// <param name="node"></param>
        /// <returns>true of succeeded</returns>
        public bool DeleteNode(DaggerNode node)
        {
            //try to disconnect all pins
            if (!node.DisconnectAllPins())
            {
                //we failed to disconnect a pin
                return false;
            }

            //see if we can remove this node from the graph
            if (!node.OnBeforeNodeRemoved())
            {
                //failed to remove node from graph
                return false;
            }
            if (!OnBeforeNodeRemoved(node))
            {
                //failed to remove node from graph
                return false;
            }

            //remove from the lists and the Controls
            _nodes.Remove(node);

            //let the node and the graph know it's toast so it can be removed from the ui also
            node.OnAfterNodeRemoved();

            if (AfterNodeRemoved != null)
            {
                AfterNodeRemoved(node);
            }

            CalculateTopology();

            return true;
        }

        /// <summary>
        /// Append DaggerGraph to this this Graph
        /// </summary>
        /// <param name="graph"></param>
        public void AppendGraph(DaggerGraph graph)
        {
            // keep track of node and noodles we've added so we can select them in UI mode
            List<IDaggerUINode> addedNodes = new List<IDaggerUINode>();
            List<IDaggerNoodle> addedNoodles = new List<IDaggerNoodle>();

            foreach (DaggerNode node in graph.AllNodes)
            {
                //disaffiliate the node from the old graph
                node.ParentGraph = null;
                node.UINode = null;

                AddNode(node);

                if (node.UINode != null)
                {
                    addedNodes.Add(node.UINode);
                }
            }

            foreach (DaggerOutputPin pin in graph.ImportedPins)
            {
                ImportedPins.Add(pin);

                //reflect imported pins to outter subnode
                if (_parentSubNode != null)
                {
                    DaggerInputPin newpin = new DaggerInputPin();
                    newpin.DataType = pin.DataType;
                    newpin.Name = pin.Name;
                    _parentSubNode.InputPins.Add(newpin);
                }

                //add noodles for imported pins
                if (_parentUIGraph != null)
                {
                    foreach (DaggerInputPin inpin in pin._connectedTo)
                    {
                        addedNoodles.Add(_parentUIGraph.AddNoodle(pin, inpin));
                    }
                }
            }

            foreach (DaggerInputPin pin in graph.ExportedPins)
            {
                ExportedPins.Add(pin);

                //reflect exported pins to outter subnode
                if (_parentSubNode != null)
                {
                    DaggerOutputPin newpin = new DaggerOutputPin();
                    newpin.DataType = pin.DataType;
                    newpin.Name = pin.Name;
                    _parentSubNode.OutputPins.Add(newpin);
                }
            }

            // update the UI of this DaggerGraph if one is attached
            if (_parentUIGraph != null)
            {
                _parentUIGraph.UpdateExportPins();
                _parentUIGraph.UpdateImportPins();

                foreach (DaggerNode node in graph.AllNodes)
                {
                    foreach (DaggerOutputPin outpin in node.OutputPins)
                    {
                        foreach (DaggerInputPin inpin in outpin.ConnectedTo)
                        {
                            addedNoodles.Add(_parentUIGraph.AddNoodle(outpin, inpin));
                        }
                    }
                }

                // if the appended graph had a stored layout apply it, offset by by location of the focus puck
                if (graph._layout != null && _parentUIGraph != null)
                {
                    foreach (GraphLayout gl in graph._layout)
                    {
                        DaggerNode node = null;

                        foreach (DaggerNode n in AllNodes)
                        {
                            if (n.InstanceGuid == gl.targetNodeGuid)
                            {
                                node = n;
                            }
                        }

                        if (node != null)
                        {
                            gl.Apply(node.UINode, _parentUIGraph.FocusLocationX,_parentUIGraph.FocusLocationY);
                        }
                    }
                }

                // if this graph is in subNode, update it's layout to include new pins
                if (_parentSubNode != null)
                {
                    _parentSubNode.UINode.CalculateLayout();
                }

                // create new selector for nodes and noodles we've added
                if (_parentUIGraph != null && addedNodes.Count != 0)
                {
                    _parentUIGraph.Select(addedNodes, addedNoodles);
                }
            }
        }

        #endregion

        #region Internal Methods
        
        /// <summary>
        /// Calculate the precedence of execution order and subgraph affiliation of each node
        /// </summary>
        internal void CalculateTopology()
        {
            //reset all presidence values and subgraph affiliations to -1
            foreach (DaggerNode node in _nodes)
            {
                node.Ordinal = -1;
                node.SubgraphAffiliation = -1;
                node._descendents.Clear();
            }

            // get the top level nodes
            List<DaggerNode> topLevelNodes = TopLevelNodes;

            // Create a List of Sets.  From the article "A Set Class" http://www.codeproject.com/csharp/Types_Set.asp
            // Each Set will hold a List of all Nodes that a top level Node touches on it's way to bottom level Nodes.
            // We'll in turn use these Sets to calculate subgraph affiliations.
            List<PIEBALD.Types.Set<DaggerNode>> touchedSetList = new List<PIEBALD.Types.Set<DaggerNode>>();

            // recursively walk each output pin marking the node's precedence and gathering a Set of all nodes it touches
            for (int i = 0; i < topLevelNodes.Count; i++)
            {
                DaggerNode node = topLevelNodes[i];

                // top level nodes always have an Ordinal of 0
                node.Ordinal = 0;

                // create a "Touched" Set
                PIEBALD.Types.Set<DaggerNode> touchedSet = new PIEBALD.Types.Set<DaggerNode>();

                foreach (DaggerOutputPin outpin in node.OutputPins)
                {
                    foreach (DaggerInputPin inpin in outpin._connectedTo)
                    {
                        // recurse through all it's connected pins
                        PIEBALD.Types.Set<DaggerNode> newset = _recurseCalculateTopology(1, inpin._parentNode, touchedSet);

                        // recreate our _descendents List from the newset
                        foreach (DaggerNode setnode in newset)
                        {
                            if (!node._descendents.Contains(setnode))
                            {
                                node._descendents.Add(setnode);
                            }
                        }
                        // sort the _descendents List by the Ordinal number
                        node._descendents.Sort(new OrdinalComparer());
                    }
                }

                // all top level nodes touch themselves <Let's keep it professional here>
                touchedSet.Add(node);

                // since we always have at least one subgraph, just add the first touched set to the list of sets
                if (i == 0)
                {
                    touchedSetList.Add(touchedSet);
                }
                else
                {
                    // if not the first Set, see if we can merge this Set with an existing touched Set
                    bool merged = false;
                    for (int u = 0; u < touchedSetList.Count; u++)
                    {
                        PIEBALD.Types.Set<DaggerNode> intersection = touchedSetList[u] & touchedSet;

                        // if the Cardinalty of the intersected Set is not 0, then these two Sets share the same subgraph
                        if (intersection.Cardinality > 0)
                        {
                            // merge the new Set with the stored one
                            touchedSetList[u] = touchedSetList[u] + touchedSet;
                            merged = true;
                            break;
                        }
                    }
                    if (!merged)
                    {
                        // we didn't find a Stored Set to merge with, so store this Set
                        touchedSetList.Add(touchedSet);
                    }
                }
            }

            // scrub through all the sets and mark the Subgraph tag of each node
            for (int i = 0; i < touchedSetList.Count; i++)
            {
                foreach (DaggerNode node in touchedSetList[i])
                {
                    node.SubgraphAffiliation = i;
                }
            }

            // let the graph know how many subgraphs it has
            _subGraphCount = touchedSetList.Count;

            // cache the connection status of the mutex pin groups for faster retrieval
            foreach (DaggerNode node in AllNodes)
            {
                PinMutexGroups allGroups = PinMutexGroups.None;

                // do the input pins
                foreach (DaggerInputPin pin in node.InputPins)
                {
                    //gather exising Mutex groups
                    allGroups |= pin.MutexGroup;
                }
                // clear the existing status dictionary
                node._inputMutexConnections.Clear();                
                if (allGroups != PinMutexGroups.None)
                {
                    int bitVal = 1;
                    for (int i = 0; i < 9; i++)
                    {
                        if (((int)allGroups & bitVal) != 0)
                        {
                            node._inputMutexConnections[(PinMutexGroups)bitVal] = node.IsInputMutexGroupsConnected((PinMutexGroups)bitVal);
                        }
                        bitVal = bitVal << 1;
                    }
                }

                allGroups = PinMutexGroups.None;
                // do the output pins
                foreach (DaggerOutputPin pin in node.OutputPins)
                {
                    // gather exising Mutex groups
                    allGroups |= pin.MutexGroup;
                }
                // clear the existing status dictionary
                node._outputMutexConnections.Clear();
                if (allGroups != PinMutexGroups.None)
                {
                    int bitVal = 1;
                    for (int i = 0; i < 9; i++)
                    {
                        if (((int)allGroups & bitVal) != 0)
                        {
                            node._outputMutexConnections[(PinMutexGroups)bitVal] = node.IsOutputMutexGroupsConnected((PinMutexGroups)bitVal);
                        }
                        bitVal = bitVal << 1;
                    }
                }
            }

            // let the Scheduler know the Topolgy has changed
            if (GraphScheduler != null)
            {
                GraphScheduler.OnTopologyChanged();
            }

            // call the OnTopologyChanged event
            if (OnTopologyChanged != null)
            {
                OnTopologyChanged(this, new EventArgs());
            }
        }

        private PIEBALD.Types.Set<DaggerNode> _recurseCalculateTopology(int level, DaggerNode node, PIEBALD.Types.Set<DaggerNode> touchedSet)
        {
            if (node == null)
            {
                // this was from an exported output pin, so return an empty set
                return new PIEBALD.Types.Set<DaggerNode>();
            }

            // set our precedence if level is larger than current value
            node.Ordinal = Math.Max(level, node.Ordinal);

            // create new set that will contain all my descendents
            PIEBALD.Types.Set<DaggerNode> newset = new PIEBALD.Types.Set<DaggerNode>();

            // recurse through all our output pins
            foreach (DaggerOutputPin outpin in node.OutputPins)
            {
                foreach (DaggerInputPin inpin in outpin._connectedTo)
                {
                    //recurse through all it's connected pins
                    newset.Add(_recurseCalculateTopology(level + 1, inpin._parentNode, touchedSet));
                }
            }

            // add this node to the touched Set (we don't want to be included in our own _descendents Set)
            touchedSet.Add(node);

            // recreate our _descendents List from the newset
            foreach (DaggerNode setnode in newset)
            {
                if (!node._descendents.Contains(setnode))
                {
                    node._descendents.Add(setnode);
                }
            }

            // sort the _descendents List by the Ordinal number
            node._descendents.Sort(new OrdinalComparer());

            // now add this node to the newset. 
            // We do this here because we don't want to include ourself in the _descendents List
            newset.Add(node);

            return newset;
        }

        internal void OnPinsConnected(DaggerOutputPin connectFrom, DaggerInputPin connectTo)
        {
            CalculateTopology();

            // call the after connect event
            if (AfterPinsConnected != null)
            {
                AfterPinsConnected(connectFrom, connectTo);
            }

            // if the pins belong to a mutex group update thier ui parent node's layout
            if (_parentUIGraph != null)
            {
                if (connectFrom.MutexGroup != PinMutexGroups.None)
                {
                    connectFrom.ParentNode.UINode.CalculateLayout();
                }

                if (connectTo.MutexGroup != PinMutexGroups.None)
                {
                    connectTo.ParentNode.UINode.CalculateLayout();
                }

                //update noodles to reflect the repositioning of available pins
                _parentUIGraph.UpdateNoodles(connectFrom.ParentNode);
                _parentUIGraph.UpdateNoodles(connectTo.ParentNode);
            }
        }

        internal void OnPinsDisonnected(DaggerOutputPin disconnectOutput, DaggerInputPin disconnectInput)
        {
            //if the pins are imported or exported, see if we need to remove them from the list
            if (_exportedPins.Contains(disconnectInput) && !disconnectInput.IsInterfacePin)
            {
                //before we remove the pin, propogate the disconnection of pins upward through parent nodes
                if (_parentSubNode != null)
                {
                    if (_parentSubNode.OutputPins[_exportedPins.IndexOf(disconnectInput)].Disconnect(false))
                    {
                        //delete outter pin
                        _parentSubNode.OutputPins.Remove(_parentSubNode.OutputPins[_exportedPins.IndexOf(disconnectInput)]);
                        //delete inner pin
                        _exportedPins.Remove(disconnectInput);
                        OnExportPinRemoved(disconnectInput);
                    }
                    else
                    {
                        //we couldn't disconnect an outter exported pin
                        return;
                    }
                }
                else
                {
                    _exportedPins.Remove(disconnectInput);
                    OnExportPinRemoved(disconnectInput);
                }
            }
            else if(_importedPins.Contains(disconnectOutput) && !disconnectOutput.IsInterfacePin)
            {
                // if no more connections remain on this imported pin, begin removal/disconnection propogation
                if (!disconnectOutput.IsConnected)
                {
                    if (_parentSubNode != null)
                    {
                        // propogate the disconnection upwards first
                        if (_parentSubNode.InputPins[_importedPins.IndexOf(disconnectOutput)].Disconnect(false))
                        {
                            // delete outter pin
                            _parentSubNode.InputPins.Remove(_parentSubNode.InputPins[_importedPins.IndexOf(disconnectOutput)]);
                            // delete inner pin
                            _importedPins.Remove(disconnectOutput);
                            OnImportPinRemoved(disconnectOutput);
                        }
                        else
                        {
                            // couldn't disconnect the outter imported pin
                            return;
                        }
                    }
                    else
                    {
                        // not embedded in a SubNode, so just delete the inner pin
                        _importedPins.Remove(disconnectOutput);
                        OnImportPinRemoved(disconnectOutput);
                    }
                }
            }

            CalculateTopology();

            // call the after disconnect event
            if (AfterPinsDisconnected != null)
            {
                AfterPinsDisconnected(disconnectOutput, disconnectInput);
            }

            // refresh the UISubNode to remove the outter pin regions
            if (_parentSubNode != null && _parentSubNode.UINode != null)
            {
                _parentSubNode.UINode.CalculateLayout();
            }

            // if the pins belong to a mutex group update thier ui parent node's layout
            if (_parentUIGraph != null)
            {
                if (disconnectOutput.MutexGroup != PinMutexGroups.None)
                {
                    disconnectOutput.ParentNode.UINode.CalculateLayout();
                }

                if (disconnectInput.MutexGroup != PinMutexGroups.None)
                {
                    disconnectInput.ParentNode.UINode.CalculateLayout();
                }

                // update noodles to reflect the repositioning of available pins
                _parentUIGraph.UpdateNoodles(disconnectOutput.ParentNode);
                _parentUIGraph.UpdateNoodles(disconnectInput.ParentNode);
            }
        }

        #endregion

        #region ICloneable

        public object Clone()
        {
            byte[] buffer = SerializeGraph();
            MemoryStream ms = new MemoryStream(buffer);
            BinaryFormatter bformatter = new BinaryFormatter();
            return bformatter.Deserialize(ms);
        }

        #endregion

        #region ISerializable

        /// <summary>
        /// Serialization GetObjectData.  DaggerGraph can serialize in one of 3 states: All, Subgraph, and Selected
        /// </summary>
        /// <param name="info"></param>
        /// <param name="ctxt"></param>
        public void GetObjectData(SerializationInfo info, StreamingContext ctxt)
        {
            List<DaggerNode> nodes = new List<DaggerNode>();
            List<DaggerNodeNonSerializationAssistant> assistNodes = new List<DaggerNodeNonSerializationAssistant>();

            // storage for the layout of ui nodes
            List<GraphLayout> layout = (_parentUIGraph != null) ? new List<GraphLayout>() : null;

            // are we serializing the entire graph, a subgraph or a selection?
            if (_selectionToSerialize != null)
            {
                // serialize a selection
                foreach (IDaggerUINode uinode in _selectionToSerialize.SelectedNodes)
                {
                    DaggerNode node = uinode.Node;

                    if (layout != null)
                    {
                        // store the uinode's layout
                        layout.Add(new GraphLayout(node.UINode));
                    }

                    if (node.GetType().IsSerializable)
                    {
                        nodes.Add(node);
                    }
                    else
                    {
                        assistNodes.Add(new DaggerNodeNonSerializationAssistant(node));
                    }
                }

                // add only the connections valid for the nodes that are selected
                info.AddValue("AllConnections", this[_selectionToSerialize]);
            }
            else if (_subGraphToSerialize == -1)
            {
                // serialize the entire graph
                foreach (DaggerNode node in AllNodes)
                {
                    if (layout != null)
                    {
                        // store the uinode's layout
                        layout.Add(new GraphLayout(node.UINode));
                    }

                    if (node.GetType().IsSerializable)
                    {
                        nodes.Add(node);
                    }
                    else
                    {
                        assistNodes.Add(new DaggerNodeNonSerializationAssistant(node));
                    }
                }

                info.AddValue("AllConnections", AllGuidConnections);
            }
            else
            {
                // serialize a subgraph
                foreach (DaggerNode node in this[_subGraphToSerialize])
                {
                    if (layout != null)
                    {
                        // store the uinode's layout
                        layout.Add(new GraphLayout(node.UINode));
                    }

                    if (node.GetType().IsSerializable)
                    {
                        nodes.Add(node);
                    }
                    else
                    {
                        assistNodes.Add(new DaggerNodeNonSerializationAssistant(node));
                    }
                }

                info.AddValue("AllConnections", this[_subGraphToSerialize, true]);
            }            

            // store nodes and assisted nodes
            info.AddValue("AllNodes", nodes);
            info.AddValue("AssistNodes", assistNodes);

            if (layout != null)
            {
                // store the layout
                info.AddValue("Layout", layout);
            }

            List<DaggerInputPin> exlist = new List<DaggerInputPin>();
            foreach (DaggerInputPin pin in ExportedPins)
            {
                if (pin.IsConnected)
                {
                    if (_selectionToSerialize != null)
                    {
                        // partial graph is being serialized.  Make sure pin is part of selection
                        foreach (PinConnection pc in this[_selectionToSerialize])
                        {
                            if (pc.ConnectedTo == pin.InstanceGuid)
                            {
                                exlist.Add(pin);
                                break;
                            }
                        }
                    }
                    else if (_subGraphToSerialize == -1)
                    {
                        // the entire graph is being serialized
                        exlist.Add(pin);
                    }
                    else
                    {
                        // Subgraph is being serialize.  Make sure it's part of the subgraph
                        if (pin.ConnectedToOutput.ParentNode.SubgraphAffiliation == _subGraphToSerialize)
                        {
                            exlist.Add(pin);
                        }
                    }
                }
            }

            List<DaggerOutputPin> inlist = new List<DaggerOutputPin>();
            foreach (DaggerOutputPin pin in ImportedPins)
            {
                if (_selectionToSerialize != null)
                {
                    // partial graph is being serialized.  Make sure pin is part of selection
                    foreach (PinConnection pc in this[_selectionToSerialize])
                    {
                        if (pc.ConnectedFrom == pin.InstanceGuid)
                        {
                            inlist.Add(pin);
                            break;
                        }
                    }
                }
                else if (_subGraphToSerialize == -1)
                {
                    // the entire graph is being serialized
                    inlist.Add(pin);
                }
                else
                {
                    // Subgraph is being serialize.  Make sure it's part of the subgraph
                    DaggerInputPin subgraphpin = null;
                    foreach (DaggerInputPin inpin in pin._connectedTo)
                    {
                        if (inpin.ParentNode.SubgraphAffiliation == _subGraphToSerialize)
                        {
                            subgraphpin = inpin;
                            break;
                        }
                    }
                    if (subgraphpin != null)
                    {
                        inlist.Add(pin);
                    }
                }
            }

            info.AddValue("ImportedPins", inlist);
            info.AddValue("ExportedPins", exlist);
        }

        /// <summary>
        /// Callback method before Deserialization begins
        /// </summary>
        /// <param name="context"></param>
        [OnDeserializing()]
        internal void OnDeserializingMethod(StreamingContext context)
        {
            // not implemented yet
        }

        /// <summary>
        /// Callback method after Deserialization completes
        /// </summary>
        /// <param name="ctxt"></param>
        [OnDeserialized()]
        private void OnDeserializedMethod(StreamingContext ctxt)
        {
            if (_isDeserialized)
            {
                //already been here
                return;
            }

            // Create default scheduler and give the scheduler this Graph
            _scheduler = new OrdinalExecutionScheduler();
            _scheduler.Graph = this;

            // mark this flag to prevent Pins from AutoCloning during the reconnection phase of deserialization
            _isDeserializing = true;

            // recreate nodes that need assistance
            if (_assistNodes.Count > 0)
            {
                foreach (DaggerNodeNonSerializationAssistant assist in _assistNodes)
                {
                    try
                    {
                        DaggerNode node = assist.CreateNode();
                        _nodes.Add(node);
                    }
                    catch (Exception ex)
                    {
                        throw new SerializationException("Error Assisting Deserialization of DaggerNode Type " + assist.NodeType.ToString(),
                            ex);
                    }
                }
            }

            // associate all the nodes
            foreach (DaggerNode node in AllNodes)
            {
                node.ParentGraph = this;
                if (node is DaggerSubNode)
                {
                    // we need to reverse the order of ondeserialization to ensure all nested
                    // subnodes are reconstructed from the bottom up
                    (node as DaggerSubNode)._subNodeGraph.OnDeserializedMethod(ctxt);
                    (node as DaggerSubNode).OnDeserializedMethod(ctxt);
                }
            }

            // add the imported and exported pins to thier collections
            foreach (DaggerOutputPin pin in inlist)
            {
                _importedPins.Add(pin);
            }
            foreach (DaggerInputPin pin in exlist)
            {
                _exportedPins.Add(pin);
            }

            // reconstruct the pin connections after deserialization is completed
            foreach (PinConnection con in _allConnections)
            {
                DaggerOutputPin outpin = FindOutputPinByGuid(con.ConnectedFrom);
                DaggerInputPin inpin = FindInputPinByGuid(con.ConnectedTo);

                if (outpin == null || inpin == null)
                {
                    throw new InvalidDataException("Pin not found.  DaggerNode not properly serialized.");
                }
                outpin.ConnectToInput(inpin);
            }

            // create new InstanceGuids for all the pins and nodes (prevents collisions when merging graphs)
            foreach (DaggerNode node in AllNodes)
            {
                foreach (DaggerInputPin pin in node.InputPins)
                {
                    pin._instanceGuid = Guid.NewGuid();
                }

                foreach (DaggerOutputPin pin in node.OutputPins)
                {
                    pin._instanceGuid = Guid.NewGuid();
                }

                Guid newNodeGuid = Guid.NewGuid();

                // if there is a layout stored, set the node's layout targetguid
                if (_layout != null)
                {
                    foreach(GraphLayout gl in _layout)
                    {
                        if (gl.targetNodeGuid == node.InstanceGuid)
                        {
                            gl.targetNodeGuid = newNodeGuid;
                            break;
                        }
                    }
                }

                node.InstanceGuid = newNodeGuid;
            }

            foreach (DaggerOutputPin pin in ImportedPins)
            {
                pin._instanceGuid = Guid.NewGuid();
            }

            foreach (DaggerInputPin pin in ExportedPins)
            {
                pin._instanceGuid = Guid.NewGuid();
            }

            _isDeserializing = false;
            _isDeserialized = true;
        }

        #endregion
    }

    #region PinConnection Class
    /// <summary>
    /// Class that represents the connection of two pins
    /// </summary>
    [Serializable]
    public class PinConnection
    {
        public Guid ConnectedFrom = Guid.Empty;
        public Guid ConnectedTo = Guid.Empty;

        public DaggerOutputPin OutputPin;
        public DaggerInputPin InputPin;

        public PinConnection()
        {

        }

        public PinConnection(DaggerOutputPin outpin, DaggerInputPin inpin)
        {
            OutputPin = outpin;
            InputPin = inpin;
        }
    }

    #endregion

    #region OrdinalComparer class

    /// <summary>
    /// Comparer class to sort DaggerNodes by thier Ordinal Number
    /// </summary>
    internal class OrdinalComparer : IComparer<DaggerNode>
    {
        public int Compare(DaggerNode x, DaggerNode y)
        {
            int result = 0;

            if (x == null && y == null)
            {
                result = 0;
            }
            else if (x == null)
            {
                result = -1;
            }
            else if (y == null)
            {
                result = 1;
            }
            else
            {
                if (x.Ordinal < y.Ordinal)
                {
                    result = -1;
                }
                else if (x.Ordinal == y.Ordinal)
                {
                    result = 0;
                }
                else result = 1;
            }

            return result;
        }
    }

    #endregion
}

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 MIT License


Written By
Software Developer (Senior)
United States United States
AKA Rich Insley.

I have over 25 years experience in programming, and I'm completely self taught. (Except for one year at California State University Fresno where I had to learn the God awful language Miranda (http://miranda.org.uk/). I've spent 10 years as a Paratrooper in the US Army during the Clinton Administration.

Comments and Discussions