Reputation: 2712
I'm trying to measure the overlap between a line segment and a rectangle. For instance, the line segment is represented as 2 points:
line = [(x1, y1), (x2, y2)]
and a rectangle as 4 points:
rect = [(x3, y3), (x4, y4), (x5, y5), (x6, y6)]
Is there an efficient way to measure the length of the overlap between the two (if any)?
Here's a simple diagram:
Edit: Just to clarify, the line segment may partially intersect with the rectangle or even not at all.
Upvotes: 1
Views: 1409
Reputation: 80187
Find affine transformation to make rectangle axis-aligned. It is just rotation by angle
fi = -atan2(y4-y3, x4-x3)
Transform coordinates of line segment ends and some rectangle coordinates with this rotation matrix
x' = x * Cos(fi) - y * Sin(fi)
y' = x * Sin(fi) + y * Cos(fi)
Note that now you need only two x- and two y-coordinates for rectangles (left=x3', right=x4', bottom=y3', top=y6'
)
Apply any line-clipping algorithm
I'd recommend Liang-Barsky one (description with code).
After calculations get distance between clip points or segment ends being inside rectangle
(cases when infinite line intersects rectangle and result parameters t0
and/or t1
are out of 0..1
range)
Upvotes: 2
Reputation: 12410
You didn't exclude external libraries, so here is shapely, that is designed for this kind of tasks. Written in C and has a Numpy array interface.
from shapely.geometry import LineString, Polygon
line_list = [(0, 0), (5, 5)]
rect_list = [(1, 1), (1, 3), (3, 3), (3, 1)]
line_in_rect = Polygon(rect_list).intersection(LineString(line_list))
if line_in_rect:
print(line_in_rect.length)
else:
print("no intersection")
Doesn't test, though, if it is a rectangle, or if the overlap is only partial.
Upvotes: -1
Reputation: 20414
I have broken down this answer into three parts:
I will also use matplotlib
to visualise examples.
This wikipedia article gives us a formula for working out the co-ordinate of the intersection point between two lines described by the coordinates:
with the co-coordinates: (x1, y1)
, (x2, y2)
, (x3, y3)
and (x4, y4)
.
If they do not intersect, then the denominator:
(x1-x2)*(y3-y4)-(y1-y2)*(x3-x4)
will be 0
.
So, using this formula, we can create a function that will return the point of intersection between two lines, or False
meaning that they do not intersect:
def intersection(x1, y1, x2, y2, x3, y3, x4, y4):
d = (x1-x2)*(y3-y4)-(y1-y2)*(x3-x4)
if not d:
return False
return (((x1*y2-y1*x2)*(x3-x4) - (x1-x2)*(x3*y4-y3*x4))/d,
((x1*y2-y1*x2)*(y3-y4) - (y1-y2)*(x3*y4-y3*x4))/d)
which we can see works with two tests:
>>> intersection(0, 0, 8, 8, 0, 8, 8, 0)
(4.0, 4.0)
>>> intersection(6, 0, 6, 8, 7, 0, 7, 8)
False
and you can visualise these with a matplotlib
plot:
We also need to be able to calculate the distance between two points. This can be done using Pythagoras's theorem for the length of the hypotenuse of a triangle:
a^2 + b^2 = c^2
As before, let's put this into a helper function:
def distance(x1, y1, x2, y2):
return ((x2-x1)**2 + (y2-y1)**2) ** 0.5
which we can test:
>>> distance(0, 0, 3, 4)
5.0
We can now create the main function which will implement the two functions defined above.
The steps for this are simply:
False
if there isn't one) between each sides and the lineFalse
- indicating that the line did not intersect the rectangleNow to implement this in Python:
#first 2 points describe the line, last 4 the rectangle
def length_inside(x1, y1, x2, y2, x3, y3, x4, y4, x5, y5, x6, y6):
intersects = [intersection(x1, y1, x2, y2, x3, y3, x4, y4),
intersection(x1, y1, x2, y2, x4, y4, x5, y5),
intersection(x1, y1, x2, y2, x5, y5, x6, y6),
intersection(x1, y1, x2, y2, x6, y6, x3, y3)]
intersects = [i for i in intersects if i]
if len(intersects) == 2:
return distance(*intersects[0], *intersects[1])
return False
And to test it:
>>> length_inside(2, 6, 5, 0, 1, 3, 2, 1, 6, 3, 5, 5)
2.23606797749979
>>> length_inside(0, 1, 0, 5, 1, 3, 2, 1, 6, 3, 5, 5)
False
which can be seen to be correct through the help of a plot:
Upvotes: 0
Reputation: 14858
From the picture
we have
P := (x2,y2) - (x1,y1) = (x2-x1, y2-y1)
Q := (x4,y4) - (x3,y3) = (x4-x3, y4-y3)
cos(α) := dotProduct(P,Q)/norm(P)norm(Q)
h := dist((x4,y4),(x5,y5))
where
dotProduct((px,py),(qx,qy)) := px*qx + py*qy
norm(a,b) := sqrt(squared(a)+squared(b))
dist(A,B) = norm(B-A)
Then d
can be calculated as
d := h/sin(α) = h/(sqrt(1-squared(cos(α)))
Upvotes: 3
Reputation: 52
I am only going over the math part, no coding
find the points of intersection (upperx,uppery) (lowerx,lowery)
find the distance from the upper point to the the rect right upper corner
find the distance from the lower point to the rect right upper corner
A = subtract the distance of one from the other
B = find the distance from the rect rect right upper corner and right lower corner
you now have A and B for the formula a^2 + b^2 = c^2
enjoy
Upvotes: 0