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);
            }
        }