Beispiel #1
0
		/// <summary>
		/// allocates larger grid arrays if necessary
		/// </summary>
		internal void allocate(int cols, int rows, RectangleF bounds, Arrow arrow)
		{
			int curCols = 0;
			int curRows = 0;

			if (costGrid != null)
			{
				curCols = costGrid.GetLength(0);
				curRows = costGrid.GetLength(1);
			}

			// allocate new arrays if needed
			if (costGrid == null || curCols < cols || curRows < rows)
			{
				int newCols = Math.Max(curCols, cols);
				int newRows = Math.Max(curRows, rows);

				costGrid = new byte[newCols, newRows];
				closedGrid = new PathNode[newCols, newRows];
				openGrid = new PathNode[newCols, newRows];
			}
			//or reuse the existing ones
			else
			{
				Array.Clear(costGrid, 0, costGrid.Length);
				Array.Clear(closedGrid, 0, closedGrid.Length);
				Array.Clear(openGrid, 0, openGrid.Length);
			}

			// mark border lines as obstacles
			for (int c = 0; c < cols; c++)
			{
				costGrid[c, 0] = 255;
				costGrid[c, rows - 1] = 255;
			}
			for (int r = 1; r < rows - 1; r++)
			{
				costGrid[0, r] = 255;
				costGrid[cols - 1, r] = 255;
			}

			// mark node locations
			markObstacles(bounds, arrow, cols, rows);
		}
Beispiel #2
0
		internal void doRoute(bool force, Link orgnLink, Link destLink, bool nowCreating)
		{
			if (!force)
				if (!autoRoute) return;

			if (flowChart.DontRouteForAwhile)
				return;

			int i;

			float gridSize = flowChart.RoutingOptions.GridSize;

			PointF startPoint = points[0];
			PointF endPoint = points[points.Count - 1];

			// get a rectangle bounding both the origin and the destination
			RectangleF bounds = orgnLink.getNodeRect(true);
			bounds = Utilities.unionRects(bounds, destLink.getNodeRect(true));
			bounds = RectangleF.Union(bounds, Utilities.normalizeRect(
				RectangleF.FromLTRB(startPoint.X, startPoint.Y, endPoint.X, endPoint.Y)));
			if (bounds.Width < gridSize * 4)
				bounds.Inflate(gridSize * 4, 0);
			if (bounds.Height < gridSize * 4)
				bounds.Inflate(0, gridSize * 4);
			bounds.Inflate(bounds.Width, bounds.Height);

			int oNearest = 0, dNearest = 0;
			routeGetEndPoints(ref startPoint, ref endPoint,
				ref oNearest, ref dNearest, orgnLink, destLink, nowCreating);

			// Get the starting and ending square
			Point ptStart = new Point((int)((startPoint.X - bounds.X) / gridSize),
				(int)((startPoint.Y - bounds.Y) / gridSize));
			Point ptEnd = new Point((int)((endPoint.X - bounds.X) / gridSize),
				(int)((endPoint.Y - bounds.Y) / gridSize));
			if (ptStart.X == ptEnd.X && ptStart.Y == ptEnd.Y)
				return;

			// init the route grid
			int gridCols = (int)(bounds.Width / gridSize);
			int gridRows = (int)(bounds.Height / gridSize);

			RoutingGrid routingGrid = flowChart.RoutingGrid;
			routingGrid.allocate(gridCols, gridRows, bounds, this);
			byte[,] grid = routingGrid.getCostGrid();
			PathNode[,] gridClosed = routingGrid.getClosedGrid();
			PathNode[,] gridOpen = routingGrid.getOpenGrid();

			bool hurry = (gridCols * gridRows > 90000) &&
				flowChart.RoutingOptions.DontOptimizeLongRoutes;
			RouteHeuristics calcRouteHeuristics = hurry ?
				RoutingOptions.DistSquare : flowChart.RoutingOptions.RouteHeuristics;

			routeFixEndRegions(grid, ref ptStart, oNearest, ref ptEnd, dNearest, gridCols, gridRows);
			grid[ptStart.X, ptStart.Y] = 0;
			grid[ptEnd.X, ptEnd.Y] = 0;

			//---------- A* algorithm initialization -----------
			SortedList open = new SortedList();
			ArrayList closed = new ArrayList();
			Stack stack = new Stack();

			PathNode temp = new PathNode(ptStart.X, ptStart.Y);
			temp.G = 0;
			temp.H = calcRouteHeuristics(ptStart, ptEnd);
			temp.F = temp.G + temp.H;
			open.Add(temp, temp);
			gridOpen[temp.X, temp.Y] = temp;

			// setup A* cost function
			int adjcCost = flowChart.RoutingOptions.LengthCost;
			int turnCost = flowChart.RoutingOptions.TurnCost;

			PathNode best = null;
			bool found = false;
			int iterations = 0;
			for ( ; ; )
			{
				iterations++;

				// Get the best node from the open list
				if (open.Count == 0) break;
				PathNode pstmp = open.GetByIndex(0) as PathNode;

				open.RemoveAt(0);
				gridOpen[pstmp.X, pstmp.Y] = null;

				closed.Add(pstmp);
				gridClosed[pstmp.X, pstmp.Y] = pstmp;

				if ((best = pstmp) == null) break;

				// If best == destination -> path found
				if (best.X == ptEnd.X && best.Y == ptEnd.Y)
				{
					found = true;
					break;
				}

				// Generate best's successors
				int x = best.X;
				int y = best.Y;

				int[,] off = new int[4, 2] { { 1, 0 }, { 0, 1 }, { -1, 0 }, { 0, -1 } };
				for (i = 0; i < 4; i++)
				{
					byte localCost = grid[x + off[i, 0], y + off[i, 1]];
					if (localCost == 255)
						continue;

					int g = best.G + adjcCost + localCost;
					bool straight = best.Parent == null ||
						(best.Parent.Y == best.Y && off[i, 1] == 0) ||
						(best.Parent.X == best.X && off[i, 0] == 0);
					if (best.Parent == null && oNearest >= 0 && (
						oNearest < 2 && off[i, 1] == 0 || oNearest >= 2 && off[i, 1] == 1))
						straight = false;
					if (!straight) g += turnCost;

					PathNode check = null;

					// if the successor is an open node, add it to the path
					check = gridOpen[x + off[i, 0], y + off[i, 1]];
					if (check != null)
					{
						best.Children[best.ChildCount++] = check;

						// and update its cost if now it is reached via a better path
						if (g < check.G)
						{
							open.Remove(check);		// keep sorted
							check.Parent = best;
							check.G = g;
							check.F = g + check.H;
							open.Add(check, check);	// keep sorted
						}
					}
					else
					{
						// if the successor is a closed node, add it to the path
						check = gridClosed[x + off[i, 0], y + off[i, 1]];
						if (check != null)
						{
							best.Children[best.ChildCount++] = check;

							// and update its cost if now it is reached via a better path
							if (g < check.G)
							{
								check.Parent = best;
								check.G = g;
								check.F = g + check.H;

								// and update its child items
								int gg = check.G;
								int cc = check.ChildCount;
								PathNode kid = null;
								for (int j = 0; j < cc; j++)
								{
									kid = check.Children[j];

									int gi = adjcCost;
									straight = check.Parent == null ||
										(check.Parent.Y == check.Y && check.Y == kid.Y) ||
										(check.Parent.X == check.X && check.X == kid.X);
									if (!straight) gi += turnCost;

									if (g + gi < kid.G)
									{
										bool wasOpen = gridOpen[kid.X, kid.Y] != null;
										if (wasOpen) open.Remove(kid);	// keep sorted

										kid.G = g + gi;
										kid.F = kid.G + kid.H;
										kid.Parent = check;
										stack.Push(kid);

										if (wasOpen) open.Add(kid, kid);
									}
								}
								PathNode parent;
								while (stack.Count > 0)
								{
									parent = stack.Pop() as PathNode;
									cc = parent.ChildCount;
									for (int j = 0; j < cc; j++)
									{
										kid = parent.Children[j];

										int gi = adjcCost;
										straight = parent.Parent == null ||
											(parent.Parent.Y == parent.Y && parent.Y == kid.Y) ||
											(parent.Parent.X == parent.X && parent.X == kid.X);
										if (!straight) gi += turnCost;

										if (parent.G + gi < kid.G)
										{
											bool wasOpen = gridOpen[kid.X, kid.Y] != null;
											if (wasOpen) open.Remove(kid);	// keep sorted

											kid.G = parent.G + gi;
											kid.F = kid.G + kid.H;
											kid.Parent = parent;
											stack.Push(kid);

											if (wasOpen) open.Add(kid, kid);
										}
									}
								}
							}
						}
						else
						{
							// haven't considered this grid square by now
							// create and initialize a path node for it 
							Point current = new Point(x + off[i, 0], y + off[i, 1]);
							PathNode newNode = new PathNode(current.X, current.Y);
							newNode.Parent = best;
							newNode.G = g;
							newNode.H = calcRouteHeuristics(current, ptEnd);
							newNode.F = newNode.G + newNode.H;

							// add it to the list of open nodes to be evaluated later
							open.Add(newNode, newNode);
							gridOpen[newNode.X, newNode.Y] = newNode;

							// add to the path
							best.Children[best.ChildCount++] = newNode;
						}
					}
				}
			}

			if (found)
			{
				PtCollection current = new PtCollection(0);

				current.Add(new Point((int)((points[points.Count - 1].X - bounds.X) / gridSize),
					(int)((points[points.Count - 1].Y - bounds.Y) / gridSize)));
				while (best != null)
				{
					current.Add(new Point(best.X, best.Y));
					best = best.Parent;
				}
				current.Add(new Point((int)((points[0].X - bounds.X) / gridSize),
					(int)((points[0].Y - bounds.Y) / gridSize)));

				// Remove all unneeded points
				Point pt1, pt2, pt3;
				for (i = 1; i < current.Count - 1;)
				{
					pt1 = current[i - 1];
					pt2 = current[i];
					pt3 = current[i + 1];

					if (pt1.X == pt2.X && pt2.X == pt3.X)
						current.RemoveAt(i);
					else if(pt1.Y == pt2.Y && pt2.Y == pt3.Y)
						current.RemoveAt(i);
					else
						i++;
				}

				// Save the first and last points of the arrow
				PointF ptFirst = points[0];
				PointF ptLast = points[points.Count - 1];

				// no perp. arrows on a single line
				if (style == ArrowStyle.Cascading && current.Count == 2 &&
					ptFirst.X != ptLast.X && ptFirst.Y != ptLast.Y)
				{
					Point orgPt = current[0];
					Point trgPt = current[current.Count-1];
					if (orgPt.X == trgPt.X || orgPt.Y == trgPt.Y)
					{
						Point insPt = new Point(
							(orgPt.X + trgPt.X) / 2, (orgPt.Y + trgPt.Y) / 2);
						current.Insert(1, insPt);
						current.Insert(1, insPt);
					}
				}

				// Re-segment the arrow
				points = new PointCollection(current.Count);
				points[0] = ptFirst;
				points[points.Count - 1] = ptLast;

				// Assign the points from the path
				i = current.Count - 1;
				i--; // Skip the first point
				while (i > 0)
				{
					Point pt = current[i];
					PointF ptDoc = new PointF(0, 0);
					ptDoc.X = bounds.X + pt.X * gridSize + gridSize / 2;
					ptDoc.Y = bounds.Y + pt.Y * gridSize + gridSize / 2;

					if (i == 1)
					{
						// Align to the last point
						if (pt.Y == current[0].Y)
							ptDoc.Y = ptLast.Y;
						else
							ptDoc.X = ptLast.X;
					}
					if (i == current.Count - 2)
					{
						// Align to the first point
						if (pt.Y == current[current.Count - 1].Y)
							ptDoc.Y = ptFirst.Y;
						else
							ptDoc.X = ptFirst.X;

						if (style == ArrowStyle.Cascading)
							cascadeStartHorizontal = (ptDoc.X != ptFirst.X);
					}

					points[current.Count - i - 1] = ptDoc;
					i--;
				}

				PointF ptf, ptf1, ptf2, ptf3;

				// If the line is perpendicular make it at least 2 segments
				if(style == ArrowStyle.Cascading && points.Count == 2)
				{
					ptf1 = points[0];
					ptf2 = points[points.Count - 1];
					ptf = ptf1;
					if (cascadeStartHorizontal)
						ptf.X = ptf2.X;
					else
						ptf.Y = ptf2.Y;
					points.Insert(1, ptf);
				}

				// If the line is straight there might be more unneeded points
				if (style == ArrowStyle.Polyline)
				{
					i = 0;
					while(i < points.Count - 2)
					{
						ptf1 = points[i];
						ptf2 = points[i + 2];

						ChartObject obj = flowChart.objectIntersectedBy(ptf1, ptf2,
							orgnLink.getNode(), destLink.getNode());
						if(obj == null)
							points.RemoveAt(i + 1);
						else
							i++;
					}
				}

				// If the line is bezier, smooth it a bit
				if (style == ArrowStyle.Bezier)
				{
					PointCollection newPoints = new PointCollection(0);
					newPoints.Add(points[0]);
					i = 0;
					while(i < points.Count - 2)
					{
						ptf1 = points[i];
						ptf2 = points[i + 1];

						newPoints.Add(ptf2);
						newPoints.Add(ptf2);
						if(i != points.Count - 3)
						{
							ptf3 = points[i + 2];
							ptf = new PointF((ptf2.X + ptf3.X) / 2, (ptf2.Y + ptf3.Y) / 2);
							newPoints.Add(ptf);
						}
						else
						{
							newPoints.Add(points[i + 2]);
						}
						i += 1;
					}

					if (newPoints.Count == 1)
					{
						newPoints = new PointCollection(4);

						ptf1 = points[0];
						ptf2 = points[points.Count - 1];
						ptf = new PointF((ptf1.X + ptf2.X) / 2, (ptf1.Y + ptf2.Y) / 2);
						newPoints[0] = ptf1;
						newPoints[1] = ptf;
						newPoints[2] = ptf;
						newPoints[3] = ptf2;
					}

					points.Clear();
					points = newPoints;
				}

				// Update SegmentCount property value
				if (style == ArrowStyle.Bezier)
					segmentCount = (short)((points.Count - 1) / 3);
				else
					segmentCount = (short)(points.Count - 1);
			}
			else
			{
				// No path found -> reset the arrow, leaving as little points as possible
				int ptsToLeave = 2;
				if (style == ArrowStyle.Cascading)
					ptsToLeave = 4;
				else if (style == ArrowStyle.Bezier)
					ptsToLeave = 4;

				if (style == ArrowStyle.Cascading)
				{
					cascadeOrientation = Orientation.Auto;
					segmentCount = 3;
				}
				else
					segmentCount = 1;

				while (points.Count > ptsToLeave)
					points.RemoveAt(1);

				if (style == ArrowStyle.Cascading && points.Count == 3)
					segmentCount = 2;

				updatePoints(points[points.Count - 1]);
			}

			updateArrowHeads();

			if (subordinateGroup != null)
			{
				subordinateGroup.onSegmentsChanged();
				subordinateGroup.updateObjects(new InteractionState(this, -1, Action.Modify));
			}

			resetCrossings();
			updateText();

			flowChart.fireArrowRoutedEvent(this);
		}