How can i extract rectangles from a rectangle intersection - algorithm

Having a rectangle (A) and intersecting it with another rectangle (B), how could I extract the other rectangles created through that intersection (C,D,E & F)?
AAAAAAAAAAAAAA CCCFFFFDDDDDDD
AAABBBBAAAAAAA CCCBBBBDDDDDDD
AAABBBBAAAAAAA -> CCCBBBBDDDDDDD
AAAAAAAAAAAAAA CCCEEEEDDDDDDD
AAAAAAAAAAAAAA CCCEEEEDDDDDDD
And could this be extended to extract rectangles from several intersections, such as this example which intersects A with B & C and extracts D, E, F & G?
BBBBAAAAAAAAAA BBBBDDDDDDDDDD
BBBBAAAAAAAAAA BBBBDDDDDDDDDD
AAAAAACCCCCAAA -> EEEEEECCCCCFFF
AAAAAACCCCCAAA EEEEEECCCCCFFF
AAAAAAAAAAAAAA EEEEEEGGGGGFFF

If the answer to TJB's question is yes, then they are:
(left, top, right, bottom) notation
C = (A.left, A.top, B.left, A.bottom)
D = (B.right, A.top, A.right, A.bottom)
E = (B.left, B.bottom, B.right, A.bottom)
E = (B.left, A.top, B.right, B.top)

Assuming B is completely Contained in A, it would be something like:
Rectangle[] GetSurrounding( Rectangle outer, Rectangle inner )
{
Rectangle left, top, right, bottom; // Initialize all of these...
left = new Rectangle( outer.Left, outer.Top, outer.Height, inner.Left - outer.Left );
top = new Rectangle( inner.Left, outer.Top, inner.Top - outer.Top, inner.Width );
// So on and so forth...
return new Rectangle[]{ left, top, right, bottom };
}
// This assumes:
Rectangle( x , y , height, width ); // Constructor
Also, deciding weather you stretch the left and right rectangles the full height or the top and bottom rectangles the full width is arbitrary, and will either need to be a constant decision or a parameter to the method. Other cases where the rectangles only partially overlap will require more logic looking at the MAX/MIN of values to check for going out of bounds etc.

If A completly contains B:
Rectange C = new Rectangle(A.X,A.Y,B.X-A.X,A.Height);
Rectange D = new Rectangle(B.Right,A.Y,A.Right-B.Right,A.Height);
Rectange E = new Rectangle(B.X,B.Bottom,B.Width,A.Bottom-A.Bottom);
Rectange F = new Rectangle(B.X,A.Y,B.Width,B.Y-A.Y);
this is .NET, I'm not sure about the language of your code, but I think most of the structures look simular in different languages, in .NET the constructor of a System.Drawing.Rectangle is (X,Y,Width,Height)

for more arbitrary shapes a scanline algorithm would work. you will get different results depending on whether you scan horizontally or vertically (your example matches a vertical scan)
essentially you scan along each column or row and break it into intervals between each shape, intervals on the next column or row with the same start and end can be merged.

Given a large rectangle with any number of smaller rectangles punched out of it, you can use a greedy algorithm to break up the remaining area of the large rectangle into smaller rectangles.
Pick the leftmost, uppermost point that hasn't been covered yet.
Start a rectangle there.
Extend it downwards as far as it can go.
Then extend it rightwards as far as it can go.
Add that rectangle to your collection and repeat.
This is not guaranteed to produce the minimum number of rectangles.
The first step is the most complicated one. If you don't mind a little randomness, an easier thing to do would be to pick random points until you find one that isn't covered yet; then go left until you hit an edge; then go up until you hit an edge.

For a general solution to this (the second half of your question), you should use a corner-stitching data structure, which does exactly this (and more).

for all rectangles A
for all corners C of A
for all other rectangles B
if C is inside B
for all corners D of B
if D is inside A
got rectangle C-D
endif
endfor
endif
endfor
endfor
endfor

Related

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

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

Sort a set of 3-D points in clockwise/counter-clockwise order

In 3-D space I have an unordered set of, say, 6 points; something like this:
(A)*
(C)*
(E)*
(F)*
(B)*
(D)*
The points form a 3-D contour but they are unordered. For unordered I mean that they are stored in an
unorderedList = [A - B - C - D - E - F]
I just want to reorganize this list starting from an arbitrary location (let's say point A) and traversing the points clockwise or counter-clockwise. Something like this:
orderedList = [A - E - B - D - F - C]
or
orderedList = [A - C - F - D - B - E]
I'm trying to implement an algorithm as simple as possible, since the set of points in mention corresponds to a N-ring neighborhood of each vertex on a mesh of ~420000 points, and I have to do this for each point on the mesh.
Some time ago there was a similar discussion regarding points in 2-D, but for now it's not clear for me how to go from this approach to my 3-D scenario.
The notion of "clockwise" or "counterclockwise" is not well-defined without an axis and orientation! (proof: What if you looked at those points from the other side of your monitor screen, or flipped them, for example!)
You must define an axis and orientation, and specify it as an additional input. Ways to specify it include:
a line (1x=2y=3z), using the right-hand rule
a (unit) vector (A_x, A_y, A_z), using the right-hand rule; this is the preferred way to do so
In order to determine the orientation, you have to look deeper at your problem: You must define a "up" and "down" size of the mesh. Then for each set of points, you must take the centroid (or another "inside" point) and construct a unit vector pointing "up" which is normal to the surface. (One way to do this would be to find the least-squares-fit plane, then find the two perpendicular vectors through that point, picking the one in the "up" direction.)
You will need to use any of the above suggestions to determine your axis. This will allow you to reformulate your problem as follows:
Inputs:
the set of points {P_i}
an axis, which we shall call "the z-axis" and treat as a unit vector centered on the centroid (or somewhere "inside") of the points
an orientation (e.g. counterclockwise) chosen by one of the above methods
Setup:
For all points, pick two mutually-orthogonal unit vectors to the axis, which we shall call "the y-axis" and "the x-axis". (Just rotate the z-axis unit-vector 90 degrees in two directions, http://en.wikipedia.org/wiki/Rotation_matrix#Basic_rotations )
Algorithm:
For each point P, project P onto the x-axis and y-axis (using the dot product), then use http://en.wikipedia.org/wiki/Atan2
Once you have the angles, you can just sort them.
I can't attest for the efficiency of this code, but it works, and you can optimize parts of it as needed, I'm just not good at it.
Code is in C#, using system collection classes, and linq.
Vector3 is a class with floats x, y, z, and static vector math functions.
Node is a class with Vector3 variable called pos
//Sort nodes with positions in 3d space.
//Assuming the points form a convex shape.
//Assuming points are on a single plain (or close to it).
public List<Node> sortVerticies( Vector3 normal, List<Node> nodes ) {
Vector3 first = nodes[0].pos;
//Sort by distance from random point to get 2 adjacent points.
List<Node> temp = nodes.OrderBy(n => Vector3.Distance(n.pos, first ) ).ToList();
//Create a vector from the 2 adjacent points,
//this will be used to sort all points, except the first, by the angle to this vector.
//Since the shape is convex, angle will not exceed 180 degrees, resulting in a proper sort.
Vector3 refrenceVec = (temp[1].pos - first);
//Sort by angle to reference, but we are still missing the first one.
List<Node> results = temp.Skip(1).OrderBy(n => Vector3.Angle(refrenceVec,n.pos - first)).ToList();
//insert the first one, at index 0.
results.Insert(0,nodes[0]);
//Now that it is sorted, we check if we got the direction right, if we didn't we reverse the list.
//We compare the given normal and the cross product of the first 3 point.
//If the magnitude of the sum of the normal and cross product is less than Sqrt(2) then then there is more than 90 between them.
if ( (Vector3.Cross( results[1].pos-results[0].pos, results[2].pos - results[0].pos ).normalized + normal.normalized).magnitude < 1.414f ) {
results.Reverse();
}
return results;
}

Inverting a set of rectangles on a 2D plane

I have a rectangular plane of integer dimension. Inside of this plane I have a set of non-intersecting rectangles (of integer dimension and at integer coordinates).
My question is how can I efficiently find the inverse of this set; that is the portions of the plane which are not contained in a sub-rectangle. Naturally, this collection of points forms a set of rectangles --- and it is these that I am interested in.
My current, naive, solution uses a boolean matrix (the size of the plane) and works by setting a point i,j to 0 if it is contained within a sub-rectangle and 1 otherwise. Then I iterate through each element of the matrix and if it is 1 (free) attempt to 'grow' a rectangle outwards from the point. Uniqueness is not a concern (any suitable set of rectangles is fine).
Are there any algorithms which can solve such a problem more effectively? (I.e, without needing to resort to a boolean matrix.
Yes, it's fairly straightforward. I've answered an almost identical question on SO before, but haven't been able to find it yet.
Anyway, essentially you can do this:
start with an output list containing a single output rect equal to the area of interest (some arbitrary bounding box which defines the area of interest and contains all the input rects)
for each input rect
if the input rect intersects any of the rects in the output list
delete the old output rect and generate up to four new output
rects which represent the difference between the intersection
and the original output rect
Optional final step: iterate through the output list looking for pairs of rects which can be merged to a single rect (i.e. pairs of rects which share a common edge can be combined into a single rect).
Alright! First implementation! (java), based of #Paul's answer:
List<Rectangle> slice(Rectangle r, Rectangle mask)
{
List<Rectangle> rects = new ArrayList();
mask = mask.intersection(r);
if(!mask.isEmpty())
{
rects.add(new Rectangle(r.x, r.y, r.width, mask.y - r.y));
rects.add(new Rectangle(r.x, mask.y + mask.height, r.width, (r.y + r.height) - (mask.y + mask.height)));
rects.add(new Rectangle(r.x, mask.y, mask.x - r.x, mask.height));
rects.add(new Rectangle(mask.x + mask.width, mask.y, (r.x + r.width) - (mask.x + mask.width), mask.height));
for (Iterator<Rectangle> iter = rects.iterator(); iter.hasNext();)
if(iter.next().isEmpty())
iter.remove();
}
else rects.add(r);
return rects;
}
List<Rectangle> inverse(Rectangle base, List<Rectangle> rects)
{
List<Rectangle> outputs = new ArrayList();
outputs.add(base);
for(Rectangle r : rects)
{
List<Rectangle> newOutputs = new ArrayList();
for(Rectangle output : outputs)
{
newOutputs.addAll(slice(output, r));
}
outputs = newOutputs;
}
return outputs;
}
Possibly working example here
You should take a look for the space-filling algorithms. Those algorithms are tyring to fill up a given space with some geometric figures. It should not be to hard to modify such algorithm to your needs.
Such algorithm is starting from scratch (empty space), so first you fill his internal data with boxes which you already have on the 2D plane. Then you let algorithm to do the rest - fill up the remaining space with another boxes. Those boxes are making a list of the inverted space chunks of your plane.
You keep those boxes in some list and then checking if a point is on the inverted plane is quite easy. You just traverse through your list and perform a check if point lies inside the box.
Here is a site with buch of algorithms which could be helpful .
I suspect you can get somewhere by ordering the rectangles by y-coordinate, and taking a scan-line approach. I may or may not actually contruct an implementation.
This is relatively simple because your rectangles are non-intersecting. The goal is basically a set of non-intersecting rectangles that fully cover the plane, some marked as original, and some marked as "inverse".
Think in terms of a top-down (or left-right or whatever) scan. You have a current "tide-line" position. Determine what the position of the next horizontal line you will encounter is that is not on the tide-line. This will give you the height of your next tide-line.
Between these tide-lines, you have a strip in which each vertical line reaches from one tide-line to the other (and perhaps beyond in both directions). You can sort the horizontal positions of these vertical lines, and use that to divide your strip into rectangles and identify them as either being (part of) an original rectangle or (part of) an inverse rectangle.
Progress to the end, and you get (probably too many too small) rectangles, and can pick the ones you want. You also have the option (with each step) of combining small rectangles from the current strip with a set of potentially-extendible rectangles from earlier.
You can do the same even when your original rectangles may intersect, but it's a little more fiddly.
Details left as an exercise for the reader ;-)

Laying out overlapping rectangles

I am trying to layout a bunch of overlapping rectangles that start out like this:
alt text http://img690.imageshack.us/img690/209/picture1bp.png
The 2-pass algorithm I thought up is roughly:
// Pass 1 - Move all rectangles to the right until they do not overlap any other rectangles
rects = getRectsSortedOnTopLeft(); // topmost first, all rects same size
foreach(rect in rects)
{
while(rect.collidingRects().size() != 0)
{
rect.x += RECT_SIZE;
}
}
This (probably) ends up with rectangles laid out like:
alt text http://img685.imageshack.us/img685/9963/picture2bc.png
This is not aesthetically pleasing so I thought of a second pass which would move them all left starting again from the topmost:
// Pass 2
foreach(rect in rects)
{
while(rect.x >= LEFT_MARGIN)
{
assert(rect.collidingRects().size() == 0);
rect.x -= RECT_WIDTH;
if(rect.collidingRects().size() != 0)
{
rect.x += RECT_WIDTH;
break;
}
}
}
I think this should end up looking like below (looks exactly correct in practice):
alt text http://img511.imageshack.us/img511/7059/picture3za.png
However, I am wary of this algorithm because I am not sure if it will lay out correctly in all cases and it may be really slow. Do you think this algorithm can work? Can you make some suggestions on a better algorithm?
I think that this problem is of polynomial complexity. Assuming your example's limitation of only two rectangles overlapping at any given point is not a true limitation of the problem, you would need to try every possible order of bumping the rectangles to the right in order to produce the optimal (least wide) result. This is a form of space packing problem, and those are Hard unless your data set is small enough to brute force.
However, one small improvement to your pseudocode is possible, which would improve its performance in many cases.
Consider this desired final result:
A
A C
A C E
A C E
B C E
B D E
B D F
B D F
D F
F
(where all four of one character are a single rectangle)
Your first pass would move everything except A to the right, forming a staircase. Then in the second pass your code would decline to move B to the left margin, because the first attempt to move it would overlap with E. What you need to do is start at the left margin and check for the leftmost position you can move each rectangle to in pass 2.
Pseudocode:
// Pass 1 - Move all rectangles to the right until they do not overlap any other rectangles
rects = getRectsSortedOnTopLeft(); // topmost first, all rects same width
foreach(rect in rects)
while(rect.collidingRects())
rect.x += RECT_WIDTH;
// Pass 2 - Move all rectangles to the leftmost position in which they don't overlap any other rectangles
foreach(rect in rects)
for(i=LEFT_MARGIN; i+=RECT_WIDTH; i<rect.x)
{
o = rect.x;
rect.x = i;
if(rect.collidingRects())
rect.x = o;
}
You could use a physics-based approach, where the blocks are rigid bodies an fall to the left:
No, this wouldn't produce the best result all the time, but having watched your screencast I think it would be very intuitive to use in an interactive program, and it might be suitable :)

Fast Collision Detection for Circle Insertion into 2D Plane

I know there are lots of posts about collision detection generally for sprites moving about a 2D plane, but my question is slightly different.
I'm inserting circles into a 2D plane. The circles have variable radii. I'm trying to optimize my method of finding a random position within the plane where I can insert a new circle without it colliding with any other circles already on the plane. Right now I'm using a very "un-optimized" approach that simply generates a random point within the plane and then checks it against all the other circles on the plane.
Are there ways to optimize this? For this particular app, the bounds of the plane can only hold 20-25 circles at a time and typically there are between 5-10 present. As you would expect, when the number of circles approaches the max that can fit, you have to test many points before finding one that works. It gets very slow.
Note: safeDistance is the radius of the circle I want to add to the plane.
Here's the code:
- (CGPoint)getSafePosition:(float)safeDistance {
// Point must be far enough from edges
// Point must be far enough from other sprites
CGPoint thePoint;
BOOL pointIsSafe = NO;
int sd = ceil(safeDistance);
while(!pointIsSafe) {
self.pointsTested++; // DEBUG
// generate a random point inside the plane boundaries to test
thePoint = CGPointMake((arc4random() % ((int)self.manager.gameView.frame.size.width - sd*2)) + sd,
(arc4random() % ((int)self.manager.gameView.frame.size.height - sd*2)) + sd);
if(self.manager.gameView.sprites.count > 0) {
for(BasicSprite *theSprite in self.manager.gameView.sprites) {
// get distance between test point and the sprite position
float distance = [BasicSprite distanceBetweenPoints:thePoint b:theSprite.position];
// check if distance is less than the sum of the min safe distances of the two entities
if(distance < (safeDistance + [theSprite minSafeDistance])) {
// point not safe
pointIsSafe = NO;
break;
}
// if we get here, the point did not collide with the last tested point
pointIsSafe = YES;
}
}
else {
pointIsSafe = YES;
}
}
return thePoint;
}
Subdivide your window into w by h blocks. You'll be maintaining a w by h array, dist. dist[x][y] contains the size of the largest circle that can be centred at (x, y). (You can use pixels as blocks, although we'll be updating the entire array with each circle placed, so you may want to choose larger blocks for improved speed, at the cost of slightly reduced packing densities.)
Initialisation
Initially, set all dist[x][y] to min(x, y, w - x, h - y). This encodes the limits given by the bounding box that is the window.
Update procedure
Every time you add a circle to the window, say one positioned at (a, b) with radius r, you need to update all elements of dist.
The update required for each position (x, y) is:
dist[x][y] = min(dist[x][y], sqrt((x - a)^2 + (y - b)^2) - r);
(Obviously, ^2 here means squaring, not XOR.) Basically, we are saying: "Set dist[x][y] to the minimum distance to the circle just placed, unless the situation is already worse than that." dist values for points inside the circle just placed will be negative, but that doesn't matter.
Finding the next location
Then, when you want to insert the next circle of radius q, just scan through dist looking for a location with dist value >= q. (If you want to randomly choose such a location, find the complete list of valid locations and then randomly choose one.)
Honestly, with only 20-25 circles, you're not going to get much of a speed boost by using a fancier algorithm or data structure (e.g. a quadtree or a kd-tree). Everything is fast for small n.
Are you absolutely sure this is the bottleneck in your application? Have you profiled? If yes, then the way you're going to speed this up is through microoptimization, not through advanced algorithms. Are you making lots of iterations through the while loop because most of the plane is unsafe?
You could split your plane in lots of little rectangles (slightly quadtree-related) and save which rectangles are hit by at least one of the circles.
When you look for a insertion-point, you'll just have to look for some "empty" ones (which doesn't need any random jumps and is possible in constant time).
The number and constellation of rectangles can be computed by the radius.
Just an outline, since this solution is fairly involved.
If you want to guarantee you always find a place to put a circle if it's possible, you can do the following. Consider each existing circle C. We will try to find a location where we can place the new circle so that it is touching C. For each circle D (other than C) that is sufficiently close to C, there will be a range of angles where placing a new circle at one of those angles around C will make it intersect with D. Some geometry will give you that range. Similarly, for each of the four boundaries that are close enough to C, there will be a range of angles where placing a new circle at one of those angles will make it intersect with the boundary. If all these intervals cover all 360 degrees around C, then you cannot place a circle adjacent to C, and you will have to try the next circle, until there are no more candidates for C. If you find a place to put the new circle, you can move it some random distance away from C so that all your new circles do not have to be adjacent to an existing circle if that is not necessary.

Resources