How to test if a line segment intersects an axis-a

2019-01-07 18:59发布

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-right points.

12条回答
放我归山
2楼-- · 2019-01-07 19: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]
查看更多
我欲成王,谁敢阻挡
3楼-- · 2019-01-07 19:55

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
}
查看更多
闹够了就滚
4楼-- · 2019-01-07 19:57

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

查看更多
淡お忘
5楼-- · 2019-01-07 19:58

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
查看更多
爷的心禁止访问
6楼-- · 2019-01-07 20:02

Some sample code for my solution (in php):

// returns 'true' on overlap checking against an array of similar objects in $this->packed
public function checkForOverlaps(BinPack_Polygon $nItem) {
  $nX = $nItem->getLeft();
  $nY = $nItem->getTop();
  $nW = $nItem->getWidth();
  $nH = $nItem->getHeight();
  // loop through the stored polygons checking for overlaps
  foreach($this->packed as $_i => $pI) {
    if(((($pI->getLeft() - $nW) < $nX) && ($nX < $pI->getRight())) && ((($pI->getTop() - $nH) < $nY) && ($nY < $pI->getBottom()))) {
      return true;
    }
  }
  return false;
}
查看更多
兄弟一词,经得起流年.
7楼-- · 2019-01-07 20:05

I did a little napkin solution..

Next find m and c and hence the equation y = mx + c

y = (Point2.Y - Point1.Y) / (Point2.X - Point1.X)

Substitute P1 co-ordinates to now find c

Now for a rectangle vertex, put the X value in the line equation, get the Y value and see if the Y value lies in the rectangle bounds shown below

(you can find the constant values X1, X2, Y1, Y2 for the rectangle such that)

X1 <= x <= X2 & 
Y1 <= y <= Y2

If the Y value satisfies the above condition and lies between (Point1.Y, Point2.Y) - we have an intersection. Try every vertex if this one fails to make the cut.

查看更多
登录 后发表回答