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