Click here to Skip to main content
15,892,927 members
Articles / Programming Languages / C# 4.0

A Complex - Yet Quite Intelligible - Pathfinding in C#

Rate me:
Please Sign up or sign in to vote.
4.96/5 (41 votes)
30 Aug 2013CPOL21 min read 59.8K   2.5K   88  
A semi-analytic 2D path-finding for large dynamic scenarios.
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using YinYang.CodeProject.Projects.SimplePathfinding.Helpers;

namespace YinYang.CodeProject.Projects.SimplePathfinding.PathFinders.Evasion
{
    public class EvasionPathfinder : BasePathfinder
    {
        #region | Helper methods |

        private static Boolean TryScanObstacle(
            Point startPoint,                           // starting point (one before hitting the obstacle)
            DirectionType direction,                    // direction of hitting the obstacle
            StopFunction stopFunction,                  
            IEnumerable<PathSegment> segments,          // path segments detected from start to end
            Int32 segmentIndex,                         // starting segment in which the obstacle was hit
            out EvasionObstacleInfo obstacleInfo)       // useful informations about obstacle, and optimal path around
        {
            // initializes the result structures
            Boolean result = false;
            List<Point> cornerPointList = new List<Point>();
            obstacleInfo = default(EvasionObstacleInfo);

            // detects all the starting points from relevant segments (to be tested for potential evading path end)
            IList<Point> finishPoints = segments.
                Skip(segmentIndex + 1).
                Select(segment => segment.FirstPoint).
                ToList();

            // initalizes the parameters
            Int32 oldSegmentIndex = segmentIndex;
            Point position = startPoint;
            Int32 totalStepCount = 0;

            // expected direction in which start point should be re-entered (this check is essential)
            DirectionType entryDirection = RotateUntil(startPoint, direction, stopFunction, false, true);
            entryDirection = DirectionHelper.Reverse(entryDirection);

            // rotates until the direction is no longer in a collision course (in a given hand side)
            direction = RotateUntil(startPoint, direction, stopFunction, true, true);

            do
            {
                // retrieves next point in a actual direction
                Point nextPoint = DirectionHelper.GetNextStep(position, direction);
                totalStepCount++;

                // we've ended up in the start position, that means we can terminate this scan
                if (nextPoint == startPoint && direction == entryDirection) break;

                // detects whether this point coincides with any finish point
                Int32 finishPointIndex = finishPoints.IndexOf(nextPoint);

                // we've ended up on ther other side, path was found, continue if we can find better finish point (in a higher segment)
                if (finishPointIndex >= 0 && finishPointIndex + oldSegmentIndex >= segmentIndex)
                {
                    obstacleInfo = new EvasionObstacleInfo(cornerPointList, totalStepCount, finishPointIndex);
                    result = true;
                }

                // rotates (starting at opposite direction) from the wall until it finds a passable spot
                DirectionType previousDirection = direction;
                direction = RotateUntil(nextPoint, DirectionHelper.Reverse(direction), stopFunction, true, true);
                if (direction != previousDirection) cornerPointList.Add(nextPoint);

                // advances to next point
                position = nextPoint;
            }
            while (true);

            // returns the points, and operation result
            if (result) obstacleInfo.SetTotalStepCount(totalStepCount);

            return result;
        }

        private static DirectionType RotateUntil(Point startPoint, DirectionType direction, StopFunction stopFunction, Boolean leftSide, Boolean untilFree)
        {
            Boolean condition;

            do // rotates until the conditions are fullfilled (determined by untilFree)
            {
                direction = DirectionHelper.Rotate(direction, leftSide, false);
                Point nextLeftPoint = DirectionHelper.GetNextStep(startPoint, direction);
                condition = untilFree ? !stopFunction(nextLeftPoint) : stopFunction(nextLeftPoint);
            }
            while (!condition);

            return direction;
        }

        #endregion

        #region << BasePathfinder >>

        /// <summary>
        /// See <see cref="BasePathfinder.OnTryFindPath"/> for more details.
        /// </summary>
        protected override Boolean OnTryFindPath(Point startPoint, Point endPoint,
                                                 StopFunction stopFunction,
                                                 out IReadOnlyCollection<Point> path, 
                                                 out IReadOnlyCollection<Point> pivotPoints,
                                                 Boolean optimize = true)
        {
            // prepares main parameters
            Boolean result = false;
            List<Point> pointList = new List<Point>();

            // detects all the path segments that are passable
            IReadOnlyList<PathSegment> segments = LineRasterizer.ScanPathSegments(startPoint, endPoint, stopFunction);
            Int32 segmentIndex = 0;
            Boolean running = true;

            while (running)
            {
                // gets this segment
                PathSegment segment = segments[segmentIndex];
                Point collisionPoint = segment.LastPoint;

                // adds already found points to a final path
                pointList.Add(segment.FirstPoint);
                pointList.Add(segment.LastPoint);

                // we have arrived at destination, we're done here
                if (collisionPoint == endPoint)
                {
                    result = true;
                    break;
                }

                // cirumvents the obstacle from both sides
                EvasionObstacleInfo obstacleInfo;

                // tries to circumvent the obstacle from both sides (left/right) to determine which on is shorter
                if (TryScanObstacle(collisionPoint, segment.Direction, stopFunction, segments, segmentIndex, out obstacleInfo))
                {
                    // adds better route points to our result structures, advances to the latest segment
                    segmentIndex += obstacleInfo.SegmentIndex + 1;

                    // determines left/right side paths step counts
                    Int32 leftPathStepCount = obstacleInfo.LeftStepCount;
                    Int32 rightPathStepCount = obstacleInfo.TotalStepCount - leftPathStepCount;

                    // determines short path
                    IEnumerable<Point> shorterPath = leftPathStepCount < rightPathStepCount ? 
                        obstacleInfo.PivotPoints.Take(obstacleInfo.LefPointCount) : 
                        obstacleInfo.PivotPoints.Skip(obstacleInfo.LefPointCount).Reverse();

                    // adds this path to overall path
                    pointList.AddRange(shorterPath);
                }
                else // path not found
                {
                    running = false;
                }
            }

            // returns the found path
            pivotPoints = pointList;
            path = pointList;
            return result;
        }

        #endregion
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Czech Republic Czech Republic
Contacts: EMAIL - smartk8@gmail.com

Comments and Discussions