From 3049bc1d5f4b8c3bbeefafba6cff66f71a6687da Mon Sep 17 00:00:00 2001 From: Maciej Suminski Date: Thu, 30 Jul 2015 13:49:35 +0200 Subject: [PATCH] Fixing SELECTION_TOOL heuristics, take #2. --- common/base_struct.cpp | 25 ++++++++++++++ include/class_eda_rect.h | 8 +++++ pcbnew/tools/selection_tool.cpp | 60 ++++++++++++++++++++++----------- 3 files changed, 73 insertions(+), 20 deletions(-) diff --git a/common/base_struct.cpp b/common/base_struct.cpp index 19396404ee..2ee9f90dfa 100644 --- a/common/base_struct.cpp +++ b/common/base_struct.cpp @@ -530,6 +530,31 @@ double EDA_RECT::GetArea() const return (double) GetWidth() * (double) GetHeight(); } + +EDA_RECT EDA_RECT::Common( const EDA_RECT& aRect ) const +{ + EDA_RECT r; + + if( Intersects( aRect ) ) + { + wxPoint originA( std::min( GetOrigin().x, GetEnd().x ), + std::min( GetOrigin().y, GetEnd().y ) ); + wxPoint originB( std::min( aRect.GetOrigin().x, aRect.GetEnd().x ), + std::min( aRect.GetOrigin().y, aRect.GetEnd().y ) ); + wxPoint endA( std::max( GetOrigin().x, GetEnd().x ), + std::max( GetOrigin().y, GetEnd().y ) ); + wxPoint endB( std::max( aRect.GetOrigin().x, aRect.GetEnd().x ), + std::max( aRect.GetOrigin().y, aRect.GetEnd().y ) ); + + + r.SetOrigin( wxPoint( std::max( originA.x, originB.x ), std::max( originA.y, originB.y ) ) ); + r.SetEnd ( wxPoint( std::min( endA.x, endB.x ), std::min( endA.y, endB.y ) ) ); + } + + return r; +} + + /* Calculate the bounding box of this, when rotated */ const EDA_RECT EDA_RECT::GetBoundingBoxRotated( wxPoint aRotCenter, double aAngle ) diff --git a/include/class_eda_rect.h b/include/class_eda_rect.h index d12b4244e0..57aa3a4de1 100644 --- a/include/class_eda_rect.h +++ b/include/class_eda_rect.h @@ -203,6 +203,14 @@ public: */ double GetArea() const; + /** + * Function Common + * returns the area that is common with another rectangle. + * @param aRect is the rectangle to find the common area with. + * @return The common area rect or 0-sized rectangle if there is no intersection. + */ + EDA_RECT Common( const EDA_RECT& aRect ) const; + /** * Function GetBoundingBoxRotated * @return the bounding box of this, after rotation diff --git a/pcbnew/tools/selection_tool.cpp b/pcbnew/tools/selection_tool.cpp index 93f5678566..44e9a01e74 100644 --- a/pcbnew/tools/selection_tool.cpp +++ b/pcbnew/tools/selection_tool.cpp @@ -1048,22 +1048,24 @@ bool SELECTION_TOOL::selectionContains( const VECTOR2I& aPoint ) const } -static double calcArea( BOARD_ITEM* aItem ) +static EDA_RECT getRect( const BOARD_ITEM* aItem ) { - switch( aItem -> Type() ) + if( aItem->Type() == PCB_MODULE_T ) + return static_cast( aItem )->GetFootprintRect(); + + return aItem->GetBoundingBox(); +} + + +static double calcArea( const BOARD_ITEM* aItem ) +{ + if( aItem->Type() == PCB_TRACE_T ) { - case PCB_MODULE_T: - return static_cast ( aItem )->GetFootprintRect().GetArea(); - - case PCB_TRACE_T: - { - TRACK* t = static_cast( aItem ); - return ( t->GetWidth() + t->GetLength() ) * t->GetWidth(); - } - - default: - return aItem->GetBoundingBox().GetArea(); + const TRACK* t = static_cast( aItem ); + return ( t->GetWidth() + t->GetLength() ) * t->GetWidth(); } + + return getRect( aItem ).GetArea(); } @@ -1100,6 +1102,12 @@ static double calcMaxArea( GENERAL_COLLECTOR& aCollector, KICAD_T aType ) } +static inline double calcCommonArea( const BOARD_ITEM* aItem, const BOARD_ITEM* aOther ) +{ + return getRect( aItem ).Common( getRect( aOther ) ).GetArea(); +} + + double calcRatio( double a, double b ) { if( a == 0.0 && b == 0.0 ) @@ -1123,6 +1131,10 @@ void SELECTION_TOOL::guessSelectionCandidates( GENERAL_COLLECTOR& aCollector ) c const double trackTrackLengthRatio = 0.3; const double textToFeatureMinRatio = 0.2; const double textToFootprintMinRatio = 0.4; + // If the common area of two compared items is above the following threshold, they cannot + // be rejected (it means they overlap and it might be hard to pick one by selecting + // its unique area). + const double commonAreaRatio = 0.6; LAYER_ID actLayer = m_frame->GetActiveLayer(); @@ -1164,13 +1176,19 @@ void SELECTION_TOOL::guessSelectionCandidates( GENERAL_COLLECTOR& aCollector ) c for( int j = 0; j < aCollector.GetCount(); ++j ) { - BOARD_ITEM* item = aCollector[j]; - double areaRatio = calcRatio( textArea, calcArea( item ) ); + if( i == j ) + continue; - if( item->Type() == PCB_MODULE_T && areaRatio < textToFootprintMinRatio ) - { + BOARD_ITEM* item = aCollector[j]; + double itemArea = calcArea( item ); + double areaRatio = calcRatio( textArea, itemArea ); + double commonArea = calcCommonArea( txt, item ); + double itemCommonRatio = calcRatio( commonArea, itemArea ); + double txtCommonRatio = calcRatio( commonArea, textArea ); + + if( item->Type() == PCB_MODULE_T && areaRatio < textToFootprintMinRatio && + itemCommonRatio < commonAreaRatio ) rejected.insert( item ); - } switch( item->Type() ) { @@ -1179,8 +1197,7 @@ void SELECTION_TOOL::guessSelectionCandidates( GENERAL_COLLECTOR& aCollector ) c case PCB_LINE_T: case PCB_VIA_T: case PCB_MODULE_T: - if( areaRatio > textToFeatureMinRatio && - !txt->GetParent()->ViewBBox().Contains( txt->ViewBBox() ) ) + if( areaRatio > textToFeatureMinRatio && txtCommonRatio < commonAreaRatio ) rejected.insert( txt ); break; default: @@ -1235,6 +1252,9 @@ void SELECTION_TOOL::guessSelectionCandidates( GENERAL_COLLECTOR& aCollector ) c for( int j = 0; j < aCollector.GetCount(); ++j ) { + if( i == j ) + continue; + BOARD_ITEM* item = aCollector[j]; double areaRatio = calcRatio( viaArea, calcArea( item ) );