/// <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(), 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 (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 -= (Size)midPoint;
			}
		}
			public Point NextPosition;	// the node's position after the next iteration

			/// <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, Point nextPosition) {
				Node = node;
				Velocity = velocity;
				NextPosition = nextPosition;
			}