Example #1
0
		public bool Arrange(IGraph graph,
			GridLayoutInfo info, LayoutProgress progress)
		{
			_graph = new Graph(graph);
			_info = info;

			if (_info.RndSeed != 0)
			{
				// Use the randomization seed supplied by the user
				_random = new Random(_info.RndSeed);
			}

			Path path = null;

			// Find the longest path between the start and the end node
			if (_info.StartNode != null && _info.EndNode != null)
			{
				path = PathFinder.FindLongestPath(graph, _info.StartNode, _info.EndNode);
			}
			else
			{
				path = PathFinder.FindLongestPath(graph);
			}

			if (path == null)
				return false;

			// Build path consisting of the corresponding GraphNode-s
			_backbone = new ArrayList();
			foreach (INode inode in path.Nodes)
			{
				foreach (GraphNode node in _graph.Nodes)
				{
					if (node.Node == inode)
					{
						_backbone.Add(node);
						break;
					}
				}
			}

			GraphNode[,] bestGrid = InitGrid();
			float bestEstimation = float.MaxValue;
			int maxShift = Math.Max(4, _gridWidth / 10); 

			// Update progress
			int current = 0;
			int total = CountIterations(maxShift);
			float ffactor = total / 100;
			int factor = 1;
			if (ffactor > 1)
			{
				factor = (int)Math.Floor((double)ffactor);
				total = total / factor + 1;
			}
			if (progress != null)
				progress(current, total);

			while (maxShift > 0)
			{
				for (int iter = 0;
					iter < (maxShift > 2 ?
						_info.Iterations :
						(_info.Iterations / 5 * maxShift * _graph.Nodes.Count));
					++iter)
				{
					// Update progress
					if (progress != null)
					{
						if (current % factor == 0)
							progress(current / factor, total);
						current++;
					}

					GraphNode[,] grid = ScrambleGrid(bestGrid, maxShift, iter);
					float estimation = EvaluateGrid(grid, maxShift);

					if (estimation < bestEstimation)
					{
						bestEstimation = estimation;
						bestGrid = grid;
					}
					else
					{
						ApplyGrid(bestGrid);
					}
				}

				maxShift--;
			}

			PlaceObjects(bestGrid);

			// Update progress
			if (progress != null)
				progress(total, total);

			return true;
		}
Example #2
0
		/// <summary>
		/// Performs the arrangement of the diagram.
		/// </summary>
		public bool Arrange(IGraph graph,
			LayeredLayoutInfo info, LayoutProgress progress)
		{
			_graph = new Graph(graph);
			_info = info;
			_progress = progress;
			_current = 0;
			_total = 
				1 /*path finding*/ + 
				1 /*layers splitting*/ +
				1 /*dummify*/ + 
				6 /*minimize crossings*/ +
				4 /*swap pairs*/ +
				1 /*layout*/ +
				1 /*apply*/ +
				1 /*compact*/ + 
				1 /*dedumify*/;

			// Update progress
			if (_progress != null)
				_progress(_current, _total);

			// Initialize nodes
			foreach (GraphNode node in _graph.Nodes)
			{
				node.SetData(_Layer, -1);
				node.SetData(_UBaryCenter, 0.0);
				node.SetData(_DBaryCenter, 0.0);
				node.SetData(_ULinkCount, 0);
				node.SetData(_DLinkCount, 0);
				node.SetData(_UPriority, 0);
				node.SetData(_DPriority, 0);
				node.SetData(_GridPosition, 0);
				node.SetData(_Dummy, false);
			}

			// Initialize links
			foreach (GraphLink link in _graph.Links)
			{
				link.SetData(_DummificationLevel, 0);
			}

			// Determine the layer depth
			Path longestPath = PathFinder.FindLongestPath(graph, info.TimeLimit);
			if (longestPath == null)
				return true; // No objects in the graph

			// Update progress
			if (_progress != null)
				_progress(_current++, _total);

			_layers = new ArrayList();
			for (int i = 0; i < longestPath.Nodes.Count; i++)
				_layers.Add(new ArrayList());

			// Distribute nodes to their appropriate layers,
			// starting with the nodes from the longest path
			for (int i = 0; i < longestPath.Nodes.Count; i++)
			{
				// Find the corresponding node in the internal graph
				GraphNode node = null;
				foreach (GraphNode n in _graph.Nodes)
				{
					if (n.Node == longestPath.Nodes[i])
					{
						node = n;
						break;
					}
				}

				node.SetData(_Layer, i);
				(_layers[i] as ArrayList).Add(node);
			}

			foreach (INode node in longestPath.Nodes)
			{
				// Find the corresponding node in the internal graph
				GraphNode gnode = null;
				foreach (GraphNode n in _graph.Nodes)
				{
					if (n.Node == node)
					{
						gnode = n;
						break;
					}
				}

				AddChildren(gnode);
			}

			// For all layers whose nodes are > 1 / 20 of the total ->
			// drop nodes. This will ensure that there are no
			// too wide layers, thus improving visibility of the graph.
			int total = 0;
			bool found;
			foreach (ArrayList layer in _layers)
				total += layer.Count;

			int threshold = total / 10;
			if (total > 50 && _info.SplitLayers)
			{
				found = true;
				while (found)
				{
					found = false;

					foreach (ArrayList layer in _layers)
					{
						if (layer.Count > threshold)
						{
							// Find candidates for dropping.
							// Good candidate is a node which
							// when moved downwards will be closer
							// to its children
							SortedList candidates = new SortedList();
							foreach (GraphNode node in layer)
							{
								// Calculate total child depth
								int factor = 0;
								int nodeLayer = (int)node.GetData(_Layer);
								foreach (GraphLink link in node.OutLinks)
								{
									int childLayer = (int)link.Destination.GetData(_Layer);
									if (childLayer <= nodeLayer)
										factor += 1;
									else
										factor -= 1;
								}
								foreach (GraphLink link in node.InLinks)
								{
									int childLayer = (int)link.Origin.GetData(_Layer);
									if (childLayer <= nodeLayer)
										factor += 1;
									else
										factor -= 1;
								}

								if (factor > 0)
									candidates[factor] = node;
							}

							if (candidates.Keys.Count == 0)
								continue;

							found = true;

							ArrayList nextLayer = null;
							int ilayer = _layers.IndexOf(layer);
							if (ilayer == _layers.Count - 1)
							{
								nextLayer = new ArrayList();
								_layers.Add(nextLayer);
							}
							else
							{
								nextLayer = _layers[ilayer + 1] as ArrayList;
							}

							while (layer.Count > threshold)
							{
								if (candidates.Keys.Count == 0)
									break;

								GraphNode node = candidates.GetByIndex(candidates.Count - 1) as GraphNode;
								candidates.RemoveAt(candidates.Count - 1);

								nextLayer.Add(node);
								layer.Remove(node);
								node.SetData(_Layer, (int)node.GetData(_Layer) + 1);
							}
						}

						if (found)
							break;
					}
				}
			}

			// Check if there are directly related nodes on the same layer
			found = true;
			while (found)
			{
				found = false;

				int ilayer = 0;
				ArrayList nodesToDrop = new ArrayList();
				foreach (ArrayList layer in _layers)
				{
					nodesToDrop.Clear();
					foreach (GraphNode node in layer)
					{
						foreach (GraphLink link in node.OutLinks)
						{
							if ((int)link.Destination.GetData(_Layer) == ilayer)
							{
								// The node's child is on the same layer.
								// Mark it for dropping
								if (!nodesToDrop.Contains(link.Destination))
									nodesToDrop.Add(link.Destination);
								found = true;
							}
						}
					}

					// Drop nodes downwards
					if (found)
					{
						ArrayList curLayer = _layers[ilayer] as ArrayList;
						ArrayList nextLayer = null;
						if (ilayer == _layers.Count - 1)
						{
							nextLayer = new ArrayList();
							_layers.Add(nextLayer);
						}
						else
						{
							nextLayer = _layers[ilayer + 1] as ArrayList;
						}

						foreach (GraphNode node in nodesToDrop)
						{
							if (curLayer.Count > 1)
							{
								nextLayer.Add(node);
								curLayer.Remove(node);
								node.SetData(_Layer, (int)node.GetData(_Layer) + 1);
							}
						}

						break;
					}

					ilayer++;
				}
			}


			// Calculate initial grid positions
			foreach (ArrayList layer in _layers)
				for (int i = 0; i < layer.Count; i++)
					(layer[i] as GraphNode).SetData(_GridPosition, (double)i);

			// Update progress
			if (_progress != null)
				_progress(_current++, _total);

			// Add dummy nodes for all links which cross one or more
			// layers. Add one dummy node for each crossed layer
			Dummify();

			// Reduce crossings
			MinimizeCrossings();

			// Further reduce crossings through pair swap
			SwapPairs();

			// Arrange nodes
			Layout();

			// Update progress
			if (_progress != null)
				_progress(_current++, _total);

			// Compact levels
			if (_info.ArrowsCompactFactor != 1)
				Compact();

			// Update progress
			if (_progress != null)
				_progress(_current++, _total);

			Apply();

			// Update progress
			if (_progress != null)
				_progress(_current++, _total);

			// Reverse dummification
			Dedummify();

			// Update progress
			if (_progress != null)
				_progress(_total, _total);

			// Update nodes positions
			foreach (GraphNode node in _graph.Nodes)
				if (node.Node != null)
					node.Node.Bounds = node.Bounds;

			// Update arrow points
			foreach (GraphLink link in _graph.Links)
			{
				if (link.Link != null)
				{
					object points = link.GetData(_LinkPoints);
					if (points != null)
						link.Link.SetPoints(points as ArrayList);
				}
			}

			return true;
		}
Example #3
0
		public bool Arrange(IGraph graph,
			SpringLayoutInfo layoutInfo, LayoutProgress progress)
		{
			_info = layoutInfo;

			if (_info.RndSeed != 0)
			{
				// Use the randomization seed supplied by the user
				_r = new Random(_info.RndSeed);
			}

			// Build the internal graph
			_graph = new Graph(graph);

			RectangleF rcDoc = CalcContentRect(_graph);
			float minNodeX = rcDoc.Left - (50f * _info.NodeDistance);
			float minNodeY = rcDoc.Top - (50f * _info.NodeDistance);
			float maxNodeX = rcDoc.Right + (50f * _info.NodeDistance);
			float maxNodeY = rcDoc.Bottom + (50f * _info.NodeDistance);

			// Initialize
			foreach (GraphNode node in _graph.Nodes)
				node.SetData(_SpringVertex, new SpringVertexData());
			foreach (GraphLink link in _graph.Links)
				link.SetData(_SpringEdge, new SpringEdgeData());

			// Calculate vertex radiuses
			foreach (GraphNode node in _graph.Nodes)
			{
				RectangleF rc = node.Bounds;
				node.SetData(_VertexRadius,
					(double)(rc.Width + rc.Height) / 4);
			}

			// Update progress
			int total = layoutInfo.IterationCount;
			float ffactor = total / 100;
			int factor = 1;
			if (ffactor > 1)
			{
				factor = (int)Math.Floor((double)ffactor);
				total = total / factor + 1;
			}
			if (progress != null)
				progress(0, total);

			// An iterative layout algorithm
			for(int step = 1; step <= layoutInfo.IterationCount; step++)
			{
				// Update progress
				if (progress != null)
				{
					if (step % factor == 0)
						progress(step / factor, total);
				}

				foreach(GraphNode node in _graph.Nodes)
				{
					SpringVertexData data = (SpringVertexData)
						node.GetData(_SpringVertex);
					data.DX /= 4;
					data.DY /= 4;
					data.EdgeDX = data.EdgeDY = 0;
					data.RepulsionDX = data.RepulsionDY = 0;
				}

				// here the "springs" confine nodes to the desired node distance
				foreach (GraphLink link in _graph.Links)
				{
					// get incident nodes
					GraphNode v1 = link.Origin;
					GraphNode v2 = link.Destination;

					// compute current distance between nodes
					PointF v1Pos = v1.Center;
					PointF v2Pos = v2.Center;
					double vx = v1Pos.X - v2Pos.X;
					double vy = v1Pos.Y - v2Pos.Y;
					double dist = Math.Sqrt(vx * vx + vy * vy);
					dist = (dist == 0) ? 0.0001 : dist;

					// get desired distance accomodated for node extents
					double desiredDist = _info.NodeDistance;
					desiredDist += ((double)v1.GetData(_VertexRadius) +
						(double)v2.GetData(_VertexRadius)) / 2;

					// now handling cluster center nodes ?
					bool masterNodes = _info.EnableClusters &&
						(v1.LinkCount > 4 && v2.LinkCount > 4);
					desiredDist *= masterNodes ? 4 : link.Link.Weight;

					// force factor: optimal length minus actual length,
					// is made smaller as the current actual length gets larger.
					double f = _ForceConstant * (desiredDist - dist) / dist;

					// nodes with few links are moved easier than ones with more
					double f1 = masterNodes ? f :
						(f * Math.Pow(_stretch / 100.0, v1.LinkCount - 1));
					double f2 = masterNodes ? f :
						(f * Math.Pow(_stretch / 100.0, v2.LinkCount - 1));

					// move first node towards the second one
					SpringVertexData v1D = (SpringVertexData)v1.GetData(_SpringVertex);
					v1D.EdgeDX += f1 * vx;
					v1D.EdgeDY += f1 * vy;

					// move second node towards the first one
					SpringVertexData v2D = (SpringVertexData)v2.GetData(_SpringVertex);
					v2D.EdgeDX += -f2 * vx;
					v2D.EdgeDY += -f2 * vy;
				}

				// here nodes tend to run away one from another
				foreach (GraphNode node in _graph.Nodes)
				{
					SpringVertexData svd = (SpringVertexData)node.GetData(_SpringVertex);
					double dx = 0;
					double dy = 0;

					foreach (GraphNode node2 in _graph.Nodes)
					{
						if (node2 == node)
							continue;

						// now handling cluster center nodes ?
						bool masterNodes = _info.EnableClusters &&
							(node.LinkCount > 4 && node2.LinkCount > 4);

						// if distance is longer than this, ignore repulsion
						double intrZone = _info.NodeDistance *
							(masterNodes ? 4.5 : _info.RepulsionFactor);

						// if distance is shorter than this, randomize node positions
						double tooClose = _info.NodeDistance / (masterNodes ? 1 : 5);

						// distance to move when using random position
						double moveRange = _info.NodeDistance * (masterNodes ? 5 : 1);

						// compute current distance between nodes
						double vx = node.Center.X - node2.Center.X;
						double vy = node.Center.Y - node2.Center.Y;
						double distance = vx * vx + vy * vy;

						// kind of simmulated annealing, use random jumps for nodes
						// that are close one to another; longer jumps for nodes that
						// overlap completely and shorter for nodes that don't overlap
						if (distance == 0)
						{
							dx += moveRange/2 + _r.Next() % ((int)(moveRange * 3));
							dy += moveRange/2 + _r.Next() % ((int)(moveRange * 3));
							if (_r.Next() % 2 == 1) dx = -dx;
							if (_r.Next() % 2 == 1) dy = -dy;
						}
						else if (distance < tooClose * tooClose)
						{
							dx += moveRange/2 + _r.Next((int)moveRange);
							dy += moveRange/2 + _r.Next((int)moveRange);
							if (_r.Next() % 2 == 1) dx = -dx;
							if (_r.Next() % 2 == 1) dy = -dy;
						}
						// if nodes are not that close, calculate normal repulsion
						else if (distance < intrZone * intrZone)
						{
							float mf = masterNodes ?
								(node.LinkCount + node2.LinkCount) : 1;
							dx += mf * vx / (distance * distance);
							dy += mf * vy / (distance * distance);
						}
					}

					// apply the summary repulsion of this node with all other nodes
					double dlen = dx * dx + dy * dy;
					if (dlen > 0)
					{
						dlen = Math.Sqrt(dlen) / 2;
						svd.RepulsionDX += dx / dlen;
						svd.RepulsionDY += dy / dlen;
					}
				}

				// sum repulsion and "spring" forces and move nodes
				foreach(GraphNode node in _graph.Nodes)
				{
					// update node movement speed
					SpringVertexData vd =
						(SpringVertexData)node.GetData(_SpringVertex);
					vd.DX += vd.RepulsionDX + vd.EdgeDX;
					vd.DY += vd.RepulsionDY + vd.EdgeDY;

					// move node
					PointF xyd = node.Center;
					xyd.X += (float)vd.DX;
					xyd.Y += (float)vd.DY;

					if (layoutInfo.EnableClusters &&
						(((xyd.X < minNodeX) || (xyd.Y < minNodeY)) || ((xyd.X > maxNodeX) || (xyd.Y > maxNodeY))))
					{
						xyd = node.Center;
						vd.DX /= 4;
						vd.DY /= 4;
					}

					node.Center = xyd;
				}

				// try to decrease crossings if that option is enabled
				if (_info.MinimizeCrossings &&
					(step % 10 == 0) &&
					(step < _info.IterationCount * 2 / 3))
				{
					int currentCrossings = CountCrossings();
					if (currentCrossings > 0)
						TryDecreaseCrossings(graph.DocRect, _info.NodeDistance, currentCrossings);
				}
			}

			// offset the graph to its original location
			RectangleF rcDoc2 = CalcContentRect(_graph);
			float pdx = rcDoc2.Left - rcDoc.Left;
			float pdy = rcDoc2.Top - rcDoc.Top;
			foreach(GraphNode node in _graph.Nodes)
			{
				PointF xyd = node.Center;
				xyd.X -= pdx;
				xyd.Y -= pdy;
				node.Center = xyd;
			}

			// update flowchart nodes positions
			foreach(GraphNode node in _graph.Nodes)
				node.Node.Bounds = node.Bounds;

			// call progress delegate
			if (progress != null)
				progress(total, total);

			return true;
		}
Example #4
0
		public bool Arrange(IGraph graph, AnnealLayoutInfo info, LayoutProgress progress)
		{
			_tempGraph = new Graph(graph);
			_info = info;

			float nsize = 0;
			foreach (INode node in graph.Nodes)
				nsize += node.Bounds.Width;
			averageNodeSize = nsize / graph.Nodes.Count;

			// determine what the graph boundaries should be
			if (info.LayoutArea != RectangleF.Empty)
			{
				_drawingArea = info.LayoutArea;
			}
			else
			{
				double squareSize = averageNodeSize * Math.Ceiling(Math.Sqrt(graph.Nodes.Count));
				double width = squareSize * info.WidthHeightRatio;
				double height = squareSize / info.WidthHeightRatio;
				_drawingArea = new RectangleF(0, 0, (float)width * 4.5f, (float)height * 4.5f);
			}

			// start with random positions
			if (info.Randomize)
			{
				Random rnd = new Random();
				foreach (GraphNode node in _tempGraph.Nodes)
				{
					node.Center = new PointF(
						_drawingArea.Left + (float)rnd.NextDouble() * _drawingArea.Width,
						_drawingArea.Top + (float)rnd.NextDouble() * _drawingArea.Height);
				}
			}

			if (progress != null)
				progress(0, _info.Stages + 2);

			SetGraphElements();
			SetInitStep();
			CalculateInitialState();

			_temperature = _info.Temperature;

			double totalCost = CostFunction();
			double previousCost;
			double stopIterCond;
			Random rand = new Random();

			// cool down for the specified number of annealing stages
			for (int stage = 0; stage < _info.Stages; stage++)
			{
				for (int iter = 0; iter < _info.IterationsPerStage; iter++)
				{
					previousCost = totalCost;

					for (int n = 0; n < _tempGraph.Nodes.Count; n++)
					{
						double oldNodeCost = evalCost(_nodes[n]);
						foreach (int i in Enum.GetValues(typeof(ShiftTo)))
						{
							ShiftNode(_nodes[n], (ShiftTo)i, false);
							if (!nodeInBounds(_nodes[n]))
							{
								ShiftNode(_nodes[n], (ShiftTo)i, true);
								continue;
							}

							double newNodeCost = evalCost(_nodes[n]);
							double newTotalCost = totalCost - oldNodeCost + newNodeCost;

							if (newNodeCost < oldNodeCost ||
								Math.Pow(Math.E, (totalCost - newTotalCost) / _temperature) > rand.NextDouble())
							{
								totalCost = newTotalCost;
								CommitMove(_nodes[n]);
								break;
							}
							else
							{
								ShiftNode(_nodes[n], (ShiftTo)i, true);
								RollbackMove(_nodes[n]);
							}
						}
					} 

					stopIterCond = previousCost / totalCost;
					if (stopIterCond > .98 && stopIterCond <= 1)
						break;
				}

				SetTemperature();
				DecreaseMoveValue();

				if (progress != null)
					progress(stage + 1, _info.Stages + 2);
			}

			FineTuning();

			if (progress != null)
				progress(_info.Stages + 2, _info.Stages + 2);

			foreach (GraphNode node in _tempGraph.Nodes)
				node.Node.Bounds = node.Bounds;

			return true;
		}
Example #5
0
		private RectangleF CalcContentRect(Graph graph)
		{
			RectangleF res = RectangleF.Empty;

			foreach(GraphNode node in graph.Nodes)
			{
				if(res == RectangleF.Empty)
					res = node.Bounds;
				else
					res = Utilities.UnionRects(res, node.Bounds);
			}

			return res;
		}