Pcbnew: fix footprint selection bug.
Do not remove footprint objects from list of selected objects when there are no drawable objects other than the reference and value text. Increase the minimum size rectangle for footprints from 0.025mm to 1mm when the footprint has no drawing objects so that it is easier to select the footprint in this case. Add some pretty colors to the footprint bounding box and hull outline so that it's possible to tell the difference between selection areas. Fixes https://gitlab.com/kicad/code/kicad/-/issues/8379
This commit is contained in:
parent
50eea4f487
commit
27e9750507
|
@ -679,9 +679,10 @@ const EDA_RECT FOOTPRINT::GetBoundingBox( bool aIncludeText, bool aIncludeInvisi
|
|||
for( FP_ZONE* zone : m_fp_zones )
|
||||
area.Merge( zone->GetBoundingBox() );
|
||||
|
||||
// Groups do not contribute to the rect, only their members
|
||||
bool noDrawItems = ( m_drawings.empty() && m_pads.empty() && m_fp_zones.empty() );
|
||||
|
||||
if( aIncludeText )
|
||||
// Groups do not contribute to the rect, only their members
|
||||
if( aIncludeText || noDrawItems )
|
||||
{
|
||||
for( BOARD_ITEM* item : m_drawings )
|
||||
{
|
||||
|
@ -710,16 +711,18 @@ const EDA_RECT FOOTPRINT::GetBoundingBox( bool aIncludeText, bool aIncludeInvisi
|
|||
}
|
||||
|
||||
|
||||
if( ( m_value->IsVisible() && valueLayerIsVisible ) || aIncludeInvisibleText )
|
||||
if( ( m_value->IsVisible() && valueLayerIsVisible )
|
||||
|| aIncludeInvisibleText || noDrawItems )
|
||||
area.Merge( m_value->GetBoundingBox() );
|
||||
|
||||
if( ( m_reference->IsVisible() && refLayerIsVisible ) || aIncludeInvisibleText )
|
||||
if( ( m_reference->IsVisible() && refLayerIsVisible )
|
||||
|| aIncludeInvisibleText || noDrawItems )
|
||||
area.Merge( m_reference->GetBoundingBox() );
|
||||
}
|
||||
|
||||
if( board )
|
||||
{
|
||||
if( aIncludeText && aIncludeInvisibleText )
|
||||
if( ( aIncludeText && aIncludeInvisibleText ) || noDrawItems )
|
||||
{
|
||||
m_boundingBoxCacheTimeStamp = board->GetTimeStamp();
|
||||
m_cachedBoundingBox = area;
|
||||
|
@ -790,9 +793,10 @@ SHAPE_POLY_SET FOOTPRINT::GetBoundingHull() const
|
|||
if( rawPolys.OutlineCount() == 0 )
|
||||
{
|
||||
// generate a small dummy rectangular outline around the anchor
|
||||
const int halfsize = Millimeter2iu( 0.02 );
|
||||
const int halfsize = Millimeter2iu( 1.0 );
|
||||
|
||||
rawPolys.NewOutline();
|
||||
|
||||
// add a square:
|
||||
rawPolys.Append( GetPosition().x - halfsize, GetPosition().y - halfsize );
|
||||
rawPolys.Append( GetPosition().x + halfsize, GetPosition().y - halfsize );
|
||||
|
@ -924,13 +928,19 @@ bool FOOTPRINT::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy )
|
|||
arect.Inflate( aAccuracy );
|
||||
|
||||
if( aContained )
|
||||
{
|
||||
return arect.Contains( GetBoundingBox( false, false ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the rect does not intersect the bounding box, skip any tests
|
||||
if( !aRect.Intersects( GetBoundingBox( false, false ) ) )
|
||||
return false;
|
||||
|
||||
// The empty footprint dummy rectangle intersects the selection area.
|
||||
if( m_pads.empty() && m_fp_zones.empty() && m_drawings.empty() )
|
||||
return GetBoundingBox( true, false ).Intersects( arect );
|
||||
|
||||
// Determine if any elements in the FOOTPRINT intersect the rect
|
||||
for( PAD* pad : m_pads )
|
||||
{
|
||||
|
@ -2087,14 +2097,18 @@ static struct FOOTPRINT_DESC
|
|||
propMgr.AddProperty( new PROPERTY<FOOTPRINT, int>( _HKI( "Solderpaste Margin Override" ),
|
||||
&FOOTPRINT::SetLocalSolderPasteMargin, &FOOTPRINT::GetLocalSolderPasteMargin,
|
||||
PROPERTY_DISPLAY::DISTANCE ) );
|
||||
propMgr.AddProperty( new PROPERTY<FOOTPRINT, double>( _HKI( "Solderpaste Margin Ratio Override" ),
|
||||
&FOOTPRINT::SetLocalSolderPasteMarginRatio, &FOOTPRINT::GetLocalSolderPasteMarginRatio ) );
|
||||
propMgr.AddProperty( new PROPERTY<FOOTPRINT,
|
||||
double>( _HKI( "Solderpaste Margin Ratio Override" ),
|
||||
&FOOTPRINT::SetLocalSolderPasteMarginRatio,
|
||||
&FOOTPRINT::GetLocalSolderPasteMarginRatio ) );
|
||||
propMgr.AddProperty( new PROPERTY<FOOTPRINT, int>( _HKI( "Thermal Relief Width" ),
|
||||
&FOOTPRINT::SetThermalWidth, &FOOTPRINT::GetThermalWidth,
|
||||
PROPERTY_DISPLAY::DISTANCE ) );
|
||||
&FOOTPRINT::SetThermalWidth,
|
||||
&FOOTPRINT::GetThermalWidth,
|
||||
PROPERTY_DISPLAY::DISTANCE ) );
|
||||
propMgr.AddProperty( new PROPERTY<FOOTPRINT, int>( _HKI( "Thermal Relief Gap" ),
|
||||
&FOOTPRINT::SetThermalGap, &FOOTPRINT::GetThermalGap,
|
||||
PROPERTY_DISPLAY::DISTANCE ) );
|
||||
&FOOTPRINT::SetThermalGap,
|
||||
&FOOTPRINT::GetThermalGap,
|
||||
PROPERTY_DISPLAY::DISTANCE ) );
|
||||
// TODO zone connection, FPID?
|
||||
}
|
||||
} _FOOTPRINT_DESC;
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2013-2019 CERN
|
||||
* Copyright (C) 2021 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
|
||||
* @author Maciej Suminski <maciej.suminski@cern.ch>
|
||||
*
|
||||
|
@ -458,18 +460,6 @@ bool PCB_PAINTER::Draw( const VIEW_ITEM* aItem, int aLayer )
|
|||
if( !item )
|
||||
return false;
|
||||
|
||||
if( ADVANCED_CFG::GetCfg().m_DrawBoundingBoxes )
|
||||
{
|
||||
// Show bounding boxes of painted objects for debugging.
|
||||
EDA_RECT box = item->GetBoundingBox();
|
||||
m_gal->SetIsFill( false );
|
||||
m_gal->SetIsStroke( true );
|
||||
m_gal->SetStrokeColor( item->IsSelected() ? COLOR4D( 1.0, 0.2, 0.2, 1 ) :
|
||||
COLOR4D( 0.2, 0.2, 0.2, 1 ) );
|
||||
m_gal->SetLineWidth( Mils2iu( 3 ) );
|
||||
m_gal->DrawRectangle( box.GetOrigin(), box.GetEnd() );
|
||||
}
|
||||
|
||||
// the "cast" applied in here clarifies which overloaded draw() is called
|
||||
switch( item->Type() )
|
||||
{
|
||||
|
@ -538,6 +528,40 @@ bool PCB_PAINTER::Draw( const VIEW_ITEM* aItem, int aLayer )
|
|||
return false;
|
||||
}
|
||||
|
||||
// Draw bounding boxes after drawing objects so they can be seen.
|
||||
if( ADVANCED_CFG::GetCfg().m_DrawBoundingBoxes )
|
||||
{
|
||||
// Show bounding boxes of painted objects for debugging.
|
||||
EDA_RECT box = item->GetBoundingBox();
|
||||
m_gal->SetIsFill( false );
|
||||
m_gal->SetIsStroke( true );
|
||||
|
||||
if( item->Type() == PCB_FOOTPRINT_T )
|
||||
m_gal->SetStrokeColor( item->IsSelected() ? COLOR4D( 1.0, 0.2, 0.2, 1 ) :
|
||||
COLOR4D( MAGENTA ) );
|
||||
else
|
||||
m_gal->SetStrokeColor( item->IsSelected() ? COLOR4D( 1.0, 0.2, 0.2, 1 ) :
|
||||
COLOR4D( 0.2, 0.2, 0.2, 1 ) );
|
||||
|
||||
m_gal->SetLineWidth( 1.5 / m_gal->GetWorldScale() );
|
||||
m_gal->DrawRectangle( box.GetOrigin(), box.GetEnd() );
|
||||
|
||||
if( item->Type() == PCB_FOOTPRINT_T )
|
||||
{
|
||||
m_gal->SetStrokeColor( item->IsSelected() ? COLOR4D( 1.0, 0.2, 0.2, 1 ) :
|
||||
COLOR4D( CYAN ) );
|
||||
|
||||
const FOOTPRINT* fp = static_cast<const FOOTPRINT*>( item );
|
||||
|
||||
if( fp )
|
||||
{
|
||||
SHAPE_POLY_SET convex = fp->GetBoundingHull();
|
||||
|
||||
m_gal->DrawPolyline( convex.COutline( 0 ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -716,9 +740,11 @@ void PCB_PAINTER::draw( const PCB_VIA* aVia, int aLayer )
|
|||
VECTOR2D textpos( 0.0, 0.0 );
|
||||
|
||||
wxString netname = UnescapeString( aVia->GetShortNetname() );
|
||||
|
||||
// calculate the size of net name text:
|
||||
double tsize = 1.5 * size / netname.Length();
|
||||
tsize = std::min( tsize, size );
|
||||
|
||||
// Use a smaller text size to handle interline, pen size..
|
||||
tsize *= 0.7;
|
||||
VECTOR2D namesize( tsize, tsize );
|
||||
|
@ -914,6 +940,7 @@ void PCB_PAINTER::draw( const PAD* aPad, int aLayer )
|
|||
// calculate the size of net name text:
|
||||
double tsize = 1.5 * padsize.x / netname.Length();
|
||||
tsize = std::min( tsize, size );
|
||||
|
||||
// Use a smaller text size to handle interline, pen size..
|
||||
tsize *= 0.7;
|
||||
VECTOR2D namesize( tsize, tsize );
|
||||
|
@ -929,6 +956,7 @@ void PCB_PAINTER::draw( const PAD* aPad, int aLayer )
|
|||
textpos.y = -textpos.y;
|
||||
double tsize = 1.5 * padsize.x / padName.Length();
|
||||
tsize = std::min( tsize, size );
|
||||
|
||||
// Use a smaller text size to handle interline, pen size..
|
||||
tsize *= 0.7;
|
||||
tsize = std::min( tsize, size );
|
||||
|
@ -1122,7 +1150,6 @@ void PCB_PAINTER::draw( const PAD* aPad, int aLayer )
|
|||
else if( effectiveMargin.x > 0 )
|
||||
{
|
||||
// A positive margin produces a larger rect, but with rounded corners
|
||||
|
||||
m_gal->DrawRectangle( r->GetPosition(), r->GetPosition() + r->GetSize() );
|
||||
|
||||
// Use segments to produce the margin with rounded corners
|
||||
|
@ -1190,7 +1217,6 @@ void PCB_PAINTER::draw( const PAD* aPad, int aLayer )
|
|||
else
|
||||
{
|
||||
// This is expensive. Avoid if possible.
|
||||
|
||||
SHAPE_POLY_SET polySet;
|
||||
aPad->TransformShapeWithClearanceToPolygon( polySet, ToLAYER_ID( aLayer ), margin.x,
|
||||
bds.m_MaxError, ERROR_INSIDE );
|
||||
|
@ -1543,19 +1569,6 @@ void PCB_PAINTER::draw( const FOOTPRINT* aFootprint, int aLayer )
|
|||
VECTOR2D center = aFootprint->GetPosition();
|
||||
m_gal->DrawLine( center - VECTOR2D( anchorSize, 0 ), center + VECTOR2D( anchorSize, 0 ) );
|
||||
m_gal->DrawLine( center - VECTOR2D( 0, anchorSize ), center + VECTOR2D( 0, anchorSize ) );
|
||||
|
||||
#if 0 // For debug purpose only: draw the footing bounding box
|
||||
double bboxThickness = 1.0 / m_gal->GetWorldScale();
|
||||
m_gal->SetLineWidth( bboxThickness );
|
||||
EDA_RECT rect = aFootprint->GetBoundingBox();
|
||||
m_gal->DrawRectangle( VECTOR2D( rect.GetOrigin() ), VECTOR2D( rect.GetEnd() ) );
|
||||
|
||||
double bboxThickness = 3.0 / m_gal->GetWorldScale();
|
||||
m_gal->SetLineWidth( bboxThickness );
|
||||
SHAPE_POLY_SET convex = aFootprint->GetBoundingHull();
|
||||
|
||||
m_gal->DrawPolyline( convex.COutline( 0 ) );
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1626,7 +1639,7 @@ void PCB_PAINTER::draw( const PCB_GROUP* aGroup, int aLayer )
|
|||
|
||||
void PCB_PAINTER::draw( const ZONE* aZone, int aLayer )
|
||||
{
|
||||
/**
|
||||
/*
|
||||
* aLayer will be the virtual zone layer (LAYER_ZONE_START, ... in GAL_LAYER_ID)
|
||||
* This is used for draw ordering in the GAL.
|
||||
* The color for the zone comes from the associated copper layer ( aLayer - LAYER_ZONE_START )
|
||||
|
@ -1752,6 +1765,7 @@ void PCB_PAINTER::draw( const PCB_DIMENSION_BASE* aDimension, int aLayer )
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Draw text
|
||||
const PCB_TEXT& text = aDimension->Text();
|
||||
VECTOR2D position( text.GetTextPos().x, text.GetTextPos().y );
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2013-2017 CERN
|
||||
* Copyright (C) 2018-2021 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
* @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
|
||||
* @author Maciej Suminski <maciej.suminski@cern.ch>
|
||||
* Copyright (C) 2018-2020 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
|
@ -76,6 +76,7 @@ public:
|
|||
|
||||
Add( PCB_ACTIONS::selectConnection );
|
||||
Add( PCB_ACTIONS::selectNet );
|
||||
|
||||
// This could be enabled if we have better logic for picking the target net with the mouse
|
||||
// Add( PCB_ACTIONS::deselectNet );
|
||||
Add( PCB_ACTIONS::selectSameSheet );
|
||||
|
@ -90,7 +91,7 @@ private:
|
|||
|
||||
|
||||
/**
|
||||
* Private implementation of firewalled private data
|
||||
* Private implementation of firewalled private data.
|
||||
*/
|
||||
class PCB_SELECTION_TOOL::PRIV
|
||||
{
|
||||
|
@ -307,9 +308,9 @@ int PCB_SELECTION_TOOL::Main( const TOOL_EVENT& aEvent )
|
|||
{
|
||||
evt->SetPassEvent();
|
||||
}
|
||||
// Single click? Select single object
|
||||
else if( evt->IsClick( BUT_LEFT ) )
|
||||
{
|
||||
// Single click? Select single object
|
||||
if( m_highlight_modifier && brd_editor )
|
||||
m_toolMgr->RunAction( PCB_ACTIONS::highlightNet, true );
|
||||
else
|
||||
|
@ -419,7 +420,8 @@ int PCB_SELECTION_TOOL::Main( const TOOL_EVENT& aEvent )
|
|||
// Yes -> run the move tool and wait till it finishes
|
||||
PCB_TRACK* track = dynamic_cast<PCB_TRACK*>( m_selection.GetItem( 0 ) );
|
||||
|
||||
// If there is only item in the selection and it's a track, then we need to route it
|
||||
// If there is only item in the selection and it's a track, then we need
|
||||
// to route it.
|
||||
bool doRouting = ( track && ( 1 == m_selection.GetSize() ) );
|
||||
|
||||
if( doRouting && trackDragAction == TRACK_DRAG_ACTION::DRAG )
|
||||
|
@ -802,9 +804,9 @@ bool PCB_SELECTION_TOOL::selectMultiple()
|
|||
int width = area.GetEnd().x - area.GetOrigin().x;
|
||||
|
||||
/* Selection mode depends on direction of drag-selection:
|
||||
* Left > Right : Select objects that are fully enclosed by selection
|
||||
* Right > Left : Select objects that are crossed by selection
|
||||
*/
|
||||
* Left > Right : Select objects that are fully enclosed by selection
|
||||
* Right > Left : Select objects that are crossed by selection
|
||||
*/
|
||||
bool windowSelection = width >= 0 ? true : false;
|
||||
|
||||
if( view->IsMirroredX() )
|
||||
|
@ -1230,7 +1232,8 @@ void PCB_SELECTION_TOOL::selectConnectedTracks( BOARD_CONNECTED_ITEM& aStartItem
|
|||
expand = true;
|
||||
}
|
||||
|
||||
if( viaMap.count( pt ) && !viaMap[ pt ]->IsSelected() && aStopCondition != STOP_AT_JUNCTION )
|
||||
if( viaMap.count( pt ) && !viaMap[ pt ]->IsSelected()
|
||||
&& aStopCondition != STOP_AT_JUNCTION )
|
||||
select( viaMap[ pt ] );
|
||||
|
||||
activePts.erase( activePts.begin() + i );
|
||||
|
@ -1333,7 +1336,8 @@ void PCB_SELECTION_TOOL::selectAllItemsOnSheet( wxString& aSheetPath )
|
|||
|
||||
for( int netCode : netcodeList )
|
||||
{
|
||||
for( BOARD_CONNECTED_ITEM* mitem : board()->GetConnectivity()->GetNetItems( netCode, padType ) )
|
||||
for( BOARD_CONNECTED_ITEM* mitem : board()->GetConnectivity()->GetNetItems( netCode,
|
||||
padType ) )
|
||||
{
|
||||
if( mitem->Type() == PCB_PAD_T && !alg::contains( footprintList, mitem->GetParent() ) )
|
||||
{
|
||||
|
@ -1360,7 +1364,8 @@ void PCB_SELECTION_TOOL::selectAllItemsOnSheet( wxString& aSheetPath )
|
|||
|
||||
for( int netCode : netcodeList )
|
||||
{
|
||||
for( BOARD_CONNECTED_ITEM* item : board()->GetConnectivity()->GetNetItems( netCode, trackViaType ) )
|
||||
for( BOARD_CONNECTED_ITEM* item : board()->GetConnectivity()->GetNetItems( netCode,
|
||||
trackViaType ) )
|
||||
localConnectionList.push_back( item );
|
||||
}
|
||||
|
||||
|
@ -1496,9 +1501,7 @@ int PCB_SELECTION_TOOL::find( const TOOL_EVENT& aEvent )
|
|||
|
||||
|
||||
/**
|
||||
* Function itemIsIncludedByFilter()
|
||||
*
|
||||
* Determine if an item is included by the filter specified
|
||||
* Determine if an item is included by the filter specified.
|
||||
*
|
||||
* @return true if aItem should be selected by this filter (i..e not filtered out)
|
||||
*/
|
||||
|
@ -1942,9 +1945,15 @@ bool PCB_SELECTION_TOOL::Selectable( const BOARD_ITEM* aItem, bool checkVisibili
|
|||
return false;
|
||||
|
||||
// Allow selection of footprints if some part of the footprint is visible.
|
||||
|
||||
const FOOTPRINT* footprint = static_cast<const FOOTPRINT*>( aItem );
|
||||
|
||||
// If the footprint has no items except the reference and value fields, include the
|
||||
// footprint in the selections.
|
||||
if( footprint->GraphicalItems().empty()
|
||||
&& footprint->Pads().empty()
|
||||
&& footprint->Zones().empty() )
|
||||
return true;
|
||||
|
||||
for( const BOARD_ITEM* item : footprint->GraphicalItems() )
|
||||
{
|
||||
if( Selectable( item, true ) )
|
||||
|
@ -2391,7 +2400,6 @@ void PCB_SELECTION_TOOL::GuessSelectionCandidates( GENERAL_COLLECTOR& aCollector
|
|||
}
|
||||
|
||||
// Prefer exact hits to sloppy ones
|
||||
|
||||
constexpr int MAX_SLOP = 5;
|
||||
|
||||
int pixel = (int) aCollector.GetGuide()->OnePixelInIU();
|
||||
|
@ -2411,7 +2419,6 @@ void PCB_SELECTION_TOOL::GuessSelectionCandidates( GENERAL_COLLECTOR& aCollector
|
|||
}
|
||||
|
||||
// Prune sloppier items
|
||||
|
||||
if( minSlop < INT_MAX )
|
||||
{
|
||||
for( std::pair<BOARD_ITEM*, int> pair : itemsBySloppiness )
|
||||
|
@ -2423,7 +2430,6 @@ void PCB_SELECTION_TOOL::GuessSelectionCandidates( GENERAL_COLLECTOR& aCollector
|
|||
|
||||
// If the user clicked on a small item within a much larger one then it's pretty clear
|
||||
// they're trying to select the smaller one.
|
||||
|
||||
constexpr double sizeRatio = 1.5;
|
||||
|
||||
std::vector<std::pair<BOARD_ITEM*, double>> itemsByArea;
|
||||
|
@ -2480,7 +2486,6 @@ void PCB_SELECTION_TOOL::GuessSelectionCandidates( GENERAL_COLLECTOR& aCollector
|
|||
|
||||
// Special case: if a footprint is completely covered with other features then there's no
|
||||
// way to select it -- so we need to leave it in the list for user disambiguation.
|
||||
|
||||
constexpr double maxCoverRatio = 0.70;
|
||||
|
||||
for( int i = 0; i < aCollector.GetCount(); ++i )
|
||||
|
@ -2493,7 +2498,6 @@ void PCB_SELECTION_TOOL::GuessSelectionCandidates( GENERAL_COLLECTOR& aCollector
|
|||
}
|
||||
|
||||
// Hopefully we've now got what the user wanted.
|
||||
|
||||
if( (unsigned) aCollector.GetCount() > rejected.size() ) // do not remove everything
|
||||
{
|
||||
for( BOARD_ITEM* item : rejected )
|
||||
|
@ -2531,7 +2535,6 @@ void PCB_SELECTION_TOOL::FilterCollectorForHierarchy( GENERAL_COLLECTOR& aCollec
|
|||
|
||||
// Set TEMP_SELECTED on all parents which are included in the GENERAL_COLLECTOR. This
|
||||
// algorithm is O3n, whereas checking for the parent inclusion could potentially be On^2.
|
||||
|
||||
for( int j = 0; j < aCollector.GetCount(); j++ )
|
||||
{
|
||||
if( aCollector[j]->GetParent() )
|
||||
|
|
Loading…
Reference in New Issue