Point crossing path - algorithm

I have a problem in which i need to alert when a point (obviously in movement) had crossed a line-path,
a line path is complex collection of lines (y=ax+b).
Postulation -
I do not know from which side the "Point" will cross the line.
The line-path can be very complex and contain many lines.
For example -
What is the best approach to this problem?
Does anyone know if there is algorithm for that?

Finding line / path intercept
There is no simple solution if you consider that the line can come from any direction and that the line segments in the path can be any configuration. You will need to check each path segment against the line
The image below shows three lines AD, BD, and CD. Lines AD and CD intercept the path at 3 locations and BD at one.
For each line segment the point closest to the start of the line segment is the point you are looking for.
To find these points we must first define a point and line segment.
// point
p.x = ?
p.y = ?
// line segment contains start and end points p1, p2
l.p1 = {x, y}
l.p2 = {x, y}
Finding smallest unit dist on line segment
Compute the unit distance on the line segment that each path segment intercepts if any. The path segment that intercepts the line segment at the smallest unit distance >= 0 will be the first point of intercept. The one you are looking for.
Pseudo-code
The following shows the steps required. Checking each path segment against the line segment. First check if the path is not parallel to the line segment. If so check if the intercept is on the path segment. If so check if the intercept is on the line segment and is the closest intercept to the line start so far found.
// l is line segment
interceptFound = false // Semaphore to indicate point found
minUnitDist = 1 // unit dist of intercept
ix = 0 // intercept point
iy = 0
pathSeg // if needed the path segment that intercepted
vx1 = l.p2.x - l.p1.x // vector of line segment
vy1 = l.p2.y - l.p1.y
for each pathSegment // p path segment
vx2 = p.p2.x - p.p1.x // vector of path segment
vy2 = p.p2.y - p.p1.y
crossProd = vx1 * vy2 - vy1 * vx2
if crossProd != 0 // Check lines are not parallel
vx3 = l.p1.x - p.p1.x // Vector from start of l to start of p
vy3 = l.p1.y - p.p1.y
unit = (vx1 * vy3 - vy1 * vx3) / crossProd // unit dist on path segment
if unit >= 0 && unit <= 1 // Code Point A.
unit = (vx2 * vy3 - vy2 * vx3) / crossProd // unit dist on line segment
if unit >= 0 && unit <= minUnitDist // Is the intercept closest to start
interceptFound = true
minUnitDist = unit
pathSeg = p // store intercepting path segment
if interceptFound // Was a intercept found
ix = l.p1.x + vx1 * minUnitDist // Calculate intercept point
iy = l.p1.y + vy1 * minUnitDist
You can pre-compute the vector for each path segment vx2, vy2 in the above example to save a little time (if the path does not change over time)
You could also exit the loop early if the minUnitDist is zero
This is relatively quick and does not need complex data structures. Most path segments will be culled at // Code Point A
AABB check
Axis Aligned Bounding Box check
If the number of path segments are very high you can perform an AABB to avoid some of the math in the above example. This will only be a benefit if the path points do not change over time and that you calculate the bounding box once at the start for each path segment
Pseudo-code AABB check
// l is a line segment
l.left = min(l.p1.x, l.p2.x)
l.right = max(l.p1.x, l.p2.x)
l.top = min(l.p1.y, l.p2.y)
l.bottom = max(l.p1.y, l.p2.y)
Then to check if two segments may intercept
// l and p are line segs with bounding boxes calculated
if l.left > p.right || l.right < p.left || l.top > p.bottom || l.bottom < p.top
// line segments do not intercept
else
// Line segments may intercept
More bounding boxes and Quad trees
If you still find that the solution is too slow you can divide the path segments into related (close together connected) groups and do the AABB test against each group before checking the path segments in the group.
Further you could consider using a quad tree to store path segments to further reduce the number of path segments you need to test the line against.

To quickly check for a possible intersection, you can plug the two endpoints of every "line" in the equation of the point trajectory (presumably itself a line segment), of the form px+qy+r=0. There is a possible intersection if the signs of px+qy+r differ. This pre-screening will be fast.
To make it even faster, you can think of enclosing the lines in a hierarchy of bounding boxes. A quite simple way would be to group every bounding boxes of the lines two by two (in the traversal order), then group the groups to by to, and so on. Then by recursively comparing the bounding box of the trajectory to the bounding boxes in the hierarchy, you can obtain fast rejection of numerous lines.

Related

How to judge the contour is line or curve by opencv?

This image have two contours. I can find both with opencv findcontour function. What I want to know is how to judge which contour is line and which contour is curve ? Can anybody please tell how to do it?
Start by assuming you have a line, and apply some basic algebra.
First find the slope of the line and the y-intercept. The slope of a line is defined as the change in y divided by the change in x. So given two points (x0,y0), (x1,y1):
slope = (y0-y1) / (x0-x1)
The y-intercept is found using the slope-intercept equation (y=mx+b) and solving for b:
y = mx + b
b = y - mx
So
y_intercept = y0 - slope * x0
Once you have the slope and the y-intercept, you just need to loop through the points of your contour and see if all of the points fall on the same line. If they do, you have a line; if they don't, you have a curve.
Since I don't know what language you're working with, here is the whole process in pseudocode:
// First assume you have a line - find the slope and y-intercept
slope = (point[0].y - point[1].y) / (point[0].x - point[1].x);
y_intercept = point[0].y - (slope * point[0].x);
// Using slope-intercept (y = mx + b), see if the other points are on the same line
for (n = 0 to numPoints)
{
if ((slope * point[n].x + y_intercept) != point[n].y)
{
// You've found a point that's not on the line - as soon as you
// find a point that's not on the line, you know that the contour
// is not a straight line
}
}
Note that you'll be dealing with floating-point numbers here, so you'll have to take that into account in the if condition - you can't directly compare floating point numbers for equality, so you'll need to round them to some acceptable degree of accuracy. I left that out to keep the pseudocode simple.
Late answer but for the record:
Compare distance between first and last points against contour length.
If they are close then contour is line-like, otherwise it's a curve.

How to resample an edge of an image in MATLAB?

I was trying to decrease the number of points of a detected edge of an image but I didn't obtain a good result. I want the result to contain exactly 200 pixels of the edges, but those points must be well chosen so the shape remain very clear. How can I do this?
Here's an example image of what I'm working with:
Here are some results that I have received with the code I wrote:
Code written
function y = echantillonnage(x)
contour_image = x;
[lignes,colonnes]=size(x);
n = nombre/200;
contour_image_200px = contour_image;
ok=0;
for i=1:lignes
for j=1:colonnes
if (contour_image_200px(i,j)>0 )
ok=ok+1;
if ( mod(ok,round(n))>0 )
contour_image_200px(i,j)=0;
end
end
end
end
figure,imshow(contour_image_200px);
%résultat
y = contour_image_200px;
end
What you can do is use bwboundaries to trace the boundaries of the objects / edges then sample from those array of points to decrease the number of edge points. The tracing is done in clockwise order, so you are sure that when you subsample from this array, you will get a semblance of order. However, bwboundaries also returns both outer and inner contour boundaries, so you only need a certain amount of traces from the output. We will talk about that later.
bwboundaries works for multiple objects, so all you'd have to do is iterate through each object, sample the edge points and write that to an output result. Note that using bwboundaries doesn't even require edges to be found... as long as the object is clean then it isn't necessary. However, I'm not sure as to the purpose of what you're doing, so let's just operate on the edge detected result.
Let's say we had the following example:
>> A = false(256, 256);
>> A(100:170,100:170) = true;
>> A(3:40,3:40) = true;
>> A(190:220,200:230) = true;
>> imshow(A)
We get this image:
If we performed an edge detection:
>> B = edge(A, 'canny');
>> imshow(B);
We would get this:
Now, if you want to do the subsampling, you would call bwboundaries this way:
[bound,L,N] = bwboundaries(B);
bwboundaries returns a cell array bound of boundaries where each cell is a N x 2 array of spatial coordinates that define the boundary. The first column are the row locations and the second column are the column locations of the boundary points. L is a label matrix that tells you which point each boundary belongs to. We don't need this for your purposes but I might as well talk about it. N is the most important parameter. This defines how many object boundaries there are. This also tells you that the first N cells of bound tells you that those belong to the outer object boundaries.
As such, you can do the following to subsample your edge points and put them into a new matrix, assuming that your edge image is stored in B. Also, you stated that you want to have 200 points per edge. Let's define that parameter as num_edge_points. However, if you have edges that are less than this amount, then I will assume that you'll just want to have all of the edge points selected.
out = false(size(B)); %// Initialize output image
num_edge_points = 200; %// Define number of edge points
%// For each object boundary
for idx = 1 : N
boundary = bound{idx}; %// Get boundary
%// Determine how many points we have
num_pts = size(boundary,1);
%// Generate indices for sampling the boundary
%// If there are less than the minimum, just choose this amount
if num_pts < num_edge_points
ind = 1:num_pts;
else
ind = floor(linspace(1,num_pts,num_edge_points));
end
%// Subsample the edge points
pts = boundary(ind,:);
%// Mark points in output
out(sub2ind(size(B), pts(:,1), pts(:,2))) = true;
end
out will contain your edge image subsampled. To illustrate that we got this right, let's create a new RGB image where we have the edges and the subsampled edge points on top of each other where the subsampled edges are in red:
out_red = 255*uint8(B);
out_greenblue = out_red;
out_greenblue(out) = 0;
out_rgb = cat(3, out_red, out_greenblue, out_greenblue);
imshow(out_rgb);
This is what we get (zoomed-in):
As you can see, the top and bottom rectangles have the full edge points show as there were less than 200 edge points. However, the one in the middle is sparsely sampled as there are more than 200 edge points, but now we are only displaying 200 of them.
If you would like a function to help you facilitate this, you can use the following. I've essentially copied all of the code above and the input is a binary image with edges and the output is a binary image with the subsampled edges:
function [out] = subsample_edge(B)
%// Obtain boundaries for edge image
[bound,L,N] = bwboundaries(B);
out = false(size(B)); %// Initialize output image
num_edge_points = 200; %// Define number of edge points
%// For each object boundary
for idx = 1 : N
boundary = bound{idx}; %// Get boundary
%// Determine how many points we have
num_pts = size(boundary,1);
%// Generate indices for sampling the boundary
%// If there are less than the minimum, just choose this amount
if num_pts < num_edge_points
ind = 1:num_pts;
else
ind = floor(linspace(1,num_pts,num_edge_points));
end
%// Subsample the edge points
pts = boundary(ind,:);
%// Mark points in output
out(sub2ind(size(B), pts(:,1), pts(:,2))) = true;
end
end
If you want to call this function, simply do:
out = subsample_edge(B);

How to identify the boundary points of area overlapped between 2 rectangles in 2D?

I'm looking to return the coordinates of the points bounding the area of overlap between 2 arbitrary rectangles in 2D. Whats the best way to approach this that would take care of all the special cases eg:
Rectangles intersecting only on a single vertex ? (the program would have to return the lone vertex)
Rectangles which share whole or part of a side ? (the program would have to return the endpoints of the common line segment)
To add to the complexity, it has to order the points in either clockwise/anticlockwise order. As such, I can use a convex hull algorithm to order them before reporting, but if there's an algorithm that can figure out the bounding points in order directly, that'll be the best !!
Any ideas of what avenues I should be looking at ? I'm not looking for code projects etc, only a general idea of a generic algorithm for which I don't have to keep a lot of
if "special case" then "do this" kind of code.
EDIT: The rectangles are arbitrary - i.e. they may/may not be parallel to X/Y axis...
Just use the general convex polygon intersection method. Look up intersect convex polygons rotating calipers.
Alright, we'll try this again...
Consider a union of the two, made up of areas defined by drawing a line from every vertex in ABCD (in black) to EFGH (in red):
The hard part here is coming up with all of the shapes defined by these lines (both the gray lines and the original lines coming from the ABCD and EFGH rectangles.)
Once you figure that out, create a linked list of these shapes, and assume every one of these shapes exists within the intersection.
Step 1. Translate & rotate everything so that ABCD has one vertex on 0,0 and its lines are parallel/perpendicular to the x and y axes.
Step 1A. Find the lowest y-value vertex in ABCD, and then subtract it from all other verts in the scene. Let's assume for the purposes of demonstration that that vertex is C. By subtracting C from every vertex in the scene, we will effectively move the origin (0,0) to C, making rotation around C easy.
for all shapes in linked list {
for all vertices in shape {
vertex -= C;
}
}
Step 1B. Rotate everything about the origin by an angle equal to the angle between the C->B vector and the x-axis, so that B lands on the x-axis:
// see http://en.wikipedia.org/wiki/Atan2
float rotRadians = atan2(B.x, B.y); // assuming ABCD vertices are labelled clockwise
for all shapes in linked list {
for all vertices in shape {
rot(thisVert, rotRadians);
}
}
// ...
// function declaration
void rot(theVertex, theta) {
tempX = theVertex.x;
tempY = theVertex.y;
theVertex.x = cos(theta) * tempX + sin(theta) * tempY;
theVertex.y = cos(theta) * tempY - sin(theta) * tempX;
}
If ABCD vertices were labelled clockwise, the ABCD vertices should now look like this:
A = ABCDx , ABCDy
B = ABCDx , 0
C = 0 , 0
D = 0 , ABCDy
(If they were not labeled clockwise, then the "lies within" check in Step 2 won't work, so make sure the vert used in the atan2(...) call is the vertex counterclockwise from the lowest vertex.)
Step 2. Now we can easily analyze whether or not a shape lies within the ABCD rectangle, e.g. if (thisVert.x >= 0 && thisVert.y >= 0 && thisVert.x <= ABCDx && thisVert.y <= ABCDy). Traverse the linked list of shapes, and check to make sure each vertex of each shape lies within ABCD. If one vertex of a shape does not lie within ABCD, then that shape is not part of the ABCD/EFGH intersection. Mark it as not part of the intersection and skip to the next shape.
Step 3. Undo the rotation from Step 1B, then undo the translation from Step 1A.
Step 4. Repeat Steps 1-3 with EFGH instead of ABCD, and you will have your intersection set.
If the only intersection between the two sets is a line, then the above will return nothing as an intersection. If the intersection == NULL, then check for lines that intersect.
If the intersection is still NULL, then check for intersecting points.
This is probably really rough but:
object rectangle {
pos { x, y } // top-left position
size { height, width } // rectangle-size
}
collision::check (rectangle rect) {
// collision-detection logic
collision->order_coords(coords); // order-coords clockwise;
return collision_result_object; // return collided vertices, ordered clockwise, or 0 if rect hit nothing
}
collision::order_rects (rectangle *rect, opt angle) {
return clockwise_rects; // returns rectangles ordered clockwise
}
collision::order_coords (coordinate *coord, opt angle) {
return ordered_coords; // recieves coordinates and returns ordered clockwise
}
rectangle rects; // bunch of rectangles
ordered_rects = collision->order_rects (rects); // order rects starting at 12PM
loop {
foreach ordered_rects as i {
if (collision->check(i)) {
array_of_collision_result_objects[i] = collision->check(i); // start checking rects starting at 12PM, if collision found, return ordered vertexes
}
}
}
Find all the intersections of segments of rectangles. The result consists of some of them and some of initial vertices. To find them just check for every point it lies in both rectangles. Remove unnecessary points (if there are 3 or more on one line). The result is convex and no point you get is strictly inside it, so (if there are at least 3 of them) sort points from some inner point by angle and enjoy the result.
I've come up with a reasonable method that should cover all possible cases:
All we need is basically 3 steps :
Step 1:
for each side Si of R1
for each side Sj of R2
Check if Si and Sj intersect. If they do, push the point in results array
(This also has to take care of the case in case Si and Sj overlap, which is
basically checking if they have the same equation or not - if so, push in
the points of overlap. This also takes care of the case where a vertex of
R2 lies on Si).
next
next
Step 2:
for each vertex Vi of R1
Check if Vi lies inside R2, If so, push it in the results array.
next
Step 3:
for each vertex Vi of R2
Check if Vi lies inside R1, If so, push it in the results array.
next
Now, order the results array, and return
For step 2 & 3 (how to find if a point lies inside a rectangle) - I'd use this excellent article (the last algorithm stated there).

Calculating the shortest distance between two lines (line segments) in 3D

I have two line segments: X1,Y1,Z1 - X2,Y2,Z2 And X3,Y3,Z3 - X4,Y4,Z4
I am trying to find the shortest distance between the two segments.
I have been looking for a solution for hours, but all of them seem to work with lines rather than line segments.
Any ideas how to go about this, or any sources of furmulae?
I'll answer this in terms of matlab, but other programming environments can be used. I'll add that this solution is valid to solve the problem in any number of dimensions (>= 3).
Assume that we have two line segments in space, PQ and RS. Here are a few random sets of points.
> P = randn(1,3)
P =
-0.43256 -1.6656 0.12533
> Q = randn(1,3)
Q =
0.28768 -1.1465 1.1909
> R = randn(1,3)
R =
1.1892 -0.037633 0.32729
> S = randn(1,3)
S =
0.17464 -0.18671 0.72579
The infinite line PQ(t) is easily defined as
PQ(u) = P + u*(Q-P)
Likewise, we have
RS(v) = R + v*(S-R)
See that for each line, when the parameter is at 0 or 1, we get one of the original endpoints on the line returned. Thus, we know that PQ(0) == P, PQ(1) == Q, RS(0) == R, and RS(1) == S.
This way of defining a line parametrically is very useful in many contexts.
Next, imagine we were looking down along line PQ. Can we find the point of smallest distance from the line segment RS to the infinite line PQ? This is most easily done by a projection into the null space of line PQ.
> N = null(P-Q)
N =
-0.37428 -0.76828
0.9078 -0.18927
-0.18927 0.61149
Thus, null(P-Q) is a pair of basis vectors that span the two dimensional subspace orthogonal to the line PQ.
> r = (R-P)*N
r =
0.83265 -1.4306
> s = (S-P)*N
s =
1.0016 -0.37923
Essentially what we have done is to project the vector RS into the 2 dimensional subspace (plane) orthogonal to the line PQ. By subtracting off P (a point on line PQ) to get r and s, we ensure that the infinite line passes through the origin in this projection plane.
So really, we have reduced this to finding the minimum distance from the line rs(v) to the origin (0,0) in the projection plane. Recall that the line rs(v) is defined by the parameter v as:
rs(v) = r + v*(s-r)
The normal vector to the line rs(v) will give us what we need. Since we have reduced this to 2 dimensions because the original space was 3-d, we can do it simply. Otherwise, I'd just have used null again. This little trick works in 2-d:
> n = (s - r)*[0 -1;1 0];
> n = n/norm(n);
n is now a vector with unit length. The distance from the infinite line rs(v) to the origin is simple.
> d = dot(n,r)
d =
1.0491
See that I could also have used s, to get the same distance. The actual distance is abs(d), but as it turns out, d was positive here anyway.
> d = dot(n,s)
d =
1.0491
Can we determine v from this? Yes. Recall that the origin is a distance of d units from the line that connects points r and s. Therefore we can write dn = r + v(s-r), for some value of the scalar v. Form the dot product of each side of this equation with the vector (s-r), and solve for v.
> v = dot(s-r,d*n-r)/dot(s-r,s-r)
v =
1.2024
This tells us that the closest approach of the line segment rs to the origin happened outside the end points of the line segment. So really the closest point on rs to the origin was the point rs(1) = s.
Backing out from the projection, this tells us that the closest point on line segment RS to the infinite line PQ was the point S.
There is one more step in the analysis to take. What is the closest point on the line segment PQ? Does this point fall inside the line segment, or does it too fall outside the endpoints?
We project the point S onto the line PQ. (This expression for u is easily enough derived from similar logic as I did before. Note here that I've used \ to do the work.)
> u = (Q-P)'\((S - (S*N)*N') - P)'
u =
0.95903
See that u lies in the interval [0,1]. We have solved the problem. The point on line PQ is
> P + u*(Q-P)
ans =
0.25817 -1.1677 1.1473
And, the distance between closest points on the two line segments was
> norm(P + u*(Q-P) - S)
ans =
1.071
Of course, all of this can be compressed into just a few short lines of code. But it helps to expand it all out to gain understanding of how it works.
One basic approach is the same as computing the shortest distance between 2 lines, with one exception.
If you look at most algorithms for finding the shortest distance between 2 lines, you'll find that it finds the points on each line that are the closest, then computes the distance from them.
The trick to extend this to segments (or rays), is to see if that point is beyond one of the end points of the line, and if so, use the end point instead of the actual closest point on the infinite line.
For a concrete sample, see:
http://softsurfer.com/Archive/algorithm_0106/algorithm_0106.htm
More specifically:
http://softsurfer.com/Archive/algorithm_0106/algorithm_0106.htm#dist3D_Segment_to_Segment()
I would parameterize both line segments to use one parameter each, bound between 0 and 1, inclusive. Then find the difference between both line functions and use that as the objective function in a linear optimization problem with the parameters as variables.
So say you have a line from (0,0,0) to (1,0,0) and another from (0,1,0) to (0,0,0) (Yeah, I'm using easy ones). The lines can be parameterized like (1*t,0*t,0*t) where t lies in [0,1] and (0*s,1*s,0*s) where s lies in [0,1], independent of t.
Then you need to minimize ||(1*t,1*s,0)|| where t, s lie in [0,1]. That's a pretty simple problem to solve.
How about extending the line segments into infinite lines and find the shortest distance between the two lines. Then find the points on each line that are the end points of the shortest distance line segment.
If the point for each line is on the original line segment, then the you have the answer. If a point for each line is not on the original segment, then the point is one of the original line segments' end points.
Finding the distance between two finite lines based on finding between two infinite lines and then bound the infinite lines to the finite lines doesn't work always. for example try this points
Q=[5 2 0]
P=[2 2 0]
S=[3 3.25 0]
R=[0 3 0]
Based on infinite approach the algorithm select R and P for distance calculation (distance=2.2361), but somewhere in the middle of R and S has got a closer distance to the P point. Apparently, selecting P and [2 3.166] from R to S line has lower distance of 1.1666. Even this answer could get better by precise calculation and finding orthogonal line from P to R S line.
First, find the closest approach Line Segment bridging between their extended lines. Let's call this LineSeg BR.
If BR.endPt1 falls on LS1 and BR.endPt2 falls on LS2, you're done...just calculate the length of BR.
If the bridge BR intersects LS1 but not LS2, use the shorter of these two distances: smallerOf(dist(BR.endPt1, LS2.endPt1), dist(BR.endPt1, LS2.endPt2))
If the bridge BR intersects LS2 but not LS1, use the shorter of these two distances: smallerOf(dist(BR.endPt2, LS1.endPt1), dist(BR.endPt2, LS1.endPt2))
If none of these conditions hold, the closest distance is the closest pairing of endpoints on opposite Line Segs.
This question is the topic of the article On fast computation of distance between line segments by Vladimir J. Lumelksy 1985. It goes even further by finding not only the minimal Euclidean distance (MinD) but a point on each segment separated by that distance.
The general algorithm is as follows:
Compute the global MinD (global means the distance between two infinite lines containing the segments) and coordinates of both points (bases) of the line of minimum distances, see skew lines; if both bases lie inside the segments, then actual MinD is equal to the global MinD; otherwise, continue.
Compute distances between the endpoints of both segments (a total of four distances).
Compute coordinates of the base points of perpendiculars from the endpoints of one segment onto the other segment; compute the lengths of those perpendiculars whose base points lie inside the corresponding segments (up to four base point, and four distances).
Out of the remaining distances, the smallest is the sought actual MinD.
Altogether, this represents the computation of six points and of nine distances.
The article then describes and proves how to reduce the amount of tests based on the data received in initial steps of the algorithm and how to handle degenerate cases (e.g. equal endpoints of a segment).
C-language implementation by Eric Larsen can be found here, see SegPoints() function.

Algorithm to detect intersection of two rectangles?

I'm looking for an algorithm to detect if two rectangles intersect (one at an arbitrary angle, the other with only vertical/horizontal lines).
Testing if a corner of one is in the other ALMOST works. It fails if the rectangles form a cross-like shape.
It seems like a good idea to avoid using slopes of the lines, which would require special cases for vertical lines.
The standard method would be to do the separating axis test (do a google search on that).
In short:
Two objects don't intersect if you can find a line that separates the two objects. e.g. the objects / all points of an object are on different sides of the line.
The fun thing is, that it's sufficient to just check all edges of the two rectangles. If the rectangles don't overlap one of the edges will be the separating axis.
In 2D you can do this without using slopes. An edge is simply defined as the difference between two vertices, e.g.
edge = v(n) - v(n-1)
You can get a perpendicular to this by rotating it by 90°. In 2D this is easy as:
rotated.x = -unrotated.y
rotated.y = unrotated.x
So no trigonometry or slopes involved. Normalizing the vector to unit-length is not required either.
If you want to test if a point is on one or another side of the line you can just use the dot-product. the sign will tell you which side you're on:
// rotated: your rotated edge
// v(n-1) any point from the edge.
// testpoint: the point you want to find out which side it's on.
side = sign (rotated.x * (testpoint.x - v(n-1).x) +
rotated.y * (testpoint.y - v(n-1).y);
Now test all points of rectangle A against the edges of rectangle B and vice versa. If you find a separating edge the objects don't intersect (providing all other points in B are on the other side of the edge being tested for - see drawing below). If you find no separating edge either the rectangles are intersecting or one rectangle is contained in the other.
The test works with any convex polygons btw..
Amendment: To identify a separating edge, it is not enough to test all points of one rectangle against each edge of the other. The candidate-edge E (below) would as such be identified as a separating edge, as all points in A are in the same half-plane of E. However, it isn't a separating edge because the vertices Vb1 and Vb2 of B are also in that half-plane. It would only have been a separating edge if that had not been the case
http://www.iassess.com/collision.png
Basically look at the following picture:
If the two boxes collide, the lines A and B will overlap.
Note that this will have to be done on both the X and the Y axis, and both need to overlap for the rectangles to collide.
There is a good article in gamasutra.com which answers the question (the picture is from the article).
I did similar algorithm 5 years ago and I have to find my code snippet to post it here later
Amendment: The Separating Axis Theorem states that two convex shapes do not overlap if a separating axis exists (i.e. one where the projections as shown do not overlap). So "A separating axis exists" => "No overlap". This is not a bi-implication so you cannot conclude the converse.
In Cocoa you could easily detect whether the selectedArea rect intersects your rotated NSView's frame rect.
You don't even need to calculate polygons, normals an such. Just add these methods to your NSView subclass.
For instance, the user selects an area on the NSView's superview, then you call the method DoesThisRectSelectMe passing the selectedArea rect. The API convertRect: will do that job. The same trick works when you click on the NSView to select it. In that case simply override the hitTest method as below. The API convertPoint: will do that job ;-)
- (BOOL)DoesThisRectSelectMe:(NSRect)selectedArea
{
NSRect localArea = [self convertRect:selectedArea fromView:self.superview];
return NSIntersectsRect(localArea, self.bounds);
}
- (NSView *)hitTest:(NSPoint)aPoint
{
NSPoint localPoint = [self convertPoint:aPoint fromView:self.superview];
return NSPointInRect(localPoint, self.bounds) ? self : nil;
}
m_pGladiator's answer is right and I prefer to it.
Separating axis test is simplest and standard method to detect rectangle overlap. A line for which the projection intervals do not overlap we call a separating axis. Nils Pipenbrinck's solution is too general. It use dot product to check whether one shape is totally on the one side of the edge of the other. This solution is actually could induce to n-edge convex polygons. However, it is not optmized for two rectangles.
the critical point of m_pGladiator's answer is that we should check two rectangles' projection on both axises (x and y). If two projections are overlapped, then we could say these two rectangles are overlapped. So the comments above to m_pGladiator's answer are wrong.
for the simple situation, if two rectangles are not rotated,
we present a rectangle with structure:
struct Rect {
x, // the center in x axis
y, // the center in y axis
width,
height
}
we name rectangle A, B with rectA, rectB.
if Math.abs(rectA.x - rectB.x) < (Math.abs(rectA.width + rectB.width) / 2)
&& (Math.abs(rectA.y - rectB.y) < (Math.abs(rectA.height + rectB.height) / 2))
then
// A and B collide
end if
if any one of the two rectangles are rotated,
It may needs some efforts to determine the projection of them on x and y axises. Define struct RotatedRect as following:
struct RotatedRect : Rect {
double angle; // the rotating angle oriented to its center
}
the difference is how the width' is now a little different:
widthA' for rectA: Math.sqrt(rectA.width*rectA.width + rectA.height*rectA.height) * Math.cos(rectA.angle)
widthB' for rectB: Math.sqrt(rectB.width*rectB.width + rectB.height*rectB.height) * Math.cos(rectB.angle)
if Math.abs(rectA.x - rectB.x) < (Math.abs(widthA' + widthB') / 2)
&& (Math.abs(rectA.y - rectB.y) < (Math.abs(heightA' + heightB') / 2))
then
// A and B collide
end if
Could refer to a GDC(Game Development Conference 2007) PPT www.realtimecollisiondetection.net/pubs/GDC07_Ericson_Physics_Tutorial_SAT.ppt
The accepted answer about the separating axis test was very illuminating but I still felt it was not trivial to apply. I will share the pseudo-code I thought, "optimizing" first with the bounding circle test (see this other answer), in case it might help other people. I considered two rectangles A and B of the same size (but it is straightforward to consider the general situation).
1 Bounding circle test:
function isRectangleACollidingWithRectangleB:
if d > 2 * R:
return False
...
Computationally is much faster than the separating axis test. You only need to consider the separating axis test in the situation that both circles collide.
2 Separating axis test
The main idea is:
Consider one rectangle. Cycle along its vertices V(i).
Calculate the vector Si+1: V(i+1) - V(i).
Calculate the vector Ni using Si+1: Ni = (-Si+1.y, Si+1.x). This vector is the blue from the image. The sign of the dot product between the vectors from V(i) to the other vertices and Ni will define the separating axis (magenta dashed line).
Calculate the vector Si-1: V(i-1) - V(i). The sign of the dot product between Si-1 and Ni will define the location of the first rectangle with respect to the separating axis. In the example of the picture, they go in different directions, so the sign will be negative.
Cycle for all vertices j of the second square and calculate the vector Sij = V(j) - V(i).
If for any vertex V(j), the sign of the dot product of the vector Sij with Ni is the same as with the dot product of the vector Si-1 with Ni, this means both vertices V(i) and V(j) are on the same side of the magenta dashed line and, thus, vertex V(i) does not have a separating axis. So we can just skip vertex V(i) and repeat for the next vertex V(i+1). But first we update Si-1 = - Si+1. When we reach the last vertex (i = 4), if we have not found a separating axis, we repeat for the other rectangle. And if we still do not find a separating axis, this implies there is no separating axis and both rectangles collide.
If for a given vertex V(i) and all vertices V(j), the sign of the dot product of the vector Sij with Ni is different than with the vector Si-1 with Ni (as occurs in the image), this means we have found the separating axis and the rectangles do not collide.
In pseudo-code:
function isRectangleACollidingWithRectangleB:
...
#Consider first rectangle A:
Si-1 = Vertex_A[4] - Vertex_A[1]
for i in Vertex_A:
Si+1 = Vertex_A[i+1] - Vertex_A[i]
Ni = [- Si+1.y, Si+1.x ]
sgn_i = sign( dot_product(Si-1, Ni) ) #sgn_i is the sign of rectangle A with respect the separating axis
for j in Vertex_B:
sij = Vertex_B[j] - Vertex_A[i]
sgn_j = sign( dot_product(sij, Ni) ) #sgnj is the sign of vertex j of square B with respect the separating axis
if sgn_i * sgn_j > 0: #i.e., we have the same sign
break #Vertex i does not define separating axis
else:
if j == 4: #we have reached the last vertex so vertex i defines the separating axis
return False
Si-1 = - Si+1
#Repeat for rectangle B
...
#If we do not find any separating axis
return True
You can find the code in Python here.
Note:
In this other answer they also suggest for optimization to try before the separating axis test whether the vertices of one rectangle are inside the other as a sufficient condition for colliding. However, in my trials I found this intermediate step to actually be less efficient.
Check to see if any of the lines from one rectangle intersect any of the lines from the other. Naive line segment intersection is easy to code up.
If you need more speed, there are advanced algorithms for line segment intersection (sweep-line). See http://en.wikipedia.org/wiki/Line_segment_intersection
One solution is to use something called a No Fit Polygon. This polygon is calculated from the two polygons (conceptually by sliding one around the other) and it defines the area for which the polygons overlap given their relative offset. Once you have this NFP then you simply have to do an inclusion test with a point given by the relative offset of the two polygons. This inclusion test is quick and easy but you do have to create the NFP first.
Have a search for No Fit Polygon on the web and see if you can find an algorithm for convex polygons (it gets MUCH more complex if you have concave polygons). If you can't find anything then email me at howard dot J dot may gmail dot com
Here is what I think will take care of all possible cases.
Do the following tests.
Check any of the vertices of rectangle 1 reside inside rectangle 2 and vice versa. Anytime you find a vertex that resides inside the other rectangle you can conclude that they intersect and stop the search. THis will take care of one rectangle residing completely inside the other.
If the above test is inconclusive find the intersecting points of each line of 1 rectangle with each line of the other rectangle. Once a point of intersection is found check if it resides between inside the imaginary rectangle created by the corresponding 4 points. When ever such a point is found conclude that they intersect and stop the search.
If the above 2 tests return false then these 2 rectangles do not overlap.
If you're using Java, all implementations of the Shape interface have an intersects method that take a rectangle.
Well, the brute force method is to walk the edges of the horizontal rectangle and check each point along the edge to see if it falls on or in the other rectangle.
The mathematical answer is to form equations describing each edge of both rectangles. Now you can simply find if any of the four lines from rectangle A intersect any of the lines of rectangle B, which should be a simple (fast) linear equation solver.
-Adam
You could find the intersection of each side of the angled rectangle with each side of the axis-aligned one. Do this by finding the equation of the infinite line on which each side lies (i.e. v1 + t(v2-v1) and v'1 + t'(v'2-v'1) basically), finding the point at which the lines meet by solving for t when those two equations are equal (if they're parallel, you can test for that) and then testing whether that point lies on the line segment between the two vertices, i.e. is it true that 0 <= t <= 1 and 0 <= t' <= 1.
However, this doesn't cover the case when one rectangle completely covers the other. That you can cover by testing whether all four points of either rectangle lie inside the other rectangle.
This is what I would do, for the 3D version of this problem:
Model the 2 rectangles as planes described by equation P1 and P2, then write P1=P2 and derive from that the line of intersection equation, which won't exist if the planes are parallel (no intersection), or are in the same plane, in which case you get 0=0. In that case you will need to employ a 2D rectangle intersection algorithm.
Then I would see if that line, which is in the plane of both rectangles, passes through both rectangles. If it does, then you have an intersection of 2 rectangles, otherwise you don't (or shouldn't, I might have missed a corner case in my head).
To find if a line passes through a rectangle in the same plane, I would find the 2 points of intersection of the line and the sides of the rectangle (modelling them using line equations), and then make sure the points of intersections are with in range.
That is the mathematical descriptions, unfortunately I have no code to do the above.
Another way to do the test which is slightly faster than using the separating axis test, is to use the winding numbers algorithm (on quadrants only - not angle-summation which is horrifically slow) on each vertex of either rectangle (arbitrarily chosen). If any of the vertices have a non-zero winding number, the two rectangles overlap.
This algorithm is somewhat more long-winded than the separating axis test, but is faster because it only require a half-plane test if edges are crossing two quadrants (as opposed to up to 32 tests using the separating axis method)
The algorithm has the further advantage that it can be used to test overlap of any polygon (convex or concave). As far as I know, the algorithm only works in 2D space.
Either I am missing something else why make this so complicated?
if (x1,y1) and (X1,Y1) are corners of the rectangles, then to find intersection do:
xIntersect = false;
yIntersect = false;
if (!(Math.min(x1, x2, x3, x4) > Math.max(X1, X2, X3, X4) || Math.max(x1, x2, x3, x4) < Math.min(X1, X2, X3, X4))) xIntersect = true;
if (!(Math.min(y1, y2, y3, y4) > Math.max(Y1, Y2, Y3, Y4) || Math.max(y1, y2, y3, y4) < Math.min(Y1, Y2, Y3, Y4))) yIntersect = true;
if (xIntersect && yIntersect) {alert("Intersect");}
I implemented it like this:
bool rectCollision(const CGRect &boundsA, const Matrix3x3 &mB, const CGRect &boundsB)
{
float Axmin = boundsA.origin.x;
float Axmax = Axmin + boundsA.size.width;
float Aymin = boundsA.origin.y;
float Aymax = Aymin + boundsA.size.height;
float Bxmin = boundsB.origin.x;
float Bxmax = Bxmin + boundsB.size.width;
float Bymin = boundsB.origin.y;
float Bymax = Bymin + boundsB.size.height;
// find location of B corners in A space
float B0x = mB(0,0) * Bxmin + mB(0,1) * Bymin + mB(0,2);
float B0y = mB(1,0) * Bxmin + mB(1,1) * Bymin + mB(1,2);
float B1x = mB(0,0) * Bxmax + mB(0,1) * Bymin + mB(0,2);
float B1y = mB(1,0) * Bxmax + mB(1,1) * Bymin + mB(1,2);
float B2x = mB(0,0) * Bxmin + mB(0,1) * Bymax + mB(0,2);
float B2y = mB(1,0) * Bxmin + mB(1,1) * Bymax + mB(1,2);
float B3x = mB(0,0) * Bxmax + mB(0,1) * Bymax + mB(0,2);
float B3y = mB(1,0) * Bxmax + mB(1,1) * Bymax + mB(1,2);
if(B0x<Axmin && B1x<Axmin && B2x<Axmin && B3x<Axmin)
return false;
if(B0x>Axmax && B1x>Axmax && B2x>Axmax && B3x>Axmax)
return false;
if(B0y<Aymin && B1y<Aymin && B2y<Aymin && B3y<Aymin)
return false;
if(B0y>Aymax && B1y>Aymax && B2y>Aymax && B3y>Aymax)
return false;
float det = mB(0,0)*mB(1,1) - mB(0,1)*mB(1,0);
float dx = mB(1,2)*mB(0,1) - mB(0,2)*mB(1,1);
float dy = mB(0,2)*mB(1,0) - mB(1,2)*mB(0,0);
// find location of A corners in B space
float A0x = (mB(1,1) * Axmin - mB(0,1) * Aymin + dx)/det;
float A0y = (-mB(1,0) * Axmin + mB(0,0) * Aymin + dy)/det;
float A1x = (mB(1,1) * Axmax - mB(0,1) * Aymin + dx)/det;
float A1y = (-mB(1,0) * Axmax + mB(0,0) * Aymin + dy)/det;
float A2x = (mB(1,1) * Axmin - mB(0,1) * Aymax + dx)/det;
float A2y = (-mB(1,0) * Axmin + mB(0,0) * Aymax + dy)/det;
float A3x = (mB(1,1) * Axmax - mB(0,1) * Aymax + dx)/det;
float A3y = (-mB(1,0) * Axmax + mB(0,0) * Aymax + dy)/det;
if(A0x<Bxmin && A1x<Bxmin && A2x<Bxmin && A3x<Bxmin)
return false;
if(A0x>Bxmax && A1x>Bxmax && A2x>Bxmax && A3x>Bxmax)
return false;
if(A0y<Bymin && A1y<Bymin && A2y<Bymin && A3y<Bymin)
return false;
if(A0y>Bymax && A1y>Bymax && A2y>Bymax && A3y>Bymax)
return false;
return true;
}
The matrix mB is any affine transform matrix that converts points in the B space to points in the A space. This includes simple rotation and translation, rotation plus scaling, and full affine warps, but not perspective warps.
It may not be as optimal as possible. Speed was not a huge concern. However it seems to work ok for me.
Here is a matlab implementation of the accepted answer:
function olap_flag = ol(A,B,sub)
%A and B should be 4 x 2 matrices containing the xy coordinates of the corners in clockwise order
if nargin == 2
olap_flag = ol(A,B,1) && ol(B,A,1);
return;
end
urdl = diff(A([1:4 1],:));
s = sum(urdl .* A, 2);
sdiff = B * urdl' - repmat(s,[1 4]);
olap_flag = ~any(max(sdiff)<0);
This is the conventional method, go line by line and check whether the lines are intersecting. This is the code in MATLAB.
C1 = [0, 0]; % Centre of rectangle 1 (x,y)
C2 = [1, 1]; % Centre of rectangle 2 (x,y)
W1 = 5; W2 = 3; % Widths of rectangles 1 and 2
H1 = 2; H2 = 3; % Heights of rectangles 1 and 2
% Define the corner points of the rectangles using the above
R1 = [C1(1) + [W1; W1; -W1; -W1]/2, C1(2) + [H1; -H1; -H1; H1]/2];
R2 = [C2(1) + [W2; W2; -W2; -W2]/2, C2(2) + [H2; -H2; -H2; H2]/2];
R1 = [R1 ; R1(1,:)] ;
R2 = [R2 ; R2(1,:)] ;
plot(R1(:,1),R1(:,2),'r')
hold on
plot(R2(:,1),R2(:,2),'b')
%% lines of Rectangles
L1 = [R1(1:end-1,:) R1(2:end,:)] ;
L2 = [R2(1:end-1,:) R2(2:end,:)] ;
%% GEt intersection points
P = zeros(2,[]) ;
count = 0 ;
for i = 1:4
line1 = reshape(L1(i,:),2,2) ;
for j = 1:4
line2 = reshape(L2(j,:),2,2) ;
point = InterX(line1,line2) ;
if ~isempty(point)
count = count+1 ;
P(:,count) = point ;
end
end
end
%%
if ~isempty(P)
fprintf('Given rectangles intersect at %d points:\n',size(P,2))
plot(P(1,:),P(2,:),'*k')
end
the function InterX can be downloaded from: https://in.mathworks.com/matlabcentral/fileexchange/22441-curve-intersections?focused=5165138&tab=function
I have a simplier method of my own, if we have 2 rectangles:
R1 = (min_x1, max_x1, min_y1, max_y1)
R2 = (min_x2, max_x2, min_y2, max_y2)
They overlap if and only if:
Overlap = (max_x1 > min_x2) and (max_x2 > min_x1) and (max_y1 > min_y2) and (max_y2 > min_y1)
You can do it for 3D boxes too, actually it works for any number of dimensions.
Enough has been said in other answers, so I'll just add pseudocode one-liner:
!(a.left > b.right || b.left > a.right || a.top > b.bottom || b.top > a.bottom);
Check if the center of mass of all the vertices of both rectangles lies within one of the rectangles.

Resources