Example #1
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 #2
0
            public Vector Velocity; // the node's current velocity, expressed in vector form

            #endregion Fields

            #region Constructors

            /// <summary>
            /// Initialises a new instance of the Diagram.NodeLayoutInfo class, using the specified parameters.
            /// </summary>
            /// <param name="node"></param>
            /// <param name="velocity"></param>
            /// <param name="nextPosition"></param>
            public NodeLayoutInfo(Node node, Vector velocity, PointF nextPosition)
            {
                Node = node;
                Velocity = velocity;
                NextPosition = nextPosition;
            }
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)
        {
            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;
            }
        }