Exemplo n.º 1
0
        /// <summary>
        /// Determines if the circle at the specified position intersects the line,
        /// which is at its true position and rotation, when the line is assumed to be horizontal.
        /// </summary>
        /// <param name="circle">The circle</param>
        /// <param name="line">The line</param>
        /// <param name="circleCenter">The center of the circle</param>
        /// <param name="strict">If overlap is required for intersection</param>
        /// <returns>If the circle with center circleCenter intersects the horizontal line</returns>
        protected static bool CircleIntersectsHorizontalLine(Circle2 circle, Line2 line, Vector2 circleCenter, bool strict)
        {
            // This is exactly the same process as CircleIntersectsLine, except the projetions are easier
            var lineY = line.Start.Y;

            // Step 1 - Find closest distance
            var vecCircleCenterToLine1D = lineY - circleCenter.Y;
            var closestDistance         = Math.Abs(vecCircleCenterToLine1D);

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

            // Step 2 - Find closest point
            var closestPointX = circleCenter.X;

            // Step 3 - Is closest point on line
            if (AxisAlignedLine2.Contains(line.Start.X, line.End.X, closestPointX, false, true))
            {
                return(true);
            }

            // Step 4 - Find edgeClosest
            float edgeClosestX;

            if (line.Start.X < line.End.X)
            {
                edgeClosestX = (closestPointX <= line.Start.X) ? line.Start.X : line.End.X;
            }
            else
            {
                edgeClosestX = (closestPointX >= line.Start.X) ? line.Start.X : line.End.X;
            }

            // Step 5 - Circle-point intersection on closest point
            var distClosestEdgeToCircleSq = new Vector2(circleCenter.X - edgeClosestX, circleCenter.Y - lineY).LengthSquared();

            if (strict)
            {
                return(distClosestEdgeToCircleSq < circle.Radius * circle.Radius);
            }
            else
            {
                return(distClosestEdgeToCircleSq <= circle.Radius * circle.Radius);
            }
        }
Exemplo n.º 2
0
        /// <summary>
        /// Determines if the circle at the specified position intersects the line, which
        /// is at its true position and rotation, when the line is assumed to be vertical
        /// </summary>
        /// <param name="circle">The circle</param>
        /// <param name="line">The line</param>
        /// <param name="circleCenter">The center of the circle</param>
        /// <param name="strict">If overlap is required for intersection</param>
        /// <returns>If the circle with center circleCenter intersects the line</returns>
        protected static bool CircleIntersectsVerticalLine(Circle2 circle, Line2 line, Vector2 circleCenter, bool strict)
        {
            // Same process as horizontal, but axis flipped
            var lineX = line.Start.X;
            // Step 1 - Find closest distance
            var vecCircleCenterToLine1D = lineX - circleCenter.X;
            var closestDistance         = Math.Abs(vecCircleCenterToLine1D);

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

            // Step 2 - Find closest point
            var closestPointY = circleCenter.Y;

            // Step 3 - Is closest point on line
            if (AxisAlignedLine2.Contains(line.Start.Y, line.End.Y, closestPointY, false, true))
            {
                return(true);
            }

            // Step 4 - Find edgeClosest
            float edgeClosestY;

            if (line.Start.Y < line.End.Y)
            {
                edgeClosestY = (closestPointY <= line.Start.Y) ? line.Start.Y : line.End.Y;
            }
            else
            {
                edgeClosestY = (closestPointY >= line.Start.Y) ? line.Start.Y : line.End.Y;
            }

            // Step 5 - Circle-point intersection on closest point
            var distClosestEdgeToCircleSq = new Vector2(circleCenter.X - lineX, circleCenter.Y - edgeClosestY).LengthSquared();

            if (strict)
            {
                return(distClosestEdgeToCircleSq < circle.Radius * circle.Radius);
            }
            else
            {
                return(distClosestEdgeToCircleSq <= circle.Radius * circle.Radius);
            }
        }
Exemplo n.º 3
0
        /// <summary>
        /// Determines if the given line contains the given point.
        /// </summary>
        /// <param name="line">The line to check</param>
        /// <param name="pos">The offset for the line</param>
        /// <param name="pt">The point to check</param>
        /// <returns>True if pt is on the line, false otherwise</returns>
        public static bool Contains(Line2 line, Vector2 pos, Vector2 pt)
        {
            // The horizontal/vertical checks are not required but are
            // very fast to calculate and short-circuit the common case
            // (false) very quickly
            if (line.Horizontal)
            {
                return(Math2.Approximately(line.Start.Y + pos.Y, pt.Y) &&
                       AxisAlignedLine2.Contains(line.MinX, line.MaxX, pt.X - pos.X, false, false));
            }
            if (line.Vertical)
            {
                return(Math2.Approximately(line.Start.X + pos.X, pt.X) &&
                       AxisAlignedLine2.Contains(line.MinY, line.MaxY, pt.Y - pos.Y, false, false));
            }

            // Our line is not necessarily a linear space, but if we shift
            // our line to the origin and adjust the point correspondingly
            // then we have a linear space and the problem remains the same.

            // Our line at the origin is just the infinite line with slope
            // Axis. We can form an orthonormal basis of R2 as (Axis, Normal).
            // Hence we can write pt = line_part * Axis + normal_part * Normal.
            // where line_part and normal_part are floats. If the normal_part
            // is 0, then pt = line_part * Axis, hence the point is on the
            // infinite line.

            // Since we are working with an orthonormal basis, we can find
            // components with dot products.

            // To check the finite line, we consider the start of the line
            // the origin. Then the end of the line is line.Magnitude * line.Axis.

            Vector2 lineStart = pos + line.Start;

            float normalPart = Math2.Dot(pt - lineStart, line.Normal);

            if (!Math2.Approximately(normalPart, 0))
            {
                return(false);
            }

            float axisPart = Math2.Dot(pt - lineStart, line.Axis);

            return(axisPart > -Math2.DEFAULT_EPSILON &&
                   axisPart < line.Magnitude + Math2.DEFAULT_EPSILON);
        }
Exemplo n.º 4
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
        }
Exemplo n.º 5
0
 /// <summary>
 /// Determines if the box when at pos contains point.
 /// </summary>
 /// <param name="box">The box</param>
 /// <param name="pos">Origin of box</param>
 /// <param name="point">Point to check</param>
 /// <param name="strict">true if the edges do not count</param>
 /// <returns>If the box at pos contains point</returns>
 public static bool Contains(Rect2 box, Vector2 pos, Vector2 point, bool strict)
 {
     return(AxisAlignedLine2.Contains(box.Min.X + pos.X, box.Max.X + pos.X, point.X, strict, false) &&
            AxisAlignedLine2.Contains(box.Min.Y + pos.Y, box.Max.Y + pos.Y, point.Y, strict, false));
 }
Exemplo n.º 6
0
        /// <summary>
        /// Determines if line1 intersects line2, when line1 is offset by pos1 and line2
        /// is offset by pos2.
        /// </summary>
        /// <param name="line1">Line 1</param>
        /// <param name="line2">Line 2</param>
        /// <param name="pos1">Origin of line 1</param>
        /// <param name="pos2">Origin of line 2</param>
        /// <param name="strict">If overlap is required for intersection</param>
        /// <returns>If line1 intersects line2</returns>
        public static bool Intersects(Line2 line1, Line2 line2, Vector2 pos1, Vector2 pos2, bool strict)
        {
            if (line1.Horizontal && line2.Horizontal)
            {
                return(AxisAlignedLine2.Intersects(line1.MinX + pos1.X, line1.MaxX + pos1.X, line2.MinX + pos2.X, line2.MaxX + pos2.X, strict, false));
            }
            else if (line1.Vertical && line2.Vertical)
            {
                return(AxisAlignedLine2.Intersects(line1.MinY + pos1.Y, line1.MaxY + pos1.Y, line2.MinY + pos2.Y, line2.MaxY + pos2.Y, strict, false));
            }
            else if (line1.Horizontal || line2.Horizontal)
            {
                if (line2.Horizontal)
                {
                    // swap line 1 and 2 to prevent duplicating everything
                    var tmp  = line1;
                    var tmpp = pos1;
                    line1 = line2;
                    pos1  = pos2;
                    line2 = tmp;
                    pos2  = tmpp;
                }

                if (line2.Vertical)
                {
                    return(AxisAlignedLine2.Contains(line1.MinX + pos1.X, line1.MaxX + pos1.X, line2.Start.X + pos2.X, strict, false) &&
                           AxisAlignedLine2.Contains(line2.MinY + pos2.Y, line2.MaxY + pos2.Y, line1.Start.Y + pos1.Y, strict, false));
                }
                else
                {
                    // recalculate line2 y intercept
                    // y = mx + b
                    // b = y - mx
                    var line2YIntInner = line2.Start.Y + pos2.Y - line2.Slope * (line2.Start.X + pos2.X);
                    // check line2.x at line1.y
                    // line2.y = line2.slope * line2.x + line2.yintercept
                    // line1.y = line2.slope * line2.x + line2.yintercept
                    // line1.y - line2.yintercept = line2.slope * line2.x
                    // (line1.y - line2.yintercept) / line2.slope = line2.x
                    var line2XAtLine1Y = (line1.Start.Y + pos1.Y - line2YIntInner) / line2.Slope;
                    return(AxisAlignedLine2.Contains(line1.MinX + pos1.X, line1.MaxX + pos1.X, line2XAtLine1Y, strict, false) &&
                           AxisAlignedLine2.Contains(line2.MinX + pos2.X, line2.MaxX + pos2.X, line2XAtLine1Y, strict, false));
                }
            }
            else if (line1.Vertical)
            {
                // vertical line with regular line
                var line2YIntInner = line2.Start.Y + pos2.Y - line2.Slope * (line2.Start.X + pos2.X);
                var line2YAtLine1X = line2.Slope * (line1.Start.X + pos1.X) + line2YIntInner;
                return(AxisAlignedLine2.Contains(line1.MinY + pos1.Y, line1.MaxY + pos1.Y, line2YAtLine1X, strict, false) &&
                       AxisAlignedLine2.Contains(line2.MinY + pos2.Y, line2.MaxY + pos2.Y, line2YAtLine1X, strict, false));
            }

            // two non-vertical, non-horizontal lines
            var line1YInt = line1.Start.Y + pos1.Y - line1.Slope * (line1.Start.X + pos1.X);
            var line2YInt = line2.Start.Y + pos2.Y - line2.Slope * (line2.Start.X + pos2.X);

            if (Math.Abs(line1.Slope - line2.Slope) <= Math2.DEFAULT_EPSILON)
            {
                // parallel lines
                if (line1YInt != line2YInt)
                {
                    return(false); // infinite lines don't intersect
                }
                // parallel lines with equal y intercept. Intersect if ever at same X coordinate.
                return(AxisAlignedLine2.Intersects(line1.MinX + pos1.X, line1.MaxX + pos1.X, line2.MinX + pos2.X, line2.MaxX + pos2.X, strict, false));
            }
            else
            {
                // two non-parallel lines. Only one possible intersection point

                // y1 = y2
                // line1.Slope * x + line1.YIntercept = line2.Slope * x + line2.YIntercept
                // line1.Slope * x - line2.Slope * x = line2.YIntercept - line1.YIntercept
                // x (line1.Slope - line2.Slope) = line2.YIntercept - line1.YIntercept
                // x = (line2.YIntercept - line1.YIntercept) / (line1.Slope - line2.Slope)
                var x = (line2YInt - line1YInt) / (line1.Slope - line2.Slope);

                return(AxisAlignedLine2.Contains(line1.MinX + pos1.X, line1.MaxX + pos1.X, x, strict, false) &&
                       AxisAlignedLine2.Contains(line2.MinX + pos1.X, line2.MaxX + pos2.X, x, strict, false));
            }
        }