private void Traverse(profile p, int currentCheckedLevel, bool depthFirst, Action <bool, profile, profile> callback)
        {
            List <profile> uncheckedProfiles = null;

            if (depthFirst)
            {
                uncheckedProfiles = new List <profile>();
            }

            for (int i = 0; i < p.forward.Count; i++)
            {
                profile f = p.forward[i];

                if (f.Checked(currentCheckedLevel))
                {
                    continue;
                }

                callback.Invoke(true, p, f);

                if (depthFirst)
                {
                    uncheckedProfiles.Add(f);
                }
                else
                {
                    Traverse(f, currentCheckedLevel, false, callback);
                }
            }

            for (int i = 0; i < p.backward.Count; i++)
            {
                profile b = p.backward[i];

                if (b.Checked(currentCheckedLevel))
                {
                    continue;
                }

                callback.Invoke(false, p, b);

                if (depthFirst)
                {
                    uncheckedProfiles.Add(b);
                }
                else
                {
                    Traverse(b, currentCheckedLevel, false, callback);
                }
            }

            if (depthFirst)
            {
                for (int i = 0; i < uncheckedProfiles.Count; i++)
                {
                    Traverse(uncheckedProfiles[i], currentCheckedLevel, true, callback);
                }
            }
        }
        public void Layout()
        {
            if (Graph == null)
            {
                return;
            }

            profiles       = new List <profile>();
            profilesByNode = new Dictionary <NoFlo_Basic.Component, profile>();

            foreach (NoFlo_Basic.Component c in Graph.NodesByName.Values)
            {
                profile p = new profile()
                {
                    Component = c,
                };
                profilesByNode.Add(c, p);
                profiles.Add(p);
            }

            for (int i = 0; i < profiles.Count; i++)
            {
                profile p = profiles[i];
                p.forward  = ForwardConnectedNodes(p.Component);
                p.backward = BackwardConnectedNodes(p.Component);
            }

            // 0 or 1, already done
            if (profiles.Count <= 1)
            {
                return;
            }

            int currentCheckedLevel = 1;

            // Check each profile only if they haven't been checked yet
            for (int i = 0; i < profiles.Count; i++)
            {
                profile p = profiles[i];

                if (p.Checked(currentCheckedLevel))
                {
                    continue;
                }

                // Set initial rank
                p.rank = 0;

                // Traverse the network, checking each profile.
                // Each forward connections adds one to the rank, each backward substracts one
                Traverse(p, currentCheckedLevel, true, (isForward, l, h) => {
                    h.rank = l.rank.Value + (isForward ? 1 : -1);
                });
            }

            currentCheckedLevel = 2;
            float      hsep = GraphEditor.Layout.HorizontalSeparation;
            float      vsep = GraphEditor.Layout.VerticalSeparation;
            List <int> OrderedRanks;
            Dictionary <int, List <profile> > profilesByRank             = OrderProfilesByRank(out OrderedRanks);
            Dictionary <int, int>             NumberOfNodesGraphedByRank = new Dictionary <int, int>();
            Func <profile, int, float>        xPos = (p, o) => (p.rank.Value + o) * hsep;
            Func <float, int, float>          yPos = (y, n) => y + n * vsep;

            foreach (int r in profilesByRank.Keys)
            {
                NumberOfNodesGraphedByRank.Add(r, 0);
            }

            // Position according to rank, for each sub graph
            for (int i = 0; i < OrderedRanks.Count; i++)
            {
                int            rank          = OrderedRanks[i];
                List <profile> profileOfRank = profilesByRank[rank];

                int rankOffset;
                if (rank < 0)
                {
                    rankOffset = -rank;
                }
                else
                {
                    rankOffset = 0;
                }

                for (int j = 0; j < profileOfRank.Count; j++)
                {
                    float   currentY = rank * vsep; // TODO include full size of previous graph
                    profile p        = profileOfRank[j];

                    if (p.Checked(currentCheckedLevel))
                    {
                        continue;
                    }

                    p.Component.MetadataPosition = new Vector3(xPos(p, rankOffset), yPos(currentY, 0));

                    Traverse(p, currentCheckedLevel, true, (isForward, l, h) => {
                        int number = NumberOfNodesGraphedByRank[h.rank.Value];
                        NumberOfNodesGraphedByRank[h.rank.Value] = number + 1;

                        h.Component.MetadataPosition = new Vector3(xPos(h, rankOffset), yPos(currentY, number));
                    });
                }
            }

            // Center the whole graph at the local zero for the panel
            GraphEditor.CenterGraph();
        }