Here's a way to do this without using rasterization, that is, using only pure numbers.
Note: This is not O(n log n), more like O(n^2). It is, however, a solution. Whether it is an answer, probably not if O(n log n) is a requirement.
- Create a list of all the unique X-coordinates of the left and right edges (in the same list)
- When you build this list, for each coordinate, also associate a list of rectangles with it, and denote in this list whether the X-coordinate the list is associated with is the left or the right edge of the rectangle
- Sort the list of coordinates so that it is ascending, going from left to right
- Create a new list of rectangles, initially empty
- Run through the list of coordinates, and process the associated list of rectangles for it
- All rectangles in the associated list that are denoted as having the coordinate as the left edge should be added to the list you created in 4.
- All rectangles with the coordinate as the right edge should be removed.
- The order of adding and removing should actually be done in the opposite order, first you want to remove the right-edge-rectangles, and then you add all the left-edge-rectangles
- Now, before you remove the rectangles from the list you created in 4, you want to process them. You process them by treating them as "sub-rectangles". Their "new" left edge coordinate is the X-coordinate you processed the previous iteration of the loop (in 5), and the new right edge is the current X-coordinate being processed
- The output of the algorithm is a collection of coordinate-pairs (the two X-coordinates left and right), each pair having an associated list of rectangles (the vertical strip)
The output should thus be:
- X=1..2, Rects=A
- X=2..3, Rects=A, B
- X=3..4, Rects=A, B, C
- X=4..5, Rects=A, C
- X=5..6, Rects=C
Let me illustrate the process so far
+-------------------+
|A |
| +----------+-----+
| |C | |
| +-----+----+ | |
| |B | | | |
| | +----+-----+-----+
| | | |
+--+----------+-----+
| |
+----------+
^ ^ ^ ^ ^ ^
1 2 3 4 5 6 <-- X-coordinates
Associated rectangles:
- left: A, right: (none)
- left: B, right: (none)
- left: C, right: (none)
- left: (none), right: B
- left: (none), right: A
- left: (none), right: C
You now create an empty list, L=[]
, and start processing the coordinates and associated rectangles:
X=1
List is empty, nothing to process
Nothing to remove
Add A: L=[ A ]
X=2
List contains rectangles, process list as rectangles that have a left edge of X=1, and a right edge of X=2 (the two coordinates we've processed so far), and use their original top and bottom coordinates.
Nothing to remove.
Add B: L=[ A, B ]
X=3
List contains rectangles, process list (both A and B) the same way, treat them as temporarily having left and right coordinates as X=2 and X=3, and use their original top and bottom coordinates.
Nothing to remove
Add C: L=[ A, B, C ]
X=4
Process the three rectangles the same way as above, temporary left and right coordinates are X=3 and X=4
Remove B: L=[A, C ]
Nothing to add
X=5 and X=6
Process these in the exact same manner.
This means you will end up with "strips" of rectangles, like this (I've pulled them a bit apart to clearer illustrate that they are strips, but they are located side-by-side continously like in the original diagram):
+--+ +-----+ +----+ ------+
|A | | | | | | |
| | | | +----+ +-----+ +-----+
| | | | |C | | | | |
| | +-----| +----+ | | | |
| | |B | | | | | | |
| | | | +----+ +-----| +-----+
| | | | | | | |
+--+ +-----+ +----+ +-----+
| | | |
+-----+ +----+
^ ^ ^ ^ ^ ^ ^ ^ ^ ^
1 2 2 3 3 4 4 5 5 6
Ok, so now you have your output, a collection of coordinate-pairs, each pair having an associated list of rectangles.
Now we do a trick. We process the vertical strip in the exact same manner, only this time we use the Y coordinates as the delimiters. Let's handle the strip between 3 and 4 above. Remember that the strip has a left and right coordinates of 3 and 4.
Strip looks like this:
^ +----+ <-- 1
A | |
| ^ +----+ <-- 2
| C | |
| ^ | +----+ <-- 3
| B | | |
| | V +----+ <-- 4
| | | |
V | +----+ <-- 5
| | |
V +----+ <-- 6
List of coordinates with rectangles:
- Top=A, Bottom=(none)
- Top=C, Bottom=(none)
- Top=B, Bottom=(none)
- Top=(none), Bottom=C
- Top=(none), Bottom=A
- Top=(none), Bottom=B
New empty list L=[]
Process the coordinates:
Y=1
Nothing to process (L=[])
Add A to list, L=[ A ]
Y=2
Process A with temporarily having a top and bottom coordinates of Y=1 and 2 (and remember that it also has a temporary left and right coordinates of X=3 and 4
Add C, L=[ A, C ]
Y=3
Process A and C, both now having temporary coordinates of (3, 2)-(4, 3)
Add B, L=[ A, B, C ]
Y=4
Process A, B and C, all having coordinates of (3, 3)-(4, 4)
Remove C, L=[ A, B ]
Y=5
Process A and B, coordinates (3, 4)-(4, 5)
Remove A, L=[ B ]
Y=6
Process B, coordinates (3, 5)-(4, 6)
Final output:
pairs of Y-coordinates, with rectangles associated with them (that also have temporarily got new X-coordinates):
- (3, 1)-(4, 2) - A
- (3, 2)-(4, 3) - A, C
- (3, 3)-(4, 4) - A, B, C
- (3, 4)-(4, 5) - A, B
- (3, 5)-(4, 6) - B
Now, suppose you want to ask the question: Is B fully covered by all any combination of the other rectangles.
The answer can be worked out as follows:
- Loop through all the rectangles in the final list above
- If B is NOT part of the associated rectangle, ignore this rectangle
- If there is any other of the original rectangles associated with the coordinates, then this part of B is covered by that/those rectangle(s)
- If there is no other of the original rectangles associated with the coordinates, then this part of B is not covered.
In the above example, we see that the 3rd and 4rd rectangle in the final list contains B, but also contains other rectangles, hence those parts of B is covered, but the final rectangle in the list also contains B, but no other rectangle, hence this part is not covered.
According to the original diagram, the final result would include rectangles as follows (the letters inside each denote which original rectangle is associated with this new rectangle):
+--+-----+----+-----+
|A |A |A |A |
| | +----+-----+-----+
| | |AC |AC |C |
| +-----+----+ | |
| |AB |ABC | | |
| | +----+-----+-----+
| | |AB |A |
+--+-----+----+-----+
|B |B |
+-----+----+
If we now take a new look at the original diagram, I have shaded out the parts that the above algorithm would find contains B, but no other rectangle:
+-------------------+
|A |
| +----------+-----+
| |C | |
| +-----+----+ | |
| |B | | | |
| | +----+-----+-----+
| | | |
+--+-----+----+-----+
|#####|####|
+-----+----+
The vertical bar in the middle there is to illustrate that the part would be returned as two rectangles, split at that location, due to the way the vertical strips were worked out.
I seriously hope I made myself understood here. I have some code that can help you with the implementation of each run through the lists of coordinates, but it's 01:21 past midnight here and I'm going to bed, but leave a comment if you wish to see some actual code for this.
Or it would be a great exercise to implement it yourself :)
Here's the link to the class containing the method in question: RangeExtensions.cs.
The method is the Slice
method, just search the page for it. The type in question, Range, is basically a range from one value to another, so there's a bit of data construction and maintenance in addition to the above algorithm.