how to simulate a rectangle union starting with a rectangle intersection - algorithm

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?

Related

Finding all the rectangles within a given rectangle that do not intersect with an arbitrary shape

I need to find an algorithm to find the least number of overlapping rectangles within a given rectangle R that do not intersect with the union of a set of other rectangles contained within R. These inner rectangles can overlap. Here's a terrible ASCII drawing of an example R:
A-----------------B-------------------------+
| |
| |
| |
| |
| +--------+ |
| |........| |
| |........| |
C +---D........| |
| |.........+--+ |
| |.........| |
| ++........+------+ |
| |...............| |
G +---H...........| |
| |...........| |
| |...........| |
| |...........| |
| +-----------+ |
| |
| |
| |
E-------------I----F------------------------+
The rectangles here would include (A, D), (A, I), (G, F). This seems like a problem for which the solution is well-understood, but for which I simply lack the vocabulary. Any answers, pointers, RTFMs cheerfully and gratefully accepted. Thanks!
EDIT: As per #Paul's observation below, what I'm trying to do is find a set of rectangles that cover the space in R without covering any of polygon comprised of the union of the inner set. If that makes sense.
I believe this is one possible way to solve.
I will refer to the overlapping rectangles as "white", and the ones in the solution as "black"
First of all, let's assume we have a data structure suitable for search on intersection. One possible data structure is an Interval Tree, using points on one of the coordinates as intervals (for example, if a rectangle is defined by two points (x0,y0) and (x1, y1), use (x0, y1) as interval. The link also explain how to extend to higher dimensions (in your case you need 2).
I won't go in the detail of an implementation of such data structure, but let's assume we have one called Rectangles, with the following API defined:
void add(Rectangle r)
void remove(Rectangle r)
Rectangle[] getIntersecting(Rectangle r)
Rectangle[] getAdjacent(Rectangle r)
Ok, now create two instances of Rectangles called black and white. Initialize white with all the white rectangles. Initializie black with the R rectangle (the whole domain).
For each rectangle rw in white get the array arr of intersecting rectangles from black.
For each rectangle rb in black, determine the result of rw-rb. This is a set set of 0, 1, 2, 3 or 4 rectangles, depending on how the two rectangles intersect.
remove rw from white, and add the content of set to it. This may require merging rectangles from set with rectangle already in white, if such rectangles together form a larger rectangle (they're adjacent sharing one side)
remove rb from black
repeat from 1 until there are no more rectangles in black
Using some basic math, we could say the solution to your problem would be the decomposition of the rectilinear polygon R \ union(rs), where union(rs) represents the polygon inside R. Calculating R \ union(rs) can be done using the Greiner-Hormann-algorithm. Note that this step will result in a polygon with holes and - only if the inner polygon contains holes - multiple other polygons. The decomposition is described here (this is only an approximation, but i wasn't able to find a precise algorithm so far).

Efficient way to combine intersecting bounding rectangles

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

Minimum area enclosing rectangles?

I need an idea for an algorithm to solve the following problem (I already tried some personal
solutions but they don't seem to be optimal)
If given a surface with marked and unmarked zones (in matrix form), and 2 rectangles
that you can manipulate in any form or position, find the possible shape and position
of the rectangles such that they cover all the marked zones while keeping the minimum
surface area possible.
This answer is assuming you can not rotate the rectangles and the sides are always parallel to the x and y axis.
First, find the rectangle that encloses the complete area. An algorithm for that goes like this (assuming the origin is at the topleft):
For each marked spot in the matrix:
if spot.x < rectangle.left:
rectangle.left = spot.x
if spot.x > rectangle.right:
rectangle.left = spot.x
if spot.y < rectangle.top:
rectangle.left = spot.x
if spot.y < rectangle.bottom:
rectangle.left = spot.x
Then, find the largest horizontal gap like this:
largest_gap = -1
For each column in matrix:
last_marked_spot = 0, 0
For each spot in column:
if spot.marked:
if spot.x - last_marked_spot.x > largest_gap:
largest_gap = spot.x - last_marked_spot.x
last_marked_spot = spot
Same goes for vertical gap. Then check which gap is the biggest.
Then divide the all-including rectangle in two parts using the biggest gap as seperator. The final step is to collapse the two rectangles (using the reverse of the algorithm on the top).

Finding shared volume of two overlapping cuboids

What is the most efficient way to find the shared space occupied by two overlapping cube objects?
I'm not necessarily looking for source code, just the general idea on how it should be solved.
To simplify, the algorithm doesn't have to take into account rotated cubes.
The overlap of two non-rotated cubes is still a 'box'. If two corner points of box A are (x,y,z) and (x',y',z') (x'>x,y'>y,z'>z) and two corner points of box B are (a,b,c) and (a',b',c') (a'>a,b'>b,c'>c) then the volume of the overlap is
max(min(a',x')-max(a,x),0)
* max(min(b',y')-max(b,y),0)
* max(min(c',z')-max(c,z),0)
How the read the formula:
The overlap starts on the X axis at the maximum of the two coordinates x and a and ends at the minimum of a' and x'. If a' < x (i.e. a < a' < x < x') then there's no overlap and what happens is that max(a,x) = x > min(a',x') = a', so the difference becomes negative and the volume is zero (hence the outer max(...,0)) term. The same applies for the other two axes.
If the cubes aren't rotated (axis aligned), the overlap of the dimensions is enough to describe the overlap of the cubes.
Consider the problem in 2 dimensions:
________
| S2 |
____|___ |
| | | |
| |___|____|
| S1 |
|________|
The overlapping area is described by a width of S1.xmax - S2.xmin, and a height of S1.ymax - S2.ymin. To determine the order of subtraction requires a couple of if tests. You may find there's no overlap at all. To do this for cubes, consider the z dimension in addition to x and y.
Compute the min/max in each dimension of each of your cubes and then test these against each other e.g. in the x direction where the cubes are labeled 1 and 2
XminInt;
if ( Xmin1 > Xmax2 )
{
// no intersection
}
else if ( Xmin1 >= Xmin2 )
{
// possible cube intersection
XminInt = Xmin1;
}
else if ( Xmin2 <= Xmax1 )
{
// possible cube intersection
XminInt = Xmin2;
}
Do something similar for the max and repeat both for y and z. If you hit the no intersection case in any of them then you can exit early. If you don't exit early in any of the six possible early exit clauses then you will have all six defining values for the intersection cube i.e. min/max for each dimension.
The six early returns are pretty much the simplest example there is of a separating axis method. Since your shapes are cubes and are axis-aligned the Cartesian axes are the possible separating axes. Testing is then a simple matter of comparing the min/max values as above.
Note that I have implemented the above method in C++ and confirmed that it works.
I have the same question and this is the best thread that comes up but the best answer does not address the question. After sometime, I have it solved. Antti answer calculates the volumne as a scalar rather than defining the overlapping cube points. Using his definition to define a cube, to get the shared space occupied by two overlapping cube objects, that shared cube is:
let box A be a vector (Ax,Ay,Az) and A' be (Ax',Ay',Az') with (Ax'>Ax,Ay'>Ay,Az'>Az)
let box B be a vector (Bx,By,Bz) and B' be (Bx',By',Bz') with (Bx'>Bx,By'>By,Bz'>Bz)
let box C be our new overlapping cube
then the intersection between the two cubes is:
C = Min(A', B');
C' = Max(A, B);
Note if the boxes touch only at a single point, then C = C'. If only one dimension is different, touches at a line, and if only 2 dimensions are different touches at a plane.

Positioning squares on a circle with minimum diameter

Given n squares with edge length l, how can I determine the minimum radius r of the circle so that I can distribute all squares evenly along the perimeter of the circle without them overlapping? (Constraint: the first square will always be positioned at 12 o'clock.)
Followup question: how can I place n identical rectangles with height h and width w?
(source: n3rd.org)
There may be a mathematically clever way to do this, but I wouldn't know.
I think it's complicated a bit by the fact that the geometry is different for every different number of squares; for 4 it's a rhombus, for 5 it's a pentagon and so on.
What I'd do is place those squares on a 1 unit circle (much too small, I know, bear with me) distributed equally on it. That's easy enough, just subtend (divide) your 360 degrees by the number of squares. Then just test all your squares for overlap against their neighbors; if they overlap, increase the radius.
You can make this procedure less stupid than it sounds by using an intelligent algorithm to approach the right size. I'm thinking of something like Newton's algorithm: Given two successive guesses, of which one is too small and one is too big, your next guess needs to be the average of those two.
You can iterate down to any precision you like. Stop whenever the distance between guesses is smaller than some arbitrary small margin of error.
EDIT I have a better solution:
I was thinking about what to tell you if you asked "how will I know if squares overlap?" This gave me an idea on how to calculate the circle size exactly, in one step:
Place your squares on a much-too-small circle. You know how: Calculate the points on the circle where your 360/n angles intersect it, and put the center of the square there. Actually, you don't need to place squares yet, the next steps only require midpoints.
To calculate the minimum distance of a square to its neighbor: Calculate the difference in X and the difference in Y of the midpoints, and take the minimum of those. The X's and Y's are actually just cosines and sines on the circle.
You'll want the minimum of any square against its neighbor (clockwise, say). So you need to work your way around the circle to find the very smallest one.
The minimum (X or Y) distance between the squares needs to become 1.0 . So just take the reciprocal of the minimum distance and multiply the circle's size by that. Presto, your circle is the right size.
EDIT
Without losing generality, I think it's possible to nail my solution down a bit so it's close to coding. Here's a refinement:
Assume the squares have size 1, i.e. each side has a length of 1 unit. In the end, your boxes will surely be larger than 1 pixel but it's just a matter of scaling.
Get rid of the corner cases:
if (n < 2) throw new IllegalArgumentException();
if (n == 2) return 0.5; // 2 squares will fit exactly on a circle of radius 0.5
Start with a circle size r of 0.5, which will surely be too small for any number of squares > 2.
r = 0.5;
dmin = 1.0; // start assuming minimum distance is fine
a = 2 * PI / n;
for (p1 = 0.0; p1 <= PI; p1+=a) { // starting with angle 0, try all points till halfway around
// (yeah, we're starting east, not north. doesn't matter)
p2 = p1 + a; // next point on the circle
dx = abs(r * cos(p2) - r * cos(p1))
dy = abs(r * sin(p2) - r * sin(p1))
dmin = min(dmin, dx, dy)
}
r = r / dmin;
EDIT
I turned this into real Java code and got something quite similar to this to run. Code and results here: http://ideone.com/r9aiu
I created graphical output using GnuPlot. I was able to create simple diagrams of boxes arranged in a circle by cut-and-pasting the point sets from the output into a data file and then running
plot '5.dat' with boxxyerrorbars
The .5's in the file serve to size the boxes... lazy but working solution. The .5 is applied to both sides of the center, so the boxes end up being exactly 1.0 in size.
Alas, my algorithm doesn't work. It makes the radii far too large, thus placing the boxes much further apart than necessary. Even scaling down by a factor of 2 (could have been a mistake to use 0.5 in some places) didn't help.
Sorry, I give up. Maybe my approach can be salvaged, but it doesn't work the way I had though it would. :(
EDIT
I hate giving up. I was about to leave my PC when I thought of a way to salvage my algorithm:
The algorithm was adjusting the smaller of the X or Y distances to be at least 1. It's easy to demonstrate that's just plain silly. When you have a lot of boxes then at the eastern and western edges of the circle you have boxes stacked almost directly on top of each other, with their X's very close to one another but they are saved from touching by having just enough Y distance between them.
So... to make this work, you must scale the maximum of dx and dy to be (for all cases) at least the radius (or was it double the radius?).
Corrected code is here: http://ideone.com/EQ03g http://ideone.com/VRyyo
Tested again in GnuPlot, it produces beautiful little circles of boxes where sometimes just 1 or 2 boxes are exactly touching. Problem solved! :)
(These images are wider than they are tall because GnuPlot didn't know I wanted proportional layout. Just imagine the whole works squeezed into a square shape :) )
I would calculate an upper bound of the minimum radius, by working with circles enclosing the squares instead of with the squares themselves.
My calculation results in:
Rmin <= X / (sqrt(2) * sin (180/N) )
Where:
X is the square side length, and N is the required number of squares.
I assume that the circles are positioned such that their centers fall on the big circle's circumference.
-- EDIT --
Using the idea of Dave in the comment below, we can also calculate a nice lower bound, by considering the circles to be inside the squares (thus having radius X/2). This bound is:
Rmin >= X / (2 * sin (180/N) )
As already noted, the problem of positioning n points equally spaced round the circumference of a circle is trivial. The (not-terribly) difficult part of the problem is to figure out the radius of the circle needed to give a pleasing layout of the squares. I suggest you follow one of the other answers and think of the squares being inside a circular 'buffer' big enough to contain the square and enough space to satisfy your aesthetic requirements. Then check the formula for the chord length between the centres of neighbouring squares. Now you have the angle, at the centre of the circle, subtended by the chord between square centres, and can easily compute the radius of the circle from the trigonometry of a triangle.
And, as to your follow up question: I suggest that you work out the problem for squares of side length min(h,w) on a circle, then transform the squares to rectangles and the circle to an ellipse with eccentricity h/w (or w/h).
I would solve it like this:
To find the relation between the radius r and length l let's analyze dimensionless representation
get the centres on a circle (x1,y1)..(xn,yn)
from each center get lower right corner of the i-th square and upper left corner of the i+1-th square
the two points should either have equal x or equal y, whichever yields smaller l
procedure should be repeated for each center and the one that yields smallest l is the final solution.
This is the optimal solution and can be solved it terms of r = f(l).
The solution can be adapted to rectangles by adjusting the formula for xLR[i] and yUL[i+1].
Will try to give some pseudo code.
EDIT:
There's a bug in the procedure, lower right and upper left are not necessary closest points for two neighbouring squares/rectangles.
Let's assume you solved the problem for 3 or 4 squares.
If you have n >= 5 squares, and position one square at the top of the circle, you'll have another square fall into the first quadrant of a cartesian plane concentric with your circle.
The problem is then to find a radius r for the circle such that the left side of the circle next to the top one, and the right side of the top circle do not 'cross' each other.
The x coordinate of the right side of the top circle is x1 = L/2, where L is the side of a square. The x coordinate of the left side of the circle next to the top one is x2 = r cos a - L/2, where r is the radius and a is the angle between each pair of square centres (a = 360/n degrees).
So we need to solve x1 <= x2, which leads to
r >= L / cos a.
L and a are known, so we're done :-)
You start with an arbitrary circle (e.g., with a diameter of (* n l)) and position the squares evenly on the circumference. Then you go through each pair of adjacent squares and:
calculate the straight line connecting their mid points,
calculate the intersection of this line with the intervening square sides (M1 and M2 are the mid points, S1 and S2 the corresponding intersections with the square side:
S2 S1
M1--------------*----------*---------------M2
------------------------
| |
| |
| |
| |
| M1 |
| \ |
| \ |
| -------*------- +--------
| | \ | |
| | \ | |
-------+---------*------ |
| \ |
| M2 |
| |
| |
| |
| |
-------------------------
calculate the scale factor you would need to make S1 and S2 fall together (simply the ratio of the sum of M1-S1 and S2-M2 to M1-M2), and
finally scale the circle by the maximum of the found scale factors.
Edit: This is the exact solution. However, a little thought can optimize this further for speed:
You only need to do this for the squares closest to 45° (if n is even) resp. 45° and 135° (if n is odd; actually, you might prove that only one of these is necessary).
For large n, the optimal spacing of the squares on the circle will quickly approach the length of a diagonal of a square. You could thus precompute the scaling factors for a few small n (up to a dozen or so), and then have a good enough approximation with the diagonal.

Resources