Problem: Please implement a function which gets area of a set of rectangles, whose edges are parallel to Xaxis or Yaxis.
A solution to split overlapping rectangles is based on xvalues of all left and right edges. For example, there are three rectangles in Figure 1.
We get all xvalues of the three rectangles, which are X1, X2, …, X6 in Figure 2, and then split the merged shape into five rectangles based on xvalues.
Since ranges of xvalues and yvalues are necessary information to calculate area, we can define a class Range:
The function InsertRangeY inserts a range of yvalue into a list. When the range overlaps with another range existing in the list, we should remove the existing range and then insert a new merged range.
void InsertRangeY(list<Range>& rangesOfY, Range& rangeY)
Rectangles are defined as the following:
/* === Assumming: left <= right; top <= bottom === */
struct Rect
{
int left;
int right;
int top;
int bottom;
};
Analysis: The merged shape of overlapping rectangles is no longer a rectangle, and there is no equation to calculate the merged area directly. Therefore, we should split it into a set of rectangles.
A solution to split overlapping rectangles is based on xvalues of all left and right edges. For example, there are three rectangles in Figure 1.
Figure 1: Three rectangles

We should merge yvalues of rectangles in ranges of xvalue. For instance, both Rect1 and Rect2 fill into the xvalue range between X2 and X3. Therefore, we should merge the yvalue range of Rect1 and Rect2 to calculate the rectangle piece between X2 and X3.
Figure 2: Split rectangles into pieces based on xvalues

struct Range
{
int less;
int greater;
Range(int l, int g)
{
less = (l < g) ? l : g;
greater = (l + g)  less;
}
bool IsOverlapping(const Range& other)
{
return !(less > other.greater  other.less > greater);
}
void Merge(const Range& other)
{
if(IsOverlapping(other))
{
less = (less < other.less) ? less : other.less;
greater = (greater > other.greater) ? greater : other.greater;
}
}
};
The overall algorithm to calculate the area of overlapping rectangles is: We firstly get all xvalues of all rectangles, and then check whether every rectangle fills into each range formed by two neighboring xvalues. If there are multiple rectangles fill into an xvalue range, we merge their ranges of yvalue before we calculate the area of a piece of merged rectangle.
The following function GetArea implements this algorithm. For optimization purpose, we sort all input rectangles according to their xvalues of right edges, and sort a vector of all xvalues.
int GetArea(vector<Rect>& rects)
{
// sort rectangles according to xvalue of right edges
sort(rects.begin(), rects.end());
vector<int> xes;
GetAllX(rects, xes);
sort(xes.begin(), xes.end());
int area = 0;
vector<int>::iterator iterX1 = xes.begin();
vector<Rect>::const_iterator iterRect = rects.begin();
for(; iterX1 != xes.end() && iterX1 != xes.end()  1; ++ iterX1)
{
vector<int>::iterator iterX2 = iterX1 + 1;
// filter out duplicated Xes
if(*iterX1 < *iterX2)
{
Range rangeX(*iterX1, *iterX2);
while(iterRect>right < *iterX1)
++ iterRect;
list<Range> rangesOfY;
GetRangesOfY(rects, iterRect, rangeX, rangesOfY);
area += GetRectArea(rangeX, rangesOfY);
}
}
return area;
}
bool operator < (const Rect& rect1, const Rect& rect2)
{
return (rect1.right < rect2.right);
}
The following function GetAllX gets all xvalues of all input rectangles.
void GetAllX(const vector<Rect>& rects, vector<int>& xes)
{
vector<Rect>::const_iterator iter = rects.begin();
for(; iter != rects.end(); ++ iter)
{
xes.push_back(iter>left);
xes.push_back(iter>right);
}
}
The function GetRangesOfY gets the ranges of yvalues which fill into a range of xvalue. It should be noticed that there might be multiple nonoverlapping rectangles fill into an xvalue range. For example, Rect1 and Rect2 in Figure do not overlap with each other. Therefore, we cannot merge their ranges of yvalues. That is why it has a list of ranges of yvalues for a range of xvalue in the function GetRangesOfY.
Figure 3: Another example of three overlaping rectangles 
void GetRangesOfY(const vector<Rect>& rects, vector<Rect>::const_iterator iterRect, const Range& rangeX, list<Range>& rangesOfY)
{
for(; iterRect != rects.end(); ++ iterRect)
{
if(rangeX.less < iterRect>right && rangeX.greater > iterRect>left)
InsertRangeY(rangesOfY, Range(iterRect>top, iterRect>bottom));
}
}
The function InsertRangeY inserts a range of yvalue into a list. When the range overlaps with another range existing in the list, we should remove the existing range and then insert a new merged range.
void InsertRangeY(list<Range>& rangesOfY, Range& rangeY)
{
list<Range>::iterator iter = rangesOfY.begin();
while(iter != rangesOfY.end())
{
if(rangeY.IsOverlapping(*iter))
{
rangeY.Merge(*iter);
list<Range>::iterator iterCopy = iter;
++ iter;
rangesOfY.erase(iterCopy);
}
else
++ iter;
}
rangesOfY.push_back(rangeY);
}
The function GetRectArea below gets the total area of all rectangle pieces in a range of xvalue.
int GetRectArea(const Range& rangeX, const list<Range>& rangesOfY)
{
int width = rangeX.greater  rangeX.less;
list<Range>::const_iterator iter = rangesOfY.begin();
int area = 0;
for(; iter != rangesOfY.end(); ++ iter)
{
int height = iter>greater  iter>less;
area += width * height;
}
return area;
}
The author Harry He owns all the rights of this post. If you are going to use part of or the whole of this ariticle in your blog or webpages, please add a reference to http://codercareer.blogspot.com/. If you are going to use it in your printed books, please contact me (zhedahht@gmail.com) . Thanks.
No comments:
Post a Comment