Efficient way to combine intersecting bounding rectangles - image

I'm trying to simplify the following image using OpenCV:
What we have here are lots of red shapes. Some of them completely contain others. Some of them intersect their neighbors. My goal is to unify all intersecting shapes by replacing any two intersecting shapes with the bounding box of their union's polygon. (repeating until there are no more intersecting shapes).
By intersecting I mean also touching. Hope this makes it 100% clear:
I'm trying to do this efficiently using standard morphology operations; obviously it can be done naively in O(N^2), but that'll be too slow. Dilation doesn't help because some shapes are only 1px apart and I don't want them merged if they're not intersecting.

UPDATE: I misunderstood the question earlier. We don't want to remove rectangles which are completely inside others. We only want to replace the intersecting rectangles. Therefore for the first case, we have to do nothing.
New api (2.4.9) supports & and | operators.
From opencv doc:
rect = rect1 & rect2 (rectangle intersection)
rect = rect1 | rect2 (minimum area rectangle containing rect2 and rect3 )
It also supports equality comparision (==)
rect == rect1
So it is now very easy to accomplish the task. For every pair of rectangle rect1 and rect2,
if((rect1 & rect2) == rect1) ... // rect1 is completely inside rect2; do nothing.
else if((rect1 & rect2).area() > 0) // they intersect; merge them.
newrect = rect1 | rect2;
... // remove rect1 and rect2 from list and insert newrect.
UPDATE 2: (for translating in java)
I know java very little. I also never used the java API. I am giving here some psudo-code (which I think can be easily translated)
For & operator, we need a method which finds the intersect of two rectangles.
Method: Intersect (Rect A, Rect B)
left = max(A.x, B.x)
top = max(A.y, B.y)
right = min(A.x + A.width, B.x + B.width)
bottom = min(A.y + A.height, B.y + B.height)
if(left <= right && top <= bottom) return Rect(left, top, right - left, bottom - top)
else return Rect()
For | operator, we need a similar method
Method: Merge (Rect A, Rect B)
left = min(A.x, B.x)
top = min(A.y, B.y)
right = max(A.x + A.width, B.x + B.width)
bottom = max(A.y + A.height, B.y + B.height)
return Rect(left, top, right - left, bottom - top)
For == operator, we can use overloaded equals method.

To accomplish what you want we'll be using findContours. The key point here is to understand how it works when mode is set to CV_RETR_TREE. In this case, hierarchy is constructed in a way that every even depth level contains external contours, while odd depth levels contain internal contours. What we need here is to traverse the hierarchy tree printing the contours associated with even depth levels.
First we find the contours of an image called original
typedef std::vector<std::vector<cv::Point> > Contours;
typedef std::vector<cv::Vec4i> Hierarchy;
Contours contours;
Hierarchy hierarchy;
cv::findContours(original, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_NONE);
To print the external contours on an image called processed we need a recursive function.
void printExternalContours(cv::Mat img, Contours const& contours, Hierarchy const& hierarchy, int const idx)
{
//for every contour of the same hierarchy level
for(int i = idx; i >= 0; i = hierarchy[i][0])
{
//print it
cv::drawContours(img, contours, i, cv::Scalar(255));
//for every of its internal contours
for(int j = hierarchy[i][2]; j >= 0; j = hierarchy[j][0])
{
//recursively print the external contours of its children
printExternalContours(img, contours, hierarchy, hierarchy[j][2]);
}
}
}
printExternalContours(processed, contours, hierarchy, 0);
The result is shown bellow, where original and processed are displayed side by side.
If you absolutely need rectangular shapes, you just need to use boundingRect to get the minimum enclosing rectangle given a set of points (every single contour in this case) and use rectangle for the drawing. In other words, substitute
cv::drawContours(img, contours, i, cv::Scalar(255));
by
cv::rectangle(img, cv::boundingRect(contours[i]), cv::Scalar(255));
findContours expects a single 8-bit image, sou you could either make a gray image from your originals and then threshold it to get a perfect black background or, perhaps it would suffice to use the red channel in your case, just make sure the background is perfectly black.
Regarding the complexity of findContours, I can't attest it is any better than O(N^2), nor haven't I found any input on that after a quick google search, but I trust OpenCV implements the best known algorithm.

Given two bounding box contours in the form of (x,y,w,h), here's a function to create a single bounding box (assuming the boxes are touching or within each other). Returns (x,y,w,h) of the combined bounding box i.e, top-left x, top-left y, width, and height. Here's an illustration
(x1,y1) w1 (x3,y3) w3
._____________________. .____________________________.
| | | |
| | h1 | |
| (x2,y2) | | |
| ._______________|_______. --> | |
| | | | | | h3
._____|_______________. | | |
| | h2 | |
| | | |
| w2 | | |
._______________________. .____________________________.
Code
def combineBoundingBox(box1, box2):
x = min(box1[0], box2[0])
y = min(box1[1], box2[1])
w = box2[0] + box2[2] - box1[0]
h = max(box1[1] + box1[3], box2[1] + box2[3]) - y
return (x, y, w, h)
Example
With these two bounding boxes,
>>> print(box1)
>>> print(box2)
(132, 85, 190, 231)
(264, 80, 121, 230)
>>> new = combineBoundingBox(box1, box2)
>>> print(new)
(132, 80, 253, 236)
Here's the visual result: Before -> After

Related

Ordering coordinates from top left to bottom right

How can I go about trying to order the points of an irregular array from top left to bottom right, such as in the image below?
Methods I've considered are:
calculate the distance of each point from the top left of the image (Pythagoras's theorem) but apply some kind of weighting to the Y coordinate in an attempt to prioritise points on the same 'row' e.g. distance = SQRT((x * x) + (weighting * (y * y)))
sort the points into logical rows, then sort each row.
Part of the difficulty is that I do not know how many rows and columns will be present in the image coupled with the irregularity of the array of points. Any advice would be greatly appreciated.
Even though the question is a bit older, I recently had a similar problem when calibrating a camera.
The algorithm is quite simple and based on this paper:
Find the top left point: min(x+y)
Find the top right point: max(x-y)
Create a straight line from the points.
Calculate the distance of all points to the line
If it is smaller than the radius of the circle (or a threshold): point is in the top line.
Otherwise: point is in the rest of the block.
Sort points of the top line by x value and save.
Repeat until there are no points left.
My python implementation looks like this:
#detect the keypoints
detector = cv2.SimpleBlobDetector_create(params)
keypoints = detector.detect(img)
img_with_keypoints = cv2.drawKeypoints(img, keypoints, np.array([]), (0, 0, 255),
cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
points = []
keypoints_to_search = keypoints[:]
while len(keypoints_to_search) > 0:
a = sorted(keypoints_to_search, key=lambda p: (p.pt[0]) + (p.pt[1]))[0] # find upper left point
b = sorted(keypoints_to_search, key=lambda p: (p.pt[0]) - (p.pt[1]))[-1] # find upper right point
cv2.line(img_with_keypoints, (int(a.pt[0]), int(a.pt[1])), (int(b.pt[0]), int(b.pt[1])), (255, 0, 0), 1)
# convert opencv keypoint to numpy 3d point
a = np.array([a.pt[0], a.pt[1], 0])
b = np.array([b.pt[0], b.pt[1], 0])
row_points = []
remaining_points = []
for k in keypoints_to_search:
p = np.array([k.pt[0], k.pt[1], 0])
d = k.size # diameter of the keypoint (might be a theshold)
dist = np.linalg.norm(np.cross(np.subtract(p, a), np.subtract(b, a))) / np.linalg.norm(b) # distance between keypoint and line a->b
if d/2 > dist:
row_points.append(k)
else:
remaining_points.append(k)
points.extend(sorted(row_points, key=lambda h: h.pt[0]))
keypoints_to_search = remaining_points
Jumping on this old thread because I just dealt with the same thing: sorting a sloppily aligned grid of placed objects by left-to-right, top to bottom location. The drawing at the top in the original post sums it up perfectly, except that this solution supports rows with varying numbers of nodes.
S. Vogt's script above was super helpful (and the script below is entirely based on his/hers), but my conditions are narrower. Vogt's solution accommodates a grid that may be tilted from the horizontal axis. I assume no tilting, so I don't need to compare distances from a potentially tilted top line, but rather from a single point's y value.
Javascript below:
interface Node {x: number; y: number; width:number; height:number;}
const sortedNodes = (nodeArray:Node[]) => {
let sortedNodes:Node[] = []; // this is the return value
let availableNodes = [...nodeArray]; // make copy of input array
while(availableNodes.length > 0){
// find y value of topmost node in availableNodes. (Change this to a reduce if you want.)
let minY = Number.MAX_SAFE_INTEGER;
for (const node of availableNodes){
minY = Math.min(minY, node.y)
}
// find nodes in top row: assume a node is in the top row when its distance from minY
// is less than its height
const topRow:Node[] = [];
const otherRows:Node[] = [];
for (const node of availableNodes){
if (Math.abs(minY - node.y) <= node.height){
topRow.push(node);
} else {
otherRows.push(node);
}
}
topRow.sort((a,b) => a.x - b.x); // we have the top row: sort it by x
sortedNodes = [...sortedNodes,...topRow] // append nodes in row to sorted nodes
availableNodes = [...otherRows] // update available nodes to exclude handled rows
}
return sortedNodes;
};
The above assumes that all node heights are the same. If you have some nodes that are much taller than others, get the value of the minimum node height of all nodes and use it instead of the iterated "node.height" value. I.e., you would change this line of the script above to use the minimum height of all nodes rather that the iterated one.
if (Math.abs(minY - node.y) <= node.height)
I propose the following idea:
1. count the points (p)
2. for each point, round it's x and y coordinates down to some number, like
x = int(x/n)*n, y = int(y/m)*m for some n,m
3. If m,n are too big, the number of counts will drop. Determine m, n iteratively so that the number of points p will just be preserved.
Starting values could be in alignment with max(x) - min(x). For searching employ a binary search. X and Y scaling would be independent of each other.
In natural words this would pin the individual points to grid points by stretching or shrinking the grid distances, until all points have at most one common coordinate (X or Y) but no 2 points overlap. You could call that classifying as well.

Calculate area given list of directions

Let's say you're given a list of directions:
up, up, right, down, right, down, left, left
If you follow the directions, you will always return to the starting location. Calculate the area of the shape that you just created.
The shape formed by the directions above would look something like:
___
| |___
|_______|
Clearly, from the picture, you can see that the area is 3.
I tried to use a 2d matrix to trace the directions, but unsure how to get the area from that...
For example, in my 2d array:
O O
O O O
O O O
This is probably not a good way of handling this, any ideas?
Since the polygon you create has axis-aligned edges only, you can calculate the total area from vertical slabs.
Let's say we are given a list of vertices V. I assume we have wrapping in this list, so we can query V.next(v) for every vertex v in V. For the last one, the result is the first.
First, try to find the leftmost and rightmost point, and the vertex where the leftmost point is reached (in linear time).
x = 0 // current x-position
xMin = inf, xMax = -inf // leftmost and rightmost point
leftVertex = null // leftmost vertex
foreach v in V
x = x + (v is left ? -1 : v is right ? 1 : 0)
xMax = max(x, xMax)
if x < xMin
xMin = x
leftVertex = V.next(v)
Now we create a simple data structure: for every vertical slab we keep a max heap (a sorted list is fine as well, but we only need to repetitively fetch the maximum element in the end).
width = xMax - xMin
heaps = new MaxHeap[width]
We start tracing the shape from vertex leftVertex now (the leftmost vertex we found in the first step). We now choose that this vertex has x/y-position (0, 0), just because it is convenient.
x = 0, y = 0
v = leftVertex
do
if v is left
x = x-1 // use left endpoint for index
heaps[x].Add(y) // first dec, then store
if v is right
heaps[x].Add(y) // use left endpoint for index
x = x+1 // first store, then inc
if v is up
y = y+1
if v is down
y = y-1
v = V.next(v)
until v = leftVertex
You can build this structure in O(n log n) time, because adding to a heap costs logarithmic time.
Finally, we need to compute the area from the heap. For a well-formed input, we need to get two contiguous y-values from the heap and subtract them.
area = 0
foreach heap in heaps
while heap not empty
area += heap.PopMax() - heap.PopMax() // each polygon's area
return area
Again, this takes O(n log n) time.
I ported the algorithm to a java implementation (see Ideone). Two sample runs:
public static void main (String[] args) {
// _
// | |_
// |_ _ |
Direction[] input = { Direction.Up, Direction.Up,
Direction.Right, Direction.Down,
Direction.Right, Direction.Down,
Direction.Left, Direction.Left };
System.out.println(computeArea(input));
// _
// |_|_
// |_|
Direction[] input2 = { Direction.Up, Direction.Right,
Direction.Down, Direction.Down,
Direction.Right, Direction.Up,
Direction.Left, Direction.Left };
System.out.println(computeArea(input2));
}
Returns (as expected):
3
2
Assuming some starting point (say, (0,0)) and the y direction is positive upwards:
left adds (-1,0) to the last point.
right adds (+1,0) to the last point.
up adds (0,+1) to the last point.
down adds (0,-1) to the last point.
A sequence of directions would then produce a list of (x,y) vertex co-ordinates from which the area of the resulting (implied closed) polygon can be found from How do I calculate the surface area of a 2d polygon?
EDIT
Here's an implementation and test in Python. The first two functions are from the answer linked above:
def segments(p):
return zip(p, p[1:] + [p[0]])
def area(p):
return 0.5 * abs(sum(x0*y1 - x1*y0
for ((x0, y0), (x1, y1)) in segments(p)))
def mkvertices(pth):
vert = [(0,0)]
for (dx,dy) in pth:
vert.append((vert[-1][0]+dx,vert[-1][1]+dy))
return vert
left = (-1,0)
right = (+1,0)
up = (0,+1)
down = (0,-1)
# _
# | |_
# |__|
print (area(mkvertices([up, up, right, down, right, down, left, left])))
# _
# |_|_
# |_|
print (area(mkvertices([up, right, down, down, right, up, left, left])))
Output:
3.0
0.0
Note that this approach fails for polygons that contain intersecting lines as in the second example.
This can be implemented in place using Shoelace formula for simple polygons.
For each segment (a, b) we have to calculate (b.x - a.x)*(a.y + b.y)/2. The sum over all segments is the signed area of a polygon.
What's more, here we're dealing only with axis aligned segments of length 1. Vertical segments can be ignored because b.x - a.x = 0.
Horizontal segments have a.y + b.y / 2 = a.y = b.y and b.x - a.x = +-1.
So in the end we only have to keep track of y and the area added is always +-y
Here is a sample C++ code:
#include <iostream>
#include <vector>
enum struct Direction
{
Up, Down, Left, Right
};
int area(const std::vector<Direction>& moves)
{
int area = 0;
int y = 0;
for (auto move : moves)
{
switch(move)
{
case Direction::Left:
area += y;
break;
case Direction::Right:
area -= y;
break;
case Direction::Up:
y -= 1;
break;
case Direction::Down:
y += 1;
break;
}
}
return area < 0 ? -area : area;
}
int main()
{
std::vector<Direction> moves{{
Direction::Up,
Direction::Up,
Direction::Right,
Direction::Down,
Direction::Right,
Direction::Down,
Direction::Left,
Direction::Left
}};
std::cout << area(moves);
return 0;
}
I assume there should be some restrictions on the shapes you are drawing (Axis aligned, polygonal graph, closed, non intersecting lines) to be able to calculate the area.
Represent the the shape using segments, each segments consists of two points, each has two coordinates: x and y.
Taking these assumptions into consideration, we can say that any horizontal segment has one parallel segment that has the same x dimensions for its two points but different y dimensions.
The surface area between these two segments equal the hight difference between them.Summing the area for all the horizontal segments gives you the total surface area of the shape.

how to simulate a rectangle union starting with a rectangle intersection

Given rectangle_A intersecting rectangle_B, which has a union defined such that it is the rectangle containing both rectangles, I want to determine the coordinates of the (not overlapping) rectangles required to add to rectangle_A to create the union of rectangle_A and rectangle_B:
Note: this is just one configuration of the solution set of rectangles. the white rectangles above could be configured differently, as long as they don't overlap.
Is there a simple algorithm for every case of rectangle intersection? I've done a first pass and I miss some corners. Evidently not my forté.
Why? When panning in a UI, I only want to (i) update the new parts of the canvas (ii) keep track of what has been painted as a rectangle (the union of rectangle_A and rectangle_B).
If you are not concerned with minimizing the number of rectangles returned, you can simplify the thought process to one that always returns no more than 8 rectangles:
U
+----------+----+-------+
| | | |
| 1 | 2 | 3 |
+----------+----+-------+
| | | |
| 4 | A | 5 |
| | | |
+----------+----+-------+
| 6 | 7 | 8 |
+----------+----+-------+
U.x1 = min(A.x1,B.x1)
U.x2 = max(A.x2,B.x2)
U.y1 = min(A.y1,B.y1)
U.y2 = max(A.y2,B.y2)
R1.x1 = R4.x1 = R6.x1 = U.x1
R2.x1 = R7.x1 = R1.x2 = R4.x2 = R6.x2 = A.x1
R2.x2 = R7.x2 = R3.x1 = R5.x1 = R8.x1 = A.x2
R3.x2 = R5.x2 = R8.x2 = U.x2
R1.y1 = R2.y1 = R3.y1 = U.y1
R1.y2 = R2.y2 = R3.y2 = R4.y1 = R5.y1 = A.y1
R4.y2 = R5.y2 = R6.y1 = R7.y1 = R8.y1 = A.y2
R6.y2 = R7.y2 = R8.y2 = U.y2
If you wanted, you could then quickly check each rectangle to see if r.x1 == r.x2 || r.y1 == r.y2 (i.e. if it has zero area), and throw it out if so. In most cases, over half of the rectangles can be thrown out this way.
For example, in your three examples, this solution would return 3, 1, and 5 rectangles, and would return 0 in the best case (when B is contained in A) and 8 in the worst case (when A is contained in B).
Say we represent rectangles by a pair of x,y coordinate pairs: x1,y1 for the top-left and x2,y2 for the bottom left corner. Let's also assume y coordinate increase downwards and x coordinates increase left to right.
Now, suppose the rectangle formed by the union of A and B (according to your definition of union) is the rectangle is U.
So,
U.x1=min(A.x1,B.x1), U.y1=min(A.y1,B.y2) --- top-left corner, take the lowest values
U.x2=max(A.x2,B.x2), U.y2=max(A.y2,B.y2) --- bottom-right corner, take the highest values
Now that we have the larger rectangle U, we can use that to compute the smaller right and bottom rectangles that have to be added to A (the left/top rectangle) to make it U. Lets call them Rt and Bot.
(This time I'm assuming A is the top-left rectangle, if it isn't swap A and B. Also assuming the layout to be similar to that of your picture. If that isn't the case you can adapt this easily).
Rt.x1=A.x2, Rt.y1=A.y1
Rt.x2=A.x2, Rt.y2=B.y2
Bot.x1=A.x1, Bot.y1=A.y2
Bot.x2=A.x2, Bot.y2=B.y2
I'm sorry i cant give a working solution, but...
At first I would try to draw such nice images for every different case that you can imagine. There will be a lot cases, where you need more than 2 rectangles, or just one, right?
I think getting the rect containing the others is trivial-but at this time I can't think of how to proceed. :)
Edit: At this time i'm thinking of a flood fill algorith, just fill up your larger rect. But there are 2 problems with this I can imagine: How to use the flood fill output to generate rects from it? Will it be the right way, or is there a linear algebra solution or something?

An algorithm for inflating/deflating (offsetting, buffering) polygons

How would I "inflate" a polygon? That is, I want to do something similar to this:
The requirement is that the new (inflated) polygon's edges/points are all at the same constant distance from the old (original) polygon's (on the example picture they are not, since then it would have to use arcs for inflated vertices, but let's forget about that for now ;) ).
The mathematical term for what I'm looking for is actually inward/outward polygon offseting. +1 to balint for pointing this out. The alternative naming is polygon buffering.
Results of my search:
Here are some links:
A Survey of Polygon Offseting Strategies
Polygon offset, PROBLEM
Buffering Polygon Data
I thought I might briefly mention my own polygon clipping and offsetting library - Clipper.
While Clipper is primarily designed for polygon clipping operations, it does polygon offsetting too. The library is open source freeware written in Delphi, C++ and C#. It has a very unencumbered Boost license allowing it to be used in both freeware and commercial applications without charge.
Polygon offsetting can be performed using one of three offset styles - squared, round and mitered.
August 2022:
Clipper2 has now been formally released and it supercedes Clipper (aka Clipper1).
The polygon you are looking for is called inward/outward offset polygon in computational geometry and it is closely related to the straight skeleton.
These are several offset polygons for a complicated polygon:
And this is the straight skeleton for another polygon:
As pointed out in other comments, as well, depending on how far you plan to "inflate/deflate" your polygon you can end up with different connectivity for the output.
From computation point of view: once you have the straight skeleton one should be able to construct the offset polygons relatively easily. The open source and (free for non-commercial) CGAL library has a package implementing these structures. See this code example to compute offset polygons using CGAL.
The package manual should give you a good starting point on how to construct these structures even if you are not going to use CGAL, and contains references to the papers with the mathematical definitions and properties:
CGAL manual: 2D Straight Skeleton and Polygon Offsetting
For these types of things I usually use JTS. For demonstration purposes I created this jsFiddle that uses JSTS (JavaScript port of JTS). You just need to convert the coordinates you have to JSTS coordinates:
function vectorCoordinates2JTS (polygon) {
var coordinates = [];
for (var i = 0; i < polygon.length; i++) {
coordinates.push(new jsts.geom.Coordinate(polygon[i].x, polygon[i].y));
}
return coordinates;
}
The result is something like this:
Additional info: I usually use this type of inflating/deflating (a little modified for my purposes) for setting boundaries with radius on polygons that are drawn on a map (with Leaflet or Google maps). You just convert (lat,lng) pairs to JSTS coordinates and everything else is the same. Example:
Sounds to me like what you want is:
Starting at a vertex, face anti-clockwise along an adjacent edge.
Replace the edge with a new, parallel edge placed at distance d to the "left" of the old one.
Repeat for all edges.
Find the intersections of the new edges to get the new vertices.
Detect if you've become a crossed polygon and decide what to do about it. Probably add a new vertex at the crossing-point and get rid of some old ones. I'm not sure whether there's a better way to detect this than just to compare every pair of non-adjacent edges to see if their intersection lies between both pairs of vertices.
The resulting polygon lies at the required distance from the old polygon "far enough" from the vertices. Near a vertex, the set of points at distance d from the old polygon is, as you say, not a polygon, so the requirement as stated cannot be fulfilled.
I don't know if this algorithm has a name, example code on the web, or a fiendish optimisation, but I think it describes what you want.
In the GIS world one uses negative buffering for this task:
http://www-users.cs.umn.edu/~npramod/enc_pdf.pdf
The JTS library should do this for you. See the documentation for the buffer operation: http://tsusiatsoftware.net/jts/javadoc/com/vividsolutions/jts/operation/buffer/package-summary.html
For a rough overview see also the Developer Guide:
http://www.vividsolutions.com/jts/bin/JTS%20Developer%20Guide.pdf
Each line should split the plane to "inside" and "outline"; you can find this out using the usual inner-product method.
Move all lines outward by some distance.
Consider all pair of neighbor lines (lines, not line segment), find the intersection. These are the new vertex.
Cleanup the new vertex by removing any intersecting parts. -- we have a few case here
(a) Case 1:
0--7 4--3
| | | |
| 6--5 |
| |
1--------2
if you expend it by one, you got this:
0----a----3
| | |
| | |
| b |
| |
| |
1---------2
7 and 4 overlap.. if you see this, you remove this point and all points in between.
(b) case 2
0--7 4--3
| | | |
| 6--5 |
| |
1--------2
if you expend it by two, you got this:
0----47----3
| || |
| || |
| || |
| 56 |
| |
| |
| |
1----------2
to resolve this, for each segment of line, you have to check if it overlap with latter segments.
(c) case 3
4--3
0--X9 | |
| 78 | |
| 6--5 |
| |
1--------2
expend by 1. this is a more general case for case 1.
(d) case 4
same as case3, but expend by two.
Actually, if you can handle case 4. All other cases are just special case of it with some line or vertex overlapping.
To do case 4, you keep a stack of vertex.. you push when you find lines overlapping with latter line, pop it when you get the latter line. -- just like what you do in convex-hull.
Here is an alternative solution, see if you like this better.
Do a triangulation, it don't have to be delaunay -- any triangulation would do.
Inflate each triangle -- this should be trivial. if you store the triangle in the anti-clockwise order, just move the lines to right-hand-side and do intersection.
Merge them using a modified Weiler-Atherton clipping algorithm
Big thanks to Angus Johnson for his clipper library.
There are good code samples for doing the clipping stuff at the clipper homepage at http://www.angusj.com/delphi/clipper.php#code
but I did not see an example for polygon offsetting. So I thought that maybe it is of use for someone if I post my code:
public static List<Point> GetOffsetPolygon(List<Point> originalPath, double offset)
{
List<Point> resultOffsetPath = new List<Point>();
List<ClipperLib.IntPoint> polygon = new List<ClipperLib.IntPoint>();
foreach (var point in originalPath)
{
polygon.Add(new ClipperLib.IntPoint(point.X, point.Y));
}
ClipperLib.ClipperOffset co = new ClipperLib.ClipperOffset();
co.AddPath(polygon, ClipperLib.JoinType.jtRound, ClipperLib.EndType.etClosedPolygon);
List<List<ClipperLib.IntPoint>> solution = new List<List<ClipperLib.IntPoint>>();
co.Execute(ref solution, offset);
foreach (var offsetPath in solution)
{
foreach (var offsetPathPoint in offsetPath)
{
resultOffsetPath.Add(new Point(Convert.ToInt32(offsetPathPoint.X), Convert.ToInt32(offsetPathPoint.Y)));
}
}
return resultOffsetPath;
}
One further option is to use boost::polygon - the documentation is somewhat lacking, but you should find that the methods resize and bloat, and also the overloaded += operator, which actually implement buffering. So for example increasing the size of a polygon (or a set of polygons) by some value can be as simple as:
poly += 2; // buffer polygon by 2
Based on advice from #JoshO'Brian, it appears the rGeos package in the R language implements this algorithm. See rGeos::gBuffer .
I use simple geometry: Vectors and/or Trigonometry
At each corner find the mid vector, and mid angle. Mid vector is the arithmetic average of the two unit vectors defined by the edges of the corner. Mid Angle is the half of the angle defined by the edges.
If you need to expand (or contract) your polygon by the amount of d from each edge; you should go out (in) by the amount d/sin(midAngle) to get the new corner point.
Repeat this for all the corners
*** Be careful about your direction. Make CounterClockWise Test using the three points defining the corner; to find out which way is out, or in.
There are a couple of libraries one can use (Also usable for 3D data sets).
https://github.com/otherlab/openmesh
https://github.com/alecjacobson/nested_cages
http://homepage.tudelft.nl/h05k3/Projects/MeshThickeningProj.htm
One can also find corresponding publications for these libraries to understand the algorithms in more detail.
The last one has the least dependencies and is self-contained and can read in .obj files.
This is a C# implementation of the algorithm explained in here . It is using Unity math library and collection package as well.
public static NativeArray<float2> ExpandPoly(NativeArray<float2> oldPoints, float offset, int outer_ccw = 1)
{
int num_points = oldPoints.Length;
NativeArray<float2> newPoints = new NativeArray<float2>(num_points, Allocator.Temp);
for (int curr = 0; curr < num_points; curr++)
{
int prev = (curr + num_points - 1) % num_points;
int next = (curr + 1) % num_points;
float2 vn = oldPoints[next] - oldPoints[curr];
float2 vnn = math.normalize(vn);
float nnnX = vnn.y;
float nnnY = -vnn.x;
float2 vp = oldPoints[curr] - oldPoints[prev];
float2 vpn = math.normalize(vp);
float npnX = vpn.y * outer_ccw;
float npnY = -vpn.x * outer_ccw;
float bisX = (nnnX + npnX) * outer_ccw;
float bisY = (nnnY + npnY) * outer_ccw;
float2 bisn = math.normalize(new float2(bisX, bisY));
float bislen = offset / math.sqrt((1 + nnnX * npnX + nnnY * npnY) / 2);
newPoints[curr] = new float2(oldPoints[curr].x + bislen * bisn.x, oldPoints[curr].y + bislen * bisn.y);
}
return newPoints;
}
Thanks for the help in this topic, here's the code in C++ if anyone interested. Tested it, its working. If you give offset = -1.5, it will shrink the polygon.
typedef struct {
double x;
double y;
} Point2D;
double Hypot(double x, double y)
{
return std::sqrt(x * x + y * y);
}
Point2D NormalizeVector(const Point2D& p)
{
double h = Hypot(p.x, p.y);
if (h < 0.0001)
return Point2D({ 0.0, 0.0 });
double inverseHypot = 1 / h;
return Point2D({ (double)p.x * inverseHypot, (double)p.y * inverseHypot });
}
void offsetPolygon(std::vector<Point2D>& polyCoords, std::vector<Point2D>& newPolyCoords, double offset, int outer_ccw)
{
if (offset == 0.0 || polyCoords.size() < 3)
return;
Point2D vnn, vpn, bisn;
double vnX, vnY, vpX, vpY;
double nnnX, nnnY;
double npnX, npnY;
double bisX, bisY, bisLen;
unsigned int nVerts = polyCoords.size() - 1;
for (unsigned int curr = 0; curr < polyCoords.size(); curr++)
{
int prev = (curr + nVerts - 1) % nVerts;
int next = (curr + 1) % nVerts;
vnX = polyCoords[next].x - polyCoords[curr].x;
vnY = polyCoords[next].y - polyCoords[curr].y;
vnn = NormalizeVector({ vnX, vnY });
nnnX = vnn.y;
nnnY = -vnn.x;
vpX = polyCoords[curr].x - polyCoords[prev].x;
vpY = polyCoords[curr].y - polyCoords[prev].y;
vpn = NormalizeVector({ vpX, vpY });
npnX = vpn.y * outer_ccw;
npnY = -vpn.x * outer_ccw;
bisX = (nnnX + npnX) * outer_ccw;
bisY = (nnnY + npnY) * outer_ccw;
bisn = NormalizeVector({ bisX, bisY });
bisLen = offset / std::sqrt((1 + nnnX * npnX + nnnY * npnY) / 2);
newPolyCoords.push_back({ polyCoords[curr].x + bisLen * bisn.x, polyCoords[curr].y + bisLen * bisn.y });
}
}
To inflate a polygon, one can implement the algorithm from "Polygon Offsetting by Computing Winding Numbers" article.
The steps of the algorithm are as follows:
Construct outer offset curve by taking every edge from input polygon and shifting it outside, then connecting shifted edged with circular arches in convex vertices of input polygon and two line segments in concave vertices of input polygon.
An example. Here input polygon is dashed blue, and red on the left - shifted edges, on the right - after connecting them in continuous self-intersecting curve:
The curve divides the plane on a number of connected components, and one has to compute the winding number in each of them, then take the boundary of all connected components with positive winding numbers:
The article proofs that the algorithm is very fast compared to competitors and robust at the same time.
To avoid implementing winding number computation, one can pass self-intersecting offset curve to OpenGL Utility library (GLU) tessellator and activate the settings GLU_TESS_BOUNDARY_ONLY=GL_TRUE (to skip triangulation) and GLU_TESS_WINDING_RULE=GLU_TESS_WINDING_POSITIVE (to output the boundary of positive winding number components).

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