Fix some issue with group in selection logic.

This commit is contained in:
Jeff Young 2020-12-01 14:03:34 +00:00
parent 288ab7ed6f
commit d50d1d84da
4 changed files with 106 additions and 89 deletions

View File

@ -172,20 +172,6 @@ public:
///> @copydoc EDA_ITEM::GetMsgPanelInfo
void GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList ) override;
/**
* Add all the immediate children of this group to the board commit. This function does not
* enter any subgroups of this group, or add the group itself.
*
* @param aCommit is the commit to add the children to.
*/
void AddChildrenToCommit( BOARD_COMMIT& aCommit ) const
{
RunOnChildren( [&]( BOARD_ITEM* bItem )
{
aCommit.Add( bItem );
} );
}
/**
* Invokes a function on all members of the group.
* Note that this function should not add or remove items to the group

View File

@ -35,6 +35,7 @@
#include <fp_shape.h>
#include <pcb_text.h>
#include <pcb_marker.h>
#include <pcb_group.h>
#include <footprint.h>
#include <view/view.h>
#include <geometry/shape_null.h>
@ -1640,7 +1641,17 @@ double FOOTPRINT::GetCoverageArea( const BOARD_ITEM* aItem, const GENERAL_COLLEC
marker->ShapeToPolygon( markerShape );
return markerShape.Area();
}
else if( aItem->Type() == PCB_FOOTPRINT_T )
else if( aItem->Type() == PCB_GROUP_T )
{
double combinedArea = 0.0;
for( BOARD_ITEM* member : static_cast<const PCB_GROUP*>( aItem )->GetItems() )
combinedArea += GetCoverageArea( member, aCollector );
return combinedArea;
}
if( aItem->Type() == PCB_FOOTPRINT_T )
{
const FOOTPRINT* footprint = static_cast<const FOOTPRINT*>( aItem );

View File

@ -2169,6 +2169,88 @@ bool SELECTION_TOOL::selectionContains( const VECTOR2I& aPoint ) const
}
int SELECTION_TOOL::hitTestDistance( const wxPoint& aWhere, BOARD_ITEM* aItem,
int aMaxDistance ) const
{
BOX2D viewportD = getView()->GetViewport();
BOX2I viewport( VECTOR2I( viewportD.GetPosition() ), VECTOR2I( viewportD.GetSize() ) );
int distance = INT_MAX;
SEG loc( aWhere, aWhere );
switch( aItem->Type() )
{
case PCB_TEXT_T:
{
PCB_TEXT* text = static_cast<PCB_TEXT*>( aItem );
text->GetEffectiveTextShape()->Collide( loc, aMaxDistance, &distance );
}
break;
case PCB_FP_TEXT_T:
{
FP_TEXT* text = static_cast<FP_TEXT*>( aItem );
text->GetEffectiveTextShape()->Collide( loc, aMaxDistance, &distance );
}
break;
case PCB_ZONE_T:
{
ZONE* zone = static_cast<ZONE*>( aItem );
// Zone borders are very specific
if( zone->HitTestForEdge( aWhere, aMaxDistance / 2 ) )
distance = 0;
else if( zone->HitTestForEdge( aWhere, aMaxDistance ) )
distance = aMaxDistance / 2;
else
aItem->GetEffectiveShape()->Collide( loc, aMaxDistance, &distance );
}
break;
case PCB_FOOTPRINT_T:
{
FOOTPRINT* footprint = static_cast<FOOTPRINT*>( aItem );
footprint->GetBoundingHull().Collide( loc, aMaxDistance, &distance );
// Consider footprints larger than the viewport only as a last resort
if( aItem->ViewBBox().GetHeight() > viewport.GetHeight()
|| aItem->ViewBBox().GetWidth() > viewport.GetWidth() )
{
distance = INT_MAX / 2;
}
}
break;
case PCB_MARKER_T:
{
PCB_MARKER* marker = static_cast<PCB_MARKER*>( aItem );
SHAPE_LINE_CHAIN polygon;
marker->ShapeToPolygon( polygon );
polygon.Move( marker->GetPos() );
polygon.Collide( loc, aMaxDistance, &distance );
}
break;
case PCB_GROUP_T:
{
PCB_GROUP* group = static_cast<PCB_GROUP*>( aItem );
for( BOARD_ITEM* member : group->GetItems() )
distance = std::min( distance, hitTestDistance( aWhere, member, aMaxDistance ) );
}
break;
default:
aItem->GetEffectiveShape()->Collide( loc, aMaxDistance, &distance );
break;
}
return distance;
}
// The general idea here is that if the user clicks directly on a small item inside a larger
// one, then they want the small item. The quintessential case of this is clicking on a pad
// within a footprint, but we also apply it for text within a footprint, footprints within
@ -2219,93 +2301,29 @@ void SELECTION_TOOL::GuessSelectionCandidates( GENERAL_COLLECTOR& aCollector,
// Prefer exact hits to sloppy ones
constexpr int maxSlop = 5;
constexpr int MAX_SLOP = 5;
BOX2D viewportD = getView()->GetViewport();
BOX2I viewport( VECTOR2I( viewportD.GetPosition() ), VECTOR2I( viewportD.GetSize() ) );
int pixel = (int) aCollector.GetGuide()->OnePixelInIU();
int minActual = INT_MAX;
SEG loc( where, where );
int pixel = (int) aCollector.GetGuide()->OnePixelInIU();
int minSlop = INT_MAX;
std::map<BOARD_ITEM*, int> itemsBySloppiness;
for( int i = 0; i < aCollector.GetCount(); ++i )
{
BOARD_ITEM* item = aCollector[i];
int actualSlop = INT_MAX;
int itemSlop = hitTestDistance( where, item, MAX_SLOP * pixel );
switch( item->Type() )
{
case PCB_TEXT_T:
{
PCB_TEXT* text = static_cast<PCB_TEXT*>( item );
text->GetEffectiveTextShape()->Collide( loc, maxSlop * pixel, &actualSlop );
}
break;
itemsBySloppiness[ item ] = itemSlop;
case PCB_FP_TEXT_T:
{
FP_TEXT* text = static_cast<FP_TEXT*>( item );
text->GetEffectiveTextShape()->Collide( loc, maxSlop * pixel, &actualSlop );
}
break;
case PCB_ZONE_T:
{
ZONE* zone = static_cast<ZONE*>( item );
// Zone borders are very specific
if( zone->HitTestForEdge( where, maxSlop * pixel / 2 ) )
actualSlop = 0;
else if( zone->HitTestForEdge( where, maxSlop * pixel ) )
actualSlop = maxSlop * pixel / 2;
else
item->GetEffectiveShape()->Collide( loc, maxSlop * pixel, &actualSlop );
}
break;
case PCB_FOOTPRINT_T:
{
FOOTPRINT* footprint = static_cast<FOOTPRINT*>( item );
footprint->GetBoundingHull().Collide( loc, maxSlop * pixel, &actualSlop );
// Consider footprints larger than the viewport only as a last resort
if( item->ViewBBox().GetHeight() > viewport.GetHeight()
|| item->ViewBBox().GetWidth() > viewport.GetWidth() )
{
actualSlop = INT_MAX / 2;
}
}
break;
case PCB_MARKER_T:
{
PCB_MARKER* marker = static_cast<PCB_MARKER*>( item );
SHAPE_LINE_CHAIN polygon;
marker->ShapeToPolygon( polygon );
polygon.Move( marker->GetPos() );
polygon.Collide( loc, maxSlop * pixel, &actualSlop );
}
break;
default:
item->GetEffectiveShape()->Collide( loc, maxSlop * pixel, &actualSlop );
break;
}
itemsBySloppiness[ item ] = actualSlop;
if( actualSlop < minActual )
minActual = actualSlop;
if( itemSlop < minSlop )
minSlop = itemSlop;
}
// Prune sloppier items
for( std::pair<BOARD_ITEM*, int> pair : itemsBySloppiness )
{
if( pair.second > minActual + pixel )
if( pair.second > minSlop + pixel )
aCollector.Transfer( pair.first );
}
@ -2322,10 +2340,10 @@ void SELECTION_TOOL::GuessSelectionCandidates( GENERAL_COLLECTOR& aCollector,
double area;
if( item->Type() == PCB_ZONE_T
&& static_cast<ZONE*>( item )->HitTestForEdge( where, maxSlop * pixel / 2 ) )
&& static_cast<ZONE*>( item )->HitTestForEdge( where, MAX_SLOP * pixel / 2 ) )
{
// Zone borders are very specific, so make them "small"
area = maxSlop * pixel * pixel;
area = MAX_SLOP * pixel * pixel;
}
else
{

View File

@ -353,6 +353,8 @@ private:
*/
bool selectionContains( const VECTOR2I& aPoint ) const;
int hitTestDistance( const wxPoint& aWhere, BOARD_ITEM* aItem, int aMaxDistance ) const;
/**
* Event handler to update the selection VIEW_ITEM.
*/