How to test if a line segment intersects an axis-aligned rectange in 2D?

前端 未结 12 732
执念已碎
执念已碎 2020-11-30 07:33

How to test if a line segment intersects an axis-aligned rectange in 2D? The segment is defined with its two ends: p1, p2. The rectangle is defined with top-left and bottom-

相关标签:
12条回答
  • 2020-11-30 07:40

    Here's a javascript version of @metamal's answer

    var isRectangleIntersectedByLine = function (
      a_rectangleMinX,
      a_rectangleMinY,
      a_rectangleMaxX,
      a_rectangleMaxY,
      a_p1x,
      a_p1y,
      a_p2x,
      a_p2y) {
    
      // Find min and max X for the segment
      var minX = a_p1x
      var maxX = a_p2x
    
      if (a_p1x > a_p2x) {
        minX = a_p2x
        maxX = a_p1x
      }
    
      // Find the intersection of the segment's and rectangle's x-projections
      if (maxX > a_rectangleMaxX)
        maxX = a_rectangleMaxX
    
      if (minX < a_rectangleMinX)
        minX = a_rectangleMinX
    
      // If their projections do not intersect return false
      if (minX > maxX)
        return false
    
      // Find corresponding min and max Y for min and max X we found before
      var minY = a_p1y
      var maxY = a_p2y
    
      var dx = a_p2x - a_p1x
    
      if (Math.abs(dx) > 0.0000001) {
        var a = (a_p2y - a_p1y) / dx
        var b = a_p1y - a * a_p1x
        minY = a * minX + b
        maxY = a * maxX + b
      }
    
      if (minY > maxY) {
        var tmp = maxY
        maxY = minY
        minY = tmp
      }
    
      // Find the intersection of the segment's and rectangle's y-projections
      if(maxY > a_rectangleMaxY)
        maxY = a_rectangleMaxY
    
      if (minY < a_rectangleMinY)
        minY = a_rectangleMinY
    
      // If Y-projections do not intersect return false
      if(minY > maxY)
        return false
    
      return true
    }
    
    0 讨论(0)
  • 2020-11-30 07:44

    You could also create a rectangle out of the segment and test if the other rectangle collides with it, since it is just a series of comparisons. From pygame source:

    def _rect_collide(a, b):
        return a.x + a.w > b.x and b.x + b.w > a.x and \
               a.y + a.h > b.y and b.y + b.h > a.y
    
    0 讨论(0)
  • 2020-11-30 07:49

    Or just use/copy the code already in the Java method

    java.awt.geom.Rectangle2D.intersectsLine(double x1, double y1, double x2, double y2)
    

    Here is the method after being converted to static for convenience:

    /**
     * Code copied from {@link java.awt.geom.Rectangle2D#intersectsLine(double, double, double, double)}
     */
    public class RectangleLineIntersectTest {
        private static final int OUT_LEFT = 1;
        private static final int OUT_TOP = 2;
        private static final int OUT_RIGHT = 4;
        private static final int OUT_BOTTOM = 8;
    
        private static int outcode(double pX, double pY, double rectX, double rectY, double rectWidth, double rectHeight) {
            int out = 0;
            if (rectWidth <= 0) {
                out |= OUT_LEFT | OUT_RIGHT;
            } else if (pX < rectX) {
                out |= OUT_LEFT;
            } else if (pX > rectX + rectWidth) {
                out |= OUT_RIGHT;
            }
            if (rectHeight <= 0) {
                out |= OUT_TOP | OUT_BOTTOM;
            } else if (pY < rectY) {
                out |= OUT_TOP;
            } else if (pY > rectY + rectHeight) {
                out |= OUT_BOTTOM;
            }
            return out;
        }
    
        public static boolean intersectsLine(double lineX1, double lineY1, double lineX2, double lineY2, double rectX, double rectY, double rectWidth, double rectHeight) {
            int out1, out2;
            if ((out2 = outcode(lineX2, lineY2, rectX, rectY, rectWidth, rectHeight)) == 0) {
                return true;
            }
            while ((out1 = outcode(lineX1, lineY1, rectX, rectY, rectWidth, rectHeight)) != 0) {
                if ((out1 & out2) != 0) {
                    return false;
                }
                if ((out1 & (OUT_LEFT | OUT_RIGHT)) != 0) {
                    double x = rectX;
                    if ((out1 & OUT_RIGHT) != 0) {
                        x += rectWidth;
                    }
                    lineY1 = lineY1 + (x - lineX1) * (lineY2 - lineY1) / (lineX2 - lineX1);
                    lineX1 = x;
                } else {
                    double y = rectY;
                    if ((out1 & OUT_BOTTOM) != 0) {
                        y += rectHeight;
                    }
                    lineX1 = lineX1 + (y - lineY1) * (lineX2 - lineX1) / (lineY2 - lineY1);
                    lineY1 = y;
                }
            }
            return true;
        }
    }
    
    0 讨论(0)
  • 2020-11-30 07:52

    A quick Google search popped up a page with C++ code for testing the intersection.

    Basically it tests the intersection between the line, and every border or the rectangle.

    Rectangle and line intersection code

    0 讨论(0)
  • 2020-11-30 07:53

    I was looking at a similar problem and here's what I came up with. I was first comparing the edges and realized something. If the midpoint of an edge that fell within the opposite axis of the first box is within half the length of that edge of the outer points on the first in the same axis, then there is an intersection of that side somewhere. But that was thinking 1 dimensionally and required looking at each side of the second box to figure out.

    It suddenly occurred to me that if you find the 'midpoint' of the second box and compare the coordinates of the midpoint to see if they fall within 1/2 length of a side (of the second box) of the outer dimensions of the first, then there is an intersection somewhere.

    i.e. box 1 is bounded by x1,y1 to x2,y2
    box 2 is bounded by a1,b1 to a2,b2
    
    the width and height of box 2 is:
    w2 = a2 - a1   (half of that is w2/2)
    h2 = b2 - b1   (half of that is h2/2)
    the midpoints of box 2 are:
    am = a1 + w2/2
    bm = b1 + h2/2
    
    So now you just check if
    (x1 - w2/2) < am < (x2 + w2/2) and (y1 - h2/2) < bm < (y2 + h2/2) 
    then the two overlap somewhere.
    If you want to check also for edges intersecting to count as 'overlap' then
     change the < to <=
    

    Of course you could just as easily compare the other way around (checking midpoints of box1 to be within 1/2 length of the outer dimenions of box 2)

    And even more simplification - shift the midpoint by your half lengths and it's identical to the origin point of that box. Which means you can now check just that point for falling within your bounding range and by shifting the plain up and to the left, the lower corner is now the lower corner of the first box. Much less math:

    (x1 - w2) < a1 < x2
    &&
    (y1 - h2) < b1 < y2
    [overlap exists]
    

    or non-substituted:

    ( (x1-(a2-a1)) < a1 < x2 ) && ( (y1-(b2-b1)) < b1 < y2 ) [overlap exists]
    ( (x1-(a2-a1)) <= a1 <= x2 ) && ( (y1-(b2-b1)) <= b1 <= y2 ) [overlap or intersect exists]
    
    0 讨论(0)
  • 2020-11-30 07:56

    Wrote quite simple and working solution:

          bool SegmentIntersectRectangle(double a_rectangleMinX,
                                     double a_rectangleMinY,
                                     double a_rectangleMaxX,
                                     double a_rectangleMaxY,
                                     double a_p1x,
                                     double a_p1y,
                                     double a_p2x,
                                     double a_p2y)
      {
        // Find min and max X for the segment
    
        double minX = a_p1x;
        double maxX = a_p2x;
    
        if(a_p1x > a_p2x)
        {
          minX = a_p2x;
          maxX = a_p1x;
        }
    
        // Find the intersection of the segment's and rectangle's x-projections
    
        if(maxX > a_rectangleMaxX)
        {
          maxX = a_rectangleMaxX;
        }
    
        if(minX < a_rectangleMinX)
        {
          minX = a_rectangleMinX;
        }
    
        if(minX > maxX) // If their projections do not intersect return false
        {
          return false;
        }
    
        // Find corresponding min and max Y for min and max X we found before
    
        double minY = a_p1y;
        double maxY = a_p2y;
    
        double dx = a_p2x - a_p1x;
    
        if(Math::Abs(dx) > 0.0000001)
        {
          double a = (a_p2y - a_p1y) / dx;
          double b = a_p1y - a * a_p1x;
          minY = a * minX + b;
          maxY = a * maxX + b;
        }
    
        if(minY > maxY)
        {
          double tmp = maxY;
          maxY = minY;
          minY = tmp;
        }
    
        // Find the intersection of the segment's and rectangle's y-projections
    
        if(maxY > a_rectangleMaxY)
        {
          maxY = a_rectangleMaxY;
        }
    
        if(minY < a_rectangleMinY)
        {
          minY = a_rectangleMinY;
        }
    
        if(minY > maxY) // If Y-projections do not intersect return false
        {
          return false;
        }
    
        return true;
      }
    
    0 讨论(0)
提交回复
热议问题