diff --git a/Source/Primitives2D/Point/PointExtensions.cs b/Source/Primitives2D/Point/PointExtensions.cs index 22d718b..9d3a62b 100644 --- a/Source/Primitives2D/Point/PointExtensions.cs +++ b/Source/Primitives2D/Point/PointExtensions.cs @@ -21,49 +21,15 @@ using System; using System.Collections.Generic; +using System.Linq; namespace DotImaging.Primitives2D { /// - /// Defined functions can be used as object extensions. /// Provides point extension methods. /// public static class Point32iExtensions { - /// - /// Selects points which satisfy minimal specified distance. - /// - /// Points sorted by importance. Points are tested by sequentially. - /// Minimal enforced distance. - /// Filtered points which are spread by minimal . - public static List EnforceMinimalDistance(this IEnumerable candidates, float minimalDistance) - { - var minDistSqr = minimalDistance * minimalDistance; - List filteredPoints = new List(); - - foreach (var candidate in candidates) - { - bool isEnoughFar = true; - foreach (var filteredPt in filteredPoints) - { - int dx = candidate.X - filteredPt.X; - int dy = candidate.Y - filteredPt.Y; - int featureDistanceSqr = dx * dx + dy * dy; - - if (featureDistanceSqr < minDistSqr) - { - isEnoughFar = false; - break; - } - } - - if (isEnoughFar) - filteredPoints.Add(candidate); - } - - return filteredPoints; - } - /// /// Clamps point coordinate according to the specified size (0,0, size.Width, size.Height). /// @@ -122,7 +88,6 @@ public static double DistanceTo(this Point pointA, Point pointB) } /// - /// Defined functions can be used as object extensions. /// Provides point extension methods. /// public static class Point32fExtensions @@ -313,4 +278,365 @@ public static Point Round(this PointF point) return Point.Round(point); } } + + + /// + /// Provides point collection extensions. + /// + public static class Point32fCollectionExtensions + { + //taken from:http://stackoverflow.com/questions/4243042/c-sharp-point-in-polygon and modified + /// + /// Checks whether the specified location is in the polygon. + /// + /// Polygon. + /// Horizontal coordinate. + /// VErtical coordinate. + /// True if the point resides inside the polygon, false otherwise. + public static bool IsInPolygon(this IList poly, float x, float y) + { + PointF p1, p2; + + bool inside = false; + + if (poly.Count < 3) + { + return inside; + } + + var oldPoint = new PointF(poly[poly.Count - 1].X, poly[poly.Count - 1].Y); + + for (int i = 0; i < poly.Count; i++) + { + var newPoint = new PointF(poly[i].X, poly[i].Y); + + if (newPoint.X > oldPoint.X) + { + p1 = oldPoint; + p2 = newPoint; + } + + else + { + p1 = newPoint; + p2 = oldPoint; + } + + + if ((newPoint.X < x) == (x <= oldPoint.X) && + (y - (long)p1.Y) * (p2.X - p1.X) < (p2.Y - (long)p1.Y) * (x - p1.X)) + { + inside = !inside; + } + + + oldPoint = newPoint; + } + + return inside; + } + + /// + /// Gets the minimum bounding rectangle around the points. + /// + /// Contour points. + /// Bounding rectangle. + public static RectangleF BoundingRect(this IEnumerable points) + { + if (points.Any() == false) return RectangleF.Empty; + + float minX = Single.MaxValue, maxX = Single.MinValue, + minY = Single.MaxValue, maxY = Single.MinValue; + + foreach (var pt in points) + { + if (pt.X < minX) + minX = pt.X; + if (pt.X > maxX) + maxX = pt.X; + + if (pt.Y < minY) + minY = pt.Y; + if (pt.Y > maxY) + maxY = pt.Y; + } + + return new RectangleF(minX, minY, maxX - minX, maxY - minY); + } + + /// + /// Gets the center of the mass of the contour. + /// + /// Contour points. + /// The center of the mass of the contour. + public static PointF Center(this IEnumerable points) + { + PointF average = new PointF(); + int nSamples = 0; + + foreach (var pt in points) + { + average.X += pt.X; + average.Y += pt.Y; + nSamples++; + } + + average.X /= nSamples; + average.Y /= nSamples; + + return average; + } + + /// + /// Determines whether the polygon forms rectangle. + /// + /// Polygon. + /// True if the polygon forms rectangle, false otherwise. + public static bool IsRectangle(this IEnumerable points) + { + if (points.Count() != 4) + return false; + + var rect = points.BoundingRect(); + + bool hasTopLeft = false, hasTopRight = false, hasBottomLeft = false, hasBottomRight = false; + + foreach (var pt in points) + { + if (rect.Top == pt.Y) + { + if (rect.X == pt.X) + hasTopLeft = true; + + if (rect.Right == pt.X) + hasTopRight = true; + } + + if (rect.Bottom == pt.Y) + { + if (rect.X == pt.X) + hasBottomLeft = true; + + if (rect.Right == pt.X) + hasBottomRight = true; + } + } + + return hasTopLeft && hasTopRight && hasBottomLeft && hasBottomRight; + } + + /// + /// Selects points which satisfy minimal specified distance. + /// + /// Points sorted by importance. Points are tested by sequentially. + /// Minimal enforced distance. + /// Filtered points which are spread by minimal . + public static List EnforceMinimalDistance(this IEnumerable candidates, float minimalDistance) + { + var minDistSqr = minimalDistance * minimalDistance; + var filteredPoints = new List(); + + foreach (var candidate in candidates) + { + bool isEnoughFar = true; + foreach (var filteredPt in filteredPoints) + { + var dx = candidate.X - filteredPt.X; + var dy = candidate.Y - filteredPt.Y; + var featureDistanceSqr = dx * dx + dy * dy; + + if (featureDistanceSqr < minDistSqr) + { + isEnoughFar = false; + break; + } + } + + if (isEnoughFar) + filteredPoints.Add(candidate); + } + + return filteredPoints; + } + } + + /// + /// Provides point collection extensions. + /// + public static class Point32iCollectionExtensions + { + //taken from:http://stackoverflow.com/questions/4243042/c-sharp-point-in-polygon and modified + /// + /// Checks whether the specified location is in the polygon. + /// + /// Polygon. + /// Horizontal coordinate. + /// VErtical coordinate. + /// True if the point resides inside the polygon, false otherwise. + public static bool IsInPolygon(this IList poly, float x, float y) + { + Point p1, p2; + + bool inside = false; + + if (poly.Count < 3) + { + return inside; + } + + var oldPoint = new Point(poly[poly.Count - 1].X, poly[poly.Count - 1].Y); + + for (int i = 0; i < poly.Count; i++) + { + var newPoint = new Point(poly[i].X, poly[i].Y); + + if (newPoint.X > oldPoint.X) + { + p1 = oldPoint; + p2 = newPoint; + } + + else + { + p1 = newPoint; + p2 = oldPoint; + } + + + if ((newPoint.X < x) == (x <= oldPoint.X) && + (y - (long)p1.Y) * (p2.X - p1.X) < (p2.Y - (long)p1.Y) * (x - p1.X)) + { + inside = !inside; + } + + + oldPoint = newPoint; + } + + return inside; + } + + /// + /// Gets the minimum bounding rectangle around the points. + /// + /// Contour points. + /// Bounding rectangle. + public static Rectangle BoundingRect(this IEnumerable points) + { + if (points.Any() == false) return Rectangle.Empty; + + int minX = Int32.MaxValue, maxX = Int32.MinValue, + minY = Int32.MaxValue, maxY = Int32.MinValue; + + foreach (var pt in points) + { + if (pt.X < minX) + minX = pt.X; + if (pt.X > maxX) + maxX = pt.X; + + if (pt.Y < minY) + minY = pt.Y; + if (pt.Y > maxY) + maxY = pt.Y; + } + + return new Rectangle(minX, minY, maxX - minX, maxY - minY); + } + + /// + /// Gets the center of the mass of the contour. + /// + /// Contour points. + /// The center of the mass of the contour. + public static PointF Center(this IEnumerable points) + { + PointF average = new PointF(); + int nSamples = 0; + + foreach (var pt in points) + { + average.X += pt.X; + average.Y += pt.Y; + nSamples++; + } + + average.X /= nSamples; + average.Y /= nSamples; + + return average; + } + + /// + /// Determines whether the polygon forms rectangle. + /// + /// Polygon. + /// True if the polygon forms rectangle, false otherwise. + public static bool IsRectangle(this IEnumerable points) + { + if (points.Count() != 4) + return false; + + var rect = points.BoundingRect(); + + bool hasTopLeft = false, hasTopRight = false, hasBottomLeft = false, hasBottomRight = false; + + foreach (var pt in points) + { + if (rect.Top == pt.Y) + { + if (rect.X == pt.X) + hasTopLeft = true; + + if (rect.Right == pt.X) + hasTopRight = true; + } + + if (rect.Bottom == pt.Y) + { + if (rect.X == pt.X) + hasBottomLeft = true; + + if (rect.Right == pt.X) + hasBottomRight = true; + } + } + + return hasTopLeft && hasTopRight && hasBottomLeft && hasBottomRight; + } + + /// + /// Selects points which satisfy minimal specified distance. + /// + /// Points sorted by importance. Points are tested by sequentially. + /// Minimal enforced distance. + /// Filtered points which are spread by minimal . + public static List EnforceMinimalDistance(this IEnumerable candidates, float minimalDistance) + { + var minDistSqr = minimalDistance * minimalDistance; + var filteredPoints = new List(); + + foreach (var candidate in candidates) + { + bool isEnoughFar = true; + foreach (var filteredPt in filteredPoints) + { + var dx = candidate.X - filteredPt.X; + var dy = candidate.Y - filteredPt.Y; + var featureDistanceSqr = dx * dx + dy * dy; + + if (featureDistanceSqr < minDistSqr) + { + isEnoughFar = false; + break; + } + } + + if (isEnoughFar) + filteredPoints.Add(candidate); + } + + return filteredPoints; + } + } }