Beispiel #1
0
        /// <summary>
        /// Determines the minimum translation vector to be applied to the circle to
        /// prevent overlap with the rectangle, when they are at their given positions.
        /// </summary>
        /// <param name="circle">The circle</param>
        /// <param name="rect">The rectangle</param>
        /// <param name="pos1">The top-left of the circles bounding box</param>
        /// <param name="pos2">The rectangles origin</param>
        /// <returns>MTV for circle at pos1 to prevent overlap with rect at pos2</returns>
        public static Tuple <Vector2, float> IntersectMTV(Circle2 circle, Rect2 rect, Vector2 pos1, Vector2 pos2)
        {
            // Same as polygon rect, just converted to rects points
            HashSet <Vector2> checkedAxis = new HashSet <Vector2>();

            Vector2 bestAxis        = Vector2.Zero;
            float   shortestOverlap = float.MaxValue;

            Func <Vector2, bool> checkAxis = (axis) =>
            {
                var standard = Math2.MakeStandardNormal(axis);
                if (!checkedAxis.Contains(standard))
                {
                    checkedAxis.Add(standard);
                    var circleProj = Circle2.ProjectAlongAxis(circle, pos1, axis);
                    var rectProj   = Rect2.ProjectAlongAxis(rect, pos2, axis);

                    var mtv = AxisAlignedLine2.IntersectMTV(circleProj, rectProj);
                    if (!mtv.HasValue)
                    {
                        return(false);
                    }

                    if (Math.Abs(mtv.Value) < Math.Abs(shortestOverlap))
                    {
                        bestAxis        = axis;
                        shortestOverlap = mtv.Value;
                    }
                }
                return(true);
            };

            var circleCenter = new Vector2(pos1.X + circle.Radius, pos1.Y + circle.Radius);
            int last         = 4;
            var lastVec      = rect.UpperRight + pos2;

            for (int curr = 0; curr < 4; curr++)
            {
                Vector2 currVec = Vector2.Zero;
                switch (curr)
                {
                case 0:
                    currVec = rect.Min + pos2;
                    break;

                case 1:
                    currVec = rect.LowerLeft + pos2;
                    break;

                case 2:
                    currVec = rect.Max + pos2;
                    break;

                case 3:
                    currVec = rect.UpperRight + pos2;
                    break;
                }

                // Test along circle center -> vector
                if (!checkAxis(Vector2.Normalize(currVec - circleCenter)))
                {
                    return(null);
                }

                // Test along line normal
                if (!checkAxis(Vector2.Normalize(Math2.Perpendicular(currVec - lastVec))))
                {
                    return(null);
                }

                last    = curr;
                lastVec = currVec;
            }

            return(Tuple.Create(bestAxis, shortestOverlap));
        }
Beispiel #2
0
        /// <summary>
        /// Determines if the circle whose bounding boxs top left is at the first postion intersects the line
        /// at the second position who is rotated the specified amount about the specified point.
        /// </summary>
        /// <param name="circle">The circle</param>
        /// <param name="line">The line</param>
        /// <param name="pos1">The top-left of the circles bounding box</param>
        /// <param name="pos2">The origin of the line</param>
        /// <param name="rot2">What rotation the line is under</param>
        /// <param name="about2">What the line is rotated about</param>
        /// <param name="strict">If overlap is required for intersection</param>
        /// <returns>If the circle at pos1 intersects the line at pos2 rotated rot2 about about2</returns>
        protected static bool CircleIntersectsLine(Circle2 circle, Line2 line, Vector2 pos1, Vector2 pos2, Rotation2 rot2, Vector2 about2, bool strict)
        {
            // Make more math friendly
            var actualLine   = new Line2(Math2.Rotate(line.Start, about2, rot2) + pos2, Math2.Rotate(line.End, about2, rot2) + pos2);
            var circleCenter = new Vector2(pos1.X + circle.Radius, pos1.Y + circle.Radius);

            // Check weird situations
            if (actualLine.Horizontal)
            {
                return(CircleIntersectsHorizontalLine(circle, actualLine, circleCenter, strict));
            }
            if (actualLine.Vertical)
            {
                return(CircleIntersectsVerticalLine(circle, actualLine, circleCenter, strict));
            }

            // Goal:
            // 1. Find closest distance, closestDistance, on the line to the circle (assuming the line was infinite)
            //   1a Determine if closestPoint is intersects the circle according to strict
            //    - If it does not, we've shown there is no intersection.
            // 2. Find closest point, closestPoint, on the line to the circle (assuming the line was infinite)
            // 3. Determine if closestPoint is on the line (including edges)
            //   - If it is, we've shown there is intersection.
            // 4. Determine which edge, edgeClosest, is closest to closestPoint
            // 5. Determine if edgeClosest intersects the circle according to strict
            //   - If it does, we've shown there is intersection
            //   - If it does not, we've shown there is no intersection

            // Step 1
            // We're trying to find closestDistance

            // Recall that the shortest line from a line to a point will be normal to the line
            // Thus, the shortest distance from a line to a point can be found by projecting
            // the line onto it's own normal vector and projecting the point onto the lines
            // normal vector; the distance between those points is the shortest distance from
            // the two points.

            // The projection of a line onto its normal will be a single point, and will be same
            // for any point on that line. So we pick a point that's convienent (the start or end).
            var lineProjectedOntoItsNormal = Vector2.Dot(actualLine.Start, actualLine.Normal);
            var centerOfCircleProjectedOntoNormalOfLine = Vector2.Dot(circleCenter, actualLine.Normal);
            var closestDistance = Math.Abs(centerOfCircleProjectedOntoNormalOfLine - lineProjectedOntoItsNormal);

            // Step 1a
            if (strict)
            {
                if (closestDistance >= circle.Radius)
                {
                    return(false);
                }
            }
            else
            {
                if (closestDistance > circle.Radius)
                {
                    return(false);
                }
            }

            // Step 2
            // We're trying to find closestPoint

            // We can just walk the vector from the center to the closest point, which we know is on
            // the normal axis and the distance closestDistance. However it's helpful to get the signed
            // version End - Start to walk.
            var signedDistanceCircleCenterToLine = lineProjectedOntoItsNormal - centerOfCircleProjectedOntoNormalOfLine;
            var closestPoint = circleCenter - actualLine.Normal * signedDistanceCircleCenterToLine;

            // Step 3
            // Determine if closestPoint is on the line (including edges)

            // We're going to accomplish this by projecting the line onto it's own axis and the closestPoint onto the lines
            // axis. Then we have a 1D comparison.
            var lineStartProjectedOntoLineAxis = Vector2.Dot(actualLine.Start, actualLine.Axis);
            var lineEndProjectedOntoLineAxis   = Vector2.Dot(actualLine.End, actualLine.Axis);

            var closestPointProjectedOntoLineAxis = Vector2.Dot(closestPoint, actualLine.Axis);

            if (AxisAlignedLine2.Contains(lineStartProjectedOntoLineAxis, lineEndProjectedOntoLineAxis, closestPointProjectedOntoLineAxis, false, true))
            {
                return(true);
            }

            // Step 4
            // We're trying to find edgeClosest.
            //
            // We're going to reuse those projections from step 3.
            //
            // (for each "point" in the next paragraph I mean "point projected on the lines axis" but that's wordy)
            //
            // We know that the start is closest iff EITHER the start is less than the end and the
            // closest point is less than the start, OR the start is greater than the end and
            // closest point is greater than the end.

            var closestEdge = Vector2.Zero;

            if (lineStartProjectedOntoLineAxis < lineEndProjectedOntoLineAxis)
            {
                closestEdge = (closestPointProjectedOntoLineAxis <= lineStartProjectedOntoLineAxis) ? actualLine.Start : actualLine.End;
            }
            else
            {
                closestEdge = (closestPointProjectedOntoLineAxis >= lineEndProjectedOntoLineAxis) ? actualLine.Start : actualLine.End;
            }

            // Step 5
            // Circle->Point intersection for closestEdge

            var distToCircleFromClosestEdgeSq = (circleCenter - closestEdge).LengthSquared();

            if (strict)
            {
                return(distToCircleFromClosestEdgeSq < (circle.Radius * circle.Radius));
            }
            else
            {
                return(distToCircleFromClosestEdgeSq <= (circle.Radius * circle.Radius));
            }

            // If you had trouble following, see the horizontal and vertical cases which are the same process but the projections
            // are simpler
        }
Beispiel #3
0
 /// <summary>
 /// Determines if the specified circle, at the given position, intersects the specified polygon,
 /// at the given position and rotation.
 /// </summary>
 /// <param name="circle">The circle</param>
 /// <param name="poly">The polygon</param>
 /// <param name="pos1">The top-left of the circles bounding box</param>
 /// <param name="pos2">The origin of the polygon</param>
 /// <param name="rot2">The rotation of the polygon</param>
 /// <param name="strict">If overlap is required for intersection</param>
 /// <returns>If circle at pos1 intersects poly at pos2 with rotation rot2</returns>
 public static bool Intersects(Circle2 circle, Polygon2 poly, Vector2 pos1, Vector2 pos2, Rotation2 rot2, bool strict)
 {
     return(Intersects(poly, circle, pos2, pos1, rot2, strict));
 }
Beispiel #4
0
 /// <summary>
 /// Determines if the specified rectangle and circle intersect at their given positions.
 /// </summary>
 /// <param name="rect">The rectangle</param>
 /// <param name="circle">The circle</param>
 /// <param name="pos1">The origin of the rectangle</param>
 /// <param name="pos2">The top-left of the circles bounding box</param>
 /// <param name="strict">If overlap is required for intersection</param>
 /// <returns></returns>
 public static bool Intersects(Rect2 rect, Circle2 circle, Vector2 pos1, Vector2 pos2, bool strict)
 {
     return(Intersects(circle, rect, pos2, pos1, strict));
 }
Beispiel #5
0
 /// <summary>
 /// Determines if the specified polygon at the specified position and rotation
 /// intersects the specified circle at it's respective position.
 /// </summary>
 /// <param name="poly">The polygon</param>
 /// <param name="circle">The circle</param>
 /// <param name="pos1">The origin for the polygon</param>
 /// <param name="pos2">The top-left of the circles bounding box</param>
 /// <param name="rot1">The rotation of the polygon</param>
 /// <param name="strict">If overlap is required for intersection</param>
 /// <returns>If poly at pos1 with rotation rot1 intersects the circle at pos2</returns>
 public static bool Intersects(Polygon2 poly, Circle2 circle, Vector2 pos1, Vector2 pos2, Rotation2 rot1, bool strict)
 {
     // look at pictures of https://stackoverflow.com/questions/401847/circle-rectangle-collision-detection-intersection if you don't
     // believe this is true
     return(poly.Lines.Any((l) => CircleIntersectsLine(circle, l, pos2, pos1, rot1, poly.Center, strict)) || Polygon2.Contains(poly, pos1, rot1, new Vector2(pos2.X + circle.Radius, pos2.Y + circle.Radius), strict));
 }
Beispiel #6
0
        /// <summary>
        /// Determines the minimum translation that must be applied the specified polygon (at the given position
        /// and rotation) to prevent intersection with the circle (at its given rotation). If the two are not overlapping,
        /// returns null.
        ///
        /// Returns a tuple of the axis to move the polygon in (unit vector) and the distance to move the polygon.
        /// </summary>
        /// <param name="poly">The polygon</param>
        /// <param name="circle">The circle</param>
        /// <param name="pos1">The origin of the polygon</param>
        /// <param name="pos2">The top-left of the circles bounding box</param>
        /// <param name="rot1">The rotation of the polygon</param>
        /// <returns></returns>
        public static Tuple <Vector2, float> IntersectMTV(Polygon2 poly, Circle2 circle, Vector2 pos1, Vector2 pos2, Rotation2 rot1)
        {
            // We have two situations, either the circle is not strictly intersecting the polygon, or
            // there exists at least one shortest line that you could push the polygon to prevent
            // intersection with the circle.

            // That line will either go from a vertix of the polygon to a point on the edge of the circle,
            // or it will go from a point on a line of the polygon to the edge of the circle.

            // If the line comes from a vertix of the polygon, the MTV will be along the line produced
            // by going from the center of the circle to the vertix, and the distance can be found by
            // projecting the cirle on that axis and the polygon on that axis and doing 1D overlap.

            // If the line comes from a point on the edge of the polygon, the MTV will be along the
            // normal of that line, and the distance can be found by projecting the circle on that axis
            // and the polygon on that axis and doing 1D overlap.

            // As with all SAT, if we find any axis that the circle and polygon do not overlap, we've
            // proven they do not intersect.

            // The worst case performance is related to 2x the number of vertices of the polygon, the same speed
            // as for 2 polygons of equal number of vertices.

            HashSet <Vector2> checkedAxis = new HashSet <Vector2>();

            Vector2 bestAxis        = Vector2.Zero;
            float   shortestOverlap = float.MaxValue;

            Func <Vector2, bool> checkAxis = (axis) =>
            {
                var standard = Math2.MakeStandardNormal(axis);
                if (!checkedAxis.Contains(standard))
                {
                    checkedAxis.Add(standard);
                    var polyProj   = Polygon2.ProjectAlongAxis(poly, pos1, rot1, axis);
                    var circleProj = Circle2.ProjectAlongAxis(circle, pos2, axis);

                    var mtv = AxisAlignedLine2.IntersectMTV(polyProj, circleProj);
                    if (!mtv.HasValue)
                    {
                        return(false);
                    }

                    if (Math.Abs(mtv.Value) < Math.Abs(shortestOverlap))
                    {
                        bestAxis        = axis;
                        shortestOverlap = mtv.Value;
                    }
                }
                return(true);
            };

            var circleCenter = new Vector2(pos2.X + circle.Radius, pos2.Y + circle.Radius);
            int last         = poly.Vertices.Length - 1;
            var lastVec      = Math2.Rotate(poly.Vertices[last], poly.Center, rot1) + pos1;

            for (int curr = 0; curr < poly.Vertices.Length; curr++)
            {
                var currVec = Math2.Rotate(poly.Vertices[curr], poly.Center, rot1) + pos1;

                // Test along circle center -> vector
                if (!checkAxis(Vector2.Normalize(currVec - circleCenter)))
                {
                    return(null);
                }

                // Test along line normal
                if (!checkAxis(Vector2.Normalize(Math2.Perpendicular(currVec - lastVec))))
                {
                    return(null);
                }

                last    = curr;
                lastVec = currVec;
            }

            return(Tuple.Create(bestAxis, shortestOverlap));
        }
Beispiel #7
0
 /// <summary>
 /// Projects the specified circle with the upper-left at the specified position onto
 /// the specified axis.
 /// </summary>
 /// <param name="circle">The circle</param>
 /// <param name="pos">The position of the circle</param>
 /// <param name="axis">the axis to project along</param>
 /// <returns>Projects circle at pos along axis</returns>
 public static AxisAlignedLine2 ProjectAlongAxis(Circle2 circle, Vector2 pos, Vector2 axis)
 {
     return(ProjectAlongAxis(circle.Radius, pos, axis));
 }
Beispiel #8
0
 /// <summary>
 /// Determines the shortest axis and overlap for which the first circle at the specified position
 /// overlaps the second circle at the specified position. If the circles do not overlap, returns null.
 /// </summary>
 /// <param name="circle1">First circle</param>
 /// <param name="circle2">Second circle</param>
 /// <param name="pos1">Top-left of the first circles bounding box</param>
 /// <param name="pos2">Top-left of the second circles bounding box</param>
 /// <returns></returns>
 public static Tuple <Vector2, float> IntersectMTV(Circle2 circle1, Circle2 circle2, Vector2 pos1, Vector2 pos2)
 {
     return(IntersectMTV(circle1.Radius, circle2.Radius, pos1, pos2));
 }
Beispiel #9
0
 /// <summary>
 /// Determines if the first circle at the specified position intersects the second circle
 /// at the specified position.
 /// </summary>
 /// <param name="circle1">First circle</param>
 /// <param name="circle2">Second circle</param>
 /// <param name="pos1">Top-left of the bounding box of the first circle</param>
 /// <param name="pos2">Top-left of the bounding box of the second circle</param>
 /// <param name="strict">If overlap is required for intersection</param>
 /// <returns>If circle1 at pos1 intersects circle2 at pos2</returns>
 public static bool Intersects(Circle2 circle1, Circle2 circle2, Vector2 pos1, Vector2 pos2, bool strict)
 {
     return(Intersects(circle1.Radius, circle2.Radius, pos1, pos2, strict));
 }
Beispiel #10
0
 /// <summary>
 /// Determines the minimum translation vector to be applied to the circle to prevent
 /// intersection with the specified polyogn, when they are at the given positions.
 /// </summary>
 /// <param name="circle">The circle</param>
 /// <param name="poly">The polygon</param>
 /// <param name="pos1">The top-left of the circles bounding box</param>
 /// <param name="pos2">The origin of the polygon</param>
 /// <returns></returns>
 public static Tuple <Vector2, float> IntersectMTV(Circle2 circle, Polygon2 poly, Vector2 pos1, Vector2 pos2)
 {
     return(IntersectMTV(circle, poly, pos1, pos2, Rotation2.Zero));
 }
Beispiel #11
0
 /// <summary>
 /// Determines if the circle and polygon intersect when at the given positions.
 /// </summary>
 /// <param name="circle">The circle</param>
 /// <param name="poly">The polygon</param>
 /// <param name="pos1">The top-left of the circles bounding box</param>
 /// <param name="pos2">The origin of the polygon</param>
 /// <param name="strict">If overlap is required for intersection</param>
 /// <returns>If circle at pos1 intersects poly at pos2</returns>
 public static bool Intersects(Circle2 circle, Polygon2 poly, Vector2 pos1, Vector2 pos2, bool strict)
 {
     return(Intersects(circle, poly, pos1, pos2, Rotation2.Zero, strict));
 }