#ifndef __TEST_RECTANGLE_H__
#define __TEST_RECTANGLE_H__
/*
* Testing for rectangle overlap
*/
struct TestPoint {
int x;
int y;
TestPoint() : x(0), y(0) {}
TestPoint(int x, int y)
{
this->x = x;
this->y = y;
}
};
struct TestRect {
int m_left;
int m_top;
int m_right;
int m_bottom;
TestRect()
{
SetEmpty();
}
TestRect(const TestRect & aCopy)
{
SetRect(aCopy);
}
TestRect(int left, int top, int right, int bottom)
{
SetRect(left,top,right,bottom);
}
void SetEmpty()
{
m_left = m_top = m_right = m_bottom;
}
void SetRect(const TestRect & aCopy)
{
SetRect(aCopy.m_left,aCopy.m_top,aCopy.m_right,aCopy.m_bottom);
}
void SetRect(int left, int top, int right, int bottom)
{
m_left = left;
m_top = top;
m_right = right;
m_bottom = bottom;
}
TestPoint TopLeft() const
{
return TestPoint(m_top,m_left);
}
TestPoint TopRight() const
{
return TestPoint(m_top,m_right);
}
TestPoint BottomLeft() const
{
return TestPoint(m_bottom,m_left);
}
TestPoint BottomRight() const
{
return TestPoint(m_bottom,m_right);
}
bool IsEqual(const TestRect & other) const
{
return( (other.m_left == m_left ) && (other.m_top == m_top ) &&
(other.m_right == m_right) && (other.m_bottom == m_bottom) );
}
int Width() const
{
return m_right-m_left;
}
int Height() const
{
return m_bottom-m_top;
}
bool HasArea() const
{
return(Width() > 0 && Height() > 0);
}
bool PointOnRect(int x, int y) const
{
return( x >= m_left && x <= m_right && y >= m_top && y <= m_bottom );
}
bool PointInRect(TestPoint point) const
{
return PointInRect(point.x, point.y);
}
bool PointInRect(int x, int y) const
{
return( x > m_left && x < m_right && y > m_top && y < m_bottom );
}
#ifdef _DEBUG
static bool TestGetUnion()
{
/* Samples of content overlap we check for
0123456789ABCD 0123456789ABC 0123456789 0123456
1 aaaaa aaaaa aaaaa aaaaaaaaaa aaaabbb
2 a a a a a a a a a a b
3 a BBBBBBBB a a a a bbbb a aaaa b
4 a B a a B a a a a b b a b b
5 aaBaa aaBaa bbbbabbbabbbb a b b a bbbbbbb
6 B B b a a b a bbbb a
7 aaBaa aaBaa bbbbabbbabbbb a a ababab
8 a B a a B a a a aaaaaaaaaa b a
9 a BBBBBBBB a a a ababab
A a a a a a a
B aaaaa aaaaa aaaaa
1 aaaa aaabbbaaa
2 a a a b b a
3 bbbbbbb aaababbaa
4 b a b b b
5 bbbbbbb bbb
If the content does not overlap, we're not interested...
0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789
1 aaaaa aaaa aaa
2 a bbbbb a a a a
3 a a b aaaBbbb aaa
4 a b b b b
5 a abbbb bbbb bbbbb
6 aaaaa
*/
typedef struct _tagTestData {
int aLeft; int aTop; int aRight; int aBottom;
int bLeft; int bTop; int bRight; int bBottom;
bool shouldPass;
} TestData;
TestData data[] = {
// Connect 4
{ 0x2,0x1,0x5,0x6, 0x4,0x3,0xb,0x9, true },
{ 0x9,0x1,0xd,0x5, 0x4,0x3,0xb,0x9, true },
{ 0x2,0x7,0x6,0xb, 0x4,0x3,0xb,0x9, true },
{ 0x9,0x7,0xd,0xb, 0x4,0x3,0xb,0x9, true },
// +
{ 0x4,0x1,0x8,0xb, 0x0,0x5,0xc,0x7, true },
// doughnut
{ 0x0,0x0,0x8,0x9, 0x3,0x3,0x6,0x6, true },
// same overlapping area
{ 0,0,5,5, 0,0,5,5, true },
// smaller, but in the corner
{ 0,0,3,3, 0,0,6,6, true },
// "L"
{ 1,1,4,5, 1,3,7,5, true },
// "T"
{ 1,1,9,3, 4,1,6,5, true },
// Side by side
{ 1,1,6,6, 6,2,9,5, false },
// Shared corner
{ 0,0,3,3, 3,3,6,6, false },
// Off to the right
{ 0,0,2,2, 3,0,5,2, false },
// Off to the bottom right
{ 0,0,2,2, 4,4,6,6, false },
// Off to the bottom
{ 0,0,2,2, 0,4,2,4, false },
// Null rect
{ 0,0,0,0, 0,0,4,4, false },
// Inverted rect
{ 5,5,0,0, 0,0,5,5, false }
};
const int count = sizeof(data)/sizeof(data[0]);
TestRect aBox,bBox;
bool aResult = false, bResult = false;
int maxX = 0, maxY = 0, x = 0, y = 0;
enum {
space = 0x00,
alpha = 0x01,
beta = 0x02,
overlap = alpha|beta
};
int character = space;
for( int idx = 0; idx < count; idx++ )
{
// Build the rects
aBox.SetRect( data[idx].aLeft, data[idx].aTop, data[idx].aRight, data[idx].aBottom );
bBox.SetRect( data[idx].bLeft, data[idx].bTop, data[idx].bRight, data[idx].bBottom );
// Test it both ways
aResult = aBox.GetUnion(bBox, NULL);
bResult = bBox.GetUnion(aBox, NULL);
// In an ideal world - this will always be false, but then again - we wouldn't need this test harness either
if( (aResult != bResult) || (aResult != data[idx].shouldPass) )
{
ATLTRACE("\nFound a problem with (%d,%d,%d,%d) (%d,%d,%d,%d) %d %d %d\n",
aBox.m_left,aBox.m_top,aBox.m_right,aBox.m_bottom,
bBox.m_left,bBox.m_top,bBox.m_right,bBox.m_bottom,
aResult, bResult, data[idx].shouldPass );
// Dump the rects to the output window so we can see what happened
maxX = max(aBox.m_right, bBox.m_right);
maxY = max(aBox.m_bottom, bBox.m_bottom);
for( y = 0; y <= maxY; y++ )
{
for( x = 0; x <= maxX; x++ )
{
character = space;
if( (aBox.m_left == x && aBox.m_top == y) || (aBox.m_left == x && aBox.m_bottom == y) ||
(aBox.m_right == x && aBox.m_top == y) || (aBox.m_right == x && aBox.m_bottom == y) )
{
character |= alpha;
}
if( (bBox.m_left == x && bBox.m_top == y) || (bBox.m_left == x && bBox.m_bottom == y) ||
(bBox.m_right == x && bBox.m_top == y) || (bBox.m_right == x && bBox.m_bottom == y) )
{
character |= beta;
}
switch( character )
{
case space: ATLTRACE("."); break;
case alpha: ATLTRACE("A"); break;
case beta: ATLTRACE("B"); break;
case overlap: ATLTRACE("X"); break;
default: ATLTRACE("?");
}
}
ATLTRACE("\n");
}
ATLASSERT(!"Not what we were expecting!");
return false;
}
}
return true;
}
#endif //_DEBUG
bool GetUnion(const TestRect & other, TestRect * result) const
{
TestRect calc;
// Make sure we have area
if( !HasArea() || !other.HasArea() )
return false;
// Check for identical
if( IsEqual(other) )
{
if( result )
result->SetRect(calc);
return true;
}
// Disregard the obvious - boxes that are outside other boxes
if( m_right < other.m_left )
return false;
if( m_bottom < other.m_top )
return false;
if( m_left > other.m_right )
return false;
if( m_top > other.m_bottom )
return false;
// Snap the edges to the minimum constraints of both boxes
calc.m_left = max(m_left, other.m_left);
calc.m_right = min(m_right, other.m_right);
calc.m_top = max(m_top, other.m_top);
calc.m_bottom = min(m_bottom,other.m_bottom);
// No area == no union
if( !calc.HasArea() )
return false;
// Set the union result value?
if( result )
result->SetRect(calc);
return true;
}
};
#endif //__TEST_RECTANGLE_H__