Fixed ::HitTest for Circle shape

- Testing against rectangle intersection now works correctly
- Previously tested against BoundingBox() not circle outline
This commit is contained in:
Oliver Walters 2017-04-21 00:53:57 +10:00 committed by Maciej Suminski
parent ef25ffbab7
commit f8734bd057
4 changed files with 113 additions and 14 deletions

View File

@ -443,6 +443,66 @@ bool EDA_RECT::Intersects( const EDA_RECT& aRect ) const
return rc;
}
const wxPoint EDA_RECT::ClosestPointTo( const wxPoint& aPoint ) const
{
EDA_RECT me(*this);
me.Normalize(); // ensure size is >= 0
// Determine closest point to the circle centre within this rect
int nx = std::max( me.GetLeft(), std::min( aPoint.x, me.GetRight() ) );
int ny = std::max( me.GetTop(), std::min( aPoint.y, me.GetBottom() ) );
return wxPoint( nx, ny );
}
const wxPoint EDA_RECT::FarthestPointTo( const wxPoint& aPoint ) const
{
EDA_RECT me(*this);
me.Normalize(); // ensure size is >= 0
int fx = std::max( std::abs( aPoint.x - me.GetLeft() ), std::abs( aPoint.x - me.GetRight() ) );
int fy = std::max( std::abs( aPoint.y - me.GetTop() ), std::abs( aPoint.y - me.GetBottom() ) );
return wxPoint( fx, fy );
}
/* IntersectsCircle
* test for common area between this rect and a circle
*/
bool EDA_RECT::IntersectsCircle( const wxPoint& aCenter, const int aRadius ) const
{
wxPoint closest = ClosestPointTo( aCenter );
double dx = aCenter.x - closest.x;
double dy = aCenter.y - closest.y;
double r = (double) aRadius;
return ( dx * dx + dy * dy ) <= ( r * r );
}
bool EDA_RECT::IntersectsCircleEdge( const wxPoint& aCenter, const int aRadius, const int aWidth ) const
{
EDA_RECT me(*this);
me.Normalize(); // ensure size is >= 0
// Test if the circle intersects at all
if( !IntersectsCircle( aCenter, aRadius + aWidth / 2 ) )
{
return false;
}
wxPoint far = FarthestPointTo( aCenter );
// Farthest point must be further than the inside of the line
double fx = (double) far.x;
double fy = (double) far.y;
double r = (double) aRadius - (double) aWidth / 2;
return ( fx * fx + fy * fy ) > ( r * r );
}
EDA_RECT& EDA_RECT::Inflate( int aDelta )
{

View File

@ -168,6 +168,36 @@ public:
*/
bool Intersects( const wxPoint& aPoint1, const wxPoint& aPoint2 ) const;
/**
* Return the point in this rect that is closest to the provided point
*/
const wxPoint ClosestPointTo( const wxPoint& aPoint ) const;
/**
* Return the point in this rect that is farthest from the provided point
*/
const wxPoint FarthestPointTo( const wxPoint& aPoint ) const;
/**
* Function IntersectsCircle
* tests for a common area between a circle and this rectangle
*
* @param aCenter center of the circle
* @param aRadius radius of the circle
*/
bool IntersectsCircle( const wxPoint& aCenter, const int aRadius ) const;
/**
* IntersectsCircleEdge
* Tests for intersection between this rect and the edge (radius) of a circle
*
* @param aCenter center of the circle
* @param aRadius radius of the circle
* @param aWidth width of the circle edge
*/
bool IntersectsCircleEdge( const wxPoint& aCenter, const int aRadius, const int aWidth ) const;
/**
* Function operator(wxRect)
* overloads the cast operator to return a wxRect

View File

@ -522,6 +522,8 @@ bool DRAWSEGMENT::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy
EDA_RECT arect = aRect;
arect.Inflate( aAccuracy );
EDA_RECT arcRect;
switch( m_Shape )
{
case S_CIRCLE:
@ -529,7 +531,18 @@ bool DRAWSEGMENT::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy
if( aContained )
return arect.Contains( GetBoundingBox() );
else
return arect.Intersects( GetBoundingBox() );
{
// If the rectangle does not intersect the bounding box, this is a much quicker test
if( !aRect.Intersects( GetBoundingBox() ) )
{
return false;
}
else
{
return arect.IntersectsCircleEdge( GetCenter(), GetRadius(), GetWidth() );
}
}
break;
case S_ARC:

View File

@ -525,25 +525,21 @@ bool SELECTION_TOOL::selectMultiple()
* Right > Left : Select objects that are crossed by selection
*/
// Add only those items that are visible
if( !item->IsSelected() && selectable( item ) )
if( width >= 0 )
{
if( item->HitTest( selectionRect, width >= 0) )
if( selectionBox.Contains( item->ViewBBox() ) )
{
select( item );
}
}
else
{
if( item->HitTest( selectionRect, false ) )
{
select( item );
}
}
/*
// Selecting left->right requires full enclosure
if ( xDelta >= 0 && selectionBox.Contains( item->ViewBBox() ) )
{
select( item );
}
// Selecting right->left requires only
else if
*/
}
if( m_selection.Size() == 1 )