From f8734bd0578376cd740328f2248cd4159f2a8da9 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Fri, 21 Apr 2017 00:53:57 +1000 Subject: [PATCH] Fixed ::HitTest for Circle shape - Testing against rectangle intersection now works correctly - Previously tested against BoundingBox() not circle outline --- common/base_struct.cpp | 60 +++++++++++++++++++++++++++++++++ include/class_eda_rect.h | 30 +++++++++++++++++ pcbnew/class_drawsegment.cpp | 15 ++++++++- pcbnew/tools/selection_tool.cpp | 22 +++++------- 4 files changed, 113 insertions(+), 14 deletions(-) diff --git a/common/base_struct.cpp b/common/base_struct.cpp index 76f51fba7e..8839ad8356 100644 --- a/common/base_struct.cpp +++ b/common/base_struct.cpp @@ -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 ) { diff --git a/include/class_eda_rect.h b/include/class_eda_rect.h index 8849942966..d9a67c1a03 100644 --- a/include/class_eda_rect.h +++ b/include/class_eda_rect.h @@ -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 diff --git a/pcbnew/class_drawsegment.cpp b/pcbnew/class_drawsegment.cpp index 568c8be983..89947ae150 100644 --- a/pcbnew/class_drawsegment.cpp +++ b/pcbnew/class_drawsegment.cpp @@ -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: diff --git a/pcbnew/tools/selection_tool.cpp b/pcbnew/tools/selection_tool.cpp index d31eee7887..0ea9529b1a 100644 --- a/pcbnew/tools/selection_tool.cpp +++ b/pcbnew/tools/selection_tool.cpp @@ -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 )