private void ScaleGraph(Graph graph, RectangleF area) { float fullSize = (float)Math.Min(area.Width, area.Height); for (int i = 0; i < graph.Ranks.Length; i++) { var rank = graph.Ranks[i]; if (DrawCallGraphVertically) { for (int x = 0; x < rank.Column.Count; x++) { var node = rank.Column[x]; var temp = node.ScaledLocation.X; node.ScaledLocation.X = node.ScaledLocation.Y; node.ScaledLocation.Y = temp; } } float right = area.X + area.Width; if (ShowLabels) { if (i < graph.Ranks.Length - 1) right = area.X + area.Width * graph.Ranks[i + 1].Column[0].ScaledLocation.X - (graph.Ranks[i + 1].Column.Max(c => fullSize * c.ScaledSize) / 2); } // set node area for (int x = 0; x < rank.Column.Count; x++) { var node = rank.Column[x]; float size = fullSize * node.ScaledSize; float halfSize = size / 2; if (size < MinCallNodeSize) size = MinCallNodeSize; node.SetArea(new RectangleF( area.X + area.Width * node.ScaledLocation.X - halfSize, area.Y + area.Height * node.ScaledLocation.Y - halfSize, size, size)); } if (ShowLabels) SetLabelArea(graph, area, rank, right); } }
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; } } }
private void Uncross(Graph graph) { // moves nodes closer to attached nodes in adjacent ranks foreach (Rank rank in graph.Ranks) { // foreach node average y pos form all connected edges foreach (var node in rank.Column) node.ScaledLocation.Y = AvgPos(node); // set rank list to new node list rank.Column = rank.Column.OrderBy(n => n.ScaledLocation.Y).ToList(); PositionRank(graph, rank, rank.Column[0].ScaledLocation.X); } }
private void Spring(Graph graph) { float total_kinetic_energy = 40; // running sum of total kinetic energy over all particles float spring_const = -1; float damping = 0.8f; // between 0 and 1 float timestep = 0.1f; do { // for each node foreach (var rank in graph.Ranks) { float minY = 0; float maxY = 1; foreach (var node in rank.Column) { float forceY = 0; // running sum of total force on this particular node // for each other node apply Coulomb repulsion 1/distance foreach (var other_node in rank.Column.Where(n => n != node)) { float distance = GetDistanceY(other_node, node); if (distance > 0) forceY += 1.0f / distance; else forceY += .1f; } // for each spring connected to this node net-force + Hooke_attraction( this_node, spring ) if (node.EdgesOut != null) foreach (var destId in node.EdgesOut) if (PositionMap.ContainsKey(destId)) { if (node.Intermediates != null && node.Intermediates.ContainsKey(destId)) forceY += -spring_const * GetDistanceY(node.Intermediates[destId][0], node); else forceY += -spring_const * GetDistanceY(PositionMap[destId], node); } 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)) forceY += -spring_const * GetDistanceY(sourceNode.Intermediates[node.ID].Last(), node); else forceY += -spring_const * GetDistanceY(PositionMap[source], node); } // should only be attached to intermediate nodes if (node.Adjacents != null) foreach (var adj in node.Adjacents) forceY += -spring_const * GetDistanceY(adj, node); // without damping, it moves forever node.VelocityY = (node.VelocityY + timestep * forceY) * damping; node.ScaledLocation.Y += timestep * node.VelocityY; if (node.ScaledLocation.Y < minY) minY = node.ScaledLocation.Y; if (node.ScaledLocation.Y > maxY) maxY = node.ScaledLocation.Y; total_kinetic_energy--; // += node.Value * (float)Math.Pow(node.VelocityY, 2); // node.value is mass } // scale positions of nodes back float range = maxY - minY; if (range > 1) foreach (var node in rank.Column) node.ScaledLocation.Y /= range; } } while (total_kinetic_energy > 0); // the simulation has stopped moving }
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; } }
private void MinDistance(Graph graph) { // moves nodes with-in rank closer to their adjacent nodes without changing order in rank try { foreach (Rank rank in graph.Ranks) { var nodes = rank.Column; // divide the min space allotted for column by the number of nodes in it, *2 for top/bottom of node float minHeightSpace = graph.ScaledHeight * (1.0f - GraphFillSpace) / ((float) nodes.Count * 2); // foreach node average y pos form all connected edges for (int x = 0; x < nodes.Count; x++) { var node = nodes[x]; float lowerbound = graph.ScaledOffset; if (x > 0) { var prevNode = nodes[x - 1]; lowerbound = prevNode.ScaledLocation.Y + (prevNode.ScaledSize / 2); } lowerbound += minHeightSpace; float upperbound = graph.ScaledOffset + graph.ScaledHeight; if (x < nodes.Count - 1) { var nextNode = nodes[x + 1]; upperbound = nextNode.ScaledLocation.Y - (nextNode.ScaledSize / 2); } upperbound -= minHeightSpace; //Debug.Assert(lowerbound <= upperbound); if (lowerbound >= upperbound) { // usually if this happens they're very close XRay.LogError("lower bound greater than upper in layout. pos: {0}, nodeID: {1}, lower: {2}, upper: {3}, minheight: {4}", x, node.ID, lowerbound, upperbound, minHeightSpace); //continue; } float optimalY = AvgPos(node); float halfSize = node.ScaledSize / 2; if (optimalY - halfSize < lowerbound) optimalY = lowerbound + halfSize; else if (optimalY + halfSize > upperbound) optimalY = upperbound - halfSize; node.ScaledLocation.Y = optimalY; } } } catch (Exception ex) { XRay.LogError(ex.Message + "\r\n" + ex.StackTrace); } }