/// <summary> /// Checks if the gradient of ab = cd /// </summary> public static Boolean GradientEquals(Point2D a, Point2D b, Point2D c, Point2D d) { float run1 = b.X - a.X; float run2 = d.X - c.X; float rise1 = b.Y - a.Y; float rise2 = d.Y - c.Y; if (run1 == 0) if (run2 == 0) return true; else return false; if (run2 == 0) return false; if (rise1 == 0) if (rise2 == 0) return true; else return false; if (rise2 == 0) return false; return rise1/run1 == rise2/run2; }
/// <summary> /// Gets the next point in the list - usually counter clockwise direction /// </summary> public static Point2D GetNextPoint(List<Point2D> polygon, Point2D currentPoint) { var index = PolygonManipulation.IndexOf(polygon, currentPoint); if (index == polygon.Count - 1) return polygon[0]; else return polygon[index+1]; }
/// <summary> /// Whether a list of points contains a certain point /// </summary> public static Boolean Contains(List<Point2D> points, Point2D point) { for (int i = 0; i < points.Count; i++) { if (points[i].X == point.X && points[i].Y == point.Y) return true; } return false; }
/// <summary> /// Constructor used when a list of polygons and the start and end coordinates are /// passed /// </summary> public Map(IEnumerable<List<Point2D>> polygons, Point2D start, Point2D end) : this() { foreach (var polygon in polygons) { //Add the polygon to the map with its unique sprite Polygons.Add(polygon); } //Set the start and end coordinates Start = start; End = end; }
/// <summary> /// Given a convex hull and a start point and end point which are points on the convex hull /// Traverses the convex hull both clockwise and counterwise and returns the list of points with the minimum length /// </summary> public static List<Point2D> GetMinimumPolygonChain(List<Point2D> convexHull, Point2D start, Point2D end) { var circularHull = convexHull;// OrganiseClockwise(convexHull); var clockwiseChain = new List<Point2D>(); var counterClockwiseChain = new List<Point2D>(); //Clockwise chain //Start at the start index Int32 currentIndex = circularHull.IndexOf(start); //While the end isn't reached while (!PolygonManipulation.Equals(circularHull[currentIndex], end) || circularHull.Count == clockwiseChain.Count) { //Add the point to the polygon chain clockwiseChain.Add(circularHull[currentIndex]); //Go to next point if (currentIndex < circularHull.Count - 1) currentIndex++; else currentIndex = 0; } //Add the end clockwiseChain.Add(end); //Counterclockwise chain //Start at the start index currentIndex = circularHull.IndexOf(start); //While the end isn't reached while (!PolygonManipulation.Equals(circularHull[currentIndex], end) || circularHull.Count == counterClockwiseChain.Count) { //Add the point to the polygon chain counterClockwiseChain.Add(circularHull[currentIndex]); //Go to prev point if (currentIndex > 0) currentIndex--; else currentIndex = circularHull.Count - 1; } //Add the end counterClockwiseChain.Add(end); //Return the shortest chain return GetPolygonChainDistance(counterClockwiseChain) > GetPolygonChainDistance(clockwiseChain) ? clockwiseChain : counterClockwiseChain; }
/// <summary> /// Gets the point on a polygon which is closest to a given point /// </summary> public static Point2D GetClosestPoint(List<Point2D> polygon, Point2D from) { float minDistance = float.MaxValue; Point2D closestPoint = null; foreach (var point in polygon) { var distance = ConvexHull.GetDistance(point, from); if (distance < minDistance && !PolygonManipulation.Equals(from, point)) { minDistance = distance; closestPoint = point; } } return closestPoint; }
/// <summary> /// Determines is pq is the lower tangent of the polygon /// Pseudocode from https://facwiki.cs.byu.edu/cs312ringger/index.php/Project_2 /// (Geometry for Common Tangents) /// </summary> public static Boolean IsLowerTangent(List<Point2D> polygon, Point2D p, Point2D q) { //Calculate the gradient taking precautions againist division by zero double m; Int32 sum = p.X - q.X; if (sum == 0) //Not double.Max because we don't want to overflow m = 9999999; else m = ((double)p.Y - (double)q.Y) / (double)sum; //Calculate the y intercept double b = -1 * m * (double)p.X + (double)p.Y; //Check if each point in the polygon is a lower tangent foreach (var point in polygon) { if (!PolygonManipulation.Equals(point, p) && !PolygonManipulation.Equals(point, q)) if ((m * (double)point.X + b - (double)point.Y) > 0) return false; } return true; }
/// <summary> /// Finds the quickest path avoiding the polygons given a start and end point /// Excludes start and end from the path /// Need to check for collisions with other polygons /// </summary> private IEnumerable<Point2D> FindQuickestPathMultiplePolygons(Point2D start, Point2D end) { //Maps a path to the polygon it avoids var paths = new Dictionary<List<Point2D>, List<Point2D>>(); foreach (var polygon in Polygons) { //Calcualte convex hull with start and end point and polygon var newPolygon = new List<Point2D>(); newPolygon.Add(start); newPolygon.AddRange(polygon); newPolygon.Add(end); var convexHull = ConvexHull.Solve(newPolygon); if (convexHull.Contains(start) && convexHull.Contains(end)) { var path = ConvexHull.GetMinimumPolygonChain(convexHull, start, end); paths.Add(path, polygon); } } //Get all paths with more than two points var correctPaths = paths.Keys.Where(p => p.Count >= 2).ToList(); if (correctPaths.Count == 0) //All paths go straight from the start to end //Therefore no obstacles are in the way return (new Point2D[] { start, end }).ToList(); if (correctPaths.Count == 1) //Only one path has an obstacle in the way //So only one obstacle is between start and end return correctPaths.First(); //Merge all the paths which are blocked by polygons together return MergePaths(correctPaths); }
/// <summary> /// Gets the squared distance between two points /// </summary> public static float GetDistanceSquared(Point2D a, Point2DF b) { return (a.X - b.X) * (a.X - b.X) + (a.Y - b.Y) * (a.Y - b.Y); }
/// <summary> /// Gets the Manhattan distance between two points /// </summary> public static float GetDistance(Point2D a, Point2DF b) { return (float)Math.Sqrt(GetDistanceSquared(a, b)); }
/// <summary> /// Whether three points make a right turn /// </summary> private static Boolean NoRightTurn(Point2D a, Point2D b, Point2D c) { return CrossProduct(a, b, c) <= 0; }
/// <summary> /// Calculates the cross product of 3 2d points /// </summary> public static Int32 CrossProduct(Point2D a, Point2D b, Point2D c) { return (b.X - a.X) * (c.Y - a.Y) - (b.Y - a.Y)*(c.X - a.X); }
/// <summary> /// Generates a random polygon /// </summary> public static List<Point2D> GenerateRandomPolygon(Int32 maxPointNumber, Int32 maxX, Int32 maxY, Int32 offsetX = 0, Int32 offsetY = 0) { //Create points List<Point2D> pointList = new List<Point2D>(); for (int p = 0; p < maxPointNumber; p++) { Point2D newPoint = new Point2D(random.Next(maxX) + offsetX, random.Next(maxY) + offsetY); if (!PolygonManipulation.Contains(pointList, newPoint)) pointList.Add(newPoint); } pointList = pointList.Distinct().ToList(); return pointList; }
/// <summary> /// Polygon count must be between 1 and 4 /// </summary> public static Map GenerateMultipleObstacleMap(Int32 polygonNumber, Int32 maxPointNumber, Int32 maxX, Int32 maxY) { if (polygonNumber == 1) return GenerateSingleObstacleMap(maxPointNumber, maxX, maxY); if (polygonNumber > 4) polygonNumber = 4; List<List<Point2D>> polygons = new List<List<Point2D>>(); Point2D start = null; Point2D mid = null; Point2D end = null; //Spread the polygons out based in the number if (polygonNumber == 2) { //Arrange side by side /* ##### ##### * # # # # * ##### ##### */ polygons.Add(GenerateRandomPolygon(maxPointNumber, maxX, maxY, 2, 2)); polygons.Add(GenerateRandomPolygon(maxPointNumber, maxX, maxY, maxX + 3, 2)); Int32 maxHeight = GetMaximumHeight(polygons); start = new Point2D(0, random.Next(maxHeight)); mid = new Point2D(maxX + 2 + random.Next(1), random.Next(maxHeight)); end = new Point2D(2 * maxX + 3, random.Next(maxHeight)); } else if (polygonNumber == 3) { //Arrange in tri-shape /* ##### * # # * ##### * * ##### ##### * # # # # * ##### ##### */ polygons.Add(GenerateRandomPolygon(maxPointNumber, maxX, maxY, maxX / 2 + 2, 2)); polygons.Add(GenerateRandomPolygon(maxPointNumber, maxX, maxY, 2, maxY + 3)); polygons.Add(GenerateRandomPolygon(maxPointNumber, maxX, maxY, maxX + 3, maxY + 3)); Int32 maxHeight = GetMaximumHeight(polygons); start = new Point2D(0, random.Next(maxHeight)); mid = new Point2D(maxX + 2 + random.Next(1), random.Next(maxHeight / 2) + maxHeight / 2 + 2); end = new Point2D(2 * maxX + 3, random.Next(maxHeight)); } else if (polygonNumber == 4) { //Arrange in sqaure /* ##### ##### * # # # # * ##### ##### * * ##### ##### * # # # # * ##### ##### */ polygons.Add(GenerateRandomPolygon(maxPointNumber, maxX, maxY, 2, 2)); polygons.Add(GenerateRandomPolygon(maxPointNumber, maxX, maxY, maxX + 3, 2)); polygons.Add(GenerateRandomPolygon(maxPointNumber, maxX, maxY, 2, maxY + 3)); polygons.Add(GenerateRandomPolygon(maxPointNumber, maxX, maxY, maxX + 3, maxY + 3)); Int32 maxHeight = GetMaximumHeight(polygons); start = new Point2D(0, random.Next(maxHeight)); mid = new Point2D(maxX + 2 + random.Next(1), random.Next(maxHeight)); end = new Point2D(2 * maxX + 3, random.Next(maxHeight)); } return new Map(polygons, start, end) { Mid = mid }; }
/// <summary> /// Returns a map which is a single obstacle test /// </summary> public static Map GenerateSingleObstacleMap(Int32 maxPointNumber, Int32 maxX, Int32 maxY) { //Generate the map var polygon = new List<List<Point2D>>(); polygon.Add(GenerateRandomPolygon(maxPointNumber, maxX, maxY, 2, 2)); //Insert polygon into generated Map Int32 polygonWidth = polygon.First().Max(p => p.X); Int32 polygonHeight = polygon.First().Max(p => p.Y); Int32 mapWidth = polygonWidth + 2; Int32 mapHeight = polygonHeight + 2; Point2D start = new Point2D(random.Next(2), Convert.ToInt32((random.Next(mapHeight) - 0.5 * mapHeight) + (float)mapHeight / 2)); Point2D end = new Point2D(mapWidth - random.Next(2), mapHeight - start.Y); return new Map(polygon, start, end); }
/// <summary> /// Finds the quickest path avoiding the polygon given a start and end point /// Excludes start and end from the path /// </summary> private IEnumerable<Point2D> FindQuickestPathSinglePolygon(Point2D start, Point2D end) { return FindQuickestPathSpecifiedPolygon(Polygons.First(), start, end); }
/// <summary> /// Checks whether two given points are equal /// </summary> public static Boolean Equals(Point2D a, Point2D b) { return (a.X == b.X) && (a.Y == b.Y); }
private static IEnumerable<Point2D> FindQuickestPathSpecifiedPolygon(IEnumerable<Point2D> polygon, Point2D start, Point2D end) { //Calcualte convex hull with start and end point and polygon var newPolygon = new List<Point2D>(); newPolygon.Add(start); newPolygon.AddRange(polygon); newPolygon.Add(end); newPolygon = ConvexHull.GetMinimumPolygonChain(ConvexHull.Solve(newPolygon), start, end); //If we haven't found a path yet or this path is the shortest newPolygon.Remove(start); newPolygon.Remove(end); return newPolygon; }
/// <summary> /// Gets a point previous in the list - usually clockwise direction /// </summary> public static Point2D GetPreviousPoint(List<Point2D> polygon, Point2D currentPoint) { var index = PolygonManipulation.IndexOf(polygon, currentPoint); if (index == 0) return polygon[polygon.Count - 1]; else return polygon[index-1]; }
/// <summary> /// Calculates the gradient between two points /// </summary> private static float CalculateGradient(Point2D a, Point2D b) { float rise = (float)b.Y - (float)a.Y; float run = (float)b.X - (float)a.X; if (run == 0) return 999999; else if (rise == 0) //Have to return an abnormal value here, else if m1.run = 0 and m2.rise = 0 //Apparentally the gradients are equal return 888888; else return rise / run; }
/// <summary> /// Value equivalent of .IndexOf /// </summary> public static Int32 IndexOf(List<Point2D> polygon, Point2D p) { for (int i = 0; i < polygon.Count; i++) { if (polygon[i].X == p.X && polygon[i].Y == p.Y) return i; } return -1; }