Пример #1
0
        private void PositionRank(Graph graph, Rank rank, float xOffset)
        {
            // spreads nodes in rank across y-axis at even intervals

            var nodes = rank.Column;

            float spacePerRow = graph.ScaledHeight / (float)nodes.Count;
            float yOffset = spacePerRow / 2.0f;

            foreach (var node in nodes)
            {
                node.ScaledLocation.X = xOffset;
                node.ScaledLocation.Y = graph.ScaledOffset + yOffset;

                yOffset += spacePerRow;
            }
        }
Пример #2
0
        private void SetLabelArea(Graph graph, RectangleF area, Rank rank, float right)
        {
            var rankNodes = rank.Column.Where(n => n.ID != 0).ToArray();

            for (int x = 0; x < rankNodes.Length; x++)
            {
                var node = rankNodes[x];

                // check if enough room in box for label
                node.RoomForLabel = false;
                node.LabelClipped = false;

                SizeF textSize = Renderer.MeasureString(node.Name, TextFont);

                // first see if label fits above node, we prefer this because it looks better when zoomed in
                float left = node.AreaF.Left;
                float top = area.Y + area.Height * graph.ScaledOffset;
                bool topFit = true;

                if (x > 0)
                    top = rankNodes[x - 1].AreaF.Bottom;

                float bottom = node.AreaF.Top;

                node.LabelRect = new RectangleF(left, top, right - left, bottom - top);

                // if label doesnt fit above, put it to the right of the node
                if (textSize.Height > node.LabelRect.Height)
                {
                    //area from middle of node to edges of midpoint between adjacent nodes, and length to next rank - max node's width /2
                    topFit = false;

                    left = node.AreaF.Right;
                    top = graph.ScaledOffset;

                    var thisY = rankNodes[x].ScaledLocation.Y;

                    if (x > 0)
                    {
                        float aboveY = rankNodes[x - 1].ScaledLocation.Y;
                        float distance = thisY - aboveY;
                        top = aboveY + (distance / 2f);
                    }

                    bottom = graph.ScaledOffset + graph.ScaledHeight;
                    if (x < rankNodes.Length - 1)
                    {
                        float belowY = rankNodes[x + 1].ScaledLocation.Y;
                        float distance = belowY - thisY;
                        bottom = thisY + (distance / 2f);
                    }

                    float distanceFromCenter = Math.Min(node.ScaledLocation.Y - top, bottom - node.ScaledLocation.Y);
                    top = area.Y + (node.ScaledLocation.Y - distanceFromCenter) * area.Height;
                    bottom = area.Y + (node.ScaledLocation.Y + distanceFromCenter) * area.Height;

                    node.LabelRect = new RectangleF(left, top, right - left, bottom - top);
                }

                // if can fit height and 30% of the text label
                if (textSize.Height < node.LabelRect.Height && textSize.Width * 0.3f < node.LabelRect.Width)
                {
                    node.RoomForLabel = true;

                    if (node.LabelRect.Width < textSize.Width)
                        node.LabelClipped = true;
                    else
                        node.LabelRect.Width = textSize.Width; // trim label size

                    if (topFit)
                        node.LabelRect.Y = node.LabelRect.Bottom - textSize.Height;
                    else
                        node.LabelRect.Y = (node.LabelRect.Y + node.LabelRect.Height / 2f) - (textSize.Height / 2f);

                    node.LabelRect.Height = textSize.Height;
                }
            }
        }
Пример #3
0
        private void BuildGraphs()
        {
            foreach (var node in PositionMap.Values)
                node.Rank = null;

            do
            {
                // group nodes into connected graphs
                var graph = new Dictionary<int, NodeModel>();

                // add first unranked node to a graph
                var unrankedNode = PositionMap.Values.First(n => n.Rank == null);

                LayoutGraph(graph, unrankedNode, 0, new List<int>());

                // while group contains unranked nodes
                while (graph.Values.Any(n => n.Rank == null && n.EdgesOut != null))
                {
                    // head node to start traversal
                    unrankedNode = graph.Values.First(n => n.Rank == null && n.EdgesOut != null);

                    // only way node could be in group is if child added it, so there is a minrank
                    // min rank is 1 back from the lowest ranked child of the node
                    int? minRank = unrankedNode.EdgesOut.Min(dest =>
                    {
                        if (PositionMap.ContainsKey(dest))
                        {
                            var destNode = PositionMap[dest];
                            if (destNode.Rank.HasValue)
                                return destNode.Rank.Value;
                        }

                        return int.MaxValue;
                    });

                    LayoutGraph(graph, unrankedNode, minRank.Value - 1, new List<int>());
                }

                // remove graphs with 1 element
                if (graph.Count == 1)
                {
                    bool remove = false;
                    var onlyNode = graph.First().Value;

                    if (GraphMode == CallGraphMode.Dependencies || GraphMode == CallGraphMode.Method)
                        remove = true;

                    if(GraphMode == CallGraphMode.Class || GraphMode == CallGraphMode.Layers)
                    {
                        // dont remove method/field if alone as a graph because it may be still connect to other classes
                        if (onlyNode.ObjType == XObjType.Method || onlyNode.ObjType == XObjType.Field)
                        {
                            // in class mode edges between nodes set dynamically, in layers mode edges are based on calls
                            if ((GraphMode == CallGraphMode.Layers && onlyNode.XNode.CalledIn == null && onlyNode.XNode.CallsOut == null) ||
                                (GraphMode == CallGraphMode.Class && onlyNode.EdgesIn == null && onlyNode.EdgesOut == null))
                                remove = true;
                        }
                        // remove empty lonesome classes
                        else if (onlyNode.ObjType == XObjType.Class && !Model.ShowMethods && !Model.ShowFields)
                            remove = true;

                        // if node is by lonesome and has no sub-graphs inside of it
                        else if (Subsets.ContainsKey(onlyNode.ID) && Subsets[onlyNode.ID].Graphs.Count == 0)
                            remove = true;
                    }

                    if(remove)
                    {
                        PositionMap.Remove(onlyNode.ID);
                        CenterMap.Remove(onlyNode.ID);
                        continue;
                    }
                }

                // normalize ranks so sequential without any missing between
                int nextSequentialRank = -1;
                int currentRank = int.MinValue;
                foreach (var n in graph.Values.OrderBy(v => v.Rank))
                {
                    if (n.Rank != currentRank)
                    {
                        currentRank = n.Rank.Value;
                        nextSequentialRank++;
                    }

                    n.Rank = nextSequentialRank;
                }

                // put all nodes into a rank based multi-map
                Rank[] ranks = new Rank[nextSequentialRank + 1];
                for (int i = 0; i < ranks.Length; i++)
                    ranks[i] = new Rank();

                long graphWeight = 0;

                foreach (var source in graph.Values)
                {
                    graphWeight += source.Value;

                    ranks[source.Rank.Value].Column.Add(source);

                    if (source.EdgesOut == null)
                        continue;

                    foreach (var destId in source.EdgesOut)
                    {
                        if (!graph.ContainsKey(destId))
                            continue;

                        var destination = graph[destId];

                        // ranks are equal if nodes are outside zoom
                        if (source.ID == destination.ID || destination.Rank == source.Rank)
                            continue;

                        if (source.Intermediates != null)
                            source.Intermediates.Remove(destId);

                        // if destination is not 1 forward/1 back then create intermediate nodes
                        if (source.Rank != destination.Rank + 1 &&
                            source.Rank != destination.Rank - 1)
                        {
                            if (source.Intermediates == null)
                                source.Intermediates = new Dictionary<int, List<NodeModel>>();

                            source.Intermediates[destId] = new List<NodeModel>();

                            bool increase = destination.Rank > source.Rank;
                            int nextRank = increase ? source.Rank.Value + 1 : source.Rank.Value - 1;
                            var lastNode = source;

                            while (nextRank != destination.Rank)
                            {

                                // create new node
                                var intermediate = new NodeModel(Model);
                                intermediate.Rank = nextRank;
                                intermediate.Value = 10; // todo make smarter -
                                intermediate.Adjacents = new List<NodeModel>();

                                // add forward node to prev
                                if (lastNode != source)
                                    lastNode.Adjacents.Add(intermediate);

                                // add back node to curr
                                intermediate.Adjacents.Add(lastNode);

                                // add to temp path, rank map
                                source.Intermediates[destId].Add(intermediate);
                                ranks[nextRank].Column.Add(intermediate);
                                //PositionMap not needed because we dont need any mouse over events? just follow along and draw from list, not id

                                lastNode = intermediate;
                                nextRank = increase ? nextRank + 1 : nextRank - 1;
                            }

                            try
                            {
                                lastNode.Adjacents.Add(destination);
                                source.Intermediates[destId].Add(destination);
                            }
                            catch
                            {
                                System.IO.File.WriteAllText("debugX.txt", string.Format("{0}\r\n{1}\r\n", source.Rank, destination.Rank));

                                throw new Exception("wtf");
                            }
                        }
                    }
                }

                Graphs.Add(new Graph() { Ranks = ranks, Weight = graphWeight });

            } while (PositionMap.Values.Any(n => n.Rank == null));
        }