Find totally covered circles in a set of circles - algorithm

We have a set of circles in a rectangular area, all the circles have the same radii, I want to find the circles totally covered by other circles.
is there any algorithme for that ?

Geometry
First, find all the circles whose center is closer than 2×r to the center of the main circle; circles further away do not overlap.
Example: the main (black) circle and 3 overlapping circles.
Then, to know that the main circle is completely covered, you have to find a set of circles for which every intersecting point of two circles which lies within the main (black) circle, is covered by a third circle.
Algorithm
Practically, you start with 2 circles (e.g. blue and red), and find the 2 points where they intersect (purple dots). If one or both points are within the main (black) circle, then these points have to be covered by an additional circle.
Then, one by one, add an additional circle (e.g. green), and see whether it covers non-covered points (in the example it does). However, this new circle adds new intersection points with the other circles already in the set (blue and red); find these points (teal and brown dots) and check whether they are covered by any of the circles (the teal one is covered by the red circle, but the brown one is not covered by the blue circle).
Keep adding circles to the set until every intersection point inside the main (black) circle is covered by another circle in the set (in which case the whole main circle is covered), or until you run out of circles (in which case the main circle is not completely covered).
Special cases:
If one of the circles has the exact same center point as the main circle, it covers the main circle on its own.
If none of the circles have intersection points inside the main circle, the main circle is not covered.
Code example
This code example demonstrates how to find the intersecting points of two circles, which takes care of most of the geometry needed in the algorithm.
function intersections(p, q, r) {
var d = distance(p, q);
if (d > 2 * r) return [];
var m = middle(p, q);
if (d == 2 * r) return [m];
var a = angle(p, q) + Math.PI / 2;
var l = length(d, r);
return [{x: m.x + l * Math.cos(a), y: m.y + l * Math.sin(a)},
{x: m.x - l * Math.cos(a), y: m.y - l * Math.sin(a)}];
function distance(p, q) {
return Math.sqrt(Math.pow(p.x - q.x, 2) + Math.pow(p.y - q.y, 2));
}
function middle(p, q) {
return {x: (p.x + q.x) / 2, y: (p.y + q.y) / 2};
}
function angle(p, q) {
return Math.atan2(q.y - p.y, q.x - p.x);
}
function length(d, r) {
return Math.sqrt(Math.pow(r, 2) - Math.pow(d, 2) / 4);
}
}
document.write(JSON.stringify(intersections({x:1, y:2}, {x:3, y:-4}, 5)));

Related

Algorithm to predict if trajectory of a line will come in contact with a rectangle?

I am unsure of what math to use here, as I am very inexperienced in the area of using math along with coding to solve problems such as this, so I was wondering if anyone here could either give me some pointers or give me somewhere to look at this specific problem. I have the x and y trajectory of the single point (or ball) and when it moves, it acts like a line, moving from one place in which is has to stop, then bouncing off of it (reflecting the trajectory), and going in that bounced trajectory. I just need the algorithm to give a true/false (boolean) to whether the current slope will come in contact with the rectangle. I have the 4 edge points of the rectangle and the middle point of the rectangle if needed.
After some back-and-forth in the comments, here is a function that may better suit OP's purpose. The is_intersect() function takes as input the position of the point on the trajectory, its direction, and a quadrilateral, and returns true if the point is on an intersecting trajectory, false otherwise.
pos is a table containing the position of the point, of the form:
pos = { x=x1, y=y1 }
dir is a number containing a positive angle in radians (0 <= θ < 2π) with respect to the positive x-axis, representing the direction of travel of the point.
quad is a table representing a quadrilateral, of the form:
quad = {{x=x1, y=y1}, {x=x2, y=y2}, {x=x3, y=y3}, {x=x4, y=y4}}
Note that it would be a simple matter, and perhaps desirable, to adapt the code to use integer-indexed tables instead, such as pos = {x1, y1} and quad = {{x1, y1}, {x2, y2},...}.
This function works for quadrilaterals situated anywhere in the plane, and for trajectory points situated anywhere in the plane. It works by finding the positive angles with respect to the positive x-axis of a line through the trajectory point and each of the corners of the quadrilateral. The function returns true if the direction angle is in this range.
function is_intersect(pos, dir, quad)
local theta_corner, theta_min, theta_max
for i = 1, 4 do
local x, y = quad[i].x, quad[i].y
-- Find angle of line from pos to corner
theta_corner = math.atan((y - pos.y) / (x - pos.x))
-- Adjust angle to quadrant
if x < pos.x then
theta_corner = theta_corner + math.pi
elseif y < pos.y then
theta_corner = theta_corner + 2*math.pi
end
-- Keep max and min angles
if (not theta_min) or theta_corner < theta_min then
theta_min = theta_corner
end
if (not theta_max) or theta_corner > theta_max then
theta_max = theta_corner
end
end
-- Compare direction angle with max and min angles
return theta_min <= dir and dir <= theta_max
end
Here is a sample interaction:
> test = {{x = 1, y = 1}, {x = 1, y = 2}, {x = 2, y = 2}, {x = 2, y = 1}}
> position = {x = 3, y = 3}
> pi = math.pi
> is_intersect(position, 5*pi/4, test)
true
> angle = math.atan(.5)
> -- checking corners
> is_intersect(position, pi + angle, test)
true
> is_intersect(position, 3*pi/2 - angle, test)
true
> -- checking slightly inside corners
> is_intersect(position, 1.01*(pi + angle), test)
true
> is_intersect(position, .99*(3*pi/2 - angle), test)
true
> -- checking slightly outside corners
> is_intersect(position, .99*(pi + angle), test)
false
> is_intersect(position, 1.01*(3*pi/2 - angle), test)
false
How it Works
This section is for OP's benefit. Read no further if you do not want a Trigonometry refresher.
In this diagram, there are two points with directions represented by a green and a yellow arrow. The red lines connect the points with the corners of the rectangle. The is_intersect() function works by calculating the angles, measured from the positive x-axis, as in the diagram, to the lines connecting the point to each of the corners of the rectangle. You can see that there will be four such lines for each point, but only two are marked in the diagram. One of these is the largest such angle, and the other is the smallest. The direction of travel for the point is specified by an angle, also measured from the positive x-axis. If this angle is between the angles to the two red lines, then the point is on an intersecting trajectory with the rectangle. The green point is on an intersecting trajectory: the angle from the positive x-axis to the line of travel for the green point (what we might call its velocity vector) is between the other two angles for this point. But the yellow point is not on an intersecting trajectory: the angle from the positive x-axis to the line of travel for the yellow point is larger than both of the other two angles.
Now, in this diagram there are four triangles. Each of the triangles has an angle at the origin of the coordinate system. We define the tangent of this angle to be the ratio of the length of the side opposite the angle (the vertical leg of the triangle) to the length of the side adjacent to the angle (the horizontal leg). That is:
tan(A) = y/x
Furthermore, the angle A is the arctangent of y/x:
A = atan(y/x)
From this diagram, it can be seen that to find the direction angle of the line connecting the point on the trajectory to the corner of the rectangle, we can calculate the angle A from the triangle, and add 270°. We really add 3π/2 radians. For a variety of reasons which I will not go into now, radians are better. Once you get used to them, you will never use degrees for any sort of calculations ever again. If you ever study Calculus, you will have to use radians. But the practical issue at the moment is that Lua's trig functions take arguments in radians, and report angles in radians.
So, the angle A is atan(x/y). How to find x and y? By subtracting the value of the x-coordinate for the point from the x-coordinate of the rectangle corner we can find x. Similarly, by subtracting the value of the y-coordinate of the point from the y-coordinate of the rectangle corner, we can find y.
We still need to know which quadrant the angle is in so that we know how much to add to A. The quadrants are traditionally labelled with the Roman numerals from Ⅰ to Ⅳ, starting from the upper right quadrant of the x-y axis. The angle in the diagram is in quadrant Ⅳ. We can tell which quadrant the angle is in by checking to see if the point is to the left or right of the corner of the rectangle, and above or below the corner of the rectangle.
With this information, the direction angles to each of the corners from the point can be found. The largest and smallest are kept and compared with the direction angle for the trajectory of the point to see if the trajectory will intersect the rectangle.
The above exposition is a pretty good description of what the code in the is_intersect() function is doing. There is a subtlety in that the subtraction to find the sides of the triangles gives negative side-lengths in some cases. This is a normal issue in trigonometric calculations; the code needs to know how the atan() function being used handles negative values. The code under the comment -- Adjust angle to quadrant takes this into account.
For the case of a coordinate system with the origin at the upper left, you simply need to measure angles in a clockwise fashion instead. This is analagous to flipping the original coordinate system upside down. By the way, the original coordinate system (with the origin at the lower-left) is the one usually found in Mathematics, and is called a right-handed coordinate system. The other system, with the origin at the upper-left, is called a left-handed coordinate system.
Addendum
In an attempt to help OP understand these functions, I provided this simple testing code. What follows is a further attempt to explicate these tests.
The above diagram shows in part the situation represented in the tests. There is a 1X1 rectangle located in quadrant I. Note that these are screen coordinates, with the origin at the upper left. All angles are measured in a clockwise direction from the positive x-axis.
In the tests you will find:
pos1 = { x = 0, y = 0 }
pos3 = { x = 3, y = 3 }
These are two positions from which we wish to test the functions. The first position is at the origin, the second is at the location (3, 3). The red lines in the diagram help us to see the direction angles from the test points to the corners of the rectangle.
First note that A = atan(1 / 2), or A = atan(.5). This angle will show up again, so I have defined the constants:
angle = math.atan(.5)
pi = math.pi
Next notice that the angle B is (π/2 - A) radians. Convince yourself that this is true by looking at the symmetry of the situation. There is no need to convert any of these angles to degrees, in fact do not do this! This will only cause problems; the usual trig functions expect arguments in radians, and return angles in radians.
Continuing, if you look closely you should be able to see that the angle C is (π + A) radians, and the angle D is (3π/2 - A) radians.
So we have, for example, these tests:
{ pass = true, args = { pos1, pi/4, rect } },
{ pass = true, args = { pos3, 5*pi/4, rect } },
The first of these tests the trajectory from position pos1, (0, 0), with a direction angle of π/4 radians. This should intersect the corner nearest pos1. The second tests the trajectory from position pos3, (3, 3), with a direction angle of 5π/4 radians. This should intersect the corner nearest pos3. So both tests should return true.
There are also tests like:
{ pass = true, args = { pos3, pi + angle, rect } },
{ pass = true, args = { pos3, 3*pi/2 - angle, rect } },
{ pass = true, args = { pos3, 1.1*(pi + angle), rect} },
{ pass = false, args = { pos3, 1.1*(3*pi/2 - angle), rect } },
{ pass = false, args = { pos3, .99*(pi + angle), rect } },
{ pass = true, args = { pos3, .99*(3*pi/2 - angle), rect } },
Now pi + angle and 3*pi/2 - angle are the direction angles to the outside corners of the rectangle from pos3, as previously noted. So the first two tests should return true, since we expect trajectories directed at the corners to intersect the rectangle. The remaining tests make the direction angles larger or smaller. Looking back at the diagram, note that if the direction angle of the trajectory from pos3 is made larger than C, there should be an intersection, but if it is made smaller than C, there should not be an intersection. Similarly, if the direction angle of the trajectory from pos3 is made larger than D, there should be no intersection, while if it is is made smaller, there should be an intersection.
All of this is just to explain what is happening in the tests, so that you can understand them and write tests of your own. In practice, you only need to specify the quadrilateral with the quad table, a position with the pos table, and a direction with dir, which is a number in radians. You specified straight-line motion; curvilinear motion is more involved.
After all of this, I still strongly suggest, as I did earlier in the comments, that you consider using #Gene's function. To use this function you specify a direction vector instead of an angle, using a table: dir = {x = x1, y = y1). This will save you many troubles, and as you mentioned earlier, you already have x- and y-components for the direction of the trajectory. Converting this information to an angle will require you to understand how to make the correct adjustments for the quadrant of the angle. Don't mess with this right now.
Here are the tests of position pos3 for Gene's function:
{ pass = true, args = { pos3, {x=-1.01, y=-2}, rect} },
{ pass = false, args = { pos3, {x=-0.99, y=-2}, rect } },
{ pass = false, args = { pos3, {x=-2.01, y=-1}, rect } },
{ pass = true, args = { pos3, {x=-1.99, y=-1}, rect } },
Instead of worrying about angles, and referring back to the last diagram, note that arrows from pos3 to the outside corners of the rectangle can be drawn by going 1 in the negative x-direction, and 2 in the negative y-direction, or by going 2 in the negative x-direction and 1 in the negative y-direction. So to test for slightly off-corner intersections, as before, the x-components of the trajectory direction vectors are modified. For example, from pos3, the trajectory {-2, -1} should intersect at the corner nearest the y-axis. So, the trajectory {-2.01, -1} should not intersect with this corner, and the trajectory {-1.99, -1} should intersect.
One caveat. Notice that there are four tests, and not six as before. Gene's function will report that there is no intersection from pos3 with the trajectory vector {-2, -1}. This is not incorrect, but just a difference in the way his code handles corner cases. In fact, I should probably remove the tests that directly test at the corners of the rectangle for my function, and just test near the corners, which is what really matters. Both functions report the same results for all other cases. The tiniest bit away from a corner, and both functions report false; the tiniest bit inside the corners, and both functions report true.
IMO atan is a bad idea. It's expensive, and the quadrant adjustment is way too hard.
You can simplify the accepted code by using atan2 rather than atan, but that's still too expensive.
You don't really care about the angle. You care about the signs of the angles between the direction vector and the extreme vectors from current position to box corners.
For this, cross-products are cheaper, simpler, and better-suited. If you have two vectors a and b, then the quantity:
s = a.x * b.y - a.y * b.x
is positive if the acute angle from a to b is positive (counter-clockwise) and vice versa.
You need only to verify that the acute angles formed by the direction vector and lines from the current point to each of the box corners include at least one positive and one negative result.
function is_intersect(pos, dir, quad)
local n_pos, n_neg = 0, 0
for i = 1, 4 do
local dx, dy = quad[i].x - pos.x, quad[i].y - pos.y
local s = dir.x * dy - dir.y * dx
if s > 0 then n_pos = n_pos + 1 end
if s < 0 then n_neg = n_neg + 1 end
end
return n_pos > 0 and n_neg > 0
end
Sorry I'm not a Lua programmer. This is a best guess at Lua syntax using the already-accepted answer as a pattern.

How to get radius of lower and upper bound of circle?

Problem and nearby solution image:
Problem: A mesh equally distant from each node. Each small node of grid is a circle(fig: grey circle) of radius r, if we make any circle(fig:blue color) then how can we make a set of that circle having upper(fig:red circle) and lower bound(fig:red circle). Basically we need to find upper bound radius, and lower bound radius.
Value known are:
Radius of small circle(grey) r.
Distance between two grey circle/node_of_grid from center to center = 4 X r.
Radius of big circle (blue) rb. //this may vary but will be in multiple of r. In this figure case is diameter d = 2(20 x r).
Center of big circle can only be center of small circle.
How can i find the radius of upper circle and lower circle. Each blue small circle who comes within the big circle circumference should come under upper bound and vice versa for lower bound.
Currently i came up with this solution which is incorrect :
unpper_bound_radius = rb + (r + (rb/(3.1415926 * 2r)))
lower_bound_radius = rb - (r + (rb/(3.1415926 * 2r)))
Thanks
If you're looking for an algorithm to decide which grey dots intersect with the blue circle, and calculate the radius of the boundaries that contain the selected grey dots, you could do something like this:
Give the inner and outer boundary a radius equal to the blue circle.
The grey dot straight above the center, which intersect with the blue circle or is just above it, is the starting point.
Calculate the distance from that point to the center point; if it is between rb-r and rb+r, the blue circle intersects with this grey dot.
If it intersects, adjust the inner boundary to distance - r and the outer boundary to distance + r as necessary.
If it doesn't, and the distance is greater, try the grid point below it; if it doesn't, and the distance is smaller, try the grid point to the right.
Do this until you move out of a 45° sector; you only need to check one eigth of the circle, the other results will be symmetrical.
Example calculation:
The first grid point, at the top, is 5×4r from the center; the boundaries are at 5×4r-r and 5×4r+r.
The next grid point to the right is at 5.09902×4r from the center, so the outer boundary increases to 5.09902×4r+r.
The next grid point to the right is at 5.38516×4r from the center, which is 1.54066×r outside the blue circle (too far).
The next grid point downward is at 4.47216×4r from the center, which is 1.88854×r inside the blue circle (too close).
The next grid point to the right is at exactly 5×4r from the center, so the boundaries don't change.
The next grid point to the right is at 5.65686×4r from the center, which is 2.62742×r outside the blue circle (too far).
The next grid point down is more than 45° to the right.
Result: inner boundary is at 5×4r-r, outer boundary is at 5.09902×4r+r.
function boundaries(b) {
var i = b, o = b, x = 0, y = Math.ceil(b / 4) * 4;
while (Math.atan2(y, x) >= Math.PI / 4) {
var d = Math.sqrt(x * x + y * y);
if (d < b - 1) x += 4; // too close, go right
else if (d > b + 1) y -= 4; // too far, go down
else { // intersection
if (i > d - 1) i = d - 1; // adjust inner
if (o < d + 1) o = d + 1; // adjust outer
x += 4; // go right
}
}
return {i: i, o: o};
}
var res = boundaries(20);
document.write("inner: r×" + res.i + "<br>outer: r×" + res.o);

Polygon Algorithm

I'm trying to code a general algorithm that can find a polygon from the area swept out by a circle (red line) that follows some known path (green line), and where the circle gets bigger as it moves further down the known path. Basically, can anyone point me down a direction to solve this, please? I can't seem to nail down which tangent points are part of the polygon for any point (and thus circle) on the path.
Any help is appreciated.
Well, the easiest is to approximate your path by small segments on which your path is linear, and your circle grows linearly.
Your segments and angles will likely be small, but for the sake of the example, let's take bigger (and more obvious) angles.
Going through the geometry
Good lines for the edges of your polygon are the tangents to both circles. Note that there aren't always close to the lines defined by the intersections between the circles and the orthogonal line to the path, especially with stronger growth speeds. See the figure below, where (AB) is the path, we want the (OE) and (OF) lines, but not the (MN) one for example :
The first step is to identify the point O. It is the only point that defines a homothetic transformation between both circles, with a positive ratio.
Thus ratio = OA/OB = (radius C) / (radius C') and O = A + AB/(1-ratio)
Now let u be the vector from O to A normalized, and v a vector orthogonal to u (let us take it in the direction from A to M).
Let us call a the vector from O to E normalized, and beta the angle EOA. Then, since (OE) and (AE) are perpendicular, sin(beta) = (radius C) / OA. We also have the scalar product a.u = cos(beta) and since the norm of a is 1, a = u * cos(beta) + v * sin(beta)
Then it comes easily that with b the vector from O to F normalized, b = u * cos(beta) - v * sin(beta)
Since beta is an angle less than 90° (otherwise the growth of the circle would be so much faster than it going forward, that the second circle contains the first completely), we know that cos(beta) > 0.
Pseudo-code-ish solution
For the first and last circles you can do something closer to them -- fort the sake of simplicity, I'm just going to use the intersection between the lines I'm building and the tangent to the circle that's orthogonal to the first (or last) path, as illustrated in the first figure of this post.
Along the path, you can make your polygon arbitrarily close to the real swept area by making the segments smaller.
Also, I assume you have a function find_intersection that, given two parametric equations of two lines, returns the point of intersection between them. First of all, it makes it trivial to see if they are parallel (which they should never be), and it allows to easily represent vertical lines.
w = 1; // width of the first circle
C = {x : 0, y : 0}; // first circle center
while( (new_C, new_w) = next_step )
{
// the vector (seg_x, seg_y) is directing the segment
seg = new_C - C;
norm_seg = sqrt( seg.x * seg.x + seg.y * seg.y );
// the vector (ortho_x, ortho_y) is orthogonal to the segment, with same norm
ortho = { x = -seg.y, y = seg.x };
// apply the formulas we devised : get ratio-1
fact = new_w / w - 1;
O = new_C - seg / fact;
sin_beta = w * fact / norm_seg;
cos_beta = sqrt(1 - sin_beta * sin_beta);
// here you know the two lines, parametric equations are O+t*a and O+t*b
a = cos_beta * seg + sin_beta * ortho;
b = cos_beta * seg - sin_beta * ortho;
if( first iteration )
{
// initialize both "old lines" to a line perpendicular to the first segment
// that passes through the opposite side of the circle
old_a = ortho;
old_b = -ortho;
old_O = C - seg * (w / norm_seg);
}
P = find_intersection(old_O, old_a, O, a);
// add P to polygon construction clockwise
Q = find_intersection(old_O, old_b, O, b);
// add Q to polygon construction clockwise
old_a = a;
old_b = b;
old_O = O;
w = new_w;
C = new_C;
}
// Similarly, finish with line orthogonal to last direction, that is tangent to last circle
O = C + seg * (w / norm_seg);
a = ortho;
b = -ortho;
P = find_intersection(old_O, old_a, O, a);
// add P to polygon construction clockwise
Q = find_intersection(old_O, old_b, O, b);
// add Q to polygon construction clockwise
Let's suppose the centers are along the positive x-axis, and the lines in the envelope are y=mx and y=-mx for some m>0. The distance from (x,0) to y=mx is mx/sqrt(1+m^2). So, if the radius is increasing at a rate of m/sqrt(1+m^2) times the distance moved along the x-axis, the enveloping lines are y=mx and y=-mx.
Inverting this, if you put a circle of radius cx at the center of (x,0), then c=m/sqrt(1+m^2) so
m = c/sqrt(1-c^2).
If c=1 then you get a vertical line, and if c>1 then every point in the plane is included in some circle.
This is how you can tell how much faster than sound a supersonic object is moving from the Mach angle of the envelope of the disturbed medium.
You can rotate this to nonhorizontal lines. It may help to use the angle formulation mu = arcsin(c), where mu is the angle between the envelope and the path, and the Mach number is 1/c.

Bounding circle of set of circles

I'm trying to implement the following in Java.
Given a list of circles of different sizes (possibly) and positions, determine a large circle (position and size) which just exactly encloses all the circles.
public class Circle {
public int x, y, radius;
}
Any ideas?
The miniball-of-balls problem has been studied in "The smallest enclosing ball of balls: combinatorial structure and algorithms", for example. One outcome of this research was that at least some algorithms for the miniball-of-points problem (like Welzl's algorithm) cannot easily be generalised from points to balls.
The above paper presents an O(n)-algorithm to compute the miniball of a set of balls (n being the number of input balls, i.e., circles in 2D). A C++ implementation thereof is available in the Computational Geometry Algorithms Library (CGAL). (You do not need to use all of CGAL; simply extract the required header and source files.)
Note: I am a co-author of the above paper and the author of the CGAL Min_sphere_of_spheres package.
I have a roughly O(n4) true solution that I'm implementing for a product in JavaScript:
You need a function to determine whether a solution is valid: to be precise, a function that will check if all the circles lie within the proposed super-circle. This is fairly trivial: for every circle Ci, require that the distance from the centre of the super circle to the centre of Ci plus the radius of Ci is less than or equal to the radius of the super-circle.
Then, construct a super-circle out of every pair and every triple of circles.
For a pair, draw a line from the centre of Ci to the centre of Cj. Extend the line out on each end by the radius of the respective circle. The midpoint of the line is the centre of the super-circle, and its radius is half the length of the line.
For 3 circles, this is the Problem of Apollonius: http://mathworld.wolfram.com/ApolloniusProblem.html; noting that you need to get the correct signs to get one that will include all three circles.
The correct solution is the valid super-circle with the smallest radius.
Here's my code:
'use strict';
/**
* Epsilon value for floating point equality.
* #const
*/
var EPSILON = 1E-6;
/**
* Calculates the minimum bounding circle for a set of circles.
* O(n^4)
*
* #param {Array.<Object.<string, number>>} circles A list of 2+ circles.
* #return {Object.<string, number>} {cx, cy, radius} of the circle.
*/
function minimumBoundingCircleForCircles(circles) {
var areAllCirclesInOrOnCircle = function(circle) {
for (var i = 0; i < circles.length; i++) {
if (!isCircleInOrOnCircle(circles[i], circle)) return false;
}
return true;
};
// try every pair and triple
var best = {radius: 9E9};
for (var i = 0; i < circles.length; i++) {
for (var j = i + 1; j < circles.length; j++) {
var circle = circleFrom2Circles(circles[i], circles[j]);
if (areAllCirclesInOrOnCircle(circle) &&
circle.radius < best.radius) {
best.cx = circle.cx; best.cy = circle.cy;
best.radius = circle.radius;
}
for (var k = j + 1; k < circles.length; k++) {
var signs = [-1, 1, 1, 1];
circle = apollonius(circles[i], circles[j], circles[k],
signs);
if (areAllCirclesInOrOnCircle(circle) &&
circle.radius < best.radius) {
best.cx = circle.cx; best.cy = circle.cy;
best.radius = circle.radius;
}
}
}
}
return best;
}
/**
* Calculates a circle from 2 circles.
*
* #param {Object.<string, number>} circle1 The first circle.
* #param {Object.<string, number>} circle2 The second circle.
* #return {Object.<string, number>} cx, cy, radius of the circle.
*/
function circleFrom2Circles(circle1, circle2) {
var angle = Math.atan2(circle1.cy - circle2.cy,
circle1.cx - circle2.cx);
var lineBetweenExtrema = [[circle1.cx + circle1.radius * Math.cos(angle),
circle1.cy + circle1.radius * Math.sin(angle)],
[circle2.cx - circle2.radius * Math.cos(angle),
circle2.cy - circle2.radius * Math.sin(angle)]];
var center = lineMidpoint(lineBetweenExtrema[0], lineBetweenExtrema[1]);
return { cx: center[0],
cy: center[1],
radius: lineLength(lineBetweenExtrema[0],
lineBetweenExtrema[1]) / 2
};
}
/**
* Solve the Problem of Apollonius: a circle tangent to all 3 circles.
* http://mathworld.wolfram.com/ApolloniusProblem.html
*
* #param {Object.<string, number>} circle1 The first circle.
* #param {Object.<string, number>} circle2 The second circle.
* #param {Object.<string, number>} circle3 The third circle.
* #param {Array.<number>} signs The array of signs to use.
* [-1, 1, 1, 1] gives max circle.
* #return {Object.<string, number>} The tangent circle.
*/
function apollonius(circle1, circle2, circle3, signs) {
var sqr = function(x) { return x * x };
var a1 = 2 * (circle1.cx - circle2.cx);
var a2 = 2 * (circle1.cx - circle3.cx);
var b1 = 2 * (circle1.cy - circle2.cy);
var b2 = 2 * (circle1.cy - circle3.cy);
var c1 = 2 * (signs[0] * circle1.radius + signs[1] * circle2.radius);
var c2 = 2 * (signs[0] * circle1.radius + signs[2] * circle3.radius);
var d1 = (sqr(circle1.cx) + sqr(circle1.cy) - sqr(circle1.radius)) -
(sqr(circle2.cx) + sqr(circle2.cy) - sqr(circle2.radius));
var d2 = (sqr(circle1.cx) + sqr(circle1.cy) - sqr(circle1.radius)) -
(sqr(circle3.cx) + sqr(circle3.cy) - sqr(circle3.radius));
// x = (p+q*r)/s; y = (t+u*r)/s
var p = b2 * d1 - b1 * d2;
var q = (- b2 * c1) + (b1 * c2);
var s = a1 * b2 - b1 * a2;
var t = - a2 * d1 + a1 * d2;
var u = a2 * c1 - a1 * c2;
// you are not expected to understand this.
// It was generated using Mathematica's Solve function.
var det = (2 * (-sqr(q) + sqr(s) - sqr(u)));
var r = (1 / det) *
(2 * p * q + 2 * circle1.radius * sqr(s) + 2 * t * u -
2 * q * s * circle1.cx - 2 * s * u * circle1.cy + signs[3] *
Math.sqrt(sqr(-2 * p * q - 2 * circle1.radius * sqr(s) - 2 * t * u +
2 * q * s * circle1.cx + 2 * s * u * circle1.cy) -
4 * (-sqr(q) + sqr(s) - sqr(u)) *
(-sqr(p) + sqr(circle1.radius) * sqr(s) - sqr(t) +
2 * p * s * circle1.cx - sqr(s) * sqr(circle1.cx) +
2 * s * t * circle1.cy - sqr(s) * sqr(circle1.cy))))
//console.log(r);
r = Math.abs(r);
var x = (p + q * r) / s;
var y = (t + u * r) / s;
//console.log(x); console.log(y);
return {cx: x, cy: y, radius: r};
}
/**
* Is the circle inside/on another circle?
*
* #param {Object.<string, number>} innerCircle the inner circle.
* #param {Object.<string, number>} outerCircle the outer circle.
* #return {boolean} is the circle inside/on the circle?
*/
function isCircleInOrOnCircle(innerCircle, outerCircle) {
return ((lineLength([innerCircle.cx, innerCircle.cy],
[outerCircle.cx, outerCircle.cy]) +
innerCircle.radius - EPSILON) < outerCircle.radius);
}
/**
* Calculates the length of a line.
* #param {Array.<number>} pt1 The first pt, [x, y].
* #param {Array.<number>} pt2 The second pt, [x, y].
* #return {number} The length of the line.
*/
function lineLength(pt1, pt2) {
return Math.sqrt(Math.pow(pt1[0] - pt2[0], 2) +
Math.pow(pt1[1] - pt2[1], 2));
}
/**
* Calculates the midpoint of a line.
* #param {Array.<number>} pt1 The first pt, [x, y].
* #param {Array.<number>} pt2 The second pt, [x, y].
* #return {Array.<number>} The midpoint of the line, [x, y].
*/
function lineMidpoint(pt1, pt2) {
return [(pt1[0] + pt2[0]) / 2,
(pt1[1] + pt2[1]) / 2];
}
The Wikipedia article Smallest circle problem describes a linear average time algorithm for the case where the sizes of the initial circles are equal. It looks straightforward to generalize it to differently-sized initial circles, though I'm not sure what happens to the complexity analysis then.
You could find the max boundaries (xmin, xmax, ymin, ymax), then take max on both axis, then ask java to draw an ellipse in that square or take the center of it and the side as diameter.
No ?
Regards,
Stéphane
Oops, the following does not work, as noted in comments:
Start by solving the problem for 3 circles. In that case the circumscribed circle will touch each of the three inner circles, and you can find its center as the intersection of two hyperbolas. (The locus of points that have a given difference in distance to two fixed points is a hyperbola). After a bit of algebra, that boils down to a quadratic equation.
Now add more inner circles by induction. At the beginning of each step you know the smallest circle that encompasses all of the old circles; it will touch three particular old "corner" circles. If the new circle is inside that one, there's nothing to do. If not, combine the new circle with all three ways to chose two of the old corner circles and compute the circumscribed circle for each of those triples. One of them should include the fourth one, which is now not a corner circle anymore.
Proceed until you have added all circles.
This gives a linear-time algorithm with bounded rounding errors (because each circumscribed circle is computed afresh from pristine input coordinates).
My suggested algorithm is similar to that of Svante but with a few differences.
The idea is to first create a circle which trivially encompasses all subcircles and then shrink it like a bubble until it is pinned by 1,2, or 3 circles.
first approximation:
a circle with centre 0,0 and radius max(distance from 0,0 of subcircle + radius of subcircle)
if the subcircle which determines the radius of the circle encloses all other subcircles, then it is trivially the correct result, and can be returned
second approximation:
reduce the radius of the circle found in the first approximation while keeping it tangent to the subcircle which it is 'pinned' to, moving the centre towards the pinned subcircle, until it becomes tangent to another subcircle.
final result:
reduce the radius of the circle again, keeping it tangent to the two subcircles found above, until either another subcircle becomes tangent to the circle, or the centre of the circle lies on the line between the centres of the two subcircles it is pinned to. This should be the minimum because there is no way from here to reduce the radius of the circle without one of the subcircles 'breaking through'
The part of the algorithm I'm not sure about is the 'reduce the radius until another subcircle becomes tangent' part. Obviously a binary search can give a good enough approximation in a decent amount of time, but I suspect you can reduce it to an equation.
I think that this can be done in three steps:
First bounding circle c1:
The centre is determined by xc1 = (xmax + xmin) / 2
and yc1 = (ymax + ymin) / 2.
For each circle, calculate the distance of its centre to the centre of c1 plus its radius (I call this the over-distance).
The maximum of these values is the radius of c1. The corresponding circle is a.
Second bounding circle c2:
(In this step, you move the centre of c1 in direction of a as far as possible.)
For each circle except a, determine how much you have to move the centre of c1 in the direction of a so that the over-distance from there to this circle is the same as to a. The minimum of this determines the centre of c2. The corresponding circle is b. The over-distance to a and b (both are the same) is the radius of c2.
Third bounding circle c3:
(In this step, you move the centre of c2 in the direction between a and b as far as possible.)
Determine the direction v in which you can move c2 such that the over-distance to a and b stays the same.
For each circle except a and b, determine how much you have to move the centre of c2 in direction v so that the over-distance from there to this circle is the same as to a and b. The minimum of this determines the centre of c3. The radius is the over-distance to the three circles found.
I believe that c3 is the solution (edit) a good first approximation. You can get better solutions by iteratively dropping the first circle and repeating the third step. If you arrive at a set of three circles that you have already seen, this should might be the final solution.
I'd try to find the topmost western point, then the southern downmost point, then make a circle with those points as diameter.
In order to find those points, I'd cycle through the center of the circles and their radius.
So it ends up in:
initiate max object and min object to average center of circle and zero radius
For every circle object
calculate topmost western point of the circle
check if further away than current point
if further, calculate position of topmost western point required to pass over this new point and set as new min object
calculate down most eastern point of the circle
do the same as previous step, only for max object
make a circle with center equals to middle of min-max line and radius equals to half min-max line length
If you are the bookworm type, good university libraries stock this: E.Welzl, Smallest Enclosing Disks (Balls and Ellipsoids), in H. Maurer (Ed.), New Results and New Trends in Computer Science, Lecture Notes in Computer Science, Vol. 555, Springer-Verlag, 359–37 (1991)
And if you want to read C++ code and adapt it to Java, http://miniball.sourceforge.net/.
With circles, d=2, of course.
With two circles it is easy. A line through both centres will hit the perimeter where an enclosing circle would contact them.
With more circles you would need to apply FEM (finite element analysis- http://en.wikipedia.org/wiki/Finite_element_method) on each perimeter point with the potential to be a point of contact with the outside circle. This rules out those segments which are facing other circles, for example. The computation is still rather large as you proceed to apply different radius's to your points until you find the smallest ones that intersects all the others at a common point- the centre of your enclosing cirlce.
I don't think its a packing problem per se. It sounds more like convex-hull. I think the question is:
You are given a set of circles on the plane. Find the center point and radius of the smallest circle for which every boundary point of each circle lies within or on the boundary of the containing circle.
For that, you can just run recursively: find the smallest circle containing the first two circles (and that circle's center lies on the line connecting the two centers, and its radius should be simple to determine as well), replace the first two circles with the new circle, and repeat.
The correctness of this algorithm belongs in mathematics.
It is very difficult problem, I would just outline possible ways to it, you have to finish it yourself. I assume you want to find minimal bounding circle.
Formalizing your problem - having xi, yi, ri for i = 1..N, you are looking for point [x, y] such that:
max(distance([x, y], [xi, yi]) + ri)
is minimal. This is a non-trivial minimax problem. First look at the simpler version of this problem Smallest circle problem, which is just for points:
max(distance([x, y], [xi, yi]))
So first try to improve the algorithms proposed in the above link to solve the more complex case. If this way is impassable, you might probably need to go for quadratic programming. Good luck...

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