diff --git a/pcbnew/footprint.cpp b/pcbnew/footprint.cpp index a94f06e61b..887b55b663 100644 --- a/pcbnew/footprint.cpp +++ b/pcbnew/footprint.cpp @@ -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( _HKI( "Solderpaste Margin Override" ), &FOOTPRINT::SetLocalSolderPasteMargin, &FOOTPRINT::GetLocalSolderPasteMargin, PROPERTY_DISPLAY::DISTANCE ) ); - propMgr.AddProperty( new PROPERTY( _HKI( "Solderpaste Margin Ratio Override" ), - &FOOTPRINT::SetLocalSolderPasteMarginRatio, &FOOTPRINT::GetLocalSolderPasteMarginRatio ) ); + propMgr.AddProperty( new PROPERTY( _HKI( "Solderpaste Margin Ratio Override" ), + &FOOTPRINT::SetLocalSolderPasteMarginRatio, + &FOOTPRINT::GetLocalSolderPasteMarginRatio ) ); propMgr.AddProperty( new PROPERTY( _HKI( "Thermal Relief Width" ), - &FOOTPRINT::SetThermalWidth, &FOOTPRINT::GetThermalWidth, - PROPERTY_DISPLAY::DISTANCE ) ); + &FOOTPRINT::SetThermalWidth, + &FOOTPRINT::GetThermalWidth, + PROPERTY_DISPLAY::DISTANCE ) ); propMgr.AddProperty( new PROPERTY( _HKI( "Thermal Relief Gap" ), - &FOOTPRINT::SetThermalGap, &FOOTPRINT::GetThermalGap, - PROPERTY_DISPLAY::DISTANCE ) ); + &FOOTPRINT::SetThermalGap, + &FOOTPRINT::GetThermalGap, + PROPERTY_DISPLAY::DISTANCE ) ); // TODO zone connection, FPID? } } _FOOTPRINT_DESC; diff --git a/pcbnew/pcb_painter.cpp b/pcbnew/pcb_painter.cpp index 222150d930..26fcfbc778 100644 --- a/pcbnew/pcb_painter.cpp +++ b/pcbnew/pcb_painter.cpp @@ -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 * @author Maciej Suminski * @@ -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( 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 ); diff --git a/pcbnew/tools/pcb_selection_tool.cpp b/pcbnew/tools/pcb_selection_tool.cpp index e2c1da6306..ffefa820de 100644 --- a/pcbnew/tools/pcb_selection_tool.cpp +++ b/pcbnew/tools/pcb_selection_tool.cpp @@ -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 * @author Maciej Suminski - * 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( 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( 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 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> 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() )