Example #1
0
 /// <summary>
 /// Gets the bounding rectangle for label editing.</summary>
 /// <param name="info">Node layout information</param>
 /// <returns>
 /// Bounding rectangle for label editing</returns>
 protected override Rectangle GetLabelEditBounds(NodeLayoutInfo info)
 {
     // adjust the width to keep textbox for label editing away from the data columns
     var bounds = base.GetLabelEditBounds(info);
     bounds.Width = TreeWidth - bounds.Left;
     return bounds;
 }
Example #2
0
 private Rectangle GetEditArea(NodeLayoutInfo nodeLayoutInfo, DataEditor dataEditor)
 {
     int height = GetRowHeight(nodeLayoutInfo.Node);
     int xLeft = TreeWidth;
     int width = -1;
     for (int i = 0; i < Columns.Count; ++i)
     {
         if (Columns[i].Label == dataEditor.Name)
         {
             width = Columns[i].ActualWidth;
             break;
         }
         xLeft += Columns[i].ActualWidth;
     }
     Debug.Assert(width>=0);
     return new Rectangle(xLeft, nodeLayoutInfo.Y, width, height);
 }
Example #3
0
    /// <summary>
    /// Runs the force-directed layout algorithm on this Diagram, using the specified parameters.
    /// </summary>
    /// <param name="damping">Value between 0 and 1 that slows the motion of the nodes during layout.</param>
    /// <param name="springLength">Value in pixels representing the length of the imaginary springs that run along the connectors.</param>
    /// <param name="maxIterations">Maximum number of iterations before the algorithm terminates.</param>
    /// <param name="deterministic">Whether to use a random or deterministic layout.</param>
    public void Arrange(double damping, int springLength, int maxIterations, bool deterministic)
    {
        // random starting positions can be made deterministic by seeding System.Random with a constant
        Random rnd = deterministic ? new Random(0) : new Random();

        // copy nodes into an array of metadata and randomise initial coordinates for each node
        NodeLayoutInfo[] layout = new NodeLayoutInfo[mNodes.Count];
        for (int i = 0; i < mNodes.Count; i++)
        {
            layout[i] = new NodeLayoutInfo(mNodes[i], new Vector(), new Point());
            layout[i].Node.Location = new Point(rnd.Next(-50, 50), rnd.Next(-50, 50));
        }

        int stopCount = 0;
        int iterations = 0;

        while (true)
        {
            double totalDisplacement = 0;

            for (int i = 0; i < layout.Length; i++)
            {
                NodeLayoutInfo current = layout[i];

                // express the node's current position as a vector, relative to the origin
                Vector currentPosition = new Vector(CalcDistance(new Point(), current.Node.Location), GetBearingAngle(new Point(), current.Node.Location));
                Vector netForce = new Vector(0, 0);

                // determine repulsion between nodes
                foreach (Node other in mNodes)
                {
                    if (other != current.Node)
                        netForce += CalcRepulsionForce(current.Node, other);
                }

                // determine attraction caused by connections
                foreach (Node child in current.Node.Connections)
                {
                    netForce += CalcAttractionForce(current.Node, child, springLength);
                }
                foreach (Node parent in mNodes)
                {
                    if (parent.Connections.Contains(current.Node))
                        netForce += CalcAttractionForce(current.Node, parent, springLength);
                }

                // apply net force to node velocity
                current.Velocity = (current.Velocity + netForce) * damping;

                // apply velocity to node position
                current.NextPosition = (currentPosition + current.Velocity).ToPoint();
            }

            // move nodes to resultant positions (and calculate total displacement)
            for (int i = 0; i < layout.Length; i++)
            {
                NodeLayoutInfo current = layout[i];

                totalDisplacement += CalcDistance(current.Node.Location, current.NextPosition);
                current.Node.Location = current.NextPosition;
            }

            iterations++;
            if (totalDisplacement < 10)
                stopCount++;
            if (stopCount > 15)
                break;
            if (iterations > maxIterations)
                break;
        }

        // center the diagram around the origin
        Rectangle logicalBounds = GetDiagramBounds();
        Point midPoint = new Point(logicalBounds.X + (logicalBounds.Width / 2), logicalBounds.Y + (logicalBounds.Height / 2));

        foreach (Node node in mNodes)
        {
            node.Location = new Point(node.Location.X - midPoint.X, node.Location.Y - midPoint.Y);
        }
    }
Example #4
0
        /// <summary>
        /// Runs the force-directed layout algorithm on this Diagram, using the specified parameters.
        /// </summary>
        /// <param name="damping">Value between 0 and 1 that slows the motion of the nodes during layout.</param>
        /// <param name="springLength">Value in pixels representing the length of the imaginary springs that run along the connectors.</param>
        /// <param name="maxIterations">Maximum number of iterations before the algorithm terminates.</param>
        /// <param name="deterministic">Whether to use a random or deterministic layout.</param>
        public void Arrange(double damping, int springLength, int maxIterations, bool deterministic)
        {
            // random starting positions can be made deterministic by seeding System.Random with a constant
            Random rnd = deterministic ? new Random(0) : new Random();

            // copy nodes into an array of metadata and randomise initial coordinates for each node
            NodeLayoutInfo[] layout = new NodeLayoutInfo[mNodes.Count];
            for (int i = 0; i < mNodes.Count; i++)
            {
                layout[i] = new NodeLayoutInfo(mNodes[i], new Layv(), Point.Empty);
                layout[i].Layn.Location = new Point(rnd.Next(-50, 50), rnd.Next(-50, 50));
            }

            int stopCount  = 0;
            int iterations = 0;

            while (true)
            {
                double totalDisplacement = 0;

                for (int i = 0; i < layout.Length; i++)
                {
                    NodeLayoutInfo current = layout[i];

                    // express the node's current position as a vector, relative to the origin
                    Layv currentPosition = new Layv(CalcDistance(Point.Empty, current.Layn.Location), GetBearingAngle(Point.Empty, current.Layn.Location));
                    Layv netForce        = new Layv(0, 0);

                    // determine repulsion between nodes
                    foreach (Layn other in mNodes)
                    {
                        if (other != current.Layn)
                        {
                            netForce += CalcRepulsionForce(current.Layn, other);
                        }
                    }

                    // determine attraction caused by connections
                    foreach (Layn child in current.Layn.Connections)
                    {
                        netForce += CalcAttractionForce(current.Layn, child, springLength);
                    }
                    foreach (Layn parent in mNodes)
                    {
                        if (parent.Connections.Contains(current.Layn))
                        {
                            netForce += CalcAttractionForce(current.Layn, parent, springLength);
                        }
                    }

                    // apply net force to node velocity
                    current.Velocity = (current.Velocity + netForce) * damping;

                    // apply velocity to node position
                    current.NextPosition = (currentPosition + current.Velocity).ToPoint();
                }

                // move nodes to resultant positions (and calculate total displacement)
                for (int i = 0; i < layout.Length; i++)
                {
                    NodeLayoutInfo current = layout[i];

                    totalDisplacement    += CalcDistance(current.Layn.Location, current.NextPosition);
                    current.Layn.Location = current.NextPosition;
                }

                iterations++;
                if (totalDisplacement < 10)
                {
                    stopCount++;
                }
                if (stopCount > 15)
                {
                    break;
                }
                if (iterations > maxIterations)
                {
                    break;
                }
            }

            // center the diagram around the origin
            Rectangle logicalBounds = GetDiagramBounds();
            Point     midPoint      = new Point(logicalBounds.X + (logicalBounds.Width / 2), logicalBounds.Y + (logicalBounds.Height / 2));

            foreach (Layn node in mNodes)
            {
                node.Location -= (Size)midPoint;
            }
        }
        /// <summary>
        /// Runs the force-directed layout algorithm on this Diagram, using the specified parameters.
        /// </summary>
        /// <param name="damping">Value between 0 and 1 that slows the motion of the nodes during layout.</param>
        /// <param name="springLength">Value in pixels representing the length of the imaginary springs that run along the connectors.</param>
        /// <param name="maxIterations">Maximum number of iterations before the algorithm terminates.</param>
        /// <param name="seed">A seed value for the random number generator.</param>
        public void Arrange(double damping, int springLength, int maxIterations, Int32 seed)
        {
            // random starting positions can be made deterministic by seeding System.Random with a constant
            Random rnd = new Random(seed);

            // copy nodes into an array of metadata and randomise initial coordinates for each node
            NodeLayoutInfo[] layout = new NodeLayoutInfo[mNodes.Count];
            for (int i = 0; i < mNodes.Count; i++)
            {
                layout[i] = new NodeLayoutInfo(mNodes[i], new Vector(), Point.Empty);
                layout[i].Node.Location = new Point(rnd.Next(-50, 50), rnd.Next(-50, 50));
            }

            int stopCount  = 0;
            int iterations = 0;

            Stopwatch stopWatch = new Stopwatch();

            stopWatch.Reset();
            stopWatch.Start();

            while (true)
            {
                double totalDisplacement = 0;

                for (int i = 0; i < layout.Length; i++)
                {
                    NodeLayoutInfo current = layout[i];

                    // express the node's current position as a vector, relative to the origin
                    Vector currentPosition = new Vector(CalcDistance(Point.Empty, current.Node.Location), GetBearingAngle(Point.Empty, current.Node.Location));
                    Vector netForce        = new Vector(0, 0);

                    // determine repulsion between nodes
                    foreach (Node other in mNodes)
                    {
                        if (other != current.Node)
                        {
                            netForce += CalcRepulsionForce(current.Node, other);
                        }
                    }

                    // determine attraction caused by connections
                    foreach (Node child in current.Node.Connections)
                    {
                        netForce += CalcAttractionForce(current.Node, child, springLength);
                    }
                    foreach (Node parent in mNodes)
                    {
                        if (parent.Connections.Contains(current.Node))
                        {
                            netForce += CalcAttractionForce(current.Node, parent, springLength);
                        }
                    }

                    // apply net force to node velocity
                    current.Velocity = (current.Velocity + netForce) * damping;

                    // apply velocity to node position
                    current.NextPosition = (currentPosition + current.Velocity).ToPoint();
                }

                // move nodes to resultant positions (and calculate total displacement)
                for (int i = 0; i < layout.Length; i++)
                {
                    NodeLayoutInfo current = layout[i];

                    totalDisplacement    += CalcDistance(current.Node.Location, current.NextPosition);
                    current.Node.Location = current.NextPosition;
                }

                iterations++;
                if (totalDisplacement < MinimumDisplacement)
                {
                    stopCount++;
                }
                if (stopCount > 15 || iterations > maxIterations)
                {
                    break;
                }
            }

            stopWatch.Stop();
            ElapsedGenerationMilliseconds = stopWatch.ElapsedMilliseconds;
            ElapsedGenerationTicks        = stopWatch.ElapsedTicks;
            IterationsCount = iterations;

            // center the diagram around the origin
            Rectangle logicalBounds = GetDiagramBounds();
            Point     midPoint      = new Point(logicalBounds.X + (logicalBounds.Width / 2), logicalBounds.Y + (logicalBounds.Height / 2));

            foreach (Node node in mNodes)
            {
                node.Location -= (Size)midPoint;
            }
        }
Example #6
0
        /// <summary>
        /// Runs the force-directed layout algorithm on this Diagram, using the specified parameters.
        /// </summary>
        /// <param name="damping">Value between 0 and 1 that slows the motion of the nodes during layout.</param>
        /// <param name="springLength">Value in pixels representing the length of the imaginary springs that run along the connectors.</param>
        /// <param name="maxIterations">Maximum number of iterations before the algorithm terminates.</param>
        /// <param name="deterministic">Whether to use a random or deterministic layout.</param>
        public void Arrange(double damping, int springLength, int maxIterations, bool deterministic)
        {
            springLength = mNodes.Count * 5;                                    // for example, for 100 nodes, a spring length of at least 450 is needed to prevent the algorithm from going haywire.  What the particular problem is remains to be understood.
            // random starting positions can be made deterministic by seeding System.Random with a constant
            Random rnd = deterministic ? new Random(0) : new Random();

            // copy nodes into an array of metadata and randomise initial coordinates for each node
            NodeLayoutInfo[] layout = new NodeLayoutInfo[mNodes.Count];
            for (int i = 0; i < mNodes.Count; i++)
            {
                layout[i] = new NodeLayoutInfo(mNodes[i], new Vector(), Point.Empty);
                layout[i].Node.Location = new Point(rnd.Next(-50, 50), rnd.Next(-50, 50));
            }

            int      stopCount  = 0;
            int      iterations = 0;
            DateTime now        = DateTime.Now;

            while (true)
            {
                double totalDisplacement = 0;

                for (int i = 0; i < layout.Length; i++)
                {
                    NodeLayoutInfo current = layout[i];

                    // express the node's current position as a vector, relative to the origin
                    Vector currentPosition = new Vector(CalcDistance(Point.Empty, current.Node.Location), GetBearingAngle(Point.Empty, current.Node.Location));
                    Vector netForce        = new Vector(0, 0);

                    // determine repulsion between nodes
                    foreach (Node other in mNodes)
                    {
                        if (other != current.Node)
                        {
                            netForce += CalcRepulsionForce(current.Node, other);
                        }
                    }

                    // determine attraction caused by connections to children
                    foreach (Node child in current.Node.Connections)
                    {
                        netForce += CalcAttractionForce(current.Node, child, springLength);
                    }

                    // attraction caused by connections to parents.
                    foreach (Node parent in mNodes)
                    {
                        if (parent.Connections.Contains(current.Node))
                        {
                            netForce += CalcAttractionForce(current.Node, parent, springLength);
                        }
                    }

                    // apply net force to node velocity
                    current.Velocity = (current.Velocity + netForce) * damping;

                    // apply velocity to node position
                    current.NextPosition = (currentPosition + current.Velocity).ToPoint();
                }

                // move nodes to resultant positions (and calculate total displacement)
                for (int i = 0; i < layout.Length; i++)
                {
                    NodeLayoutInfo current = layout[i];

                    totalDisplacement    += CalcDistance(current.Node.Location, current.NextPosition);
                    current.Node.Location = current.NextPosition;
                }

                iterations++;
                if (totalDisplacement < 10)
                {
                    stopCount++;
                }
                if (stopCount > 15)
                {
                    break;
                }
                if (iterations > maxIterations)
                {
                    break;
                }

                // Don't spend more than 300ms processing!
                if ((DateTime.Now - now).TotalMilliseconds > 500)
                {
                    break;
                }
            }

            // center the diagram around the origin
            Rectangle logicalBounds = GetDiagramBounds();
            Point     midPoint      = new Point(logicalBounds.X + (logicalBounds.Width / 2), logicalBounds.Y + (logicalBounds.Height / 2));

            foreach (Node node in mNodes)
            {
                node.Location -= (Size)midPoint;
            }
        }
Example #7
0
 /// <summary>
 /// Gets the bounding rectangle for label editing.</summary>
 /// <param name="info">Node layout information</param>
 /// <returns>Bounding rectangle for label editing</returns>
 protected virtual Rectangle GetLabelEditBounds(NodeLayoutInfo info)
 {
     return new Rectangle(
                 info.LabelLeft,
                 info.Y,
                 m_clientSize.Width - info.LabelLeft + 1,
                 FontHeight + Margin.Top + 1);
 }
Example #8
0
        public override void UpdateLayout(IEnumerable<Node> nodes)
        {
            if ((!Configuration.ApplyAttractionForce) && (!Configuration.ApplyRepulsionForce)) {
                return;
            }

            #region Separate overlapping nodes
            if (Configuration.ApplyRepulsionForce) {
                foreach (Node node1 in nodes) {
                    foreach (Node node2 in nodes) {
                        if ((node1 != node2) && (CalcDistance(node1.Location, node2.Location) < 1.0d)) {
                            node1.Location += new Vector(random.NextDouble() * 100d - 50d, random.NextDouble() * 100d - 50d);
                        }
                    }
                }
            }
            #endregion

            #region Update nodeToLayoutInfo
            foreach (Node node in nodeToLayoutInfo.Keys.ToArray()) {
                if (!nodes.Contains(node)) {
                    nodeToLayoutInfo.Remove(node);
                }
            }
            foreach (Node node in nodes) {
                if (!nodeToLayoutInfo.ContainsKey(node)) {
                    NodeLayoutInfo layoutInfo = new NodeLayoutInfo(node, new Vector(), node.Location);
                    nodeToLayoutInfo.Add(node, layoutInfo);
                }
            }
            #endregion

            foreach (NodeLayoutInfo current in nodeToLayoutInfo.Values) {
                Point currentPosition = current.Node.Location;
                Vector netForce = new Vector(0, 0);

                // determine repulsion between nodes
                if (Configuration.ApplyRepulsionForce) {
                    foreach (Node other in nodes) {
                        if (other != current.Node) {
                            netForce += CalcRepulsionForce(current.Node, other);
                        }
                    }
                }

                // determine attraction caused by connections
                if (Configuration.ApplyAttractionForce) {
                    foreach (Node child in current.Node.GetAdjacentNodes()) {
                        netForce += CalcAttractionForce(current.Node, child, Configuration.AttractionSpringLength);
                    }
                    foreach (Node parent in nodes) {
                        if (parent.GetAdjacentNodes().Contains(current.Node)) {
                            netForce += CalcAttractionForce(current.Node, parent, Configuration.AttractionSpringLength);
                        }
                    }
                }

                // apply net force to node velocity
                current.Velocity = (current.Velocity + netForce) * DEFAULT_DAMPING;

                if (current.Velocity.Length >= 1) {
                    // apply velocity to node position
                    current.NextPosition = currentPosition + current.Velocity;
                } else {
                    current.NextPosition = currentPosition;
                }
            }

            // move nodes to resultant positions
            foreach (NodeLayoutInfo current in nodeToLayoutInfo.Values) {
                current.Node.Location = current.NextPosition;
            }
        }
Example #9
0
        public void Iterate(double damping, int springLength, int maxIterations)
        {
            for (int i = 0; i < layout.Count; i++)
            {
                NodeLayoutInfo current = layout[i];

                // express the node's current position as a vector, relative to the origin
                Vector currentPosition = new Vector(CalcDistance(Point.Empty, current.Node.Location), GetBearingAngle(Point.Empty, current.Node.Location));
                Vector netForce        = new Vector(0, 0);

                // determine repulsion between nodes
                foreach (Node other in nodes)
                {
                    if (other != current.Node)
                    {
                        netForce += CalcRepulsionForce(current.Node, other);
                    }
                }

                // determine attraction caused by connections
                foreach (Node child in current.Node.Connections)
                {
                    netForce += CalcAttractionForce(current.Node, child, springLength);
                }

                foreach (Node parent in nodes)
                {
                    if (parent.Connections.Contains(current.Node))
                    {
                        netForce += CalcAttractionForce(current.Node, parent, springLength);
                    }
                }

                // apply net force to node velocity
                current.Velocity = (current.Velocity + netForce) * damping;

                if (current.Velocity.Magnitude > 5)
                {
                    current.Velocity.Magnitude = 5;
                }

                // apply velocity to node position
                current.NextPosition = (currentPosition + current.Velocity).ToPointF();
            }

            // move nodes to resultant positions (and calculate total displacement)
            for (int i = 0; i < layout.Count; i++)
            {
                NodeLayoutInfo current = layout[i];

                // totalDisplacement += CalcDistance(current.Node.Location, current.NextPosition);
                current.Node.Location = current.NextPosition;
            }

            // center the diagram around the origin
            Rectangle logicalBounds = GetDiagramBounds();
            Point     midPoint      = new Point(logicalBounds.X + (logicalBounds.Width / 2), logicalBounds.Y + (logicalBounds.Height / 2));

            foreach (Node node in nodes)
            {
                node.Location -= (Size)midPoint;
            }
        }
Example #10
0
        private void ApplyForces(Dictionary <Node, NodeDrawInfo> nodes, int pbHeight, int pbWidth, float spring, float charge, float damping)
        {
            // random starting positions can be made deterministic by seeding System.Random with a constant
            Random.InitState(0);

            // copy nodes into an array of metadata and randomise initial coordinates for each node
            NodeLayoutInfo[] layoutInfos = new NodeLayoutInfo[nodes.Count];

            int index = 0;

            foreach (var node in nodes)
            {
                layoutInfos[index] = new NodeLayoutInfo(node.Key, node.Value)
                {
                    NodeDrawInfo =
                    {
                        Position = new Vector2(Random.value * pbHeight, Random.value * pbWidth)
                    }
                };
                index++;
            }

            float diff       = 0;
            float diff1      = 1;
            var   nodesCount = layoutInfos.Length;

            while (CheckDisplacement(diff, diff1))
            {
                diff1 = diff;
                diff  = 0;

                for (int i = 0; i < nodesCount; i++)
                {
                    var node = layoutInfos[i];
                    for (int j = 0; j < nodesCount; j++)
                    {
                        var otherNode = layoutInfos[j];
                        if (node.Node != otherNode.Node)
                        {
                            float dx       = otherNode.NodeDrawInfo.Position.x - node.NodeDrawInfo.Position.x;
                            float dy       = otherNode.NodeDrawInfo.Position.y - node.NodeDrawInfo.Position.y;
                            var   velocity = new Vector2(dx, dy);

                            float hypotenuse = Mathf.Sqrt(Mathf.Pow(dx, 2) + Mathf.Pow(dy, 2));
                            float force      = 0;
                            if (node.Node.GetArcTo(otherNode.Node) != null || otherNode.Node.GetArcTo(node.Node) != null)
                            {
                                force = (hypotenuse - spring) / 2.0f;
                            }
                            else
                            {
                                force = -((node.Mass * otherNode.Mass) / Mathf.Pow(hypotenuse, 2)) * charge;
                            }

                            velocity      /= hypotenuse;
                            velocity      *= force;
                            node.Velocity += velocity;
                        }
                    }
                    node.NodeDrawInfo.Position += node.Velocity;
                    node.Velocity *= damping;
                    diff          += Mathf.Abs(node.Velocity.x) + Mathf.Abs(node.Velocity.y);
                }
            }

            AlignCenterNodes(layoutInfos);
        }
Example #11
0
        // Runs the force-directed layout algorithm on this Diagram, using the specified parameters.
        public void Arrange(double damping, int springLength, int maxIterations, bool deterministic)
        {
            // random starting positions can be made deterministic by seeding System.Random with a constant
            Random rnd = deterministic ? new Random(0) : new Random();

            // copy nodes into an array of metadata and randomise initial coordinates for each node
            NodeLayoutInfo[] layout = new NodeLayoutInfo[nodes.Count];
            for (int i = 0; i < nodes.Count; i++)
            {
                layout[i] = new NodeLayoutInfo(nodes[i], new Vector(), Point.Empty);
                layout[i].Node.Location = new Point(rnd.Next(-50, 50), rnd.Next(-50, 50));
            }

            int stopCount  = 0;
            int iterations = 0;

            while (true)
            {
                double totalDisplacement = 0;

                for (int i = 0; i < layout.Length; i++)
                {
                    NodeLayoutInfo current = layout[i];

                    // express the node's current position as a vector, relative to the origin
                    Vector currentPosition = new Vector(CalcDistance(Point.Empty, current.Node.Location), GetBearingAngle(Point.Empty, current.Node.Location));
                    Vector netForce        = new Vector(0, 0);

                    // determine repulsion between nodes
                    foreach (AccountNode other in nodes)
                    {
                        if (other != current.Node)
                        {
                            netForce += CalcRepulsionForce(current.Node, other);
                        }
                    }

                    // determine attraction caused by neighbours
                    foreach (AccountNode child in current.Node.neighbours.Keys)
                    {
                        netForce += CalcAttractionForce(current.Node, child, springLength);
                    }
                    foreach (AccountNode parent in nodes)
                    {
                        if (parent.neighbours.ContainsKey(current.Node))
                        {
                            netForce += CalcAttractionForce(current.Node, parent, springLength);
                        }
                    }

                    // apply net force to node velocity
                    current.Velocity = (current.Velocity + netForce) * damping;

                    // apply velocity to node position
                    current.NextPosition = (currentPosition + current.Velocity).ToPoint();
                }

                // move nodes to resultant positions (and calculate total displacement)
                for (int i = 0; i < layout.Length; i++)
                {
                    NodeLayoutInfo current = layout[i];

                    totalDisplacement    += CalcDistance(current.Node.Location, current.NextPosition);
                    current.Node.Location = current.NextPosition;
                }

                iterations++;
                if (totalDisplacement < 10)
                {
                    stopCount++;
                }
                if (stopCount > 15)
                {
                    break;
                }
                if (iterations > maxIterations)
                {
                    break;
                }
            }
        }
Example #12
0
        /// <summary>
        /// Runs the force-directed layout algorithm on this Diagram, using the specified parameters.
        /// </summary>
        /// <param name="damping">
        /// Value between 0 and 1 that slows the motion of the nodes during layout.
        /// </param>
        /// <param name="springLength">
        /// Value in pixels representing the length of the imaginary springs that run along the
        /// connectors.
        /// </param>
        /// <param name="maxIterations">
        /// Maximum number of iterations before the algorithm terminates.
        /// </param>
        /// <param name="deterministic">
        /// Whether to use a random or deterministic layout.
        /// </param>
        ///
        public void Arrange(double damping, int springLength, int maxIterations, bool deterministic)
        {
            // random starting positions can be made deterministic by seeding System.Random with a
            // constant
            Random rnd = deterministic ? new Random(0) : new Random();

            // copy nodes into an array of metadata and randomise initial coordinates for each node
            NodeLayoutInfo[] layout = new NodeLayoutInfo[Shapes.Count];
            for (int i = 0; i < Shapes.Count; i++)
            {
                layout[i] = new NodeLayoutInfo(Shapes[i], new Vector(), new PointD(0, 0));
                layout[i].Node.Location = new PointD(rnd.Next(-50, 50), rnd.Next(-50, 50));
            }

            int stopCount  = 0;
            int iterations = 0;

            while (true)
            {
                double totalDisplacement = 0;

                for (int i = 0; i < layout.Length; i++)
                {
                    NodeLayoutInfo current = layout[i];

                    // express the node's current position as a vector, relative to the origin
                    int    distance = CalcDistance(new PointD(0, 0), current.Node.Location);
                    Vector currentPosition
                        = new Vector(distance, GetBearingAngle(new PointD(0, 0), current.Node.Location));
                    Vector netForce = new Vector(0, 0);

                    // determine repulsion between nodes
                    foreach (Shape other in Shapes)
                    {
                        if (other != current.Node)
                        {
                            netForce += CalcRepulsionForce(current.Node, other);
                        }
                    }

                    // if it was a connected node
                    if (current.Node is RelationShape)
                    {
                        RelationShape relationShape = current.Node as RelationShape;
                        // determine attraction caused by connections
                        foreach (Shape child in relationShape.Related)
                        {
                            netForce += CalcAttractionForce(relationShape, child, springLength);
                        }

                        foreach (Shape parent in Shapes)
                        {
                            if (parent is RelationShape)
                            {
                                RelationShape parentRelationShape = parent as RelationShape;
                                if (relationShape.Related.Contains(current.Node))
                                {
                                    netForce += CalcAttractionForce(current.Node, parentRelationShape, springLength);
                                }
                            }
                        }
                    }
                    // apply net force to node velocity
                    current.Velocity = (current.Velocity + netForce) * damping;

                    // apply velocity to node position
                    current.NextPosition = (currentPosition + current.Velocity).ToPointD();
                }

                // move nodes to resultant positions (and calculate total displacement)
                for (int i = 0; i < layout.Length; i++)
                {
                    NodeLayoutInfo current = layout[i];

                    totalDisplacement    += CalcDistance(current.Node.Location, current.NextPosition);
                    current.Node.Location = current.NextPosition;
                }

                iterations++;
                if (totalDisplacement < 10)
                {
                    stopCount++;
                }
                if (stopCount > 15)
                {
                    break;
                }
                if (iterations > maxIterations)
                {
                    break;
                }
            }

            // center the diagram around the origin
            Rectangle logicalBounds = GetDiagramBounds();
            PointD    midPoint      = new PointD(logicalBounds.X + (logicalBounds.Width / 2),
                                                 logicalBounds.Y + (logicalBounds.Height / 2));

            foreach (Shape node in Shapes)
            {
                node.Location = new PointD(Math.Abs(midPoint.X - node.Location.X),
                                           Math.Abs(midPoint.Y - node.Location.Y));
            }
        }
Example #13
0
        /// <summary>
        /// Runs the force-directed layout algorithm on this Diagram, using the specified parameters.
        /// </summary>
        /// <param name="damping">Value between 0 and 1 that slows the motion of the nodes during layout.</param>
        /// <param name="springLength">Value in pixels representing the length of the imaginary springs that run along the connectors.</param>
        /// <param name="maxIterations">Maximum number of iterations before the algorithm terminates.</param>
        /// <param name="deterministic">Whether to use a random or deterministic layout.</param>
        public void Arrange(double damping, int springLength, int maxIterations, bool deterministic)
        {
            springLength = mNodes.Count * 5;			// for example, for 100 nodes, a spring length of at least 450 is needed to prevent the algorithm from going haywire.  What the particular problem is remains to be understood.
            // random starting positions can be made deterministic by seeding System.Random with a constant
            Random rnd = deterministic ? new Random(0) : new Random();

            // copy nodes into an array of metadata and randomise initial coordinates for each node
            NodeLayoutInfo[] layout = new NodeLayoutInfo[mNodes.Count];
            for (int i = 0; i < mNodes.Count; i++)
            {
                layout[i] = new NodeLayoutInfo(mNodes[i], new Vector(), Point.Empty);
                layout[i].Node.Location = new Point(rnd.Next(-50, 50), rnd.Next(-50, 50));
            }

            int stopCount = 0;
            int iterations = 0;
            DateTime now = DateTime.Now;

            while (true)
            {
                double totalDisplacement = 0;

                for (int i = 0; i < layout.Length; i++)
                {
                    NodeLayoutInfo current = layout[i];

                    // express the node's current position as a vector, relative to the origin
                    Vector currentPosition = new Vector(CalcDistance(Point.Empty, current.Node.Location), GetBearingAngle(Point.Empty, current.Node.Location));
                    Vector netForce = new Vector(0, 0);

                    // determine repulsion between nodes
                    foreach (Node other in mNodes)
                    {
                        if (other != current.Node)
                        {
                            netForce += CalcRepulsionForce(current.Node, other);
                        }
                    }

                    // determine attraction caused by connections to children
                    foreach (Node child in current.Node.Connections)
                    {
                        netForce += CalcAttractionForce(current.Node, child, springLength);
                    }

                    // attraction caused by connections to parents.
                    foreach (Node parent in mNodes)
                    {
                        if (parent.Connections.Contains(current.Node))
                        {
                            netForce += CalcAttractionForce(current.Node, parent, springLength);
                        }
                    }

                    // apply net force to node velocity
                    current.Velocity = (current.Velocity + netForce) * damping;

                    // apply velocity to node position
                    current.NextPosition = (currentPosition + current.Velocity).ToPoint();
                }

                // move nodes to resultant positions (and calculate total displacement)
                for (int i = 0; i < layout.Length; i++)
                {
                    NodeLayoutInfo current = layout[i];

                    totalDisplacement += CalcDistance(current.Node.Location, current.NextPosition);
                    current.Node.Location = current.NextPosition;
                }

                iterations++;
                if (totalDisplacement < 10) stopCount++;
                if (stopCount > 15) break;
                if (iterations > maxIterations) break;

                // Don't spend more than 300ms processing!
                if ((DateTime.Now - now).TotalMilliseconds > 500) break;
            }

            // center the diagram around the origin
            Rectangle logicalBounds = GetDiagramBounds();
            Point midPoint = new Point(logicalBounds.X + (logicalBounds.Width / 2), logicalBounds.Y + (logicalBounds.Height / 2));

            foreach (Node node in mNodes)
            {
                node.Location -= (Size)midPoint;
            }
        }