HitTest for Rectangular pads

HitTest for Rectangular pads

- Works at any rotation, even with Shape Offset
- Fixed bugs in D_PAD BoundingBox calculation
This commit is contained in:
Oliver Walters 2017-04-23 10:06:56 +10:00 committed by Maciej Suminski
parent a0afcd5a62
commit f338d46476
4 changed files with 141 additions and 9 deletions

View File

@ -443,6 +443,110 @@ bool EDA_RECT::Intersects( const EDA_RECT& aRect ) const
return rc;
}
bool EDA_RECT::Intersects( const EDA_RECT& aRect, double aRot ) const
{
/* Most rectangles will be axis aligned.
* It is quicker to check for this case and pass the rect
* to the simpler intersection test
*/
// Prevent floating point comparison errors
static const double ROT_EPS = 0.000000001;
static const double ROT_PARALLEL[] = { -3600, -1800, 0, 1800, 3600 };
static const double ROT_PERPENDICULAR[] = { -2700, -900, 0, 900, 2700 };
NORMALIZE_ANGLE_POS<double>( aRot );
// Test for non-rotated rectangle
for( int ii=0; ii<5; ii++ )
{
if( std::fabs( aRot - ROT_PARALLEL[ii] ) < ROT_EPS )
{
return Intersects( aRect );
}
}
// Test for rectangle rotated by multiple of 90 degrees
for( int jj=0; jj<4; jj++ )
{
if( std::fabs( aRot - ROT_PERPENDICULAR[jj] ) < ROT_EPS )
{
EDA_RECT rotRect;
// Rotate the supplied rect by 90 degrees
rotRect.SetOrigin( aRect.Centre() );
rotRect.Inflate( aRect.GetHeight(), aRect.GetWidth() );
return Intersects( rotRect );
}
}
/* There is some non-orthogonal rotation.
* There are three cases to test:
* A) One point of this rect is inside the rotated rect
* B) One point of the rotated rect is inside this rect
* C) One of the sides of the rotated rect intersect this
*/
wxPoint corners[4];
/* Test A : Any corners exist in rotated rect? */
corners[0] = m_Pos;
corners[1] = m_Pos + wxPoint( m_Size.x, 0 );
corners[2] = m_Pos + wxPoint( m_Size.x, m_Size.y );
corners[3] = m_Pos + wxPoint( 0, m_Size.y );
wxPoint rCentre = aRect.Centre();
for( int i=0; i<4; i++ )
{
wxPoint delta = corners[i] - rCentre;
RotatePoint( &delta, -aRot );
delta += rCentre;
if( aRect.Contains( delta ) )
{
return true;
}
}
/* Test B : Any corners of rotated rect exist in this one? */
int w = aRect.GetWidth() / 2;
int h = aRect.GetHeight() / 2;
// Construct corners around center of shape
corners[0] = wxPoint( -w, -h );
corners[1] = wxPoint( w, -h );
corners[2] = wxPoint( w, h );
corners[3] = wxPoint( -w, h );
// Rotate and test each corner
for( int j=0; j<4; j++ )
{
RotatePoint( &corners[j], aRot );
corners[j] += rCentre;
if( Contains( corners[j] ) )
{
return true;
}
}
/* Test C : Any sides of rotated rect intersect this */
if( Intersects( corners[0], corners[1] ) ||
Intersects( corners[1], corners[2] ) ||
Intersects( corners[2], corners[3] ) ||
Intersects( corners[3], corners[0] ) )
{
return true;
}
return false;
}
const wxPoint EDA_RECT::ClosestPointTo( const wxPoint& aPoint ) const
{
EDA_RECT me(*this);

View File

@ -157,6 +157,15 @@ public:
*/
bool Intersects( const EDA_RECT& aRect ) const;
/**
* Tests for a common area between this rectangle,
* and a rectangle with arbitrary rotation
*
* @param aRect a rectangle to test intersection with
* @param aRot rectangle rotation (in 1/10 degrees)
*/
bool Intersects( const EDA_RECT& aRect, double aRot ) const;
/**
* Function Intersects
* tests for a common area between a segment and this rectangle.

View File

@ -556,7 +556,6 @@ bool DRAWSEGMENT::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy
else
{
arcRect = arcRect.Common( arect );
//arcRect.Inflate( GetWidth() );
/* All following tests must pass:
* 1. Rectangle must intersect arc BoundingBox
@ -568,9 +567,15 @@ bool DRAWSEGMENT::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy
break;
case S_SEGMENT:
if( aContained )
{
return arect.Contains( GetStart() ) && aRect.Contains( GetEnd() );
}
else
{
// Account for the width of the line
arect.Inflate( GetWidth() / 2 );
return arect.Intersects( GetStart(), GetEnd() );
}
break;

View File

@ -178,6 +178,9 @@ int D_PAD::GetRoundRectCornerRadius( const wxSize& aSize ) const
}
/**
* Return the BoundingBox for a D_PAD
*/
const EDA_RECT D_PAD::GetBoundingBox() const
{
EDA_RECT area;
@ -192,7 +195,7 @@ const EDA_RECT D_PAD::GetBoundingBox() const
break;
case PAD_SHAPE_OVAL:
// Calculate the position of each rounded ent
// Calculate the position of each rounded end
quadrant1.x = m_Size.x/2;
quadrant1.y = 0;
quadrant2.x = 0;
@ -207,7 +210,7 @@ const EDA_RECT D_PAD::GetBoundingBox() const
dy = std::max( std::abs( quadrant1.y ) , std::abs( quadrant2.y ) );
// Set the bbox
area.SetOrigin( m_Pos );
area.SetOrigin( ShapePos() );
area.Inflate( dx, dy );
break;
@ -226,7 +229,7 @@ const EDA_RECT D_PAD::GetBoundingBox() const
dy = std::max( std::abs( quadrant1.y ) , std::abs( quadrant2.y ) );
// Set the bbox
area.SetOrigin( m_Pos );
area.SetOrigin( ShapePos() );
area.Inflate( dx, dy );
break;
@ -251,6 +254,7 @@ const EDA_RECT D_PAD::GetBoundingBox() const
y = std::min( quadrant1.y, std::min( quadrant2.y, std::min( quadrant3.y, quadrant4.y) ) );
dx = std::max( quadrant1.x, std::max( quadrant2.x, std::max( quadrant3.x, quadrant4.x) ) );
dy = std::max( quadrant1.y, std::max( quadrant2.y, std::max( quadrant3.y, quadrant4.y) ) );
area.SetOrigin( m_Pos.x+x, m_Pos.y+y );
area.SetSize( dx-x, dy-y );
break;
@ -782,18 +786,28 @@ bool D_PAD::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy ) con
arect.Normalize();
arect.Inflate( aAccuracy );
if( !arect.Intersects( GetBoundingBox() ) )
EDA_RECT shapeRect;
shapeRect.SetOrigin( ShapePos() );
shapeRect.Inflate( GetSize().x / 2, GetSize().y / 2 );
EDA_RECT bb = GetBoundingBox();
if( !arect.Intersects( bb ) )
return false;
if( aContained )
return arect.Contains( GetBoundingBox() );
int dist;
// This covers total containment for all test cases
if( arect.Contains( bb ) )
return true;
switch( GetShape() )
{
case PAD_SHAPE_CIRCLE:
return arect.IntersectsCircle( GetPosition(), GetBoundingRadius() );
case PAD_SHAPE_RECT:
break;
return arect.Intersects( shapeRect, m_Orient );
case PAD_SHAPE_OVAL:
break;
case PAD_SHAPE_TRAPEZOID: