// Check if this polygon contains a point public override bool Contains(Vector2 point) { // Suppose we have a polygon with n vertices. Let p be a point, and // consider the ray emanating from p and extending toward the left. // Lastly, let n be the number of times this ray intersects the polygon's // perimeter. Then the following properties hold. // // 1.) If n is even, then p lies outside the polygon. // 2.) If n is odd, then p is inside the polygon. // // Note: This expresion assumes that the (x_0, y_0) and (x_n, y_n) are // identical, i.e. the polygon is closed. // Entry logging #if IS_LOGGING_METHODS Log.Write(String.Format("Entering method for {0}", this.Name)); #endif // Declare result bool result; #region [1] Broad phase: Bounding box test // First, check if point is outside MinBoundingBox if (!this.MinBoundingBox.Contains(point)) { result = false; goto exit; } #endregion #region [2] Narrow phase: Ray casting test // Note: We have to be very careful here. Suppose our ray intersects // with a vertex of our polygon. In this case, the ray will technically // intersect with two different edges! This is really just one point of // intersection, and so we have to be careful that it is not counted // twice! // Initialize intersection counter int count = 0; // Create a horizontal ray stemming from the point and extending to the left LineSegment ray = new LineSegment(point, new Vector2(this.MinBoundingBox.X - 1, point.Y)); // if (Globals.TestBool) { Trace.WriteLine("ray = " + ray); } // Get edges List <LineSegment> edges = this.Edges; // Loop through each edge for (int i = 0; i < this.Vertices.Count; i++) { // Point to current edge LineSegment edge = edges[i]; // if (Globals.TestBool) { Trace.WriteLine("edge[" + i + "] = " + edge + ", count = " + count); } // Check if ray intersects with edge if (ray.IntersectsWith(edge)) { // If ray is coincident with ray, count once if (edge.Point1.Y == ray.Point2.Y && edge.Point2.Y == ray.Point2.Y) { count++; } // If ray intersects with vertex, don't double count it else if (edge.Point2.Y != ray.Point2.Y) { count++; } } } // Check if count is even if (count % 2 == 0) { result = false; goto exit; } else { result = true; goto exit; } #endregion // [*] Exit trap exit: // Exit logging // if (Globals.TestBool) { Trace.WriteLine("final count = " + count); } #if IS_LOGGING_METHODS Log.Write(String.Format("Exiting method for {0}", this.Name)); #endif // Return result return(result); }
// Get point of intersection between this line and another public Vector2 IntersectionWith(LineSegment line) { // Suppose A = (x1, y1), B = (x2, y2), C = (x3, y3), D = (x4, y4). We // may define vector (ua, ub) as follows. // // ua = na / d // ub = nb / d // na = (x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3) // nb = (x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3) // d = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1) // // The following results hold true. // // 1.) If d = 0, then the line segments are parallel // 2.) If d, na, and nb all equal zero, then the line segments are // coincident (overlap). // 3.) If ua and ub lie between 0 and 1, then the intersection point // lies within the line segment' end points. In this case, the // point of intersection (x, y) may be written as follows. // // x = x1 + ua * (x2 - x1) // y = y1 + ua * (y2 - y1) // Note: If the line segments do not intersect, this function will // return positive infinity. If they overlap, it will simply return // (x1, y1). // Entry logging #if IS_LOGGING_METHODS Log.Write("Entering method"); #endif // Declare result Vector2 result; // Declare each coordinate float x1 = this.Point1.X; float x2 = this.Point2.X; float x3 = line.Point1.X; float x4 = line.Point2.X; float y1 = this.Point1.Y; float y2 = this.Point2.Y; float y3 = line.Point1.Y; float y4 = line.Point2.Y; // Calculate the denominator and each numerator float d = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1); float na = (x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3); float nb = (x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3); // Check if lines are parallel if (d == 0) { // If so, check if they are coincident if (na == 0 && nb == 0) { float minA = x1; float maxA = x2; if (x2 < x1) { minA = x2; maxA = x1; } float minB = x3; float maxB = x4; if (x4 < x3) { minB = x4; maxB = x3; } Interval intervalA = new Interval(minA, maxA); Interval intervalB = new Interval(minB, maxB); Interval overlap = intervalA.IntersectionWith(intervalB); if (!overlap.IsEmpty) { result = new Vector2(overlap.Min, y1); } else { result = Vector2.PositiveInfinity; } } else { result = Vector2.PositiveInfinity; goto exit; } } // Otherwise else { // Divide each numerator by the denominator float ua = na / d; float ub = nb / d; // Stop if ua is outside the interval (0, 1) if (ua < 0 || ua > 1) { result = Vector2.PositiveInfinity; goto exit; } // Stop if ub is outside the interval (0, 1) if (ub < 0 || ub > 1) { result = Vector2.PositiveInfinity; goto exit; } // If we haven't stopped yet, then the line segments intersect float x = x1 + ua * (x2 - x1); float y = y1 + ua * (y2 - y1); result = new Vector2(x, y); goto exit; } // Exit trap exit: // Exit logging #if IS_LOGGING_METHODS Log.Write("Exiting method"); #endif // Return result return(result); }
// Find the point on this polygon closest to a point public override Vector2 ClosestPointTo(Vector2 point) { // Let e be an arbitrary edge with direction d. Furthermore, let [a, b] and p // be the projections of e and our point onto the d-axis, respectively. Then // there are two possible cases. // 1.) p is in [a, b] // Then there is a perpendicular line connecting our point to e. This // edge might contain the closest point. // 2.) p is not in [a, b] // This edge definitely does not contain the closest point. We may // disregard it. // // We check this criterion for each edge, compute the closest point to each // edge, and that which yields the smallest distance. However, if no edges // satisfy the above criterion, then the closest point must be a vertex. // Note: If there are two equidistant, closest points, this method will only // return one of them! // Entry logging #if IS_LOGGING_METHODS Log.Write(String.Format("Entering method for {0}", this.Name)); #endif // Declare result Vector2 result = Vector2.Zero; // Initialize minDistance as positive infinity float minDistance = float.PositiveInfinity; // First, we loop through the polygon's vertices for (int i = 0; i < this.Vertices.Count; i++) { // Get current vertex Vector2 vertex = this.Vertices[i]; // Get distance between vertex and point float distance = Vector2.Distance(vertex, point); // Update minimum distance and result if (distance < minDistance) { minDistance = distance; result = vertex; } } // Get edges List <LineSegment> edges = this.Edges; // Loop through edges for (int i = 0; i < this.Vertices.Count; i++) { // Get current edge LineSegment edge = edges[i]; // Get edge's direction vector Vector2 axis = edge.Direction; // Project edge onto axis Interval edgeProjection = edge.ProjectOnto(axis); // Project point onto axis float pointProjection = Vector2.Project(point, axis); // Check if p is in [a, b] if (edgeProjection.Contains(pointProjection)) { // If so, check minimum distance between point and edge Vector2 closestEdgePoint = edge.ClosestPointTo(point); float distance = Vector2.Distance(closestEdgePoint, point); // Update minimum distance and result if (distance < minDistance) { minDistance = distance; result = closestEdgePoint; } } } // Exit logging #if IS_LOGGING_METHODS Log.Write(String.Format("Exiting method for {0}", this.Name)); #endif // Return result return(result); }