public void ClickNode(NodeModel node) { if (!CtrlDown) { FocusedNodes.ForEach(n => n.Focused = false); FocusedNodes.Clear(); } if (node == null) return; else if (node.Focused && CtrlDown) { node.Focused = false; FocusedNodes.Remove(node); } else { node.Focused = true; FocusedNodes.Add(node); MainUI.NavigatePanelTo(node); } DoRedraw = true; Renderer.ViewInvalidate(); }
public void DrawNode(Color color, RectangleF area, bool outside, NodeModel node, int depth) { if (outside) CurrentBuffer.FillPolygon(GetBrush(color), GetTriangleFromRect(area)); else CurrentBuffer.FillRectangle(GetBrush(color), area); }
public void DrawCallLine(Color color, int lineWidth, PointF start, PointF end, bool dashed, NodeModel source, NodeModel destination) { var pen = GetPen(color, lineWidth, dashed); if(dashed) pen.DashOffset = XRay.DashOffset * 3; CurrentBuffer.DrawLine(pen, start, end); }
internal void AddIntermediateDependency(NodeModel sub) { if (DependencyChainOut == null) DependencyChainOut = new Dictionary<int, NodeModel>(); DependencyChainOut[sub.ID] = sub; if (sub.DependencyChainIn == null) sub.DependencyChainIn = new Dictionary<int, NodeModel>(); sub.DependencyChainIn[ID] = this; }
public GraphSet(ViewModel model, NodeModel root, NodeModel container=null, int depth = -1) { Model = model; GraphMode = Model.GraphMode; GraphContainer = container; // iternate nodes at this zoom level if (GraphMode == CallGraphMode.Intermediates) { AddDependencyNodes(); } else if (GraphMode == CallGraphMode.Layers) { foreach (var child in root.Nodes) AddCalledNodes(child, true, 0); } else { AddCalledNodes(root, true, depth); if (GraphContainer == null) { if (Model.ShowOutside || CenterMap.Count == 1) // prevent blank screen AddCalledNodes(Model.InternalRoot, false); if (Model.ShowExternal) AddCalledNodes(Model.ExternalRoot, false); } } // process subsets before building graphs so we can prune empty subset graphs if (GraphMode == CallGraphMode.Layers) { foreach (var child in root.Nodes) Subsets[child.ID] = new GraphSet(Model, child, child); } else if (GraphMode == CallGraphMode.Class && Model.ShowMethods && GraphContainer == null) { foreach (var classNode in PositionMap.Values) Subsets[classNode.ID] = new GraphSet(Model, classNode, classNode, 1); } if (PositionMap.Count > 0) { BuildGraphs(); if (Graphs.Count > 0) LayoutGraphs(); } }
public void DrawCallLine(Color color, int lineWidth, PointF start, PointF end, bool dashed, NodeModel source, NodeModel destination) { var lineRect = new RectangleF(Math.Min(start.X, end.X), Math.Min(start.Y, end.Y), Math.Abs(end.X - start.X), Math.Abs(end.Y - start.Y)); if (!ClientRect.IntersectsWith(lineRect)) return; var a = new Vector3(start.X, start.Y, 0); var b = new Vector3(end.X, end.Y, 0); VertexBuffer vbo = null; if (!dashed) vbo = GetLineVbo(CallLines, lineWidth); else vbo = GetLineVbo(DashedCallLines, lineWidth); vbo.AddVerticies(color, Normal, a, b); }
private void TestHovered(NodeModel node, Point loc) { if (!node.Show || !node.AreaF.Contains(loc.X, loc.Y)) return; node.Hovered = true; GuiHovered.Add(node); foreach (var sub in node.Nodes) TestHovered(sub, loc); }
public ViewModel(IMainUI mainUI, IColorProfile xColors) { MainUI = mainUI; XColors = xColors; NodeModels = new NodeModel[XRay.Nodes.Length]; foreach (var node in XRay.Nodes) NodeModels[node.ID] = new NodeModel(this, XRay.Nodes[node.ID]); foreach (var uiNode in NodeModels) { if(uiNode.XNode.Parent != null) uiNode.Parent = NodeModels[uiNode.XNode.Parent.ID]; foreach (var subnode in uiNode.XNode.Nodes) uiNode.Nodes.Add(NodeModels[subnode.ID]); } TopRoot = NodeModels[XRay.RootNode.ID]; InternalRoot = TopRoot.Nodes.First(n => n.ObjType == XObjType.Internal); ExternalRoot = TopRoot.Nodes.First(n => n.ObjType == XObjType.External); CurrentRoot = InternalRoot; }
public void RecalcCover(NodeModel root, bool rootShow=true) { root.Value = 0; root.SecondaryValue = 0; // if a leaf node - method or field if (root.ObjType == XObjType.Method || root.ObjType == XObjType.Field) { if (TwoDimensionalValues) { root.Value = GetValueForLayout(root, SizeLayouts.Constant); root.SecondaryValue = GetValueForLayout(root, SizeLayout); if (root.SecondaryValue > MaxSecondaryValue) MaxSecondaryValue = root.SecondaryValue; } else root.Value = GetValueForLayout(root, SizeLayout); if ((ShowLayout == ShowNodes.Hit && !XRay.CoveredNodes[root.ID]) || (ShowLayout == ShowNodes.Unhit && XRay.CoveredNodes[root.ID])) root.Value = 0; // processs subnodes because methods/fields can have anon sub classes } foreach (var node in root.Nodes) { bool nodeShow = rootShow; // show can only be set to false if (nodeShow) { nodeShow = ShowLayout == ShowNodes.All || ShowLayout == ShowNodes.Hit || ShowLayout == ShowNodes.Unhit || (ShowLayout == ShowNodes.Instances && (node.ObjType != XObjType.Class || (node.XNode.Record != null && node.XNode.Record.Created > 0))); if ((node.ObjType == XObjType.Field && !ShowFields) || (node.ObjType == XObjType.Method && !ShowMethods) || (node.XNode.IsAnon && !ShowAnon)) nodeShow = false; if (ShowThreads != null && (node.ObjType == XObjType.Method || node.ObjType == XObjType.Field) && (node.XNode.ThreadIDs == null || !node.XNode.ThreadIDs.Any(id => ShowThreads.Contains(id)))) nodeShow = false; } RecalcCover(node, nodeShow); // if filtering on threads, dont show empty classes in the class graph if (ShowThreads != null && (node.ObjType != XObjType.Method && node.ObjType != XObjType.Field) && node.Nodes.All(n => !n.Show)) nodeShow = false; if (nodeShow) { root.Value += node.Value; root.SecondaryValue += node.SecondaryValue; if (node.Value == 0) { nodeShow = false; Utilities.RecurseTree(node, n => n.Show = false, n => n.Nodes); } } node.Show = nodeShow; } // when hide field/methods selected and user is viewing treemap - just show class map // make sure class graph still shows up with field/methods unselected // when show fields/methods is on we still want to hide certain classes that are filtered by thread for instance if (root.Value == 0 && !ShowFields && !ShowMethods && (root.ObjType != XObjType.Method && root.ObjType != XObjType.Field) && (ViewLayout == LayoutType.TreeMap || (ViewLayout == LayoutType.CallGraph && GraphMode != CallGraphMode.Method))) { root.Value = 1; // on return to calling function causes root.show = true } if (ViewLayout == LayoutType.CallGraph && SizeLayout == SizeLayouts.Constant) root.Value = 1; //XRay.LogError("Calc'd Node: {0}, Value: {1}", root.Name, root.Value); //Debug.Assert(root.Value >= 0); }
public void DrawNodeLabel(string text, Font font, Color color, RectangleF rect, NodeModel node, int depth) { if (!ClientRect.IntersectsWith(rect)) return; QFont qfont = GetQFont(font); qfont.PrintToVBO(text, QFontAlignment.Left, new Vector3(rect.X, rect.Y, 0), color, rect.Size); }
public CallItem(FunctionCall call, NodeModel node, bool perCall) { Call = call; Node = node; Text = node.AppendClassName(); if (call == null) return; if (call.StillInside > 0 ) Text += " (" + call.StillInside.ToString() + " Still Inside)"; SubItems.Add(call.TotalHits.ToString()); Hits = call.TotalHits; Inside = call.TotalTimeInsideDest; Outside = call.TotalTimeOutsideDest; if (Hits == 0) return; if (perCall) { Inside /= Hits; Outside /= Hits; } SubItems.Add(Utilities.TicksToString(Inside)); SubItems.Add(Utilities.TicksToString(Outside)); Total = Inside + Outside; // show last cal info string callInfo = ""; try { var lastParams = call.LastParameters; if (lastParams != null) callInfo += "Params: " + string.Join(", ", lastParams.Select(p => (p == null) ? "<null>" : p.ToString()).ToArray()); } catch (Exception x) { throw new Exception("AAA"); } object lastReturnValue = null; try { lastReturnValue = call.LastReturnValue; // avoid thread from changing value out from under us } catch (Exception x) { throw new Exception("b1" + x.Message); } try { if (lastReturnValue != null) callInfo += " Return: "; } catch (Exception x) { throw new Exception("b2" + x.Message); } try { if (lastReturnValue != null) callInfo += lastReturnValue.ToString(); } catch (Exception x) { throw new Exception("b3" + x.Message); } SubItems.Add(callInfo); }
private float AvgPos(NodeModel node) { float sum = 0; float count = 0; if (node.EdgesOut != null) foreach (var destId in node.EdgesOut) if (PositionMap.ContainsKey(destId)) { if (node.Intermediates != null && node.Intermediates.ContainsKey(destId)) sum += node.Intermediates[destId][0].ScaledLocation.Y; else sum += PositionMap[destId].ScaledLocation.Y; count++; } if (node.EdgesIn != null) foreach (var source in node.EdgesIn) if (PositionMap.ContainsKey(source)) { var sourceNode = PositionMap[source]; if (sourceNode.Intermediates != null && sourceNode.Intermediates.ContainsKey(node.ID)) sum += sourceNode.Intermediates[node.ID].Last().ScaledLocation.Y; else sum += PositionMap[source].ScaledLocation.Y; count++; } // should only be attached to intermediate nodes if (node.Adjacents != null) { Debug.Assert(node.ID == 0); // adjacents should only be on temp nodes foreach (var adj in node.Adjacents) { sum += adj.ScaledLocation.Y; count++; } } if (count == 0) return node.ScaledLocation.Y; return sum / count; }
private void AddEdges(NodeModel node, bool center, IEnumerable<int> callsIn, IEnumerable<int> callsOut) { if (center || ((callsIn != null && callsIn.Any(source => CenterMap.Contains(source))) || (callsOut != null && callsOut.Any(dest => CenterMap.Contains(dest))))) { PositionMap[node.ID] = node; if(center) CenterMap.Add(node.ID); node.EdgesIn = (callsIn != null) ? callsIn.ToArray() : null; node.EdgesOut = (callsOut != null) ? callsOut.ToArray() : null; } }
public void DrawCallLine(Color color, int lineWidth, PointF start, PointF end, bool live, NodeModel source, NodeModel destination) { float height = live ? LiveCallHeight : CallHeight; var a = new Vector3(start.X, height, start.Y); var b = new Vector3(end.X, height, end.Y); var vbo = GetLineVbo(live ? DashedCallLines : CallLines, lineWidth * 2); var normal = new Vector3(); vbo.AddVerticies(color, normal, a, b); }
public void NavigateTo(NodeModel node) { if (node.ObjType == XObjType.Class) { SelectedNode = node; FieldFilter = null; SummaryLabel.Text = node.Name; SummaryLabel.ForeColor = ColorProfile.ClassColor; FieldsRadioButton.Visible = true; MethodsRadioButton.Visible = true; } else if (node.ObjType == XObjType.Field) { SelectedNode = node.GetParentClass(false); FieldFilter = node.XNode.UnformattedName; SummaryLabel.Text = node.Name; SummaryLabel.ForeColor = ColorProfile.FieldColor; FieldsRadioButton.Visible = false; MethodsRadioButton.Visible = false; FieldsRadioButton.Checked = true; } else { SummaryLabel.Text = ""; return; } Model = new InstanceModel(SelectedNode.XNode, FieldFilter, GridModel_UpdateTree, GridModel_ExpandedField); XRay.UIs[Thread.CurrentThread.ManagedThreadId].CurrentInstance = Model; if (!Visible) return; CurrentDisplay = SelectedNode; FieldGrid.Nodes.Clear(); FieldGrid.Columns.Clear(); ModelRowMap = new Dictionary<int, FieldRow>(); Model.BeginUpdateTree(false); RefreshSubnodesView(); }
public void DrawNodeOutline(Color color, int lineWidth, RectangleF area, bool outside, NodeModel node, int depth) { float x = area.X, z = area.Y, width = area.Width, length = area.Height; float floor = depth * LevelSize; float height = GetNodeHeight(node); if (outside) { DrawPyramidOutline(color, lineWidth, x, z, width, length, floor, height); } else { DrawBoxOutline(color, lineWidth, x, z, width, length, floor, height); } }
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)); }
public void NavigateTo(NodeModel node) { SelectedNode = node; Reload(); }
private float GetDistanceY(NodeModel other_node, NodeModel node) { return Math.Abs(other_node.ScaledLocation.Y - node.ScaledLocation.Y); }
public void DrawNodeOutline(Color color, int lineWidth, RectangleF area, bool outside, NodeModel node, int depth) { if (!ClientRect.IntersectsWith(area)) return; float x = area.X, y = area.Y, width = area.Width, height = area.Height; var vbo = GetLineVbo(Outlines, lineWidth); if (outside) { var v1 = new Vector3(area.X, area.Y + area.Height, 0); var v2 = new Vector3(area.X + area.Width / 2f, area.Y, 0); var v3 = new Vector3(area.X + area.Width, area.Y + area.Height, 0); vbo.AddVerticies(color, Normal, v1, v2, v2, v3, v3, v1); } else { var v1 = new Vector3(x, y, 0); var v2 = new Vector3(x + width, y, 0); var v3 = new Vector3(x + width, y + height, 0); var v4 = new Vector3(x, y + height, 0); vbo.AddVerticies(color, Normal, v1, v2, v2, v3, v3, v4, v4, v1); } }
/* flow through entire list of children first keep track of node parents if any unranked parents at end of child run look at all of parents children, give rank of 1 - lowest ranked child re-run alg on that parent this way we start with first (entry) node, run through linearly from that, and tack on alternate parents later works great for application call graph, and for general graphs as well */ private void LayoutGraph(Dictionary<int, NodeModel> graph, NodeModel node, int minRank, List<int> parents) { //debugLog.Add(string.Format("Entered Node ID {0} rank {1}", ID, Rank)); // node already ranked correctly, no need to re-rank subordinates if (node.Rank != null && node.Rank.Value >= minRank) return; int? prevRank = node.Rank; // only increase rank Debug.Assert(node.Rank == null || minRank > node.Rank.Value); node.Rank = minRank; //debugLog.Add(string.Format("Node ID {0} rank set from {1} to {2}", ID, prevRank, Rank)); parents.Add(node.ID); graph[node.ID] = node; if (node.EdgesOut != null) foreach (var destId in node.EdgesOut) { if (parents.Contains(destId)) { // destination rank should be less than source //Debug.Assert(edge.Destination.Rank < edge.Source.Rank); //debugLog.Add(string.Format("Switching edge {0} -> {1}, rank {2} -> {3}", ID, edge.Destination.ID, Rank, edge.Destination.Rank)); //edge.Source = edge.Destination; //edge.Destination = this; //edge.Reversed = !edge.Reversed; continue; } // pass copy of parents list so that sub can add elemenets without affecting next iteration //debugLog.Add(string.Format("Traversing to child {0} -> {1}, rank {2} -> {3}", ID, edge.Destination.ID, Rank, edge.Destination.Rank)); if (PositionMap.ContainsKey(destId)) { var target = PositionMap[destId]; LayoutGraph(graph, target, node.Rank.Value + 1, parents.ToList());//, debugLog); } //debugLog.Add(string.Format("Return to node {0} rank {1}", ID, Rank)); } // record so later group can be traversed for null ranked members (parents) so layout can be run on them if (node.EdgesIn != null) foreach (var source in node.EdgesIn) if (PositionMap.ContainsKey(source)) graph[source] = PositionMap[source]; // check if same edges down go back up and create intermediates in that case? //debugLog.Add(string.Format("Exited Node ID {0} rank {1}", ID, Rank)); }
public void DrawNode(Color color, RectangleF area, bool outside, NodeModel node, int depth) { if (!ClientRect.IntersectsWith(area)) return; var verticies = outside ? GetTriangleVerticies(area) : GetRectVerticies(area); Nodes.AddVerticies(color, Normal, verticies); }
private long GetValueForLayout(NodeModel root, SizeLayouts layout) { long value = 0; switch (layout) { case SizeLayouts.Constant: value = 1; break; case SizeLayouts.MethodSize: value = root.XNode.Lines; break; case SizeLayouts.TimeInMethod: // why is this negetive?? HAVENT RETURNED YET, property should return 0 i think if neg, or detect still inside and return that if (root.XNode.CalledIn != null) foreach (FunctionCall call in root.XNode.CalledIn) value += call.TotalTimeInsideDest; break; case SizeLayouts.Hits: if (root.XNode.CalledIn != null) foreach (FunctionCall call in root.XNode.CalledIn) value += call.TotalHits; break; case SizeLayouts.TimePerHit: if (root.XNode.CalledIn != null) { int count = 0; foreach (FunctionCall call in root.XNode.CalledIn) if (call.TotalHits > 0) { count++; value += call.TotalTimeInsideDest / call.TotalHits; } if (count > 0) value /= count; } break; } return value; }
public void DrawNode(Color color, RectangleF area, bool outside, NodeModel node, int depth) { float x = area.X + 0.1f; float y = area.Y + 0.1f; float width = area.Width - 0.2f; float length = area.Height - 0.2f; float bottom = depth * LevelSize + 0.1f; float height = GetNodeHeight(node) - 0.2f; if (SelectionMode != SelectionModes.None) { SelectionMap[node.ID] = node; color = Color.FromArgb((255 << 24) | node.ID); } if (outside) DrawPyramid(color, x, y, width, length, bottom, height); else GLUtils.DrawCube(Nodes, color, x, y, width, length, bottom, height); }
public void SetRoot(NodeModel node, bool logHistory = true) { if (node == null) return; // setting internal root will auto show properly sized external root area if showing it is enabled ResetZoom(); CurrentRoot = (node == TopRoot) ? InternalRoot : node; if (logHistory) { // re-write forward log with new node while (CurrentHistory != HistoryList.Last) HistoryList.RemoveLast(); // dont set node if last node is already this var last = HistoryList.LastOrDefault(); if (CurrentRoot != last) { HistoryList.AddLast(CurrentRoot); CurrentHistory = HistoryList.Last; } } MainUI.UpdateBreadCrumbs(); DoRevalue = true; Renderer.ViewInvalidate(); }
public void DrawNodeLabel(string text, Font font, Color color, RectangleF rect, NodeModel node, int depth) { QFont qfont = GetQFont(font); float height = LabelHeight; if(Model.ViewLayout == LayoutType.TreeMap) height = depth * LevelSize + GetNodeHeight(node); // put over call lines if (SelectionMode != SelectionModes.None) { SelectionMap[node.ID] = node; color = Color.FromArgb((255 << 24) | node.ID); var textArea = qfont.Measure(text, rect.Size, QFontAlignment.Left); var normal = new Vector3(0, 1, 0); var v1 = new Vector3(rect.X, height, rect.Y); var v2 = new Vector3(rect.X, height, rect.Y + textArea.Height); var v3 = new Vector3(rect.X + textArea.Width, height, rect.Y + textArea.Height); var v4 = new Vector3(rect.X + textArea.Width, height, rect.Y); Nodes.AddVerticies(color, normal, v1, v2, v3, v1, v3, v4); } else qfont.PrintToVBO(text, QFontAlignment.Left, new Vector3(rect.X, rect.Y, -height), color, rect.Size); }
private void AddNodeToHovered(NodeModel node) { GuiHovered.Add(node); var parent = node.Parent; while (parent != null) { GuiHovered.Add(parent); parent = parent.Parent; } GuiHovered.Reverse(); }
public void DrawNodeOutline(Color color, int lineWidth, RectangleF area, bool outside, NodeModel node, int depth) { float x = area.X, z = area.Y, width = area.Width, length = area.Height; float floor = depth * LevelSize; float height = GetNodeHeight(node); if(outside) DrawPyramidOutline(color, lineWidth, x, z, width, length, floor, height); else DrawBoxOutline(color, lineWidth, x, z, width, length, floor, height); }
void ToggleNode(HashSet<int> map, NodeModel node) { // make sure a node cant be selected and ignored simultaneously if (map != IgnoredNodes && IgnoredNodes.Contains(node.ID)) IgnoredNodes.Remove(node.ID); if (map != FilteredNodes && FilteredNodes.Contains(node.ID)) FilteredNodes.Remove(node.ID); // toggle the setting of the node in the map if (map.Contains(node.ID)) map.Remove(node.ID); else map.Add(node.ID); DoRedraw = true; Renderer.ViewInvalidate(); }
float GetNodeHeight(NodeModel node) { if (node.ObjType == XObjType.Method) return Math.Max(100f * (float)node.SecondaryValue / (float)Model.MaxSecondaryValue, LevelSize); else return LevelSize; }
private void SizeNode(IRenderer Renderer, NodeModel root, NodeModel exclude, bool center) { if (!root.Show) return; RectangleF insideArea = root.AreaF; if (ShowLabels) { // check if enough room in root box for label var labelSpace = root.AreaF; labelSpace.Width -= LabelPadding * 2.0f; labelSpace.Height -= LabelPadding * 2.0f; var labelSize = new RectangleF(root.AreaF.Location, Renderer.MeasureString(root.Name, TextFont)); float minHeight = (root.Nodes.Count > 0) ? labelSize.Height * 2.0f : labelSize.Height; if (minHeight < labelSpace.Height && labelSize.Width / 3f < labelSpace.Width) { labelSize.X += LabelPadding; labelSize.Y += LabelPadding; if (labelSpace.Width < labelSize.Width) { root.LabelClipped = true; labelSize.Width = labelSpace.Width; } insideArea.Y += labelSize.Height; insideArea.Height -= labelSize.Height; root.RoomForLabel = true; root.LabelRect = labelSize; } } List<Sector> sectors = new TreeMap(root, exclude, insideArea.Size).Results; foreach (Sector sector in sectors) { var node = sector.OriginalValue; sector.Rect = RectangleExtensions.Contract(sector.Rect, NodeBorderWidth); if (sector.Rect.X < NodeBorderWidth) sector.Rect.X = NodeBorderWidth; if (sector.Rect.Y < NodeBorderWidth) sector.Rect.Y = NodeBorderWidth; if (sector.Rect.X > insideArea.Width - NodeBorderWidth) sector.Rect.X = insideArea.Width - NodeBorderWidth; if (sector.Rect.Y > insideArea.Height - NodeBorderWidth) sector.Rect.Y = insideArea.Height - NodeBorderWidth; sector.Rect.X += insideArea.X; sector.Rect.Y += insideArea.Y; node.SetArea(sector.Rect); PositionMap[node.ID] = node; node.RoomForLabel = false; // cant do above without graphic artifacts node.LabelClipped = false; if (center) CenterMap.Add(node.ID); if (sector.Rect.Width > 1.0f && sector.Rect.Height > 1.0f) SizeNode(Renderer, node, exclude, center); } }
public void DrawNodeLabel(string text, Font font, Color color, RectangleF rect, NodeModel node, int depth) { QFont qfont = GetQFont(font); float height = LabelHeight; if (Model.ViewLayout == LayoutType.TreeMap) { height = depth * LevelSize + GetNodeHeight(node); // put over call lines } if (SelectionMode != SelectionModes.None) { SelectionMap[node.ID] = node; color = Color.FromArgb((255 << 24) | node.ID); var textArea = qfont.Measure(text, rect.Size, QFontAlignment.Left); var normal = new Vector3(0, 1, 0); var v1 = new Vector3(rect.X, height, rect.Y); var v2 = new Vector3(rect.X, height, rect.Y + textArea.Height); var v3 = new Vector3(rect.X + textArea.Width, height, rect.Y + textArea.Height); var v4 = new Vector3(rect.X + textArea.Width, height, rect.Y); Nodes.AddVerticies(color, normal, v1, v2, v3, v1, v3, v4); } else { qfont.PrintToVBO(text, QFontAlignment.Left, new Vector3(rect.X, rect.Y, -height), color, rect.Size); } }