diff --git a/include/board_item.h b/include/board_item.h index 43546b8f84..99711ce449 100644 --- a/include/board_item.h +++ b/include/board_item.h @@ -259,7 +259,7 @@ public: * @param aLayer The layer to test for. * @return true if on given layer, else false. */ - virtual bool IsOnLayer( PCB_LAYER_ID aLayer, bool aIncludeCourtyards = false ) const + virtual bool IsOnLayer( PCB_LAYER_ID aLayer ) const { return m_layer == aLayer; } diff --git a/include/pcb_group.h b/include/pcb_group.h index 59aeea3beb..eda53898ba 100644 --- a/include/pcb_group.h +++ b/include/pcb_group.h @@ -152,7 +152,7 @@ public: PCB_GROUP* DeepDuplicate() const; /// @copydoc BOARD_ITEM::IsOnLayer - bool IsOnLayer( PCB_LAYER_ID aLayer, bool aIncludeCourtyards = false ) const override; + bool IsOnLayer( PCB_LAYER_ID aLayer ) const override; /// @copydoc EDA_ITEM::HitTest bool HitTest( const VECTOR2I& aPosition, int aAccuracy = 0 ) const override; diff --git a/pcbnew/dialogs/dialog_graphic_item_properties.cpp b/pcbnew/dialogs/dialog_graphic_item_properties.cpp index 6eb3d30294..a86c75b4da 100644 --- a/pcbnew/dialogs/dialog_graphic_item_properties.cpp +++ b/pcbnew/dialogs/dialog_graphic_item_properties.cpp @@ -146,7 +146,7 @@ void PCB_BASE_EDIT_FRAME::ShowGraphicItemPropertiesDialog( PCB_SHAPE* aShape ) if( dlg.ShowQuasiModal() == wxID_OK ) { - if( aShape->IsOnLayer( GetActiveLayer(), true ) ) + if( aShape->IsOnLayer( GetActiveLayer() ) ) { DRAWING_TOOL* drawingTool = m_toolManager->GetTool(); drawingTool->SetStroke( aShape->GetStroke(), GetActiveLayer() ); diff --git a/pcbnew/footprint.cpp b/pcbnew/footprint.cpp index 93e69145a3..99a9f3a9d0 100644 --- a/pcbnew/footprint.cpp +++ b/pcbnew/footprint.cpp @@ -1255,37 +1255,102 @@ void FOOTPRINT::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vectorIsOnLayer( aLayer ) ) + return true; + } - // No pads? Check if this entire footprint exists on the given layer for( ZONE* zone : m_zones ) { - if( !zone->IsOnLayer( aLayer ) ) - return false; + if( zone->IsOnLayer( aLayer ) ) + return true; } for( PCB_FIELD* field : m_fields ) { - if( !field->IsOnLayer( aLayer ) ) - return false; + if( field->IsOnLayer( aLayer ) ) + return true; } for( BOARD_ITEM* item : m_drawings ) { - if( !item->IsOnLayer( aLayer ) ) + if( item->IsOnLayer( aLayer ) ) + return true; + } + + return false; +} + + +bool FOOTPRINT::HitTestOnLayer( const VECTOR2I& aPosition, PCB_LAYER_ID aLayer, int aAccuracy ) const +{ + for( PAD* pad : m_pads ) + { + if( pad->IsOnLayer( aLayer ) && pad->HitTest( aPosition, aAccuracy ) ) + return true; + } + + for( ZONE* zone : m_zones ) + { + if( zone->IsOnLayer( aLayer ) && zone->HitTest( aPosition, aAccuracy ) ) + return true; + } + + for( BOARD_ITEM* item : m_drawings ) + { + if( item->Type() != PCB_TEXT_T && item->IsOnLayer( aLayer ) + && item->HitTest( aPosition, aAccuracy ) ) + { + return true; + } + } + + return false; +} + + +bool FOOTPRINT::HitTestOnLayer( const BOX2I& aRect, bool aContained, PCB_LAYER_ID aLayer, int aAccuracy ) const +{ + std::vector items; + + for( PAD* pad : m_pads ) + { + if( pad->IsOnLayer( aLayer ) ) + items.push_back( pad ); + } + + for( ZONE* zone : m_zones ) + { + if( zone->IsOnLayer( aLayer ) ) + items.push_back( zone ); + } + + for( BOARD_ITEM* item : m_drawings ) + { + if( item->Type() != PCB_TEXT_T && item->IsOnLayer( aLayer ) ) + items.push_back( item ); + } + + // If we require the elements to be contained in the rect and any of them are not, + // we can return false; + // Conversely, if we just require any of the elements to have a hit, we can return true + // when the first one is found. + for( BOARD_ITEM* item : items ) + { + if( !aContained && item->HitTest( aRect, aContained, aAccuracy ) ) + return true; + else if( aContained && !item->HitTest( aRect, aContained, aAccuracy ) ) return false; } - return true; + // If we didn't exit in the loop, that means that we did not return false for aContained or + // we did not return true for !aContained. So we can just return the bool with a test of + // whether there were any elements or not. + return !items.empty() && aContained; } diff --git a/pcbnew/footprint.h b/pcbnew/footprint.h index 4e8870052c..c5f3a9335d 100644 --- a/pcbnew/footprint.h +++ b/pcbnew/footprint.h @@ -343,11 +343,9 @@ public: bool IsFlipped() const { return GetLayer() == B_Cu; } /** - * A special IsOnLayer for footprints: return true if the footprint contains only items on the - * given layer, even if that layer is not one of the valid footprint layers F_Cu and B_Cu. - * This allows selection of "graphic" footprints that contain only silkscreen, for example. + * @copydoc BOARD_ITEM::IsOnLayer */ - bool IsOnLayer( PCB_LAYER_ID aLayer, bool aIncludeCourtyards = false ) const override; + bool IsOnLayer( PCB_LAYER_ID aLayer ) const override; // m_footprintStatus bits: #define FP_is_LOCKED 0x01 ///< footprint LOCKED: no autoplace allowed @@ -532,6 +530,18 @@ public: bool HitTest( const BOX2I& aRect, bool aContained, int aAccuracy = 0 ) const override; + /** + * Test if the point hits one or more of the footprint elements on a given layer. + * + * @param aPosition is the point to test + * @param aAccuracy is the hit test accuracy + * @param aLayer is the layer to test + * @return true if aPosition hits a footprint element on aLayer + */ + bool HitTestOnLayer( const VECTOR2I& aPosition, PCB_LAYER_ID aLayer, int aAccuracy = 0 ) const; + + bool HitTestOnLayer( const BOX2I& aRect, bool aContained, PCB_LAYER_ID aLayer, int aAccuracy = 0 ) const; + /** * @return reference designator text. */ diff --git a/pcbnew/pad.h b/pcbnew/pad.h index 7d8c76f59b..f528c8ad9c 100644 --- a/pcbnew/pad.h +++ b/pcbnew/pad.h @@ -618,7 +618,7 @@ public: void GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector& aList ) override; - bool IsOnLayer( PCB_LAYER_ID aLayer, bool aIncludeCourtyards = false ) const override + bool IsOnLayer( PCB_LAYER_ID aLayer ) const override { return m_layerMask[aLayer]; } diff --git a/pcbnew/pcb_edit_frame.cpp b/pcbnew/pcb_edit_frame.cpp index c4ad52ba5d..1fb59532cf 100644 --- a/pcbnew/pcb_edit_frame.cpp +++ b/pcbnew/pcb_edit_frame.cpp @@ -1390,7 +1390,7 @@ void PCB_EDIT_FRAME::SetActiveLayer( PCB_LAYER_ID aLayer ) // as they won't be found in the view layer's itemset for re-painting. if( GetDisplayOptions().m_ContrastModeDisplay == HIGH_CONTRAST_MODE::HIDDEN ) { - if( item->IsOnLayer( oldLayer, true ) || item->IsOnLayer( aLayer, true ) ) + if( item->IsOnLayer( oldLayer ) || item->IsOnLayer( aLayer ) ) return KIGFX::ALL; } diff --git a/pcbnew/pcb_expr_functions.cpp b/pcbnew/pcb_expr_functions.cpp index 2692ff6be7..6db1f9b203 100644 --- a/pcbnew/pcb_expr_functions.cpp +++ b/pcbnew/pcb_expr_functions.cpp @@ -117,7 +117,7 @@ static void existsOnLayerFunc( LIBEVAL::CONTEXT* aCtx, void *self ) { anyMatch = true; - if( item->IsOnLayer( ToLAYER_ID( entry.GetValue() ), true ) ) + if( item->IsOnLayer( ToLAYER_ID( entry.GetValue() ) ) ) return 1.0; } } diff --git a/pcbnew/pcb_group.cpp b/pcbnew/pcb_group.cpp index 5b7828f19d..e26e3ad61f 100644 --- a/pcbnew/pcb_group.cpp +++ b/pcbnew/pcb_group.cpp @@ -321,12 +321,12 @@ LSET PCB_GROUP::GetLayerSet() const } -bool PCB_GROUP::IsOnLayer( PCB_LAYER_ID aLayer, bool aIncludeCourtyards ) const +bool PCB_GROUP::IsOnLayer( PCB_LAYER_ID aLayer ) const { // A group is on a layer if any item is on the layer for( BOARD_ITEM* item : m_items ) { - if( item->IsOnLayer( aLayer, aIncludeCourtyards ) ) + if( item->IsOnLayer( aLayer ) ) return true; } diff --git a/pcbnew/pcb_track.cpp b/pcbnew/pcb_track.cpp index c88a042503..ffc1eda0bf 100644 --- a/pcbnew/pcb_track.cpp +++ b/pcbnew/pcb_track.cpp @@ -476,7 +476,7 @@ int PCB_VIA::GetSolderMaskExpansion() const } -bool PCB_VIA::IsOnLayer( PCB_LAYER_ID aLayer, bool aIncludeCourtyards ) const +bool PCB_VIA::IsOnLayer( PCB_LAYER_ID aLayer ) const { #if 0 // Nice and simple, but raises its ugly head in performance profiles.... diff --git a/pcbnew/pcb_track.h b/pcbnew/pcb_track.h index 47c7a808f9..b74d0be008 100644 --- a/pcbnew/pcb_track.h +++ b/pcbnew/pcb_track.h @@ -404,7 +404,7 @@ public: bool IsTented() const override; int GetSolderMaskExpansion() const; - bool IsOnLayer( PCB_LAYER_ID aLayer, bool aIncludeCourtyards = false ) const override; + bool IsOnLayer( PCB_LAYER_ID aLayer ) const override; virtual LSET GetLayerSet() const override; diff --git a/pcbnew/tools/pcb_selection_tool.cpp b/pcbnew/tools/pcb_selection_tool.cpp index d67cdb5c31..31902eb873 100644 --- a/pcbnew/tools/pcb_selection_tool.cpp +++ b/pcbnew/tools/pcb_selection_tool.cpp @@ -739,6 +739,8 @@ bool PCB_SELECTION_TOOL::selectPoint( const VECTOR2I& aWhere, bool aOnDrag, // Apply the stateful filter FilterCollectedItems( collector, false ); + FilterCollectorForFootprints( collector, aWhere ); + // For subtracting, we only want items that are selected if( m_subtractive ) { @@ -960,6 +962,7 @@ bool PCB_SELECTION_TOOL::selectMultiple() { BOARD_ITEM* item = static_cast( candidate.first ); + if( item && Selectable( item ) && item->HitTest( selectionRect, !greedySelection ) && ( greedySelection || !group_items.count( item ) ) ) { @@ -2359,7 +2362,7 @@ bool PCB_SELECTION_TOOL::Selectable( const BOARD_ITEM* aItem, bool checkVisibili for( int layer : activeLayers ) { // NOTE: Only checking the regular layers (not GAL meta-layers) - if( layer < PCB_LAYER_ID_COUNT && aItem->IsOnLayer( ToLAYER_ID( layer ), true ) ) + if( layer < PCB_LAYER_ID_COUNT && aItem->IsOnLayer( ToLAYER_ID( layer ) ) ) { onActiveLayer = true; break; @@ -2878,11 +2881,9 @@ void PCB_SELECTION_TOOL::GuessSelectionCandidates( GENERAL_COLLECTOR& aCollector for( int i = 0; i < aCollector.GetCount(); ++i ) { BOARD_ITEM* item = aCollector[i]; - KICAD_T type = item->Type(); - if( ( type == PCB_FIELD_T || type == PCB_TEXT_T || type == PCB_TEXTBOX_T - || type == PCB_SHAPE_T ) - && silkLayers[item->GetLayer()] ) + if( item->IsType( { PCB_FIELD_T, PCB_TEXT_T, PCB_TEXTBOX_T, PCB_SHAPE_T, PCB_FOOTPRINT_T } ) + && item->IsOnLayer( activeLayer ) ) { preferred.insert( item ); } @@ -3149,6 +3150,67 @@ void PCB_SELECTION_TOOL::FilterCollectorForMarkers( GENERAL_COLLECTOR& aCollecto } } +void PCB_SELECTION_TOOL::FilterCollectorForFootprints( GENERAL_COLLECTOR& aCollector, const VECTOR2I& aWhere ) const +{ + const RENDER_SETTINGS* settings = getView()->GetPainter()->GetSettings(); + + auto visibleLayers = + [&]() + { + if( m_isFootprintEditor ) + { + LSET set; + + for( PCB_LAYER_ID layer : LSET::AllLayersMask().Seq() ) + set.set( layer, view()->IsLayerVisible( layer ) ); + + return set; + } + else + { + return board()->GetVisibleLayers(); + } + }; + + LSET layers = visibleLayers(); + + if( settings->GetHighContrast() ) + { + layers.reset(); + + const std::set activeLayers = settings->GetHighContrastLayers(); + + for( int layer : activeLayers ) + { + if( layer >= 0 && layer < PCB_LAYER_ID_COUNT ) + layers.set( layer ); + } + } + + // Iterate from the back so we don't have to worry about removals. + for( int i = aCollector.GetCount() - 1; i >= 0; --i ) + { + bool has_hit = false; + BOARD_ITEM* item = aCollector[i]; + FOOTPRINT* fp = dyn_cast( item ); + + if( !fp ) + continue; + + for( PCB_LAYER_ID layer : layers.Seq() ) + { + if( fp->HitTestOnLayer( aWhere, layer ) ) + { + has_hit = true; + break; + } + } + + if( !has_hit ) + aCollector.Remove( item ); + } +} + int PCB_SELECTION_TOOL::updateSelection( const TOOL_EVENT& aEvent ) { diff --git a/pcbnew/tools/pcb_selection_tool.h b/pcbnew/tools/pcb_selection_tool.h index a8a690d251..d50d91f961 100644 --- a/pcbnew/tools/pcb_selection_tool.h +++ b/pcbnew/tools/pcb_selection_tool.h @@ -211,6 +211,11 @@ public: */ void FilterCollectedItems( GENERAL_COLLECTOR& aCollector, bool aMultiSelect ); + /** + * Drop footprints that are not directly selected + */ + void FilterCollectorForFootprints( GENERAL_COLLECTOR& aCollector, const VECTOR2I& aWhere ) const; + protected: KIGFX::PCB_VIEW* view() const { diff --git a/pcbnew/zone.cpp b/pcbnew/zone.cpp index c58c55120c..7a572c8247 100644 --- a/pcbnew/zone.cpp +++ b/pcbnew/zone.cpp @@ -342,7 +342,7 @@ double ZONE::ViewGetLOD( int aLayer, KIGFX::VIEW* aView ) const } -bool ZONE::IsOnLayer( PCB_LAYER_ID aLayer, bool aIncludeCourtyards ) const +bool ZONE::IsOnLayer( PCB_LAYER_ID aLayer ) const { return m_layerSet.test( aLayer ); } diff --git a/pcbnew/zone.h b/pcbnew/zone.h index 6f9a5618ed..7d1c157d4c 100644 --- a/pcbnew/zone.h +++ b/pcbnew/zone.h @@ -171,7 +171,7 @@ public: // Return the first layer in GUI sequence. PCB_LAYER_ID GetFirstLayer() const; - virtual bool IsOnLayer( PCB_LAYER_ID, bool aIncludeCourtyards = false ) const override; + virtual bool IsOnLayer( PCB_LAYER_ID ) const override; virtual void ViewGetLayers( int aLayers[], int& aCount ) const override;