/// <summary> /// Calculates the neighbors of a specific point based on the DestinationMode property of <paramref name="userData"/>. /// </summary> /// <param name="endPosition">The destination point.</param> /// <param name="userData">The data provided by the user.</param> /// <returns>Null if the DestinationMode property of <paramref name="userData"/> is <see cref="DestinationModeFlags.Exact"/>, otherwise an array of neighbor points.</returns> /// <exception cref="ArgumentOutOfRangeException">If userData.DestinationMode is not a valid <see cref="DestinationModeFlags"/> value.</exception> private static Point[] GetDestinationPoints(Point endPosition, FindPathData userData) { if (userData.DestinationModeFlags == DestinationModeFlags.Exact) { return(null); } var output = new List <Point>(8); if (userData.DestinationModeFlags.HasFlag(DestinationModeFlags.CardinalNeighbor)) { output.Add(new Point(endPosition.X - 1, endPosition.Y)); output.Add(new Point(endPosition.X, endPosition.Y - 1)); output.Add(new Point(endPosition.X + 1, endPosition.Y)); output.Add(new Point(endPosition.X, endPosition.Y + 1)); } if (userData.DestinationModeFlags.HasFlag(DestinationModeFlags.IntercardinalNeighbor)) { output.Add(new Point(endPosition.X - 1, endPosition.Y - 1)); output.Add(new Point(endPosition.X + 1, endPosition.Y - 1)); output.Add(new Point(endPosition.X + 1, endPosition.Y + 1)); output.Add(new Point(endPosition.X - 1, endPosition.Y + 1)); } return(output.Count == 0 ? null : output.ToArray()); }
/// <summary> /// Finds the path. /// </summary> /// <param name="startPosition">The start position.</param> /// <param name="endPosition">The end position.</param> /// <param name="depth">An output variable; the depth of the path.</param> /// <param name="userData">The user data used when processing nodes.</param> /// <returns>A list of points defining the found path.</returns> public Point[] FindPath(Point startPosition, Point endPosition, out int depth, FindPathData userData) { userData.StartPosition = startPosition; userData.EndPosition = endPosition; var closedNodes = new List <PathTreeNode>(); var possibleNodes = new List <PathTreeNode>(); var openNodes = new List <PathTreeNode> { new PathTreeNode(startPosition, null, 0) }; var destinationPoints = GetDestinationPoints(endPosition, userData); while (true) { if (openNodes.Count == 0) { if (possibleNodes.Any()) { return(CreatePath(possibleNodes.First(), out depth, userData)); } depth = 0; return(Array.Empty <Point>()); } openNodes.Sort((node1, node2) => node1.Weight.CompareTo(node2.Weight)); var currentNode = openNodes[0]; var currentPosition = currentNode.Position; if (!closedNodes.Contains(currentNode)) { if (userData.DestinationModeFlags.HasFlag(DestinationModeFlags.Exact) && currentPosition == endPosition) { return(CreatePath(currentNode, out depth, userData)); } if (destinationPoints != null && destinationPoints.Any(x => x == currentPosition)) { if (!userData.DestinationModeFlags.HasFlag(DestinationModeFlags.Exact)) { return(CreatePath(currentNode, out depth, userData)); } possibleNodes.Add(currentNode); } var left = ProcessNode(currentNode, -1, 0, openNodes, closedNodes, endPosition, userData); var up = ProcessNode(currentNode, 0, -1, openNodes, closedNodes, endPosition, userData); var right = ProcessNode(currentNode, 1, 0, openNodes, closedNodes, endPosition, userData); var down = ProcessNode(currentNode, 0, 1, openNodes, closedNodes, endPosition, userData); if (left != null && up != null) { ProcessNode(currentNode, -1, -1, openNodes, closedNodes, endPosition, userData); } if (right != null && up != null) { ProcessNode(currentNode, 1, -1, openNodes, closedNodes, endPosition, userData); } if (right != null && down != null) { ProcessNode(currentNode, 1, 1, openNodes, closedNodes, endPosition, userData); } if (left != null && down != null) { ProcessNode(currentNode, -1, 1, openNodes, closedNodes, endPosition, userData); } closedNodes.Add(currentNode); } openNodes.RemoveAt(0); } }
/// <summary> /// Finds the path. /// </summary> /// <param name="startColumn">The start column.</param> /// <param name="startRow">The start row.</param> /// <param name="endColumn">The end column.</param> /// <param name="endRow">The end row.</param> /// <param name="depth">An output variable; the depth of the path.</param> /// <param name="userData">The user data used when processing nodes.</param> /// <returns>A list of points defining the found path.</returns> public Point[] FindPath(int startColumn, int startRow, int endColumn, int endRow, out int depth, FindPathData userData) => FindPath(new Point(startColumn, startRow), new Point(endColumn, endRow), out depth, userData);
/// <summary> /// Processes the node. /// </summary> /// <param name="currentNode">The current node.</param> /// <param name="columnOffset">The column offset.</param> /// <param name="rowOffset">The row offset.</param> /// <param name="openNodes">The list of open nodes that will be added to as nodes are processed.</param> /// <param name="closedNodes">The closed nodes.</param> /// <param name="endPosition">The end position.</param> /// <param name="userData">The user data.</param> /// <returns>A new node positioned next to the current node based on columnOffset and rowOffset.</returns> private PathTreeNode ProcessNode(PathTreeNode currentNode, int columnOffset, int rowOffset, ICollection <PathTreeNode> openNodes, IEnumerable <PathTreeNode> closedNodes, Point endPosition, FindPathData userData) { var position = new Point(currentNode.Position.X + columnOffset, currentNode.Position.Y + rowOffset); var weight = currentNode.Weight + userData?.GetWeight(position, endPosition) ?? 0; var newNode = new PathTreeNode(position, currentNode, weight); if (CheckNode != null && !CheckNode(newNode.Position.X, newNode.Position.Y, userData) || AnyNodeIsAtPoint(closedNodes, newNode.Position) || AnyNodeIsAtPoint(openNodes, newNode.Position)) { return(null); } openNodes.Add(newNode); return(newNode); }
/// <summary> /// Creates the path. /// </summary> /// <param name="node">The node.</param> /// <param name="depth">An output variable; the depth of the path.</param> /// <param name="userData">The user data.</param> /// <returns>A list of points defining the found path.</returns> private Point[] CreatePath(PathTreeNode node, out int depth, FindPathData userData) { var output = new List <Point>(); var parent = node; while (parent != null) { output.Insert(0, parent.Position); parent = parent.Parent; } if (userData != null) { for (var index = output.Count - 1; index >= 0; --index) { var poppingWaypoints = userData.PopWaypointTest(output[index], index); if (!poppingWaypoints) { break; } output.RemoveAt(index); } if (output.Count == 0) { depth = 0; return(output.ToArray()); } if (userData.PopFirstWaypoint) { output.RemoveAt(0); } if (userData.PopLastNWaypoints > 0) { if (userData.PopLastNWaypoints > output.Count) { depth = 0; output.Clear(); return(output.ToArray()); } output.RemoveRange(output.Count - userData.PopLastNWaypoints, userData.PopLastNWaypoints); } } depth = output.Count; if (!TrimPaths) { return(output.ToArray()); } var indicesToRemove = new List <int>(); for (var index = 1; index < output.Count - 1; ++index) { var previousPoint = output[index - 1]; var currentPoint = output[index]; var nextPoint = output[index + 1]; if (PointsContinueHorizontally(previousPoint, currentPoint, nextPoint) || PointsContinuesVertically(previousPoint, currentPoint, nextPoint) || PointsContinueDiagonally(previousPoint, currentPoint, nextPoint)) { indicesToRemove.Add(index); } } for (var index = indicesToRemove.Count - 1; index >= 0; --index) { output.RemoveAt(indicesToRemove[index]); } return(output.ToArray()); }
/// <summary> /// Finds the path. /// </summary> /// <param name="startPosition">The start position.</param> /// <param name="endPosition">The end position.</param> /// <param name="pathingPolygon">The pathing polygon.</param> /// <param name="userData">The user data used when processing nodes.</param> /// <returns>A list of points defining the found path.</returns> public Path FindPath(Point startPosition, Point endPosition, PathingPolygon pathingPolygon, FindPathData userData) { var pathPoints = FindPath(startPosition, endPosition, out var depth, userData); if (!pathPoints.Any()) { return(new Path()); } var output = new Path { Depth = depth }; foreach (var(x, y) in pathPoints) { var node = pathingPolygon.GetNodeAtColumnRow(x, y); output.AddWaypoint(node.Bounds.Center.ToVector2(), 0f); } return(output); }
/// <summary> /// Finds the path. /// </summary> /// <param name="startColumn">The start column.</param> /// <param name="startRow">The start row.</param> /// <param name="endColumn">The end column.</param> /// <param name="endRow">The end row.</param> /// <param name="pathingPolygon">The pathing polygon to find the path inside of.</param> /// <param name="userData">The user data used when processing nodes.</param> /// <returns>A list of points defining the found path.</returns> public Path FindPath(int startColumn, int startRow, int endColumn, int endRow, PathingPolygon pathingPolygon, FindPathData userData) => FindPath(new Point(startColumn, startRow), new Point(endColumn, endRow), pathingPolygon, userData);
public Path FindPath(Point startPosition, Point endPosition, PathingPolygon pathingPolygon, FindPathData userData = null) { int depth; var pathPoints = FindPath(startPosition, endPosition, out depth, userData); if (pathPoints == null) { return(null); } var output = new Path(); output.Depth = depth; foreach (var point in pathPoints) { var node = pathingPolygon.GetNodeAtColumnRow(point.X, point.Y); output.AddWaypoint(node.Bounds.Center.ToVector2()); } return(output); }
public Point[] FindPath(Point startPosition, Point endPosition, out int depth, FindPathData userData = null) { var closedNodes = new List <PathTreeNode>(); var openNodes = new List <PathTreeNode>(); openNodes.Add(new PathTreeNode(startPosition, null, 0)); while (true) { if (openNodes.Count == 0) { depth = 0; return(null); } var currentNode = openNodes[0]; var currentPosition = currentNode.Position; if (!closedNodes.Contains(currentNode)) { if (currentPosition == endPosition) { return(CreatePath(currentNode, out depth, userData)); } PathTreeNode left = null, up = null, right = null, down = null; left = ProcessNode(currentNode, -1, 0, openNodes, closedNodes, userData); up = ProcessNode(currentNode, 0, -1, openNodes, closedNodes, userData); right = ProcessNode(currentNode, 1, 0, openNodes, closedNodes, userData); down = ProcessNode(currentNode, 0, 1, openNodes, closedNodes, userData); if (left != null && up != null) { ProcessNode(currentNode, -1, -1, openNodes, closedNodes, userData); } if (right != null && up != null) { ProcessNode(currentNode, 1, -1, openNodes, closedNodes, userData); } if (right != null && down != null) { ProcessNode(currentNode, 1, 1, openNodes, closedNodes, userData); } if (left != null && down != null) { ProcessNode(currentNode, -1, 1, openNodes, closedNodes, userData); } closedNodes.Add(currentNode); } openNodes.RemoveAt(0); } }
public Point[] FindPath(int startColumn, int startRow, int endColumn, int endRow, out int depth, FindPathData userData = null) { return(FindPath(new Point(startColumn, startRow), new Point(endColumn, endRow), out depth, userData)); }
private PathTreeNode ProcessNode(PathTreeNode currentNode, int columnOffset, int rowOffset, List <PathTreeNode> openNodes, List <PathTreeNode> closedNodes, FindPathData userData) { var newNode = new PathTreeNode(currentNode.Position.X + columnOffset, currentNode.Position.Y + rowOffset, currentNode, 0); if ((CheckNode == null || CheckNode(newNode.Position.X, newNode.Position.Y, userData)) && !CheckNodeList(closedNodes, newNode.Position) && !CheckNodeList(openNodes, newNode.Position)) { openNodes.Add(newNode); return(newNode); } return(null); }